package session import ( "fmt" "net/http" "sort" "strings" "agentu/internal/agent" "agentu/internal/config" "agentu/internal/llm" ) type Manager struct { cfg *config.Config agent *agent.Agent httpClient *http.Client providerName string provider config.ProviderConfig } func NewManager(cfg *config.Config, assistant *agent.Agent, httpClient *http.Client) (*Manager, error) { if httpClient == nil { httpClient = http.DefaultClient } providerName := cfg.DefaultProviderName() provider, ok := cfg.Providers[providerName] if !ok { return nil, fmt.Errorf("no provider is configured") } return &Manager{ cfg: cfg, agent: assistant, httpClient: httpClient, providerName: providerName, provider: provider, }, nil } func (m *Manager) CurrentProvider() string { return m.providerName } func (m *Manager) CurrentModel() string { return m.provider.Model } func (m *Manager) CurrentThinking() string { if !m.provider.ThinkingConfigured { return "unset" } return m.provider.Thinking } func (m *Manager) Info() string { var b strings.Builder fmt.Fprintf(&b, "provider: %s\n", m.CurrentProvider()) fmt.Fprintf(&b, "model: %s\n", m.CurrentModel()) fmt.Fprintf(&b, "thinking: %s\n", m.CurrentThinking()) fmt.Fprintf(&b, "providers: %s\n", strings.Join(m.providerNames(), ", ")) if len(m.provider.Models) > 0 { fmt.Fprintf(&b, "models: %s\n", strings.Join(m.provider.Models, ", ")) } else { b.WriteString("models: any model name is accepted for this provider\n") } fmt.Fprintf(&b, "thinking levels: %s\n", strings.Join(config.ThinkingLevels(), ", ")) b.WriteString("usage: /model provider | /model model | /model thinking ") return b.String() } func (m *Manager) SetProvider(name string) (string, error) { provider, ok := m.cfg.Providers[name] if !ok { return "", fmt.Errorf("unknown provider %q; available: %s", name, strings.Join(m.providerNames(), ", ")) } m.providerName = name m.provider = provider m.syncAgent(llm.NewOpenAICompatibleClient(provider.BaseURL, provider.APIKey, m.httpClient)) return "Switched provider.\n" + m.summary(), nil } func (m *Manager) SetModel(model string) (string, error) { model = strings.TrimSpace(model) if model == "" { return "", fmt.Errorf("model is required") } if len(m.provider.Models) > 0 && !contains(m.provider.Models, model) { return "", fmt.Errorf("model %q is not configured for provider %q; available: %s", model, m.providerName, strings.Join(m.provider.Models, ", ")) } m.provider.Model = model m.cfg.Providers[m.providerName] = m.provider m.syncAgent(nil) return "Switched model.\n" + m.summary(), nil } func (m *Manager) SetThinking(level string) (string, error) { level = strings.ToLower(strings.TrimSpace(level)) if !config.IsThinkingLevel(level) { return "", fmt.Errorf("thinking must be one of: %s", strings.Join(config.ThinkingLevels(), ", ")) } m.provider.Thinking = level m.provider.ThinkingConfigured = true if m.provider.ThinkingParam == "" { m.provider.ThinkingParam = "thinking" } m.cfg.Providers[m.providerName] = m.provider m.syncAgent(nil) return "Updated thinking.\n" + m.summary(), nil } func (m *Manager) summary() string { return fmt.Sprintf("provider: %s\nmodel: %s\nthinking: %s", m.CurrentProvider(), m.CurrentModel(), m.CurrentThinking()) } func (m *Manager) syncAgent(provider llm.Provider) { m.agent.SetRuntime(agent.RuntimeOptions{ Provider: provider, ProviderName: m.providerName, Model: m.provider.Model, Thinking: m.provider.Thinking, ThinkingParam: m.provider.ThinkingParam, ThinkingEnabled: m.provider.ThinkingConfigured, }) } func (m *Manager) providerNames() []string { names := make([]string, 0, len(m.cfg.Providers)) for name := range m.cfg.Providers { names = append(names, name) } sort.Strings(names) return names } func contains(values []string, value string) bool { for _, item := range values { if item == value { return true } } return false }