mirror of
https://github.com/aljazceru/opencode.git
synced 2025-12-26 04:04:22 +01:00
wip: refactoring tui
This commit is contained in:
@@ -11,62 +11,13 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/viper"
|
||||
"github.com/sst/opencode/internal/llm/models"
|
||||
)
|
||||
|
||||
// MCPType defines the type of MCP (Model Control Protocol) server.
|
||||
type MCPType string
|
||||
|
||||
// Supported MCP types
|
||||
const (
|
||||
MCPStdio MCPType = "stdio"
|
||||
MCPSse MCPType = "sse"
|
||||
)
|
||||
|
||||
// MCPServer defines the configuration for a Model Control Protocol server.
|
||||
type MCPServer struct {
|
||||
Command string `json:"command"`
|
||||
Env []string `json:"env"`
|
||||
Args []string `json:"args"`
|
||||
Type MCPType `json:"type"`
|
||||
URL string `json:"url"`
|
||||
Headers map[string]string `json:"headers"`
|
||||
}
|
||||
|
||||
type AgentName string
|
||||
|
||||
const (
|
||||
AgentPrimary AgentName = "primary"
|
||||
AgentTask AgentName = "task"
|
||||
AgentTitle AgentName = "title"
|
||||
)
|
||||
|
||||
// Agent defines configuration for different LLM models and their token limits.
|
||||
type Agent struct {
|
||||
Model models.ModelID `json:"model"`
|
||||
MaxTokens int64 `json:"maxTokens"`
|
||||
ReasoningEffort string `json:"reasoningEffort"` // For openai models low,medium,heigh
|
||||
}
|
||||
|
||||
// Provider defines configuration for an LLM provider.
|
||||
type Provider struct {
|
||||
APIKey string `json:"apiKey"`
|
||||
Disabled bool `json:"disabled"`
|
||||
}
|
||||
|
||||
// Data defines storage configuration.
|
||||
type Data struct {
|
||||
Directory string `json:"directory,omitempty"`
|
||||
}
|
||||
|
||||
// LSPConfig defines configuration for Language Server Protocol integration.
|
||||
type LSPConfig struct {
|
||||
Disabled bool `json:"enabled"`
|
||||
Command string `json:"command"`
|
||||
Args []string `json:"args"`
|
||||
Options any `json:"options"`
|
||||
}
|
||||
|
||||
// TUIConfig defines the configuration for the Terminal User Interface.
|
||||
type TUIConfig struct {
|
||||
Theme string `json:"theme,omitempty"`
|
||||
@@ -81,17 +32,13 @@ type ShellConfig struct {
|
||||
|
||||
// Config is the main configuration structure for the application.
|
||||
type Config struct {
|
||||
Data Data `json:"data"`
|
||||
WorkingDir string `json:"wd,omitempty"`
|
||||
MCPServers map[string]MCPServer `json:"mcpServers,omitempty"`
|
||||
Providers map[models.ModelProvider]Provider `json:"providers,omitempty"`
|
||||
LSP map[string]LSPConfig `json:"lsp,omitempty"`
|
||||
Agents map[AgentName]Agent `json:"agents,omitempty"`
|
||||
Debug bool `json:"debug,omitempty"`
|
||||
DebugLSP bool `json:"debugLSP,omitempty"`
|
||||
ContextPaths []string `json:"contextPaths,omitempty"`
|
||||
TUI TUIConfig `json:"tui"`
|
||||
Shell ShellConfig `json:"shell,omitempty"`
|
||||
Data Data `json:"data"`
|
||||
WorkingDir string `json:"wd,omitempty"`
|
||||
Debug bool `json:"debug,omitempty"`
|
||||
DebugLSP bool `json:"debugLSP,omitempty"`
|
||||
ContextPaths []string `json:"contextPaths,omitempty"`
|
||||
TUI TUIConfig `json:"tui"`
|
||||
Shell ShellConfig `json:"shell,omitempty"`
|
||||
}
|
||||
|
||||
// Application constants
|
||||
@@ -132,9 +79,6 @@ func Load(workingDir string, debug bool) (*Config, error) {
|
||||
|
||||
cfg = &Config{
|
||||
WorkingDir: workingDir,
|
||||
MCPServers: make(map[string]MCPServer),
|
||||
Providers: make(map[models.ModelProvider]Provider),
|
||||
LSP: make(map[string]LSPConfig),
|
||||
}
|
||||
|
||||
configureViper()
|
||||
@@ -148,15 +92,11 @@ func Load(workingDir string, debug bool) (*Config, error) {
|
||||
// Load and merge local config
|
||||
mergeLocalConfig(workingDir)
|
||||
|
||||
setProviderDefaults()
|
||||
|
||||
// Apply configuration to the struct
|
||||
if err := viper.Unmarshal(cfg); err != nil {
|
||||
return cfg, fmt.Errorf("failed to unmarshal config: %w", err)
|
||||
}
|
||||
|
||||
applyDefaultValues()
|
||||
|
||||
defaultLevel := slog.LevelInfo
|
||||
if cfg.Debug {
|
||||
defaultLevel = slog.LevelDebug
|
||||
@@ -167,16 +107,6 @@ func Load(workingDir string, debug bool) (*Config, error) {
|
||||
if err := Validate(); err != nil {
|
||||
return cfg, fmt.Errorf("config validation failed: %w", err)
|
||||
}
|
||||
|
||||
if cfg.Agents == nil {
|
||||
cfg.Agents = make(map[AgentName]Agent)
|
||||
}
|
||||
|
||||
// Override the max tokens for title agent
|
||||
cfg.Agents[AgentTitle] = Agent{
|
||||
Model: cfg.Agents[AgentTitle].Model,
|
||||
MaxTokens: 80,
|
||||
}
|
||||
return cfg, nil
|
||||
}
|
||||
|
||||
@@ -206,156 +136,6 @@ func setDefaults(debug bool) {
|
||||
}
|
||||
}
|
||||
|
||||
// setProviderDefaults configures LLM provider defaults based on provider provided by
|
||||
// environment variables and configuration file.
|
||||
func setProviderDefaults() {
|
||||
// Set all API keys we can find in the environment
|
||||
if apiKey := os.Getenv("ANTHROPIC_API_KEY"); apiKey != "" {
|
||||
viper.SetDefault("providers.anthropic.apiKey", apiKey)
|
||||
}
|
||||
if apiKey := os.Getenv("OPENAI_API_KEY"); apiKey != "" {
|
||||
viper.SetDefault("providers.openai.apiKey", apiKey)
|
||||
}
|
||||
if apiKey := os.Getenv("GEMINI_API_KEY"); apiKey != "" {
|
||||
viper.SetDefault("providers.gemini.apiKey", apiKey)
|
||||
}
|
||||
if apiKey := os.Getenv("GROQ_API_KEY"); apiKey != "" {
|
||||
viper.SetDefault("providers.groq.apiKey", apiKey)
|
||||
}
|
||||
if apiKey := os.Getenv("OPENROUTER_API_KEY"); apiKey != "" {
|
||||
viper.SetDefault("providers.openrouter.apiKey", apiKey)
|
||||
}
|
||||
if apiKey := os.Getenv("XAI_API_KEY"); apiKey != "" {
|
||||
viper.SetDefault("providers.xai.apiKey", apiKey)
|
||||
}
|
||||
if apiKey := os.Getenv("AZURE_OPENAI_ENDPOINT"); apiKey != "" {
|
||||
// api-key may be empty when using Entra ID credentials – that's okay
|
||||
viper.SetDefault("providers.azure.apiKey", os.Getenv("AZURE_OPENAI_API_KEY"))
|
||||
}
|
||||
|
||||
// Use this order to set the default models
|
||||
// 1. Anthropic
|
||||
// 2. OpenAI
|
||||
// 3. Google Gemini
|
||||
// 4. Groq
|
||||
// 5. OpenRouter
|
||||
// 6. AWS Bedrock
|
||||
// 7. Azure
|
||||
// 8. Google Cloud VertexAI
|
||||
|
||||
// Anthropic configuration
|
||||
if key := viper.GetString("providers.anthropic.apiKey"); strings.TrimSpace(key) != "" {
|
||||
viper.SetDefault("agents.primary.model", models.Claude4Sonnet)
|
||||
viper.SetDefault("agents.task.model", models.Claude4Sonnet)
|
||||
viper.SetDefault("agents.title.model", models.Claude4Sonnet)
|
||||
return
|
||||
}
|
||||
|
||||
// OpenAI configuration
|
||||
if key := viper.GetString("providers.openai.apiKey"); strings.TrimSpace(key) != "" {
|
||||
viper.SetDefault("agents.primary.model", models.GPT41)
|
||||
viper.SetDefault("agents.task.model", models.GPT41Mini)
|
||||
viper.SetDefault("agents.title.model", models.GPT41Mini)
|
||||
return
|
||||
}
|
||||
|
||||
// Google Gemini configuration
|
||||
if key := viper.GetString("providers.gemini.apiKey"); strings.TrimSpace(key) != "" {
|
||||
viper.SetDefault("agents.primary.model", models.Gemini25)
|
||||
viper.SetDefault("agents.task.model", models.Gemini25Flash)
|
||||
viper.SetDefault("agents.title.model", models.Gemini25Flash)
|
||||
return
|
||||
}
|
||||
|
||||
// Groq configuration
|
||||
if key := viper.GetString("providers.groq.apiKey"); strings.TrimSpace(key) != "" {
|
||||
viper.SetDefault("agents.primary.model", models.QWENQwq)
|
||||
viper.SetDefault("agents.task.model", models.QWENQwq)
|
||||
viper.SetDefault("agents.title.model", models.QWENQwq)
|
||||
return
|
||||
}
|
||||
|
||||
// OpenRouter configuration
|
||||
if key := viper.GetString("providers.openrouter.apiKey"); strings.TrimSpace(key) != "" {
|
||||
viper.SetDefault("agents.primary.model", models.OpenRouterClaude37Sonnet)
|
||||
viper.SetDefault("agents.task.model", models.OpenRouterClaude37Sonnet)
|
||||
viper.SetDefault("agents.title.model", models.OpenRouterClaude35Haiku)
|
||||
return
|
||||
}
|
||||
|
||||
// XAI configuration
|
||||
if key := viper.GetString("providers.xai.apiKey"); strings.TrimSpace(key) != "" {
|
||||
viper.SetDefault("agents.primary.model", models.XAIGrok3Beta)
|
||||
viper.SetDefault("agents.task.model", models.XAIGrok3Beta)
|
||||
viper.SetDefault("agents.title.model", models.XAiGrok3MiniFastBeta)
|
||||
return
|
||||
}
|
||||
|
||||
// AWS Bedrock configuration
|
||||
if hasAWSCredentials() {
|
||||
viper.SetDefault("agents.primary.model", models.BedrockClaude37Sonnet)
|
||||
viper.SetDefault("agents.task.model", models.BedrockClaude37Sonnet)
|
||||
viper.SetDefault("agents.title.model", models.BedrockClaude37Sonnet)
|
||||
return
|
||||
}
|
||||
|
||||
// Azure OpenAI configuration
|
||||
if os.Getenv("AZURE_OPENAI_ENDPOINT") != "" {
|
||||
viper.SetDefault("agents.primary.model", models.AzureGPT41)
|
||||
viper.SetDefault("agents.task.model", models.AzureGPT41Mini)
|
||||
viper.SetDefault("agents.title.model", models.AzureGPT41Mini)
|
||||
return
|
||||
}
|
||||
|
||||
// Google Cloud VertexAI configuration
|
||||
if hasVertexAICredentials() {
|
||||
viper.SetDefault("agents.coder.model", models.VertexAIGemini25)
|
||||
viper.SetDefault("agents.summarizer.model", models.VertexAIGemini25)
|
||||
viper.SetDefault("agents.task.model", models.VertexAIGemini25Flash)
|
||||
viper.SetDefault("agents.title.model", models.VertexAIGemini25Flash)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// hasAWSCredentials checks if AWS credentials are available in the environment.
|
||||
func hasAWSCredentials() bool {
|
||||
// Check for explicit AWS credentials
|
||||
if os.Getenv("AWS_ACCESS_KEY_ID") != "" && os.Getenv("AWS_SECRET_ACCESS_KEY") != "" {
|
||||
return true
|
||||
}
|
||||
|
||||
// Check for AWS profile
|
||||
if os.Getenv("AWS_PROFILE") != "" || os.Getenv("AWS_DEFAULT_PROFILE") != "" {
|
||||
return true
|
||||
}
|
||||
|
||||
// Check for AWS region
|
||||
if os.Getenv("AWS_REGION") != "" || os.Getenv("AWS_DEFAULT_REGION") != "" {
|
||||
return true
|
||||
}
|
||||
|
||||
// Check if running on EC2 with instance profile
|
||||
if os.Getenv("AWS_CONTAINER_CREDENTIALS_RELATIVE_URI") != "" ||
|
||||
os.Getenv("AWS_CONTAINER_CREDENTIALS_FULL_URI") != "" {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// hasVertexAICredentials checks if VertexAI credentials are available in the environment.
|
||||
func hasVertexAICredentials() bool {
|
||||
// Check for explicit VertexAI parameters
|
||||
if os.Getenv("VERTEXAI_PROJECT") != "" && os.Getenv("VERTEXAI_LOCATION") != "" {
|
||||
return true
|
||||
}
|
||||
// Check for Google Cloud project and location
|
||||
if os.Getenv("GOOGLE_CLOUD_PROJECT") != "" && (os.Getenv("GOOGLE_CLOUD_REGION") != "" || os.Getenv("GOOGLE_CLOUD_LOCATION") != "") {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// readConfig handles the result of reading a configuration file.
|
||||
func readConfig(err error) error {
|
||||
if err == nil {
|
||||
@@ -383,346 +163,15 @@ func mergeLocalConfig(workingDir string) {
|
||||
}
|
||||
}
|
||||
|
||||
// applyDefaultValues sets default values for configuration fields that need processing.
|
||||
func applyDefaultValues() {
|
||||
// Set default MCP type if not specified
|
||||
for k, v := range cfg.MCPServers {
|
||||
if v.Type == "" {
|
||||
v.Type = MCPStdio
|
||||
cfg.MCPServers[k] = v
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// It validates model IDs and providers, ensuring they are supported.
|
||||
func validateAgent(cfg *Config, name AgentName, agent Agent) error {
|
||||
// Check if model exists
|
||||
model, modelExists := models.SupportedModels[agent.Model]
|
||||
if !modelExists {
|
||||
slog.Warn("unsupported model configured, reverting to default",
|
||||
"agent", name,
|
||||
"configured_model", agent.Model)
|
||||
|
||||
// Set default model based on available providers
|
||||
if setDefaultModelForAgent(name) {
|
||||
slog.Info("set default model for agent", "agent", name, "model", cfg.Agents[name].Model)
|
||||
} else {
|
||||
return fmt.Errorf("no valid provider available for agent %s", name)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Check if provider for the model is configured
|
||||
provider := model.Provider
|
||||
providerCfg, providerExists := cfg.Providers[provider]
|
||||
|
||||
if !providerExists {
|
||||
// Provider not configured, check if we have environment variables
|
||||
apiKey := getProviderAPIKey(provider)
|
||||
if apiKey == "" {
|
||||
slog.Warn("provider not configured for model, reverting to default",
|
||||
"agent", name,
|
||||
"model", agent.Model,
|
||||
"provider", provider)
|
||||
|
||||
// Set default model based on available providers
|
||||
if setDefaultModelForAgent(name) {
|
||||
slog.Info("set default model for agent", "agent", name, "model", cfg.Agents[name].Model)
|
||||
} else {
|
||||
return fmt.Errorf("no valid provider available for agent %s", name)
|
||||
}
|
||||
} else {
|
||||
// Add provider with API key from environment
|
||||
cfg.Providers[provider] = Provider{
|
||||
APIKey: apiKey,
|
||||
}
|
||||
slog.Info("added provider from environment", "provider", provider)
|
||||
}
|
||||
} else if providerCfg.Disabled || providerCfg.APIKey == "" {
|
||||
// Provider is disabled or has no API key
|
||||
slog.Warn("provider is disabled or has no API key, reverting to default",
|
||||
"agent", name,
|
||||
"model", agent.Model,
|
||||
"provider", provider)
|
||||
|
||||
// Set default model based on available providers
|
||||
if setDefaultModelForAgent(name) {
|
||||
slog.Info("set default model for agent", "agent", name, "model", cfg.Agents[name].Model)
|
||||
} else {
|
||||
return fmt.Errorf("no valid provider available for agent %s", name)
|
||||
}
|
||||
}
|
||||
|
||||
// Validate max tokens
|
||||
if agent.MaxTokens <= 0 {
|
||||
slog.Warn("invalid max tokens, setting to default",
|
||||
"agent", name,
|
||||
"model", agent.Model,
|
||||
"max_tokens", agent.MaxTokens)
|
||||
|
||||
// Update the agent with default max tokens
|
||||
updatedAgent := cfg.Agents[name]
|
||||
if model.DefaultMaxTokens > 0 {
|
||||
updatedAgent.MaxTokens = model.DefaultMaxTokens
|
||||
} else {
|
||||
updatedAgent.MaxTokens = MaxTokensFallbackDefault
|
||||
}
|
||||
cfg.Agents[name] = updatedAgent
|
||||
} else if model.ContextWindow > 0 && agent.MaxTokens > model.ContextWindow/2 {
|
||||
// Ensure max tokens doesn't exceed half the context window (reasonable limit)
|
||||
slog.Warn("max tokens exceeds half the context window, adjusting",
|
||||
"agent", name,
|
||||
"model", agent.Model,
|
||||
"max_tokens", agent.MaxTokens,
|
||||
"context_window", model.ContextWindow)
|
||||
|
||||
// Update the agent with adjusted max tokens
|
||||
updatedAgent := cfg.Agents[name]
|
||||
updatedAgent.MaxTokens = model.ContextWindow / 2
|
||||
cfg.Agents[name] = updatedAgent
|
||||
}
|
||||
|
||||
// Validate reasoning effort for models that support reasoning
|
||||
if model.CanReason && provider == models.ProviderOpenAI {
|
||||
if agent.ReasoningEffort == "" {
|
||||
// Set default reasoning effort for models that support it
|
||||
slog.Info("setting default reasoning effort for model that supports reasoning",
|
||||
"agent", name,
|
||||
"model", agent.Model)
|
||||
|
||||
// Update the agent with default reasoning effort
|
||||
updatedAgent := cfg.Agents[name]
|
||||
updatedAgent.ReasoningEffort = "medium"
|
||||
cfg.Agents[name] = updatedAgent
|
||||
} else {
|
||||
// Check if reasoning effort is valid (low, medium, high)
|
||||
effort := strings.ToLower(agent.ReasoningEffort)
|
||||
if effort != "low" && effort != "medium" && effort != "high" {
|
||||
slog.Warn("invalid reasoning effort, setting to medium",
|
||||
"agent", name,
|
||||
"model", agent.Model,
|
||||
"reasoning_effort", agent.ReasoningEffort)
|
||||
|
||||
// Update the agent with valid reasoning effort
|
||||
updatedAgent := cfg.Agents[name]
|
||||
updatedAgent.ReasoningEffort = "medium"
|
||||
cfg.Agents[name] = updatedAgent
|
||||
}
|
||||
}
|
||||
} else if !model.CanReason && agent.ReasoningEffort != "" {
|
||||
// Model doesn't support reasoning but reasoning effort is set
|
||||
slog.Warn("model doesn't support reasoning but reasoning effort is set, ignoring",
|
||||
"agent", name,
|
||||
"model", agent.Model,
|
||||
"reasoning_effort", agent.ReasoningEffort)
|
||||
|
||||
// Update the agent to remove reasoning effort
|
||||
updatedAgent := cfg.Agents[name]
|
||||
updatedAgent.ReasoningEffort = ""
|
||||
cfg.Agents[name] = updatedAgent
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Validate checks if the configuration is valid and applies defaults where needed.
|
||||
func Validate() error {
|
||||
if cfg == nil {
|
||||
return fmt.Errorf("config not loaded")
|
||||
}
|
||||
|
||||
// Validate agent models
|
||||
for name, agent := range cfg.Agents {
|
||||
if err := validateAgent(cfg, name, agent); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Validate providers
|
||||
for provider, providerCfg := range cfg.Providers {
|
||||
if providerCfg.APIKey == "" && !providerCfg.Disabled {
|
||||
slog.Warn("provider has no API key, marking as disabled", "provider", provider)
|
||||
providerCfg.Disabled = true
|
||||
cfg.Providers[provider] = providerCfg
|
||||
}
|
||||
}
|
||||
|
||||
// Validate LSP configurations
|
||||
for language, lspConfig := range cfg.LSP {
|
||||
if lspConfig.Command == "" && !lspConfig.Disabled {
|
||||
slog.Warn("LSP configuration has no command, marking as disabled", "language", language)
|
||||
lspConfig.Disabled = true
|
||||
cfg.LSP[language] = lspConfig
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// getProviderAPIKey gets the API key for a provider from environment variables
|
||||
func getProviderAPIKey(provider models.ModelProvider) string {
|
||||
switch provider {
|
||||
case models.ProviderAnthropic:
|
||||
return os.Getenv("ANTHROPIC_API_KEY")
|
||||
case models.ProviderOpenAI:
|
||||
return os.Getenv("OPENAI_API_KEY")
|
||||
case models.ProviderGemini:
|
||||
return os.Getenv("GEMINI_API_KEY")
|
||||
case models.ProviderGROQ:
|
||||
return os.Getenv("GROQ_API_KEY")
|
||||
case models.ProviderAzure:
|
||||
return os.Getenv("AZURE_OPENAI_API_KEY")
|
||||
case models.ProviderOpenRouter:
|
||||
return os.Getenv("OPENROUTER_API_KEY")
|
||||
case models.ProviderBedrock:
|
||||
if hasAWSCredentials() {
|
||||
return "aws-credentials-available"
|
||||
}
|
||||
case models.ProviderVertexAI:
|
||||
if hasVertexAICredentials() {
|
||||
return "vertex-ai-credentials-available"
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// setDefaultModelForAgent sets a default model for an agent based on available providers
|
||||
func setDefaultModelForAgent(agent AgentName) bool {
|
||||
// Check providers in order of preference
|
||||
if apiKey := os.Getenv("ANTHROPIC_API_KEY"); apiKey != "" {
|
||||
maxTokens := int64(5000)
|
||||
if agent == AgentTitle {
|
||||
maxTokens = 80
|
||||
}
|
||||
cfg.Agents[agent] = Agent{
|
||||
Model: models.Claude4Sonnet,
|
||||
MaxTokens: maxTokens,
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
if apiKey := os.Getenv("OPENAI_API_KEY"); apiKey != "" {
|
||||
var model models.ModelID
|
||||
maxTokens := int64(5000)
|
||||
reasoningEffort := ""
|
||||
|
||||
switch agent {
|
||||
case AgentTitle:
|
||||
model = models.GPT41Mini
|
||||
maxTokens = 80
|
||||
case AgentTask:
|
||||
model = models.GPT41Mini
|
||||
default:
|
||||
model = models.GPT41
|
||||
}
|
||||
|
||||
// Check if model supports reasoning
|
||||
if modelInfo, ok := models.SupportedModels[model]; ok && modelInfo.CanReason {
|
||||
reasoningEffort = "medium"
|
||||
}
|
||||
|
||||
cfg.Agents[agent] = Agent{
|
||||
Model: model,
|
||||
MaxTokens: maxTokens,
|
||||
ReasoningEffort: reasoningEffort,
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
if apiKey := os.Getenv("OPENROUTER_API_KEY"); apiKey != "" {
|
||||
var model models.ModelID
|
||||
maxTokens := int64(5000)
|
||||
reasoningEffort := ""
|
||||
|
||||
switch agent {
|
||||
case AgentTitle:
|
||||
model = models.OpenRouterClaude35Haiku
|
||||
maxTokens = 80
|
||||
case AgentTask:
|
||||
model = models.OpenRouterClaude37Sonnet
|
||||
default:
|
||||
model = models.OpenRouterClaude37Sonnet
|
||||
}
|
||||
|
||||
// Check if model supports reasoning
|
||||
if modelInfo, ok := models.SupportedModels[model]; ok && modelInfo.CanReason {
|
||||
reasoningEffort = "medium"
|
||||
}
|
||||
|
||||
cfg.Agents[agent] = Agent{
|
||||
Model: model,
|
||||
MaxTokens: maxTokens,
|
||||
ReasoningEffort: reasoningEffort,
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
if apiKey := os.Getenv("GEMINI_API_KEY"); apiKey != "" {
|
||||
var model models.ModelID
|
||||
maxTokens := int64(5000)
|
||||
|
||||
if agent == AgentTitle {
|
||||
model = models.Gemini25Flash
|
||||
maxTokens = 80
|
||||
} else {
|
||||
model = models.Gemini25
|
||||
}
|
||||
|
||||
cfg.Agents[agent] = Agent{
|
||||
Model: model,
|
||||
MaxTokens: maxTokens,
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
if apiKey := os.Getenv("GROQ_API_KEY"); apiKey != "" {
|
||||
maxTokens := int64(5000)
|
||||
if agent == AgentTitle {
|
||||
maxTokens = 80
|
||||
}
|
||||
|
||||
cfg.Agents[agent] = Agent{
|
||||
Model: models.QWENQwq,
|
||||
MaxTokens: maxTokens,
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
if hasAWSCredentials() {
|
||||
maxTokens := int64(5000)
|
||||
if agent == AgentTitle {
|
||||
maxTokens = 80
|
||||
}
|
||||
|
||||
cfg.Agents[agent] = Agent{
|
||||
Model: models.BedrockClaude37Sonnet,
|
||||
MaxTokens: maxTokens,
|
||||
ReasoningEffort: "medium", // Claude models support reasoning
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
if hasVertexAICredentials() {
|
||||
var model models.ModelID
|
||||
maxTokens := int64(5000)
|
||||
|
||||
if agent == AgentTitle {
|
||||
model = models.VertexAIGemini25Flash
|
||||
maxTokens = 80
|
||||
} else {
|
||||
model = models.VertexAIGemini25
|
||||
}
|
||||
|
||||
cfg.Agents[agent] = Agent{
|
||||
Model: model,
|
||||
MaxTokens: maxTokens,
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// Get returns the current configuration.
|
||||
// It's safe to call this function multiple times.
|
||||
func Get() *Config {
|
||||
@@ -801,44 +250,6 @@ func updateCfgFile(updateCfg func(config *Config)) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func UpdateAgentModel(agentName AgentName, modelID models.ModelID) error {
|
||||
if cfg == nil {
|
||||
panic("config not loaded")
|
||||
}
|
||||
|
||||
existingAgentCfg := cfg.Agents[agentName]
|
||||
|
||||
model, ok := models.SupportedModels[modelID]
|
||||
if !ok {
|
||||
return fmt.Errorf("model %s not supported", modelID)
|
||||
}
|
||||
|
||||
maxTokens := existingAgentCfg.MaxTokens
|
||||
if model.DefaultMaxTokens > 0 {
|
||||
maxTokens = model.DefaultMaxTokens
|
||||
}
|
||||
|
||||
newAgentCfg := Agent{
|
||||
Model: modelID,
|
||||
MaxTokens: maxTokens,
|
||||
ReasoningEffort: existingAgentCfg.ReasoningEffort,
|
||||
}
|
||||
cfg.Agents[agentName] = newAgentCfg
|
||||
|
||||
if err := validateAgent(cfg, agentName, newAgentCfg); err != nil {
|
||||
// revert config update on failure
|
||||
cfg.Agents[agentName] = existingAgentCfg
|
||||
return fmt.Errorf("failed to update agent model: %w", err)
|
||||
}
|
||||
|
||||
return updateCfgFile(func(config *Config) {
|
||||
if config.Agents == nil {
|
||||
config.Agents = make(map[AgentName]Agent)
|
||||
}
|
||||
config.Agents[agentName] = newAgentCfg
|
||||
})
|
||||
}
|
||||
|
||||
// UpdateTheme updates the theme in the configuration and writes it to the config file.
|
||||
func UpdateTheme(themeName string) error {
|
||||
if cfg == nil {
|
||||
|
||||
Reference in New Issue
Block a user