返回 DeepSeek-Reasonix
reasoning_test.go
根目录 / internal / provider / reasoning_test.go
1 package provider_test
2
3 import (
4 "context"
5 "encoding/json"
6 "errors"
7 "net/http"
8 "net/http/httptest"
9 "reflect"
10 "testing"
11
12 "reasonix/internal/provider"
13 _ "reasonix/internal/provider/anthropic"
14 _ "reasonix/internal/provider/openai"
15 _ "reasonix/internal/provider/responses"
16 )
17
18 func TestAdapterReasoningExactSelectionBeforeIO(t *testing.T) {
19 for _, kind := range []string{"openai", "anthropic", "responses"} {
20 t.Run(kind, func(t *testing.T) {
21 calls := 0
22 var body map[string]any
23 server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
24 calls++
25 if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
26 t.Error(err)
27 }
28 // A terminal response exercises egress without invoking any external model.
29 w.WriteHeader(http.StatusBadRequest)
30 _, _ = w.Write([]byte(`{"error":{"message":"contract fixture"}}`))
31 }))
32 defer server.Close()
33 p, err := provider.New(kind, provider.Config{Name: "fixture", BaseURL: server.URL, Model: "exact-model", APIKey: "fixture", Extra: map[string]any{
34 "request_url": server.URL, "reasoning_protocol": "deepseek", "thinking": "adaptive",
35 "supported_efforts": []string{"low", "high", "max"}, "effort": "high",
36 }})
37 if err != nil {
38 t.Fatal(err)
39 }
40 declared, ok := p.(provider.ReasoningProvider)
41 if !ok {
42 t.Fatal("adapter does not expose reasoning capability")
43 }
44 cap := declared.ReasoningCapability()
45 if !reflect.DeepEqual(cap.IDs(), []string{"low", "high", "max"}) {
46 t.Fatalf("options: %+v", cap)
47 }
48 cap.Options[0].ID = "corrupted"
49 if declared.ReasoningCapability().IDs()[0] != "low" {
50 t.Fatal("mutable capability escaped adapter")
51 }
52 for _, bad := range []string{"medium", "HIGH", " high ", "auto", "off"} {
53 _, err = p.Stream(context.Background(), provider.Request{EffortOverride: bad})
54 var unsupported *provider.UnsupportedReasoningEffort
55 if !errors.As(err, &unsupported) {
56 t.Fatalf("%q: expected typed error, got %v", bad, err)
57 }
58 if calls != 0 {
59 t.Fatalf("unsupported %q reached network", bad)
60 }
61 }
62 for _, level := range declared.ReasoningCapability().IDs() {
63 _, _ = p.Stream(context.Background(), provider.Request{EffortOverride: level})
64 var got any
65 switch kind {
66 case "openai":
67 got = body["reasoning_effort"]
68 case "anthropic":
69 got = body["output_config"].(map[string]any)["effort"]
70 case "responses":
71 got = body["reasoning"].(map[string]any)["effort"]
72 }
73 if got != level {
74 t.Fatalf("selected %q, emitted %v", level, got)
75 }
76 }
77 if calls != 3 {
78 t.Fatalf("calls=%d", calls)
79 }
80 })
81 }
82 }
83
83 lines GO