返回 DeepSeek-Reasonix
decode.go
根目录 / internal / config / decode.go
1 package config
2
3 import (
4 "fmt"
5
6 "github.com/BurntSushi/toml"
7
8 fileencoding "reasonix/internal/fileutil/encoding"
9 )
10
11 func decodeTOMLFile(path string, v any) (toml.MetaData, error) {
12 resolved, err := resolveConfigReadPath(path)
13 if err != nil {
14 return toml.MetaData{}, err
15 }
16 return decodeTOMLFileResolved(resolved, v)
17 }
18
19 func decodeTOMLFileResolved(path string, v any) (toml.MetaData, error) {
20 data, err := fileencoding.ReadFileUTF8(path)
21 if err != nil {
22 return toml.MetaData{}, err
23 }
24 return decodeTOMLBytes(data, v)
25 }
26
27 func decodeTOMLBytes(data []byte, v any) (toml.MetaData, error) {
28 text := string(fileencoding.DecodeToUTF8(data))
29 meta, err := toml.Decode(text, v)
30 if err != nil {
31 return meta, err
32 }
33 if cfg, ok := v.(*Config); ok {
34 if err := isolateDecodedProviders(text, meta, cfg); err != nil {
35 return meta, err
36 }
37 }
38 return meta, nil
39 }
40
41 // isolateDecodedProviders stops one vendor's defaults from being written onto
42 // another vendor's provider entry.
43 //
44 // TOML array-of-tables decoding is positional: BurntSushi/toml unifies
45 // [[providers]][i] onto whatever the destination slice already holds at index i
46 // instead of onto a zero value. Every config load seeds from Default(), which
47 // ships two official DeepSeek entries, so a user's first two [[providers]]
48 // inherit every DeepSeek field they did not set themselves — balance_url,
49 // the USD price table and context_window = 1000000 (#7357, #7358).
50 //
51 // Re-decoding the same bytes onto an empty slice makes a declared provider list
52 // carry only what the file declares. Defaults that are genuinely correct for an
53 // entry are then reapplied by the explicit backfill helpers, which key on the
54 // endpoint rather than on list position.
55 func isolateDecodedProviders(text string, meta toml.MetaData, cfg *Config) error {
56 if cfg == nil || len(cfg.Providers) == 0 || !meta.IsDefined("providers") {
57 return nil
58 }
59 var isolated struct {
60 Providers []ProviderEntry `toml:"providers"`
61 }
62 if _, err := toml.Decode(text, &isolated); err != nil {
63 return fmt.Errorf("decode providers: %w", err)
64 }
65 cfg.Providers = isolated.Providers
66 return nil
67 }
68
68 lines GO