ollie/cmd/olliesrv/internal/backend/config.go

114 lines
2.8 KiB
Go

package backend
import (
"bufio"
"os"
"strings"
"ollie/paths"
)
// BackendConfig holds the per-backend configuration from backends.conf.
type BackendConfig struct {
Key string // API key / token
URL string // base URL override (empty = use backend's default)
Model string // default model override
CompactionModel string // model used for history compaction (empty = use Model)
Host string // for ollama: host address
}
// configFile is the parsed contents of backends.conf: section name → key/value pairs.
type configFile struct {
Default string // top-level "backend = ..." selects the default
Backends map[string]BackendConfig
}
// loadConfig reads ~/.config/ollie/backends.conf.
// Returns zero value if the file doesn't exist or is unreadable.
func loadConfig() configFile {
return parseConfigFile(paths.CfgDir() + "/backends.conf")
}
// CompactionModel returns the configured compaction model for a backend.
// It falls back to that backend's configured default model when no dedicated
// compaction model is set.
func CompactionModel(name string) string {
cfg := loadConfig()
bc := cfg.Backends[name]
if bc.CompactionModel != "" {
return bc.CompactionModel
}
return bc.Model
}
// parseConfigFile parses an INI-style config file.
// Format:
//
// backend = openrouter (top-level: default backend selection)
//
// [section]
// key = value
//
// Lines starting with # are comments. Blank lines are ignored.
func parseConfigFile(path string) configFile {
f, err := os.Open(path)
if err != nil {
return configFile{Backends: map[string]BackendConfig{}}
}
defer f.Close()
cfg := configFile{Backends: map[string]BackendConfig{}}
section := ""
sc := bufio.NewScanner(f)
for sc.Scan() {
line := strings.TrimSpace(sc.Text())
if line == "" || strings.HasPrefix(line, "#") {
continue
}
// Section header.
if strings.HasPrefix(line, "[") && strings.HasSuffix(line, "]") {
section = strings.TrimSpace(line[1 : len(line)-1])
if _, ok := cfg.Backends[section]; !ok {
cfg.Backends[section] = BackendConfig{}
}
continue
}
// Key = value.
k, v, ok := strings.Cut(line, "=")
if !ok {
continue
}
k = strings.TrimSpace(k)
v = strings.TrimSpace(v)
// Strip surrounding quotes.
if len(v) >= 2 && (v[0] == '"' && v[len(v)-1] == '"' || v[0] == '\'' && v[len(v)-1] == '\'') {
v = v[1 : len(v)-1]
}
// Top-level keys (before any section).
if section == "" {
if k == "backend" {
cfg.Default = v
}
continue
}
bc := cfg.Backends[section]
switch k {
case "key":
bc.Key = v
case "url":
bc.URL = v
case "model":
bc.Model = v
case "compactionModel", "compaction_model", "compaction-model":
bc.CompactionModel = v
case "host":
bc.Host = v
}
cfg.Backends[section] = bc
}
return cfg
}