package command import ( "errors" "fmt" "strconv" "strings" "github.com/memohai/memoh/internal/settings" ) func (h *Handler) buildSettingsGroup() *CommandGroup { g := newCommandGroup("settings", "View and update bot settings") g.DefaultAction = "get" g.Register(SubCommand{ Name: "get", Usage: "get - View current settings", Handler: func(cc CommandContext) (string, error) { s, err := h.settingsService.GetBot(cc.Ctx, cc.BotID) if err != nil { return "", err } return formatKV([]kv{ {"Language", s.Language}, {"ACL Default Effect", s.AclDefaultEffect}, {"Reasoning Enabled", boolStr(s.ReasoningEnabled)}, {"Reasoning Effort", s.ReasoningEffort}, {"Heartbeat Enabled", boolStr(s.HeartbeatEnabled)}, {"Heartbeat Interval", fmt.Sprintf("%d min", s.HeartbeatInterval)}, {"Chat Model", h.resolveModelName(cc, s.ChatModelID)}, {"Heartbeat Model", h.resolveModelName(cc, s.HeartbeatModelID)}, {"Search Provider", h.resolveSearchProviderName(cc, s.SearchProviderID)}, {"Memory Provider", h.resolveMemoryProviderName(cc, s.MemoryProviderID)}, {"Browser Context", h.resolveBrowserContextName(cc, s.BrowserContextID)}, }), nil }, }) g.Register(SubCommand{ Name: "update", Usage: "update [--language L] [--acl_default_effect allow|deny] ... - Update settings", IsWrite: true, Handler: func(cc CommandContext) (string, error) { if len(cc.Args) == 0 { return settingsUpdateUsage(), nil } req := settings.UpsertRequest{} args := cc.Args for i := 0; i < len(args); i++ { if i+1 >= len(args) { return fmt.Sprintf("Missing value for %s.\n\n%s", args[i], settingsUpdateUsage()), nil } switch args[i] { case "--language": i++ req.Language = args[i] case "--acl_default_effect": i++ req.AclDefaultEffect = args[i] case "--reasoning_enabled": i++ v := strings.ToLower(args[i]) == "true" req.ReasoningEnabled = &v case "--reasoning_effort": i++ req.ReasoningEffort = &args[i] case "--heartbeat_enabled": i++ v := strings.ToLower(args[i]) == "true" req.HeartbeatEnabled = &v case "--heartbeat_interval": i++ val, err := strconv.Atoi(args[i]) if err != nil { return fmt.Sprintf("Invalid heartbeat_interval: %s", args[i]), nil } req.HeartbeatInterval = &val case "--chat_model_id": i++ req.ChatModelID = args[i] case "--heartbeat_model_id": i++ req.HeartbeatModelID = args[i] default: return fmt.Sprintf("Unknown option: %s\n\n%s", args[i], settingsUpdateUsage()), nil } } _, err := h.settingsService.UpsertBot(cc.Ctx, cc.BotID, req) if err != nil { return "", err } return "Settings updated.", nil }, }) return g } func settingsUpdateUsage() string { return "Usage: /settings update [options]\n\n" + "Options:\n" + "- --language \n" + "- --acl_default_effect \n" + "- --reasoning_enabled \n" + "- --reasoning_effort \n" + "- --heartbeat_enabled \n" + "- --heartbeat_interval \n" + "- --chat_model_id \n" + "- --heartbeat_model_id " } func (h *Handler) getBotSettings(cc CommandContext) (settings.Settings, error) { if h.settingsService == nil { return settings.Settings{}, errors.New("settings service is not available") } return h.settingsService.GetBot(cc.Ctx, cc.BotID) } // resolveModelName resolves a model UUID to "model_name (provider_name)". func (h *Handler) resolveModelName(cc CommandContext, modelID string) string { if modelID == "" { return "(none)" } if h.modelsService == nil { return modelID } m, err := h.modelsService.GetByID(cc.Ctx, modelID) if err != nil { return modelID } provName := "" if h.providersService != nil { p, err := h.providersService.Get(cc.Ctx, m.ProviderID) if err == nil { provName = p.Name } } if provName != "" { return fmt.Sprintf("%s (%s)", m.Name, provName) } return m.Name } // resolveSearchProviderName resolves a search provider UUID to its name. func (h *Handler) resolveSearchProviderName(cc CommandContext, id string) string { if id == "" { return "(none)" } if h.searchProvService == nil { return id } p, err := h.searchProvService.Get(cc.Ctx, id) if err != nil { return id } return p.Name } // resolveMemoryProviderName resolves a memory provider UUID to its name. func (h *Handler) resolveMemoryProviderName(cc CommandContext, id string) string { if id == "" { return "(none)" } if h.memProvService == nil { return id } p, err := h.memProvService.Get(cc.Ctx, id) if err != nil { return id } return p.Name } // resolveBrowserContextName resolves a browser context UUID to its name. func (h *Handler) resolveBrowserContextName(cc CommandContext, id string) string { if id == "" { return "(none)" } if h.browserCtxService == nil { return id } p, err := h.browserCtxService.GetByID(cc.Ctx, id) if err != nil { return id } return p.Name }