返回 DeepSeek-Reasonix
deepseek_catalog_test.go
根目录 / internal / config / deepseek_catalog_test.go
1 package config
2
3 import (
4 "os"
5 "path/filepath"
6 "reflect"
7 "testing"
8
9 "reasonix/internal/provider"
10 )
11
12 func TestDeepSeekCurrentCatalogSurvivesSaveAndReload(t *testing.T) {
13 t.Setenv("REASONIX_HOME", t.TempDir())
14 for _, id := range []string{"deepseek-chat", "deepseek-anthropic", "deepseek-responses"} {
15 t.Run(id, func(t *testing.T) {
16 preset, _ := CuratedProviderPreset(id)
17 cfg := &Config{Providers: preset.Entries, DefaultModel: id + "/deepseek-flash"}
18 path := filepath.Join(t.TempDir(), "config.toml")
19 for range 2 {
20 if err := cfg.SaveTo(path); err != nil {
21 t.Fatal(err)
22 }
23 cfg = LoadForEditWithoutCredentials(path)
24 p, ok := cfg.Provider(id)
25 if !ok || !reflect.DeepEqual(p.ModelList(), []string{"deepseek-flash", "deepseek-v4-pro"}) || p.DefaultModel() != "deepseek-flash" {
26 t.Fatalf("saved preset catalog changed: %+v", p)
27 }
28 for _, model := range []string{"deepseek-flash", "deepseek-v4-flash", "deepseek-v4-flash-vision-exp"} {
29 resolved, ok := cfg.ResolveModel(id + "/" + model)
30 if !ok || resolved.Model != model || !EffectiveVision(resolved) {
31 t.Fatalf("model reference %q lost wire identity or vision: %+v", model, resolved)
32 }
33 }
34 }
35 })
36 }
37 }
38
39 func TestDeepSeekCuratedCatalogSurvivesRuntimeAndEditLoad(t *testing.T) {
40 for _, models := range []string{`["deepseek-flash"]`, `["deepseek-v4-flash"]`, `["deepseek-v4-flash", "deepseek-v4-pro"]`, `["deepseek-flash", "Custom-Case-ID"]`} {
41 t.Run(models, func(t *testing.T) {
42 home := t.TempDir()
43 t.Setenv("REASONIX_HOME", home)
44 path := filepath.Join(home, "config.toml")
45 raw := "config_version = 10\n[[providers]]\nname = \"deepseek-responses\"\nkind = \"responses\"\nbase_url = \"https://api.deepseek.com\"\nmodels = " + models + "\nvision_models = []\nfuture_field = \"preserve\"\n"
46 if err := os.WriteFile(path, []byte(raw), 0600); err != nil {
47 t.Fatal(err)
48 }
49 var original Config
50 if _, err := decodeTOMLFile(path, &original); err != nil {
51 t.Fatal(err)
52 }
53 runtime, err := LoadForRootReadOnly(t.TempDir())
54 if err != nil {
55 t.Fatal(err)
56 }
57 for _, cfg := range []*Config{runtime, LoadForEditWithoutCredentials(path)} {
58 p, _ := cfg.Provider("deepseek-responses")
59 if !reflect.DeepEqual(p.ModelList(), original.Providers[0].ModelList()) || p.VisionModels == nil || len(p.VisionModels) != 0 {
60 t.Fatalf("loader overwrote a curated catalog or explicit vision opt-out: %+v", p)
61 }
62 }
63 if after, err := os.ReadFile(path); err != nil || string(after) != raw {
64 t.Fatal("loading changed the user's file or unknown fields")
65 }
66 })
67 }
68 }
69
70 func TestDeepSeekLegacyReferenceCompatibilityIsScoped(t *testing.T) {
71 for _, tc := range []struct {
72 name, kind, base, request string
73 models []string
74 want bool
75 }{
76 {"chat", "openai", "https://api.deepseek.com/v1", "", []string{"deepseek-flash"}, true},
77 {"exact override", "openai", "https://api.deepseek.com", "https://api.deepseek.com/v1/chat/completions", []string{"deepseek-flash"}, true},
78 {"responses", "responses", "https://api.deepseek.com", "", []string{"deepseek-flash"}, true},
79 {"anthropic", "anthropic", "https://api.deepseek.com/anthropic", "", []string{"deepseek-flash"}, true},
80 {"third party", "openai", "https://gateway.example/v1", "", []string{"deepseek-flash"}, false},
81 {"custom route", "openai", "https://api.deepseek.com/custom", "", []string{"deepseek-flash"}, false},
82 {"custom override", "openai", "https://api.deepseek.com", "https://gateway.example/chat/completions", []string{"deepseek-flash"}, false},
83 {"Flash removed", "openai", "https://api.deepseek.com", "", []string{"deepseek-v4-pro"}, false},
84 } {
85 t.Run(tc.name, func(t *testing.T) {
86 const old = "deepseek-v4-flash"
87 price := &provider.Pricing{Input: 5, Output: 10, Currency: "$"}
88 cfg := &Config{Providers: []ProviderEntry{{Name: "test", Kind: tc.kind, BaseURL: tc.base, RequestURL: tc.request, Models: tc.models,
89 Prices: map[string]*provider.Pricing{old: price}, ModelOverrides: map[string]ProviderModelOverride{old: {SupportedEfforts: []string{"high"}, DefaultEffort: "high"}},
90 }}}
91 for _, ref := range []string{"test/" + old, old} {
92 got, ok := cfg.ResolveModel(ref)
93 if ok != tc.want {
94 t.Fatalf("ResolveModel(%q) = %v, want %v", ref, ok, tc.want)
95 }
96 if ok && (got.Model != old || !reflect.DeepEqual(got.Price, price) || !reflect.DeepEqual(got.SupportedEfforts, []string{"high"})) {
97 t.Fatalf("legacy reference lost its model-specific configuration: %+v", got)
98 }
99 }
100 for _, unknown := range []string{"DeepSeek-V4-Flash", "deepseek-v4.1-flash-expires-on-0910", "deepseek-future"} {
101 if _, ok := cfg.ResolveModel("test/" + unknown); ok {
102 t.Fatalf("invented compatibility for %q", unknown)
103 }
104 }
105 })
106 }
107 }
108
109 func TestDeepSeekEmptyEntriesUseCurrentCatalog(t *testing.T) {
110 for _, id := range []string{"deepseek-chat", "deepseek-anthropic", "deepseek-responses"} {
111 preset, _ := CuratedProviderPreset(id)
112 entry := preset.Entries[0]
113 entry.Models, entry.Model, entry.Default = nil, "", ""
114 cfg := &Config{Providers: []ProviderEntry{entry}}
115 normalizeOfficialDeepSeekModels(cfg)
116 if p := &cfg.Providers[0]; !reflect.DeepEqual(p.ModelList(), []string{"deepseek-flash", "deepseek-v4-pro"}) || p.DefaultModel() != "deepseek-flash" {
117 t.Fatalf("%s empty entry uses stale defaults: %+v", id, p)
118 }
119 }
120 cfg := Default()
121 if p, ok := cfg.ResolveModel(cfg.DefaultModel); !ok || p.Model != "deepseek-flash" {
122 t.Fatalf("new CLI default = %+v", p)
123 }
124 cfg.Desktop.ProviderAccess = []string{"deepseek"}
125 normalizeDesktopOfficialProviderAccess(cfg)
126 normalizeOfficialDeepSeekModels(cfg)
127 if p, ok := cfg.Provider("deepseek"); !ok || !reflect.DeepEqual(p.ModelList(), []string{"deepseek-flash", "deepseek-v4-pro"}) {
128 t.Fatalf("new desktop catalog reintroduced retired aliases: %+v", p)
129 }
130 }
131
131 lines GO