rony-llm-agent/pkg/config/loader.go

105 lines
2.3 KiB
Go

package config
import (
"context"
"fmt"
"os"
"path/filepath"
"gopkg.in/yaml.v3"
)
// yamlLoader loads config from YAML files with hierarchical precedence.
type yamlLoader struct{}
// NewYAMLLoader returns a Loader that reads from YAML files.
func NewYAMLLoader() Loader {
return &yamlLoader{}
}
func (l *yamlLoader) Load(ctx context.Context, workdir string) (Config, error) {
defaults := LoadDefault()
// Load from workdir
path := filepath.Join(workdir, "rony.yaml")
if _, err := os.Stat(path); err == nil {
cfg, err := loadFromFile(path)
if err != nil {
return Config{}, fmt.Errorf("loading %s: %w", path, err)
}
// Merge with defaults
cfg = merge(defaults, cfg)
return cfg, nil
}
// Load from home directory
home, err := os.UserHomeDir()
if err == nil {
homePath := filepath.Join(home, ".config", "rony", "config.yaml")
if _, err := os.Stat(homePath); err == nil {
cfg, err := loadFromFile(homePath)
if err == nil {
return merge(defaults, cfg), nil
}
}
}
return defaults, nil
}
func loadFromFile(path string) (Config, error) {
data, err := os.ReadFile(path)
if err != nil {
return Config{}, err
}
var cfg Config
if err := yaml.Unmarshal(data, &cfg); err != nil {
return Config{}, fmt.Errorf("parsing YAML: %w", err)
}
return cfg, nil
}
// merge combines two configs, with b taking precedence over a.
func merge(a, b Config) Config {
if b.Model != "" {
a.Model = b.Model
}
if b.Provider.Type != "" {
a.Provider.Type = b.Provider.Type
}
if b.Provider.Model != "" {
a.Provider.Model = b.Provider.Model
}
if b.Provider.APIKey != "" {
a.Provider.APIKey = b.Provider.APIKey
}
if b.Provider.BaseURL != "" {
a.Provider.BaseURL = b.Provider.BaseURL
}
if b.Provider.MaxTokens > 0 {
a.Provider.MaxTokens = b.Provider.MaxTokens
}
if b.Provider.Temperature > 0 {
a.Provider.Temperature = b.Provider.Temperature
}
if b.Tools.DefaultPermission != "" {
a.Tools.DefaultPermission = b.Tools.DefaultPermission
}
if len(b.Tools.AllowList) > 0 {
a.Tools.AllowList = b.Tools.AllowList
}
if len(b.Tools.DenyList) > 0 {
a.Tools.DenyList = b.Tools.DenyList
}
if b.Logging.Level != "" {
a.Logging.Level = b.Logging.Level
}
if b.Logging.Format != "" {
a.Logging.Format = b.Logging.Format
}
if b.Logging.Output != "" {
a.Logging.Output = b.Logging.Output
}
return a
}