187 lines
5.0 KiB
Go
187 lines
5.0 KiB
Go
package config
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"strconv"
|
|
|
|
"gopkg.in/yaml.v3"
|
|
)
|
|
|
|
type Config struct {
|
|
Ollama OllamaConfig `yaml:"ollama"`
|
|
Model ModelConfig `yaml:"model,omitempty"`
|
|
Agents AgentsConfig `yaml:"agents,omitempty"`
|
|
Servers []ServerConfig `yaml:"servers,omitempty"`
|
|
SkillsDir string `yaml:"skills_dir,omitempty"`
|
|
ICE ICEConfig `yaml:"ice,omitempty"`
|
|
AgentProfile string `yaml:"agent_profile,omitempty"`
|
|
Tools ToolsConfig `yaml:"tools,omitempty"`
|
|
}
|
|
|
|
type AgentsConfig struct {
|
|
Dir string `yaml:"dir,omitempty"`
|
|
AutoLoad bool `yaml:"auto_load"`
|
|
}
|
|
|
|
type ToolsConfig struct {
|
|
Timeout string `yaml:"timeout,omitempty"` // e.g., "30s", "2m"
|
|
MaxGrepResults int `yaml:"max_grep_results,omitempty"`
|
|
MaxIterations int `yaml:"max_iterations,omitempty"`
|
|
}
|
|
|
|
type ICEConfig struct {
|
|
Enabled bool `yaml:"enabled"`
|
|
EmbedModel string `yaml:"embed_model,omitempty"`
|
|
StorePath string `yaml:"store_path,omitempty"`
|
|
}
|
|
|
|
type OllamaConfig struct {
|
|
Model string `yaml:"model"`
|
|
BaseURL string `yaml:"base_url"`
|
|
NumCtx int `yaml:"num_ctx"`
|
|
}
|
|
|
|
type ServerConfig struct {
|
|
Name string `yaml:"name"`
|
|
Command string `yaml:"command,omitempty"`
|
|
Args []string `yaml:"args,omitempty"`
|
|
Env []string `yaml:"env,omitempty"`
|
|
Transport string `yaml:"transport,omitempty"`
|
|
URL string `yaml:"url,omitempty"`
|
|
}
|
|
|
|
func defaults() Config {
|
|
modelCfg := DefaultModelConfig()
|
|
return Config{
|
|
Ollama: OllamaConfig{
|
|
Model: "qwen3.5:2b",
|
|
BaseURL: "http://localhost:11434",
|
|
NumCtx: 262144,
|
|
},
|
|
Model: modelCfg,
|
|
Agents: AgentsConfig{
|
|
Dir: "",
|
|
AutoLoad: true,
|
|
},
|
|
Tools: ToolsConfig{
|
|
Timeout: "30s",
|
|
MaxGrepResults: 500,
|
|
MaxIterations: 10,
|
|
},
|
|
}
|
|
}
|
|
|
|
func Load() (*Config, error) {
|
|
cfg := defaults()
|
|
|
|
localPath := findConfigFile()
|
|
if localPath != "" {
|
|
data, err := os.ReadFile(localPath)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("read config %s: %w", localPath, err)
|
|
}
|
|
if err := yaml.Unmarshal(data, &cfg); err != nil {
|
|
return nil, fmt.Errorf("parse config %s: %w", localPath, err)
|
|
}
|
|
}
|
|
|
|
agentsDir := cfg.Agents.Dir
|
|
if agentsDir == "" {
|
|
agentsDir = FindAgentsDir()
|
|
}
|
|
|
|
var agentsData *AgentsDir
|
|
if agentsDir != "" && cfg.Agents.AutoLoad {
|
|
var err error
|
|
agentsData, err = LoadAgentsDir(agentsDir)
|
|
if err != nil {
|
|
fmt.Fprintf(os.Stderr, "warning: failed to load .agents directory: %v\n", err)
|
|
} else {
|
|
if agentsData != nil {
|
|
if cfg.Ollama.Model == "" {
|
|
cfg.Ollama.Model = cfg.Model.DefaultModel
|
|
}
|
|
|
|
if len(cfg.Servers) == 0 && agentsData.HasMCP() {
|
|
cfg.Servers = agentsData.GetMCPServers()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
applyEnvOverrides(&cfg)
|
|
return &cfg, nil
|
|
}
|
|
|
|
func LoadWithAgentsDir() (*Config, *AgentsDir, error) {
|
|
cfg, err := Load()
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
agentsDir := cfg.Agents.Dir
|
|
if agentsDir == "" {
|
|
agentsDir = FindAgentsDir()
|
|
}
|
|
var agents *AgentsDir
|
|
if agentsDir != "" && cfg.Agents.AutoLoad {
|
|
agents, _ = LoadAgentsDir(agentsDir)
|
|
}
|
|
|
|
return cfg, agents, nil
|
|
}
|
|
|
|
func findConfigFile() string {
|
|
candidates := []string{
|
|
"ai-agent.yaml",
|
|
"ai-agent.yml",
|
|
"config.yaml",
|
|
"config.yml",
|
|
}
|
|
if home, err := os.UserHomeDir(); err == nil {
|
|
candidates = append(candidates,
|
|
filepath.Join(home, ".config", "ai-agent", "config.yaml"),
|
|
filepath.Join(home, ".config", "ai-agent", "config.yml"),
|
|
)
|
|
}
|
|
for _, path := range candidates {
|
|
if _, err := os.Stat(path); err == nil {
|
|
return path
|
|
}
|
|
}
|
|
return ""
|
|
}
|
|
|
|
func applyEnvOverrides(cfg *Config) {
|
|
if v := os.Getenv("OLLAMA_HOST"); v != "" {
|
|
cfg.Ollama.BaseURL = v
|
|
}
|
|
if v := os.Getenv("LOCAL_AGENT_MODEL"); v != "" {
|
|
cfg.Ollama.Model = v
|
|
}
|
|
if v := os.Getenv("LOCAL_AGENT_AGENTS_DIR"); v != "" {
|
|
cfg.Agents.Dir = v
|
|
}
|
|
if v := os.Getenv("LOCAL_AGENT_TOOLS_TIMEOUT"); v != "" {
|
|
cfg.Tools.Timeout = v
|
|
}
|
|
if v := os.Getenv("LOCAL_AGENT_TOOLS_MAX_GREP"); v != "" {
|
|
cfg.Tools.MaxGrepResults = parseEnvInt(v, cfg.Tools.MaxGrepResults)
|
|
}
|
|
if v := os.Getenv("LOCAL_AGENT_TOOLS_MAX_ITER"); v != "" {
|
|
cfg.Tools.MaxIterations = parseEnvInt(v, cfg.Tools.MaxIterations)
|
|
}
|
|
if v := os.Getenv("LOCAL_AGENT_ICE_EMBED_MODEL"); v != "" {
|
|
cfg.ICE.EmbedModel = v
|
|
}
|
|
}
|
|
|
|
func parseEnvInt(v string, defaultVal int) int {
|
|
if i, err := strconv.Atoi(v); err == nil {
|
|
return i
|
|
}
|
|
return defaultVal
|
|
}
|