|
5 | 5 | package conf |
6 | 6 |
|
7 | 7 | import ( |
| 8 | + "crypto/rand" |
| 9 | + "encoding/hex" |
8 | 10 | "errors" |
9 | | - "io" |
10 | 11 | "log/slog" |
11 | 12 | "os" |
| 13 | + "path/filepath" |
12 | 14 | "strings" |
13 | 15 | "syscall" |
14 | 16 |
|
15 | 17 | "github.com/LM4eu/goinfer/gie" |
16 | | - "github.com/LM4eu/goinfer/proxy/config" |
17 | 18 | "github.com/pelletier/go-toml/v2" |
18 | 19 | ) |
19 | 20 |
|
| 21 | +// GoinferINI is the config filename. |
| 22 | +const GoinferINI = "goinfer.ini" |
| 23 | + |
20 | 24 | // ReadGoinferINI loads the configuration file, reads the env vars and verifies the settings. |
21 | 25 | // Always return a valid configuration, because the receiver may want to write a valid config. |
22 | 26 | func ReadGoinferINI(noAPIKey bool, extra, start string) (*Cfg, error) { |
@@ -80,15 +84,39 @@ func ReadFileData(data []byte, noAPIKey bool, extra, start string) (*Cfg, error) |
80 | 84 | return cfg, err |
81 | 85 | } |
82 | 86 |
|
83 | | -// ReadSwapFromReader uses the LoadConfigFromReader() from llama-swap project. |
84 | | -func (cfg *Cfg) ReadSwapFromReader(r io.Reader) error { |
85 | | - var err error |
86 | | - cfg.Swap, err = config.LoadConfigFromReader(r) |
87 | | - if err != nil { |
88 | | - slog.Error("Cannot load llama-swap config", "file", LlamaSwapYML, "error", err) |
89 | | - os.Exit(1) |
| 87 | +// WriteGoinferINI populates the configuration with defaults, applies environment variables, |
| 88 | +// writes the resulting configuration to the given file. |
| 89 | +func (cfg *Cfg) WriteGoinferINI(debug, noAPIKey bool) error { |
| 90 | + data, err := cfg.GenGoinferINI(debug, noAPIKey) |
| 91 | + er := writeWithHeader(GoinferINI, "# Configuration of https://github.com/LM4eu/goinfer\n\n", data) |
| 92 | + if er != nil { |
| 93 | + if err != nil { |
| 94 | + return errors.Join(err, er) |
| 95 | + } |
| 96 | + return er |
| 97 | + } |
| 98 | + return err |
| 99 | +} |
| 100 | + |
| 101 | +// GenGoinferINI sets the API keys, reads the environment variables, |
| 102 | +// fix some settings and writes the result config to a buffer. |
| 103 | +func (cfg *Cfg) GenGoinferINI(debug, noAPIKey bool) ([]byte, error) { |
| 104 | + cfg.setAPIKey(debug, noAPIKey) |
| 105 | + cfg.applyEnvVars() |
| 106 | + cfg.trimParamValues() |
| 107 | + cfg.fixDefaultModel() |
| 108 | + |
| 109 | + err := cfg.validate(noAPIKey) |
| 110 | + |
| 111 | + data, er := toml.Marshal(&cfg) |
| 112 | + if er != nil { |
| 113 | + er = gie.Wrap(err, gie.ConfigErr, "failed to yaml.Marshal", "cfg", cfg) |
| 114 | + if err != nil { |
| 115 | + return data, errors.Join(err, er) |
| 116 | + } |
| 117 | + return data, er |
90 | 118 | } |
91 | | - return cfg.ValidateSwap() |
| 119 | + return data, err |
92 | 120 | } |
93 | 121 |
|
94 | 122 | // load the configuration file (if filename not empty). |
@@ -190,3 +218,55 @@ func (cfg *Cfg) trimParamValues() { |
190 | 218 | cfg.Llama.Common = strings.TrimSpace(cfg.Llama.Common) |
191 | 219 | cfg.Llama.Goinfer = strings.TrimSpace(cfg.Llama.Goinfer) |
192 | 220 | } |
| 221 | + |
| 222 | +func writeWithHeader(path, header string, data []byte) error { |
| 223 | + path = filepath.Clean(path) |
| 224 | + file, err := os.Create(path) |
| 225 | + if err != nil { |
| 226 | + return gie.Wrap(err, gie.ConfigErr, "failed to create file="+path) |
| 227 | + } |
| 228 | + |
| 229 | + _, err = file.WriteString(header) |
| 230 | + if err == nil { |
| 231 | + _, err = file.Write(data) |
| 232 | + } |
| 233 | + |
| 234 | + er := file.Close() |
| 235 | + if err != nil { |
| 236 | + err = er |
| 237 | + } |
| 238 | + if err != nil { |
| 239 | + return gie.Wrap(err, gie.ConfigErr, "failed to write file="+path) |
| 240 | + } |
| 241 | + |
| 242 | + return nil |
| 243 | +} |
| 244 | + |
| 245 | +func (cfg *Cfg) setAPIKey(debug, noAPIKey bool) { |
| 246 | + switch { |
| 247 | + case noAPIKey: |
| 248 | + cfg.APIKey = unsetAPIKey |
| 249 | + slog.Info("Flag -no-api-key => Do not generate API key") |
| 250 | + |
| 251 | + case debug: |
| 252 | + cfg.APIKey = debugAPIKey |
| 253 | + slog.Warn("API key is DEBUG => security threat") |
| 254 | + |
| 255 | + default: |
| 256 | + cfg.APIKey = gen64HexDigits() |
| 257 | + slog.Info("Generated random API key") |
| 258 | + } |
| 259 | +} |
| 260 | + |
| 261 | +func gen64HexDigits() string { |
| 262 | + buf := make([]byte, 32) |
| 263 | + _, err := rand.Read(buf) |
| 264 | + if err != nil { |
| 265 | + slog.Warn("Failed to rand.Read", "error", err) |
| 266 | + return "" |
| 267 | + } |
| 268 | + |
| 269 | + key := make([]byte, 64) |
| 270 | + hex.Encode(key, buf) |
| 271 | + return string(key) |
| 272 | +} |
0 commit comments