]> git.giacomolli.uk - onechat/blob - config.go
1d68b67a39c28716ba4651fc8b8bafd34dfc7d14
[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 Voice VoiceConfig `json:"voice"`
59 Compactor ModelConfig `json:"compactor"`
60 }
61
62 // validate makes sure that each setting has a valid value.
63 func (c Config) validate() error {
64 if c.Endpoint != configEndpointChatGPT && c.Endpoint != configEndpointAPI {
65 return fmt.Errorf("endpoint %q is not %q or %q", c.Endpoint, configEndpointChatGPT, configEndpointAPI)
66 }
67
68 if c.Compactor.Model == "" || c.Compactor.Effort == "" {
69 return errors.New("compactor needs a model and an effort")
70 }
71
72 return nil
73 }
74
75 // ModelConfig names a model and its reasoning effort.
76 type ModelConfig struct {
77 Model string `json:"model"`
78 Effort string `json:"effort"`
79 }
80
81 // defaultConfig returns the settings that apply when config.json does not
82 // set them.
83 func defaultConfig() Config {
84 return Config{
85 Endpoint: configEndpointChatGPT,
86 Compactor: ModelConfig{
87 Model: "gpt-6.1-sol",
88 Effort: "high",
89 },
90 }
91 }