diff --git a/backend/config.go b/backend/config.go index 4214ceb..5c29d09 100644 --- a/backend/config.go +++ b/backend/config.go @@ -17,10 +17,13 @@ type BackendConfig struct { } // configFile is the parsed contents of backends.conf: section name → key/value pairs. -type configFile map[string]BackendConfig +type configFile struct { + Default string // top-level "backend = ..." selects the default + Backends map[string]BackendConfig +} // loadConfig reads ~/.config/ollie/backends.conf. -// Returns an empty map (not nil) if the file doesn't exist or is unreadable. +// Returns zero value if the file doesn't exist or is unreadable. func loadConfig() configFile { return parseConfigFile(paths.CfgDir() + "/backends.conf") } @@ -28,6 +31,8 @@ func loadConfig() configFile { // parseConfigFile parses an INI-style config file. // Format: // +// backend = openrouter (top-level: default backend selection) +// // [section] // key = value // @@ -35,11 +40,11 @@ func loadConfig() configFile { func parseConfigFile(path string) configFile { f, err := os.Open(path) if err != nil { - return configFile{} + return configFile{Backends: map[string]BackendConfig{}} } defer f.Close() - cfg := configFile{} + cfg := configFile{Backends: map[string]BackendConfig{}} section := "" sc := bufio.NewScanner(f) @@ -51,15 +56,12 @@ func parseConfigFile(path string) configFile { // Section header. if strings.HasPrefix(line, "[") && strings.HasSuffix(line, "]") { section = strings.TrimSpace(line[1 : len(line)-1]) - if _, ok := cfg[section]; !ok { - cfg[section] = BackendConfig{} + if _, ok := cfg.Backends[section]; !ok { + cfg.Backends[section] = BackendConfig{} } continue } // Key = value. - if section == "" { - continue // ignore keys before first section - } k, v, ok := strings.Cut(line, "=") if !ok { continue @@ -71,7 +73,15 @@ func parseConfigFile(path string) configFile { v = v[1 : len(v)-1] } - bc := cfg[section] + // 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 @@ -82,7 +92,7 @@ func parseConfigFile(path string) configFile { case "host": bc.Host = v } - cfg[section] = bc + cfg.Backends[section] = bc } return cfg } diff --git a/backend/new.go b/backend/new.go index b7ddd86..9bf0981 100644 --- a/backend/new.go +++ b/backend/new.go @@ -6,20 +6,27 @@ import ( ) // NewWithName constructs a Backend for the given backend name. -// If name is empty, falls back to OLLIE_BACKEND; errors if neither is set. +// If name is empty, falls back to backends.conf default, then OLLIE_BACKEND env. func NewWithName(name string) (Backend, error) { + cfg := loadConfig() + if name == "" { + name = cfg.Default + } if name == "" { name = os.Getenv("OLLIE_BACKEND") - if name == "" { - return nil, fmt.Errorf("no backend specified: set OLLIE_BACKEND or pass backend= to the session") - } } - return newBackend(name) + if name == "" { + return nil, fmt.Errorf("no backend specified: set 'backend' in backends.conf, OLLIE_BACKEND, or pass backend= to the session") + } + return newBackendFromConfig(name, cfg) } func newBackend(which string) (Backend, error) { - cfg := loadConfig() - bc := cfg[which] + return newBackendFromConfig(which, loadConfig()) +} + +func newBackendFromConfig(which string, cfg configFile) (Backend, error) { + bc := cfg.Backends[which] switch which { case "ollama": diff --git a/data/backends.conf b/data/backends.conf index 4e8bffc..35a9be9 100644 --- a/data/backends.conf +++ b/data/backends.conf @@ -1,13 +1,18 @@ # Ollie backend configuration. # Each section configures a backend. The session selects which backend to use -# via OLLIE_BACKEND env var or backend= parameter. +# via the top-level 'backend' setting or backend= parameter. # -# Fields: +# Top-level: +# backend = name (default backend for new sessions) +# +# Per-backend fields: # key = API key or token # url = base URL override (optional; each backend has a sensible default) # model = default model (optional; overridden by session model= parameter) # host = host address (ollama only) +backend = openrouter + [openrouter] # key = sk-or-v1-... # model = qwen/qwen3-235b-a22b