From 8c6f279f0adba72f53cbc4365a1cd3b4c34eae98 Mon Sep 17 00:00:00 2001 From: hkfires <10558748+hkfires@users.noreply.github.com> Date: Wed, 17 Jun 2026 08:11:25 +0800 Subject: [PATCH] refactor(tests): remove obsolete test files and update reasoning effort logic --- internal/thinking/apply_user_defined_test.go | 55 ----- internal/thinking/provider/kimi/apply_test.go | 72 ------ internal/thinking/provider/xai/apply_test.go | 51 ----- internal/thinking/reasoning_effort_test.go | 31 --- internal/thinking/validate.go | 2 +- test/thinking_conversion_test.go | 205 ++++++++++++++++++ 6 files changed, 206 insertions(+), 210 deletions(-) delete mode 100644 internal/thinking/apply_user_defined_test.go delete mode 100644 internal/thinking/provider/kimi/apply_test.go delete mode 100644 internal/thinking/provider/xai/apply_test.go delete mode 100644 internal/thinking/reasoning_effort_test.go diff --git a/internal/thinking/apply_user_defined_test.go b/internal/thinking/apply_user_defined_test.go deleted file mode 100644 index c485d252..00000000 --- a/internal/thinking/apply_user_defined_test.go +++ /dev/null @@ -1,55 +0,0 @@ -package thinking_test - -import ( - "testing" - - "github.com/router-for-me/CLIProxyAPI/v7/internal/registry" - "github.com/router-for-me/CLIProxyAPI/v7/internal/thinking" - _ "github.com/router-for-me/CLIProxyAPI/v7/internal/thinking/provider/claude" - "github.com/tidwall/gjson" -) - -func TestApplyThinking_UserDefinedClaudePreservesAdaptiveLevel(t *testing.T) { - reg := registry.GetGlobalRegistry() - clientID := "test-user-defined-claude-" + t.Name() - modelID := "custom-claude-4-6" - reg.RegisterClient(clientID, "claude", []*registry.ModelInfo{{ID: modelID, UserDefined: true}}) - t.Cleanup(func() { - reg.UnregisterClient(clientID) - }) - - tests := []struct { - name string - model string - body []byte - }{ - { - name: "claude adaptive effort body", - model: modelID, - body: []byte(`{"thinking":{"type":"adaptive"},"output_config":{"effort":"high"}}`), - }, - { - name: "suffix level", - model: modelID + "(high)", - body: []byte(`{}`), - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - out, err := thinking.ApplyThinking(tt.body, tt.model, "openai", "claude", "claude") - if err != nil { - t.Fatalf("ApplyThinking() error = %v", err) - } - if got := gjson.GetBytes(out, "thinking.type").String(); got != "adaptive" { - t.Fatalf("thinking.type = %q, want %q, body=%s", got, "adaptive", string(out)) - } - if got := gjson.GetBytes(out, "output_config.effort").String(); got != "high" { - t.Fatalf("output_config.effort = %q, want %q, body=%s", got, "high", string(out)) - } - if gjson.GetBytes(out, "thinking.budget_tokens").Exists() { - t.Fatalf("thinking.budget_tokens should be removed, body=%s", string(out)) - } - }) - } -} diff --git a/internal/thinking/provider/kimi/apply_test.go b/internal/thinking/provider/kimi/apply_test.go deleted file mode 100644 index 78069424..00000000 --- a/internal/thinking/provider/kimi/apply_test.go +++ /dev/null @@ -1,72 +0,0 @@ -package kimi - -import ( - "testing" - - "github.com/router-for-me/CLIProxyAPI/v7/internal/registry" - "github.com/router-for-me/CLIProxyAPI/v7/internal/thinking" - "github.com/tidwall/gjson" -) - -func TestApply_ModeNone_UsesDisabledThinking(t *testing.T) { - applier := NewApplier() - modelInfo := ®istry.ModelInfo{ - ID: "kimi-k2.5", - Thinking: ®istry.ThinkingSupport{Min: 1024, Max: 32000, ZeroAllowed: true, DynamicAllowed: true}, - } - body := []byte(`{"model":"kimi-k2.5","reasoning_effort":"none","thinking":{"type":"enabled","budget_tokens":2048}}`) - - out, errApply := applier.Apply(body, thinking.ThinkingConfig{Mode: thinking.ModeNone}, modelInfo) - if errApply != nil { - t.Fatalf("Apply() error = %v", errApply) - } - if got := gjson.GetBytes(out, "thinking.type").String(); got != "disabled" { - t.Fatalf("thinking.type = %q, want %q, body=%s", got, "disabled", string(out)) - } - if gjson.GetBytes(out, "thinking.budget_tokens").Exists() { - t.Fatalf("thinking.budget_tokens should be removed, body=%s", string(out)) - } - if gjson.GetBytes(out, "reasoning_effort").Exists() { - t.Fatalf("reasoning_effort should be removed in ModeNone, body=%s", string(out)) - } -} - -func TestApply_ModeLevel_UsesReasoningEffort(t *testing.T) { - applier := NewApplier() - modelInfo := ®istry.ModelInfo{ - ID: "kimi-k2.5", - Thinking: ®istry.ThinkingSupport{Min: 1024, Max: 32000, ZeroAllowed: true, DynamicAllowed: true}, - } - body := []byte(`{"model":"kimi-k2.5","thinking":{"type":"disabled"}}`) - - out, errApply := applier.Apply(body, thinking.ThinkingConfig{Mode: thinking.ModeLevel, Level: thinking.LevelHigh}, modelInfo) - if errApply != nil { - t.Fatalf("Apply() error = %v", errApply) - } - if got := gjson.GetBytes(out, "reasoning_effort").String(); got != "high" { - t.Fatalf("reasoning_effort = %q, want %q, body=%s", got, "high", string(out)) - } - if gjson.GetBytes(out, "thinking").Exists() { - t.Fatalf("thinking should be removed when reasoning_effort is used, body=%s", string(out)) - } -} - -func TestApply_UserDefinedModeNone_UsesDisabledThinking(t *testing.T) { - applier := NewApplier() - modelInfo := ®istry.ModelInfo{ - ID: "custom-kimi-model", - UserDefined: true, - } - body := []byte(`{"model":"custom-kimi-model","reasoning_effort":"none"}`) - - out, errApply := applier.Apply(body, thinking.ThinkingConfig{Mode: thinking.ModeNone}, modelInfo) - if errApply != nil { - t.Fatalf("Apply() error = %v", errApply) - } - if got := gjson.GetBytes(out, "thinking.type").String(); got != "disabled" { - t.Fatalf("thinking.type = %q, want %q, body=%s", got, "disabled", string(out)) - } - if gjson.GetBytes(out, "reasoning_effort").Exists() { - t.Fatalf("reasoning_effort should be removed in ModeNone, body=%s", string(out)) - } -} diff --git a/internal/thinking/provider/xai/apply_test.go b/internal/thinking/provider/xai/apply_test.go deleted file mode 100644 index 17f99f56..00000000 --- a/internal/thinking/provider/xai/apply_test.go +++ /dev/null @@ -1,51 +0,0 @@ -package xai - -import ( - "testing" - - "github.com/router-for-me/CLIProxyAPI/v7/internal/registry" - "github.com/router-for-me/CLIProxyAPI/v7/internal/thinking" - "github.com/tidwall/gjson" -) - -func TestApplySetsReasoningEffort(t *testing.T) { - applier := NewApplier() - modelInfo := ®istry.ModelInfo{ - ID: "grok-4.3", - Thinking: ®istry.ThinkingSupport{ - ZeroAllowed: true, - Levels: []string{"none", "low", "medium", "high"}, - }, - } - - out, err := applier.Apply([]byte(`{"input":"hello"}`), thinking.ThinkingConfig{ - Mode: thinking.ModeLevel, - Level: thinking.LevelHigh, - }, modelInfo) - if err != nil { - t.Fatalf("Apply() error = %v", err) - } - if got := gjson.GetBytes(out, "reasoning.effort").String(); got != "high" { - t.Fatalf("reasoning.effort = %q, want high; body=%s", got, string(out)) - } -} - -func TestApplyNoneFallsBackToLowestLevelWhenDisableUnsupported(t *testing.T) { - applier := NewApplier() - modelInfo := ®istry.ModelInfo{ - ID: "grok-3-mini", - Thinking: ®istry.ThinkingSupport{ - Levels: []string{"low", "medium", "high"}, - }, - } - - out, err := applier.Apply([]byte(`{"input":"hello"}`), thinking.ThinkingConfig{ - Mode: thinking.ModeNone, - }, modelInfo) - if err != nil { - t.Fatalf("Apply() error = %v", err) - } - if got := gjson.GetBytes(out, "reasoning.effort").String(); got != "low" { - t.Fatalf("reasoning.effort = %q, want low; body=%s", got, string(out)) - } -} diff --git a/internal/thinking/reasoning_effort_test.go b/internal/thinking/reasoning_effort_test.go deleted file mode 100644 index e529e115..00000000 --- a/internal/thinking/reasoning_effort_test.go +++ /dev/null @@ -1,31 +0,0 @@ -package thinking - -import "testing" - -func TestExtractReasoningEffortUsesSuffixOverBody(t *testing.T) { - got := ExtractReasoningEffort([]byte(`{"reasoning_effort":"low"}`), "openai", "gpt-5.4(high)") - if got != "high" { - t.Fatalf("ExtractReasoningEffort() = %q, want %q", got, "high") - } -} - -func TestExtractReasoningEffortConvertsBudgetToLevel(t *testing.T) { - got := ExtractReasoningEffort([]byte(`{"thinking":{"type":"enabled","budget_tokens":8192}}`), "claude", "claude-sonnet-4-5") - if got != "medium" { - t.Fatalf("ExtractReasoningEffort() = %q, want %q", got, "medium") - } -} - -func TestExtractReasoningEffortSupportsOpenAIResponses(t *testing.T) { - got := ExtractReasoningEffort([]byte(`{"reasoning":{"effort":"medium"}}`), "openai-response", "gpt-5.4") - if got != "medium" { - t.Fatalf("ExtractReasoningEffort() = %q, want %q", got, "medium") - } -} - -func TestExtractReasoningEffortMissingConfigIsEmpty(t *testing.T) { - got := ExtractReasoningEffort([]byte(`{"messages":[{"role":"user","content":"hi"}]}`), "openai", "gpt-5.4") - if got != "" { - t.Fatalf("ExtractReasoningEffort() = %q, want empty", got) - } -} diff --git a/internal/thinking/validate.go b/internal/thinking/validate.go index 909a2eea..2baa93f1 100644 --- a/internal/thinking/validate.go +++ b/internal/thinking/validate.go @@ -357,7 +357,7 @@ func isGeminiFamily(provider string) bool { func isOpenAIFamily(provider string) bool { switch provider { - case "openai", "openai-response", "codex", "xai": + case "openai", "openai-response", "codex": return true default: return false diff --git a/test/thinking_conversion_test.go b/test/thinking_conversion_test.go index 9173aa01..430eb925 100644 --- a/test/thinking_conversion_test.go +++ b/test/thinking_conversion_test.go @@ -15,6 +15,7 @@ import ( _ "github.com/router-for-me/CLIProxyAPI/v7/internal/thinking/provider/geminicli" _ "github.com/router-for-me/CLIProxyAPI/v7/internal/thinking/provider/kimi" _ "github.com/router-for-me/CLIProxyAPI/v7/internal/thinking/provider/openai" + _ "github.com/router-for-me/CLIProxyAPI/v7/internal/thinking/provider/xai" "github.com/router-for-me/CLIProxyAPI/v7/internal/registry" "github.com/router-for-me/CLIProxyAPI/v7/internal/thinking" @@ -2238,6 +2239,186 @@ func TestThinkingE2EMatrix_Body(t *testing.T) { runThinkingTests(t, cases) } +// TestThinkingE2ENewProviderTargets covers provider-specific targets that do not +// have their own public translator format but do have ApplyThinking providers. +func TestThinkingE2ENewProviderTargets(t *testing.T) { + reg := registry.GetGlobalRegistry() + uid := fmt.Sprintf("thinking-e2e-new-providers-%d", time.Now().UnixNano()) + + reg.RegisterClient(uid, "test", getTestModels()) + defer reg.UnregisterClient(uid) + + cases := []thinkingTestCase{ + // Kimi target: enabled thinking uses reasoning_effort, explicit disable uses thinking.type=disabled. + { + name: "K1", + from: "openai", + to: "kimi", + model: "kimi-level-model(high)", + inputJSON: `{"model":"kimi-level-model(high)","messages":[{"role":"user","content":"hi"}]}`, + expectField: "reasoning_effort", + expectValue: "high", + }, + { + name: "K2", + from: "openai", + to: "kimi", + model: "kimi-level-model(none)", + inputJSON: `{"model":"kimi-level-model(none)","messages":[{"role":"user","content":"hi"}]}`, + expectField: "thinking.type", + expectValue: "disabled", + }, + { + name: "K3", + from: "gemini", + to: "kimi", + model: "kimi-level-model(32768)", + inputJSON: `{"model":"kimi-level-model(32768)","contents":[{"role":"user","parts":[{"text":"hi"}]}]}`, + expectField: "reasoning_effort", + expectValue: "high", + }, + { + name: "K4", + from: "claude", + to: "kimi", + model: "kimi-level-model(0)", + inputJSON: `{"model":"kimi-level-model(0)","messages":[{"role":"user","content":"hi"}]}`, + expectField: "thinking.type", + expectValue: "disabled", + }, + { + name: "K5", + from: "openai", + to: "kimi", + model: "kimi-level-model", + inputJSON: `{"model":"kimi-level-model","messages":[{"role":"user","content":"hi"}],"reasoning_effort":"high"}`, + expectField: "reasoning_effort", + expectValue: "high", + }, + { + name: "K6", + from: "openai-response", + to: "kimi", + model: "kimi-level-model", + inputJSON: `{"model":"kimi-level-model","input":[{"role":"user","content":"hi"}],"reasoning":{"effort":"none"}}`, + expectField: "thinking.type", + expectValue: "disabled", + }, + { + name: "K7", + from: "gemini", + to: "kimi", + model: "kimi-level-model", + inputJSON: `{"model":"kimi-level-model","contents":[{"role":"user","parts":[{"text":"hi"}]}],"generationConfig":{"thinkingConfig":{"thinkingBudget":32768}}}`, + expectField: "reasoning_effort", + expectValue: "high", + }, + { + name: "K8", + from: "claude", + to: "kimi", + model: "kimi-level-model", + inputJSON: `{"model":"kimi-level-model","messages":[{"role":"user","content":"hi"}],"thinking":{"type":"enabled","budget_tokens":0}}`, + expectField: "thinking.type", + expectValue: "disabled", + }, + + // xAI target: Grok uses Responses-compatible reasoning.effort with Grok-specific levels. + { + name: "X1", + from: "openai", + to: "xai", + model: "xai-level-model(high)", + inputJSON: `{"model":"xai-level-model(high)","messages":[{"role":"user","content":"hi"}]}`, + expectField: "reasoning.effort", + expectValue: "high", + }, + { + name: "X2", + from: "openai", + to: "xai", + model: "xai-level-model(xhigh)", + inputJSON: `{"model":"xai-level-model(xhigh)","messages":[{"role":"user","content":"hi"}]}`, + expectField: "reasoning.effort", + expectValue: "high", + }, + { + name: "X3", + from: "openai-response", + to: "xai", + model: "xai-level-model(max)", + inputJSON: `{"model":"xai-level-model(max)","input":[{"role":"user","content":"hi"}]}`, + expectField: "reasoning.effort", + expectValue: "high", + }, + { + name: "X4", + from: "gemini", + to: "xai", + model: "xai-level-model(512)", + inputJSON: `{"model":"xai-level-model(512)","contents":[{"role":"user","parts":[{"text":"hi"}]}]}`, + expectField: "reasoning.effort", + expectValue: "low", + }, + { + name: "X5", + from: "claude", + to: "xai", + model: "xai-level-model(0)", + inputJSON: `{"model":"xai-level-model(0)","messages":[{"role":"user","content":"hi"}]}`, + expectField: "reasoning.effort", + expectValue: "none", + }, + { + name: "X6", + from: "openai", + to: "xai", + model: "xai-level-model", + inputJSON: `{"model":"xai-level-model","messages":[{"role":"user","content":"hi"}],"reasoning_effort":"xhigh"}`, + expectField: "reasoning.effort", + expectValue: "high", + }, + { + name: "X7", + from: "openai-response", + to: "xai", + model: "xai-level-model", + inputJSON: `{"model":"xai-level-model","input":[{"role":"user","content":"hi"}],"reasoning":{"effort":"minimal"}}`, + expectField: "reasoning.effort", + expectValue: "low", + }, + { + name: "X8", + from: "gemini", + to: "xai", + model: "xai-level-model", + inputJSON: `{"model":"xai-level-model","contents":[{"role":"user","parts":[{"text":"hi"}]}],"generationConfig":{"thinkingConfig":{"thinkingBudget":32768}}}`, + expectField: "reasoning.effort", + expectValue: "high", + }, + { + name: "X9", + from: "claude", + to: "xai", + model: "xai-level-model", + inputJSON: `{"model":"xai-level-model","messages":[{"role":"user","content":"hi"}],"thinking":{"type":"enabled","budget_tokens":0}}`, + expectField: "reasoning.effort", + expectValue: "none", + }, + { + name: "X10", + from: "claude", + to: "xai", + model: "xai-level-model", + inputJSON: `{"model":"xai-level-model","messages":[{"role":"user","content":"hi"}],"thinking":{"type":"adaptive"},"output_config":{"effort":"max"}}`, + expectField: "reasoning.effort", + expectValue: "high", + }, + } + + runThinkingTests(t, cases) +} + // TestThinkingE2EClaudeAdaptive_Body covers Group 3 cases in docs/thinking-e2e-test-cases.md. // It focuses on Claude 4.6 adaptive thinking and effort/level cross-protocol semantics (body-only). func TestThinkingE2EClaudeAdaptive_Body(t *testing.T) { @@ -2817,6 +2998,24 @@ func getTestModels() []*registry.ModelInfo { DisplayName: "Antigravity Budget Model", Thinking: ®istry.ThinkingSupport{Min: 128, Max: 20000, ZeroAllowed: true, DynamicAllowed: true}, }, + { + ID: "kimi-level-model", + Object: "model", + Created: 1700000000, + OwnedBy: "moonshot", + Type: "kimi", + DisplayName: "Kimi Level Model", + Thinking: ®istry.ThinkingSupport{Levels: []string{"low", "medium", "high"}, ZeroAllowed: true, DynamicAllowed: false}, + }, + { + ID: "xai-level-model", + Object: "model", + Created: 1700000000, + OwnedBy: "xai", + Type: "xai", + DisplayName: "xAI Level Model", + Thinking: ®istry.ThinkingSupport{Levels: []string{"none", "low", "medium", "high"}, ZeroAllowed: true, DynamicAllowed: false}, + }, { ID: "no-thinking-model", Object: "model", @@ -2850,6 +3049,12 @@ func runThinkingTests(t *testing.T, cases []thinkingTestCase) { translateTo := tc.to applyTo := tc.to + switch applyTo { + case "kimi": + translateTo = "openai" + case "xai": + translateTo = "codex" + } body := sdktranslator.TranslateRequest( sdktranslator.FromString(tc.from), -- 2.51.2