| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189 |
- package config
- import (
- "fmt"
- "os"
- "strings"
- "github.com/kujtimiihoxha/termai/internal/llm/models"
- "github.com/spf13/viper"
- )
- type MCPType string
- const (
- MCPStdio MCPType = "stdio"
- MCPSse MCPType = "sse"
- )
- 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"`
- // TODO: add permissions configuration
- // TODO: add the ability to specify the tools to import
- }
- type Model struct {
- Coder models.ModelID `json:"coder"`
- CoderMaxTokens int64 `json:"coderMaxTokens"`
- Task models.ModelID `json:"task"`
- TaskMaxTokens int64 `json:"taskMaxTokens"`
- // TODO: Maybe support multiple models for different purposes
- }
- type Provider struct {
- APIKey string `json:"apiKey"`
- Enabled bool `json:"enabled"`
- }
- type Data struct {
- Directory string `json:"directory"`
- }
- type Log struct {
- Level string `json:"level"`
- }
- type LSPConfig struct {
- Disabled bool `json:"enabled"`
- Command string `json:"command"`
- Args []string `json:"args"`
- Options any `json:"options"`
- }
- type Config struct {
- Data *Data `json:"data,omitempty"`
- Log *Log `json:"log,omitempty"`
- MCPServers map[string]MCPServer `json:"mcpServers,omitempty"`
- Providers map[models.ModelProvider]Provider `json:"providers,omitempty"`
- LSP map[string]LSPConfig `json:"lsp,omitempty"`
- Model *Model `json:"model,omitempty"`
- }
- var cfg *Config
- const (
- defaultDataDirectory = ".termai"
- defaultLogLevel = "info"
- defaultMaxTokens = int64(5000)
- termai = "termai"
- )
- func Load(debug bool) error {
- if cfg != nil {
- return nil
- }
- viper.SetConfigName(fmt.Sprintf(".%s", termai))
- viper.SetConfigType("json")
- viper.AddConfigPath("$HOME")
- viper.AddConfigPath(fmt.Sprintf("$XDG_CONFIG_HOME/%s", termai))
- viper.SetEnvPrefix(strings.ToUpper(termai))
- // Add defaults
- viper.SetDefault("data.directory", defaultDataDirectory)
- if debug {
- viper.Set("log.level", "debug")
- } else {
- viper.SetDefault("log.level", defaultLogLevel)
- }
- defaultModelSet := false
- if os.Getenv("ANTHROPIC_API_KEY") != "" {
- viper.SetDefault("providers.anthropic.apiKey", os.Getenv("ANTHROPIC_API_KEY"))
- viper.SetDefault("providers.anthropic.enabled", true)
- viper.SetDefault("model.coder", models.Claude37Sonnet)
- viper.SetDefault("model.task", models.Claude37Sonnet)
- defaultModelSet = true
- }
- if os.Getenv("OPENAI_API_KEY") != "" {
- viper.SetDefault("providers.openai.apiKey", os.Getenv("OPENAI_API_KEY"))
- viper.SetDefault("providers.openai.enabled", true)
- if !defaultModelSet {
- viper.SetDefault("model.coder", models.GPT4o)
- viper.SetDefault("model.task", models.GPT4o)
- defaultModelSet = true
- }
- }
- if os.Getenv("GEMINI_API_KEY") != "" {
- viper.SetDefault("providers.gemini.apiKey", os.Getenv("GEMINI_API_KEY"))
- viper.SetDefault("providers.gemini.enabled", true)
- if !defaultModelSet {
- viper.SetDefault("model.coder", models.GRMINI20Flash)
- viper.SetDefault("model.task", models.GRMINI20Flash)
- defaultModelSet = true
- }
- }
- if os.Getenv("GROQ_API_KEY") != "" {
- viper.SetDefault("providers.groq.apiKey", os.Getenv("GROQ_API_KEY"))
- viper.SetDefault("providers.groq.enabled", true)
- if !defaultModelSet {
- viper.SetDefault("model.coder", models.QWENQwq)
- viper.SetDefault("model.task", models.QWENQwq)
- defaultModelSet = true
- }
- }
- // TODO: add more providers
- cfg = &Config{}
- err := viper.ReadInConfig()
- if err != nil {
- if _, ok := err.(viper.ConfigFileNotFoundError); !ok {
- return err
- }
- }
- local := viper.New()
- local.SetConfigName(fmt.Sprintf(".%s", termai))
- local.SetConfigType("json")
- local.AddConfigPath(".")
- // load local config, this will override the global config
- if err = local.ReadInConfig(); err == nil {
- viper.MergeConfigMap(local.AllSettings())
- }
- viper.Unmarshal(cfg)
- if cfg.Model != nil && cfg.Model.CoderMaxTokens <= 0 {
- cfg.Model.CoderMaxTokens = defaultMaxTokens
- }
- if cfg.Model != nil && cfg.Model.TaskMaxTokens <= 0 {
- cfg.Model.TaskMaxTokens = defaultMaxTokens
- }
- for _, v := range cfg.MCPServers {
- if v.Type == "" {
- v.Type = MCPStdio
- }
- }
- workdir, err := os.Getwd()
- if err != nil {
- return err
- }
- viper.Set("wd", workdir)
- return nil
- }
- func Get() *Config {
- if cfg == nil {
- err := Load(false)
- if err != nil {
- panic(err)
- }
- }
- return cfg
- }
- func WorkingDirectory() string {
- return viper.GetString("wd")
- }
- func Write() error {
- return viper.WriteConfig()
- }
|