]> git.giacomolli.uk - onechat/blob - config.go
Give the root tools to start and message children
[onechat] / config.go
1 package main
2
3 import (
4         "bytes"
5         "encoding/json"
6         "errors"
7         "fmt"
8         "io"
9         "os"
10         "path/filepath"
11 )
12
13 const (
14         // configEndpointChatGPT names the ChatGPT backend with the subscription
15         // login.
16         configEndpointChatGPT = "chatgpt"
17
18         // configEndpointAPI names the OpenAI API with an API key.
19         configEndpointAPI = "api"
20 )
21
22 // loadConfig reads config.json in dir over the defaults. A missing file gives
23 // the defaults. An unknown field is an error, so a typo does not become a
24 // silent default.
25 func loadConfig(dir string) (Config, error) {
26         config := defaultConfig()
27         data, err := os.ReadFile(filepath.Join(dir, "config.json"))
28         if errors.Is(err, os.ErrNotExist) {
29                 return config, nil
30         }
31
32         if err != nil {
33                 return Config{}, fmt.Errorf("read config: %w", err)
34         }
35
36         decoder := json.NewDecoder(bytes.NewReader(data))
37         decoder.DisallowUnknownFields()
38
39         err = decoder.Decode(&config)
40         if err == nil && !errors.Is(decoder.Decode(&struct{}{}), io.EOF) {
41                 err = errors.New("data after the JSON object")
42         }
43
44         if err == nil {
45                 err = config.validate()
46         }
47
48         if err != nil {
49                 return Config{}, fmt.Errorf("config.json: %w", err)
50         }
51
52         return config, nil
53 }
54
55 // Config holds the settings of config.json in the data directory.
56 type Config struct {
57         Endpoint  string      `json:"endpoint"`
58         Compactor ModelConfig `json:"compactor"`
59 }
60
61 // validate makes sure that each setting has a valid value.
62 func (c Config) validate() error {
63         if c.Endpoint != configEndpointChatGPT && c.Endpoint != configEndpointAPI {
64                 return fmt.Errorf("endpoint %q is not %q or %q", c.Endpoint, configEndpointChatGPT, configEndpointAPI)
65         }
66
67         if c.Compactor.Model == "" || c.Compactor.Effort == "" {
68                 return errors.New("compactor needs a model and an effort")
69         }
70
71         return nil
72 }
73
74 // ModelConfig names a model and its reasoning effort.
75 type ModelConfig struct {
76         Model  string `json:"model"`
77         Effort string `json:"effort"`
78 }
79
80 // defaultConfig returns the settings that apply when config.json does not
81 // set them.
82 func defaultConfig() Config {
83         return Config{
84                 Endpoint: configEndpointChatGPT,
85                 Compactor: ModelConfig{
86                         Model:  "gpt-6.1-sol",
87                         Effort: "high",
88                 },
89         }
90 }