返回 DeepSeek-Reasonix
reasoning_replay_policy_test.go
根目录 / internal / provider / openai / reasoning_replay_policy_test.go
1 package openai
2
3 import (
4 "testing"
5
6 "reasonix/internal/provider"
7 )
8
9 func TestDeepSeekReplaysEveryReasoningCarryingAssistantTurn(t *testing.T) {
10 p, err := New(provider.Config{
11 Name: "deepseek", BaseURL: "https://api.deepseek.com", Model: "deepseek-v4-pro", APIKey: "k",
12 Extra: map[string]any{"reasoning_protocol": "deepseek", "thinking": "enabled"},
13 })
14 if err != nil {
15 t.Fatalf("New: %v", err)
16 }
17 if !provider.RequiresAssistantReasoningReplay(p, provider.Message{
18 Role: provider.RoleAssistant, Content: "plain", ReasoningContent: "provider reasoning",
19 }) {
20 t.Fatal("DeepSeek plain assistant reasoning must be replay-required")
21 }
22 if provider.RequiresAssistantReasoningReplay(p, provider.Message{
23 Role: provider.RoleAssistant, Content: "plain",
24 }) {
25 t.Fatal("DeepSeek plain assistant without reasoning must not invent a replay requirement")
26 }
27 if !provider.RequiresAssistantReasoningReplay(p, provider.Message{
28 Role: provider.RoleAssistant,
29 ToolCalls: []provider.ToolCall{{ID: "call_1", Name: "read_file"}},
30 }) {
31 t.Fatal("DeepSeek tool turn must remain replay-required when reasoning is missing")
32 }
33
34 disabled := p.(*client)
35 disabled.thinkingType = "disabled"
36 if !provider.RequiresAssistantReasoningReplay(disabled, provider.Message{
37 Role: provider.RoleAssistant, Content: "plain", ReasoningContent: "previous reasoning",
38 }) {
39 t.Fatal("thinking-disabled DeepSeek must still replay stored reasoning")
40 }
41 if provider.RequiresAssistantReasoningReplay(disabled, provider.Message{
42 Role: provider.RoleAssistant,
43 ToolCalls: []provider.ToolCall{{ID: "call_2", Name: "read_file"}},
44 }) {
45 t.Fatal("thinking-disabled DeepSeek must not require missing tool reasoning")
46 }
47 }
48
49 func TestGLMPreservesIssuedReasoningAndAllowsEmptyToolFallback(t *testing.T) {
50 p, err := New(provider.Config{
51 Name: "glm", BaseURL: "https://open.bigmodel.cn/api/coding/paas/v4", Model: "glm-5.2", APIKey: "k",
52 Extra: map[string]any{"reasoning_protocol": "glm"},
53 })
54 if err != nil {
55 t.Fatalf("New: %v", err)
56 }
57 if !provider.RequiresReasoningRoundTrip(p) {
58 t.Fatal("thinking-enabled GLM must preserve provider-issued reasoning")
59 }
60 if !provider.RequiresAssistantReasoningReplay(p, provider.Message{Role: provider.RoleAssistant, ReasoningContent: "provider reasoning"}) {
61 t.Fatal("GLM must replay reasoning the provider emitted")
62 }
63 if provider.RequiresAssistantReasoningReplay(p, provider.Message{Role: provider.RoleAssistant, ToolCalls: []provider.ToolCall{{ID: "call_1", Name: "read_file"}}}) {
64 t.Fatal("GLM tool turns without provider reasoning must remain replayable")
65 }
66 if !provider.AllowsEmptyReasoningFallback(p) {
67 t.Fatal("GLM must accept an empty reasoning_content value when the provider emitted none")
68 }
69 if provider.RequiresAssistantReasoningReplay(p, provider.Message{Role: provider.RoleAssistant, Content: "plain answer"}) {
70 t.Fatal("GLM plain answers without reasoning must remain replayable")
71 }
72 }
73
74 func TestGLMSerializesEmptyReasoningContentForToolHistory(t *testing.T) {
75 p, err := New(provider.Config{
76 Name: "glm", BaseURL: "https://open.bigmodel.cn/api/coding/paas/v4", Model: "glm-5.2", APIKey: "k",
77 Extra: map[string]any{"reasoning_protocol": "glm"},
78 })
79 if err != nil {
80 t.Fatalf("New: %v", err)
81 }
82 req := p.(*client).buildRequest(provider.Request{Messages: []provider.Message{
83 {Role: provider.RoleUser, Content: "inspect"},
84 {Role: provider.RoleAssistant, ToolCalls: []provider.ToolCall{{ID: "call_1", Name: "read_file", Arguments: `{}`}}},
85 {Role: provider.RoleTool, ToolCallID: "call_1", Name: "read_file", Content: "package main"},
86 }})
87 if got := req.Messages[1].ReasoningContent; got == nil || *got != "" {
88 t.Fatalf("GLM empty tool reasoning_content = %v, want explicit empty string", got)
89 }
90 }
91
91 lines GO