diff --git a/test/thinking_conversion_test.go b/test/thinking_conversion_test.go index f28aa630..91490fa2 100644 --- a/test/thinking_conversion_test.go +++ b/test/thinking_conversion_test.go @@ -22,15 +22,2425 @@ import ( "github.com/tidwall/gjson" ) -// TestThinkingE2EMatrix tests the thinking configuration transformation using the real data flow path. +// thinkingTestCase represents a common test case structure for both suffix and body tests. +type thinkingTestCase struct { + name string + from string + to string + model string + inputJSON string + expectField string + expectValue string + includeThoughts string + expectErr bool +} + +// TestThinkingE2EMatrix_Suffix tests the thinking configuration transformation using model name suffix. // Data flow: Input JSON → TranslateRequest → ApplyThinking → Validate Output // No helper functions are used; all test data is inline. -func TestThinkingE2EMatrix(t *testing.T) { - // Register test models directly +func TestThinkingE2EMatrix_Suffix(t *testing.T) { reg := registry.GetGlobalRegistry() - uid := fmt.Sprintf("thinking-e2e-%d", time.Now().UnixNano()) + uid := fmt.Sprintf("thinking-e2e-suffix-%d", time.Now().UnixNano()) - testModels := []*registry.ModelInfo{ + reg.RegisterClient(uid, "test", getTestModels()) + defer reg.UnregisterClient(uid) + + cases := []thinkingTestCase{ + // level-model (Levels=minimal/low/medium/high, ZeroAllowed=false, DynamicAllowed=false) + + // Case 1: No suffix → injected default → medium + { + name: "1", + from: "openai", + to: "codex", + model: "level-model", + inputJSON: `{"model":"level-model","messages":[{"role":"user","content":"hi"}]}`, + expectField: "reasoning.effort", + expectValue: "medium", + expectErr: false, + }, + // Case 2: Specified medium → medium + { + name: "2", + from: "openai", + to: "codex", + model: "level-model(medium)", + inputJSON: `{"model":"level-model(medium)","messages":[{"role":"user","content":"hi"}]}`, + expectField: "reasoning.effort", + expectValue: "medium", + expectErr: false, + }, + // Case 3: Specified xhigh → out of range error + { + name: "3", + from: "openai", + to: "codex", + model: "level-model(xhigh)", + inputJSON: `{"model":"level-model(xhigh)","messages":[{"role":"user","content":"hi"}]}`, + expectField: "", + expectErr: true, + }, + // Case 4: Level none → clamped to minimal (ZeroAllowed=false) + { + name: "4", + from: "openai", + to: "codex", + model: "level-model(none)", + inputJSON: `{"model":"level-model(none)","messages":[{"role":"user","content":"hi"}]}`, + expectField: "reasoning.effort", + expectValue: "minimal", + expectErr: false, + }, + // Case 5: Level auto → DynamicAllowed=false → medium (mid-range) + { + name: "5", + from: "openai", + to: "codex", + model: "level-model(auto)", + inputJSON: `{"model":"level-model(auto)","messages":[{"role":"user","content":"hi"}]}`, + expectField: "reasoning.effort", + expectValue: "medium", + expectErr: false, + }, + // Case 6: No suffix from gemini → injected default → medium + { + name: "6", + from: "gemini", + to: "codex", + model: "level-model", + inputJSON: `{"model":"level-model","contents":[{"role":"user","parts":[{"text":"hi"}]}]}`, + expectField: "reasoning.effort", + expectValue: "medium", + expectErr: false, + }, + // Case 7: Budget 8192 → medium + { + name: "7", + from: "gemini", + to: "codex", + model: "level-model(8192)", + inputJSON: `{"model":"level-model(8192)","contents":[{"role":"user","parts":[{"text":"hi"}]}]}`, + expectField: "reasoning.effort", + expectValue: "medium", + expectErr: false, + }, + // Case 8: Budget 64000 → clamped to high + { + name: "8", + from: "gemini", + to: "codex", + model: "level-model(64000)", + inputJSON: `{"model":"level-model(64000)","contents":[{"role":"user","parts":[{"text":"hi"}]}]}`, + expectField: "reasoning.effort", + expectValue: "high", + expectErr: false, + }, + // Case 9: Budget 0 → clamped to minimal (ZeroAllowed=false) + { + name: "9", + from: "gemini", + to: "codex", + model: "level-model(0)", + inputJSON: `{"model":"level-model(0)","contents":[{"role":"user","parts":[{"text":"hi"}]}]}`, + expectField: "reasoning.effort", + expectValue: "minimal", + expectErr: false, + }, + // Case 10: Budget -1 → auto → DynamicAllowed=false → medium (mid-range) + { + name: "10", + from: "gemini", + to: "codex", + model: "level-model(-1)", + inputJSON: `{"model":"level-model(-1)","contents":[{"role":"user","parts":[{"text":"hi"}]}]}`, + expectField: "reasoning.effort", + expectValue: "medium", + expectErr: false, + }, + // Case 11: Claude source no suffix → passthrough (no thinking) + { + name: "11", + from: "claude", + to: "openai", + model: "level-model", + inputJSON: `{"model":"level-model","messages":[{"role":"user","content":"hi"}]}`, + expectField: "", + expectErr: false, + }, + // Case 12: Budget 8192 → medium + { + name: "12", + from: "claude", + to: "openai", + model: "level-model(8192)", + inputJSON: `{"model":"level-model(8192)","messages":[{"role":"user","content":"hi"}]}`, + expectField: "reasoning_effort", + expectValue: "medium", + expectErr: false, + }, + // Case 13: Budget 64000 → clamped to high + { + name: "13", + from: "claude", + to: "openai", + model: "level-model(64000)", + inputJSON: `{"model":"level-model(64000)","messages":[{"role":"user","content":"hi"}]}`, + expectField: "reasoning_effort", + expectValue: "high", + expectErr: false, + }, + // Case 14: Budget 0 → clamped to minimal (ZeroAllowed=false) + { + name: "14", + from: "claude", + to: "openai", + model: "level-model(0)", + inputJSON: `{"model":"level-model(0)","messages":[{"role":"user","content":"hi"}]}`, + expectField: "reasoning_effort", + expectValue: "minimal", + expectErr: false, + }, + // Case 15: Budget -1 → auto → DynamicAllowed=false → medium (mid-range) + { + name: "15", + from: "claude", + to: "openai", + model: "level-model(-1)", + inputJSON: `{"model":"level-model(-1)","messages":[{"role":"user","content":"hi"}]}`, + expectField: "reasoning_effort", + expectValue: "medium", + expectErr: false, + }, + + // level-subset-model (Levels=low/high, ZeroAllowed=false, DynamicAllowed=false) + + // Case 16: Budget 8192 → medium → rounded down to low + { + name: "16", + from: "gemini", + to: "openai", + model: "level-subset-model(8192)", + inputJSON: `{"model":"level-subset-model(8192)","contents":[{"role":"user","parts":[{"text":"hi"}]}]}`, + expectField: "reasoning_effort", + expectValue: "low", + expectErr: false, + }, + // Case 17: Budget 1 → minimal → clamped to low (min supported) + { + name: "17", + from: "claude", + to: "gemini", + model: "level-subset-model(1)", + inputJSON: `{"model":"level-subset-model(1)","messages":[{"role":"user","content":"hi"}]}`, + expectField: "generationConfig.thinkingConfig.thinkingLevel", + expectValue: "low", + includeThoughts: "true", + expectErr: false, + }, + + // gemini-budget-model (Min=128, Max=20000, ZeroAllowed=false, DynamicAllowed=true) + + // Case 18: No suffix → passthrough + { + name: "18", + from: "openai", + to: "gemini", + model: "gemini-budget-model", + inputJSON: `{"model":"gemini-budget-model","messages":[{"role":"user","content":"hi"}]}`, + expectField: "", + expectErr: false, + }, + // Case 19: Effort medium → 8192 + { + name: "19", + from: "openai", + to: "gemini", + model: "gemini-budget-model(medium)", + inputJSON: `{"model":"gemini-budget-model(medium)","messages":[{"role":"user","content":"hi"}]}`, + expectField: "generationConfig.thinkingConfig.thinkingBudget", + expectValue: "8192", + includeThoughts: "true", + expectErr: false, + }, + // Case 20: Effort xhigh → clamped to 20000 (max) + { + name: "20", + from: "openai", + to: "gemini", + model: "gemini-budget-model(xhigh)", + inputJSON: `{"model":"gemini-budget-model(xhigh)","messages":[{"role":"user","content":"hi"}]}`, + expectField: "generationConfig.thinkingConfig.thinkingBudget", + expectValue: "20000", + includeThoughts: "true", + expectErr: false, + }, + // Case 21: Effort none → clamped to 128 (min) → includeThoughts=false + { + name: "21", + from: "openai", + to: "gemini", + model: "gemini-budget-model(none)", + inputJSON: `{"model":"gemini-budget-model(none)","messages":[{"role":"user","content":"hi"}]}`, + expectField: "generationConfig.thinkingConfig.thinkingBudget", + expectValue: "128", + includeThoughts: "false", + expectErr: false, + }, + // Case 22: Effort auto → DynamicAllowed=true → -1 + { + name: "22", + from: "openai", + to: "gemini", + model: "gemini-budget-model(auto)", + inputJSON: `{"model":"gemini-budget-model(auto)","messages":[{"role":"user","content":"hi"}]}`, + expectField: "generationConfig.thinkingConfig.thinkingBudget", + expectValue: "-1", + includeThoughts: "true", + expectErr: false, + }, + // Case 23: Claude source no suffix → passthrough + { + name: "23", + from: "claude", + to: "gemini", + model: "gemini-budget-model", + inputJSON: `{"model":"gemini-budget-model","messages":[{"role":"user","content":"hi"}]}`, + expectField: "", + expectErr: false, + }, + // Case 24: Budget 8192 → 8192 + { + name: "24", + from: "claude", + to: "gemini", + model: "gemini-budget-model(8192)", + inputJSON: `{"model":"gemini-budget-model(8192)","messages":[{"role":"user","content":"hi"}]}`, + expectField: "generationConfig.thinkingConfig.thinkingBudget", + expectValue: "8192", + includeThoughts: "true", + expectErr: false, + }, + // Case 25: Budget 64000 → clamped to 20000 (max) + { + name: "25", + from: "claude", + to: "gemini", + model: "gemini-budget-model(64000)", + inputJSON: `{"model":"gemini-budget-model(64000)","messages":[{"role":"user","content":"hi"}]}`, + expectField: "generationConfig.thinkingConfig.thinkingBudget", + expectValue: "20000", + includeThoughts: "true", + expectErr: false, + }, + // Case 26: Budget 0 → clamped to 128 (min) → includeThoughts=false + { + name: "26", + from: "claude", + to: "gemini", + model: "gemini-budget-model(0)", + inputJSON: `{"model":"gemini-budget-model(0)","messages":[{"role":"user","content":"hi"}]}`, + expectField: "generationConfig.thinkingConfig.thinkingBudget", + expectValue: "128", + includeThoughts: "false", + expectErr: false, + }, + // Case 27: Budget -1 → DynamicAllowed=true → -1 + { + name: "27", + from: "claude", + to: "gemini", + model: "gemini-budget-model(-1)", + inputJSON: `{"model":"gemini-budget-model(-1)","messages":[{"role":"user","content":"hi"}]}`, + expectField: "generationConfig.thinkingConfig.thinkingBudget", + expectValue: "-1", + includeThoughts: "true", + expectErr: false, + }, + + // gemini-mixed-model (Min=128, Max=32768, Levels=low/high, ZeroAllowed=false, DynamicAllowed=true) + + // Case 28: OpenAI source no suffix → passthrough + { + name: "28", + from: "openai", + to: "gemini", + model: "gemini-mixed-model", + inputJSON: `{"model":"gemini-mixed-model","messages":[{"role":"user","content":"hi"}]}`, + expectField: "", + expectErr: false, + }, + // Case 29: Effort high → low/high supported → high + { + name: "29", + from: "openai", + to: "gemini", + model: "gemini-mixed-model(high)", + inputJSON: `{"model":"gemini-mixed-model(high)","messages":[{"role":"user","content":"hi"}]}`, + expectField: "generationConfig.thinkingConfig.thinkingLevel", + expectValue: "high", + includeThoughts: "true", + expectErr: false, + }, + // Case 30: Effort xhigh → not in low/high → error + { + name: "30", + from: "openai", + to: "gemini", + model: "gemini-mixed-model(xhigh)", + inputJSON: `{"model":"gemini-mixed-model(xhigh)","messages":[{"role":"user","content":"hi"}]}`, + expectField: "", + expectErr: true, + }, + // Case 31: Effort none → clamped to low (min supported) → includeThoughts=false + { + name: "31", + from: "openai", + to: "gemini", + model: "gemini-mixed-model(none)", + inputJSON: `{"model":"gemini-mixed-model(none)","messages":[{"role":"user","content":"hi"}]}`, + expectField: "generationConfig.thinkingConfig.thinkingLevel", + expectValue: "low", + includeThoughts: "false", + expectErr: false, + }, + // Case 32: Effort auto → DynamicAllowed=true → -1 (budget) + { + name: "32", + from: "openai", + to: "gemini", + model: "gemini-mixed-model(auto)", + inputJSON: `{"model":"gemini-mixed-model(auto)","messages":[{"role":"user","content":"hi"}]}`, + expectField: "generationConfig.thinkingConfig.thinkingBudget", + expectValue: "-1", + includeThoughts: "true", + expectErr: false, + }, + // Case 33: Claude source no suffix → passthrough + { + name: "33", + from: "claude", + to: "gemini", + model: "gemini-mixed-model", + inputJSON: `{"model":"gemini-mixed-model","messages":[{"role":"user","content":"hi"}]}`, + expectField: "", + expectErr: false, + }, + // Case 34: Budget 8192 → 8192 (keep budget) + { + name: "34", + from: "claude", + to: "gemini", + model: "gemini-mixed-model(8192)", + inputJSON: `{"model":"gemini-mixed-model(8192)","messages":[{"role":"user","content":"hi"}]}`, + expectField: "generationConfig.thinkingConfig.thinkingBudget", + expectValue: "8192", + includeThoughts: "true", + expectErr: false, + }, + // Case 35: Budget 64000 → clamped to 32768 (max) + { + name: "35", + from: "claude", + to: "gemini", + model: "gemini-mixed-model(64000)", + inputJSON: `{"model":"gemini-mixed-model(64000)","messages":[{"role":"user","content":"hi"}]}`, + expectField: "generationConfig.thinkingConfig.thinkingBudget", + expectValue: "32768", + includeThoughts: "true", + expectErr: false, + }, + // Case 36: Budget 0 → minimal → clamped to low (min level) → includeThoughts=false + { + name: "36", + from: "claude", + to: "gemini", + model: "gemini-mixed-model(0)", + inputJSON: `{"model":"gemini-mixed-model(0)","messages":[{"role":"user","content":"hi"}]}`, + expectField: "generationConfig.thinkingConfig.thinkingLevel", + expectValue: "low", + includeThoughts: "false", + expectErr: false, + }, + // Case 37: Budget -1 → DynamicAllowed=true → -1 (budget) + { + name: "37", + from: "claude", + to: "gemini", + model: "gemini-mixed-model(-1)", + inputJSON: `{"model":"gemini-mixed-model(-1)","messages":[{"role":"user","content":"hi"}]}`, + expectField: "generationConfig.thinkingConfig.thinkingBudget", + expectValue: "-1", + includeThoughts: "true", + expectErr: false, + }, + + // claude-budget-model (Min=1024, Max=128000, ZeroAllowed=true, DynamicAllowed=false) + + // Case 38: OpenAI source no suffix → passthrough + { + name: "38", + from: "openai", + to: "claude", + model: "claude-budget-model", + inputJSON: `{"model":"claude-budget-model","messages":[{"role":"user","content":"hi"}]}`, + expectField: "", + expectErr: false, + }, + // Case 39: Effort medium → 8192 + { + name: "39", + from: "openai", + to: "claude", + model: "claude-budget-model(medium)", + inputJSON: `{"model":"claude-budget-model(medium)","messages":[{"role":"user","content":"hi"}]}`, + expectField: "thinking.budget_tokens", + expectValue: "8192", + expectErr: false, + }, + // Case 40: Effort xhigh → clamped to 32768 (matrix value) + { + name: "40", + from: "openai", + to: "claude", + model: "claude-budget-model(xhigh)", + inputJSON: `{"model":"claude-budget-model(xhigh)","messages":[{"role":"user","content":"hi"}]}`, + expectField: "thinking.budget_tokens", + expectValue: "32768", + expectErr: false, + }, + // Case 41: Effort none → ZeroAllowed=true → disabled + { + name: "41", + from: "openai", + to: "claude", + model: "claude-budget-model(none)", + inputJSON: `{"model":"claude-budget-model(none)","messages":[{"role":"user","content":"hi"}]}`, + expectField: "thinking.type", + expectValue: "disabled", + expectErr: false, + }, + // Case 42: Effort auto → DynamicAllowed=false → 64512 (mid-range) + { + name: "42", + from: "openai", + to: "claude", + model: "claude-budget-model(auto)", + inputJSON: `{"model":"claude-budget-model(auto)","messages":[{"role":"user","content":"hi"}]}`, + expectField: "thinking.budget_tokens", + expectValue: "64512", + expectErr: false, + }, + // Case 43: Gemini source no suffix → passthrough + { + name: "43", + from: "gemini", + to: "claude", + model: "claude-budget-model", + inputJSON: `{"model":"claude-budget-model","contents":[{"role":"user","parts":[{"text":"hi"}]}]}`, + expectField: "", + expectErr: false, + }, + // Case 44: Budget 8192 → 8192 + { + name: "44", + from: "gemini", + to: "claude", + model: "claude-budget-model(8192)", + inputJSON: `{"model":"claude-budget-model(8192)","contents":[{"role":"user","parts":[{"text":"hi"}]}]}`, + expectField: "thinking.budget_tokens", + expectValue: "8192", + expectErr: false, + }, + // Case 45: Budget 200000 → clamped to 128000 (max) + { + name: "45", + from: "gemini", + to: "claude", + model: "claude-budget-model(200000)", + inputJSON: `{"model":"claude-budget-model(200000)","contents":[{"role":"user","parts":[{"text":"hi"}]}]}`, + expectField: "thinking.budget_tokens", + expectValue: "128000", + expectErr: false, + }, + // Case 46: Budget 0 → ZeroAllowed=true → disabled + { + name: "46", + from: "gemini", + to: "claude", + model: "claude-budget-model(0)", + inputJSON: `{"model":"claude-budget-model(0)","contents":[{"role":"user","parts":[{"text":"hi"}]}]}`, + expectField: "thinking.type", + expectValue: "disabled", + expectErr: false, + }, + // Case 47: Budget -1 → auto → DynamicAllowed=false → 64512 (mid-range) + { + name: "47", + from: "gemini", + to: "claude", + model: "claude-budget-model(-1)", + inputJSON: `{"model":"claude-budget-model(-1)","contents":[{"role":"user","parts":[{"text":"hi"}]}]}`, + expectField: "thinking.budget_tokens", + expectValue: "64512", + expectErr: false, + }, + + // antigravity-budget-model (Min=128, Max=20000, ZeroAllowed=true, DynamicAllowed=true) + + // Case 48: Gemini to Antigravity no suffix → passthrough + { + name: "48", + from: "gemini", + to: "antigravity", + model: "antigravity-budget-model", + inputJSON: `{"model":"antigravity-budget-model","contents":[{"role":"user","parts":[{"text":"hi"}]}]}`, + expectField: "", + expectErr: false, + }, + // Case 49: Effort medium → 8192 + { + name: "49", + from: "gemini", + to: "antigravity", + model: "antigravity-budget-model(medium)", + inputJSON: `{"model":"antigravity-budget-model(medium)","contents":[{"role":"user","parts":[{"text":"hi"}]}]}`, + expectField: "request.generationConfig.thinkingConfig.thinkingBudget", + expectValue: "8192", + includeThoughts: "true", + expectErr: false, + }, + // Case 50: Effort xhigh → clamped to 20000 (max) + { + name: "50", + from: "gemini", + to: "antigravity", + model: "antigravity-budget-model(xhigh)", + inputJSON: `{"model":"antigravity-budget-model(xhigh)","contents":[{"role":"user","parts":[{"text":"hi"}]}]}`, + expectField: "request.generationConfig.thinkingConfig.thinkingBudget", + expectValue: "20000", + includeThoughts: "true", + expectErr: false, + }, + // Case 51: Effort none → ZeroAllowed=true → 0 → includeThoughts=false + { + name: "51", + from: "gemini", + to: "antigravity", + model: "antigravity-budget-model(none)", + inputJSON: `{"model":"antigravity-budget-model(none)","contents":[{"role":"user","parts":[{"text":"hi"}]}]}`, + expectField: "request.generationConfig.thinkingConfig.thinkingBudget", + expectValue: "0", + includeThoughts: "false", + expectErr: false, + }, + // Case 52: Effort auto → DynamicAllowed=true → -1 + { + name: "52", + from: "gemini", + to: "antigravity", + model: "antigravity-budget-model(auto)", + inputJSON: `{"model":"antigravity-budget-model(auto)","contents":[{"role":"user","parts":[{"text":"hi"}]}]}`, + expectField: "request.generationConfig.thinkingConfig.thinkingBudget", + expectValue: "-1", + includeThoughts: "true", + expectErr: false, + }, + // Case 53: Claude to Antigravity no suffix → passthrough + { + name: "53", + from: "claude", + to: "antigravity", + model: "antigravity-budget-model", + inputJSON: `{"model":"antigravity-budget-model","messages":[{"role":"user","content":"hi"}]}`, + expectField: "", + expectErr: false, + }, + // Case 54: Budget 8192 → 8192 + { + name: "54", + from: "claude", + to: "antigravity", + model: "antigravity-budget-model(8192)", + inputJSON: `{"model":"antigravity-budget-model(8192)","messages":[{"role":"user","content":"hi"}]}`, + expectField: "request.generationConfig.thinkingConfig.thinkingBudget", + expectValue: "8192", + includeThoughts: "true", + expectErr: false, + }, + // Case 55: Budget 64000 → clamped to 20000 (max) + { + name: "55", + from: "claude", + to: "antigravity", + model: "antigravity-budget-model(64000)", + inputJSON: `{"model":"antigravity-budget-model(64000)","messages":[{"role":"user","content":"hi"}]}`, + expectField: "request.generationConfig.thinkingConfig.thinkingBudget", + expectValue: "20000", + includeThoughts: "true", + expectErr: false, + }, + // Case 56: Budget 0 → ZeroAllowed=true → 0 → includeThoughts=false + { + name: "56", + from: "claude", + to: "antigravity", + model: "antigravity-budget-model(0)", + inputJSON: `{"model":"antigravity-budget-model(0)","messages":[{"role":"user","content":"hi"}]}`, + expectField: "request.generationConfig.thinkingConfig.thinkingBudget", + expectValue: "0", + includeThoughts: "false", + expectErr: false, + }, + // Case 57: Budget -1 → DynamicAllowed=true → -1 + { + name: "57", + from: "claude", + to: "antigravity", + model: "antigravity-budget-model(-1)", + inputJSON: `{"model":"antigravity-budget-model(-1)","messages":[{"role":"user","content":"hi"}]}`, + expectField: "request.generationConfig.thinkingConfig.thinkingBudget", + expectValue: "-1", + includeThoughts: "true", + expectErr: false, + }, + + // no-thinking-model (Thinking=nil) + + // Case 58: No thinking support → no configuration + { + name: "58", + from: "gemini", + to: "openai", + model: "no-thinking-model", + inputJSON: `{"model":"no-thinking-model","contents":[{"role":"user","parts":[{"text":"hi"}]}]}`, + expectField: "", + expectErr: false, + }, + // Case 59: Budget 8192 → no thinking support → suffix stripped → no configuration + { + name: "59", + from: "gemini", + to: "openai", + model: "no-thinking-model(8192)", + inputJSON: `{"model":"no-thinking-model(8192)","contents":[{"role":"user","parts":[{"text":"hi"}]}]}`, + expectField: "", + expectErr: false, + }, + // Case 60: Budget 0 → suffix stripped → no configuration + { + name: "60", + from: "gemini", + to: "openai", + model: "no-thinking-model(0)", + inputJSON: `{"model":"no-thinking-model(0)","contents":[{"role":"user","parts":[{"text":"hi"}]}]}`, + expectField: "", + expectErr: false, + }, + // Case 61: Budget -1 → suffix stripped → no configuration + { + name: "61", + from: "gemini", + to: "openai", + model: "no-thinking-model(-1)", + inputJSON: `{"model":"no-thinking-model(-1)","contents":[{"role":"user","parts":[{"text":"hi"}]}]}`, + expectField: "", + expectErr: false, + }, + // Case 62: Claude source no suffix → no configuration + { + name: "62", + from: "claude", + to: "openai", + model: "no-thinking-model", + inputJSON: `{"model":"no-thinking-model","messages":[{"role":"user","content":"hi"}]}`, + expectField: "", + expectErr: false, + }, + // Case 63: Budget 8192 → suffix stripped → no configuration + { + name: "63", + from: "claude", + to: "openai", + model: "no-thinking-model(8192)", + inputJSON: `{"model":"no-thinking-model(8192)","messages":[{"role":"user","content":"hi"}]}`, + expectField: "", + expectErr: false, + }, + // Case 64: Budget 0 → suffix stripped → no configuration + { + name: "64", + from: "claude", + to: "openai", + model: "no-thinking-model(0)", + inputJSON: `{"model":"no-thinking-model(0)","messages":[{"role":"user","content":"hi"}]}`, + expectField: "", + expectErr: false, + }, + // Case 65: Budget -1 → suffix stripped → no configuration + { + name: "65", + from: "claude", + to: "openai", + model: "no-thinking-model(-1)", + inputJSON: `{"model":"no-thinking-model(-1)","messages":[{"role":"user","content":"hi"}]}`, + expectField: "", + expectErr: false, + }, + + // user-defined-model (UserDefined=true, Thinking=nil) + + // Case 66: User defined model no suffix → passthrough + { + name: "66", + from: "gemini", + to: "openai", + model: "user-defined-model", + inputJSON: `{"model":"user-defined-model","contents":[{"role":"user","parts":[{"text":"hi"}]}]}`, + expectField: "", + expectErr: false, + }, + // Case 67: Budget 8192 → passthrough logic → medium + { + name: "67", + from: "gemini", + to: "openai", + model: "user-defined-model(8192)", + inputJSON: `{"model":"user-defined-model(8192)","contents":[{"role":"user","parts":[{"text":"hi"}]}]}`, + expectField: "reasoning_effort", + expectValue: "medium", + expectErr: false, + }, + // Case 68: Budget 64000 → passthrough logic → xhigh + { + name: "68", + from: "gemini", + to: "openai", + model: "user-defined-model(64000)", + inputJSON: `{"model":"user-defined-model(64000)","contents":[{"role":"user","parts":[{"text":"hi"}]}]}`, + expectField: "reasoning_effort", + expectValue: "xhigh", + expectErr: false, + }, + // Case 69: Budget 0 → passthrough logic → none + { + name: "69", + from: "gemini", + to: "openai", + model: "user-defined-model(0)", + inputJSON: `{"model":"user-defined-model(0)","contents":[{"role":"user","parts":[{"text":"hi"}]}]}`, + expectField: "reasoning_effort", + expectValue: "none", + expectErr: false, + }, + // Case 70: Budget -1 → passthrough logic → auto + { + name: "70", + from: "gemini", + to: "openai", + model: "user-defined-model(-1)", + inputJSON: `{"model":"user-defined-model(-1)","contents":[{"role":"user","parts":[{"text":"hi"}]}]}`, + expectField: "reasoning_effort", + expectValue: "auto", + expectErr: false, + }, + // Case 71: Claude to Codex no suffix → injected default → medium + { + name: "71", + from: "claude", + to: "codex", + model: "user-defined-model", + inputJSON: `{"model":"user-defined-model","messages":[{"role":"user","content":"hi"}]}`, + expectField: "reasoning.effort", + expectValue: "medium", + expectErr: false, + }, + // Case 72: Budget 8192 → passthrough logic → medium + { + name: "72", + from: "claude", + to: "codex", + model: "user-defined-model(8192)", + inputJSON: `{"model":"user-defined-model(8192)","messages":[{"role":"user","content":"hi"}]}`, + expectField: "reasoning.effort", + expectValue: "medium", + expectErr: false, + }, + // Case 73: Budget 64000 → passthrough logic → xhigh + { + name: "73", + from: "claude", + to: "codex", + model: "user-defined-model(64000)", + inputJSON: `{"model":"user-defined-model(64000)","messages":[{"role":"user","content":"hi"}]}`, + expectField: "reasoning.effort", + expectValue: "xhigh", + expectErr: false, + }, + // Case 74: Budget 0 → passthrough logic → none + { + name: "74", + from: "claude", + to: "codex", + model: "user-defined-model(0)", + inputJSON: `{"model":"user-defined-model(0)","messages":[{"role":"user","content":"hi"}]}`, + expectField: "reasoning.effort", + expectValue: "none", + expectErr: false, + }, + // Case 75: Budget -1 → passthrough logic → auto + { + name: "75", + from: "claude", + to: "codex", + model: "user-defined-model(-1)", + inputJSON: `{"model":"user-defined-model(-1)","messages":[{"role":"user","content":"hi"}]}`, + expectField: "reasoning.effort", + expectValue: "auto", + expectErr: false, + }, + // Case 76: OpenAI to Gemini budget 8192 → passthrough → 8192 + { + name: "76", + from: "openai", + to: "gemini", + model: "user-defined-model(8192)", + inputJSON: `{"model":"user-defined-model(8192)","messages":[{"role":"user","content":"hi"}]}`, + expectField: "generationConfig.thinkingConfig.thinkingBudget", + expectValue: "8192", + includeThoughts: "true", + expectErr: false, + }, + // Case 77: OpenAI to Claude budget 8192 → passthrough → 8192 + { + name: "77", + from: "openai", + to: "claude", + model: "user-defined-model(8192)", + inputJSON: `{"model":"user-defined-model(8192)","messages":[{"role":"user","content":"hi"}]}`, + expectField: "thinking.budget_tokens", + expectValue: "8192", + expectErr: false, + }, + // Case 78: Codex to Gemini budget 8192 → passthrough → 8192 + { + name: "78", + from: "codex", + to: "gemini", + model: "user-defined-model(8192)", + inputJSON: `{"model":"user-defined-model(8192)","input":[{"role":"user","content":"hi"}]}`, + expectField: "generationConfig.thinkingConfig.thinkingBudget", + expectValue: "8192", + includeThoughts: "true", + expectErr: false, + }, + // Case 79: Codex to Claude budget 8192 → passthrough → 8192 + { + name: "79", + from: "codex", + to: "claude", + model: "user-defined-model(8192)", + inputJSON: `{"model":"user-defined-model(8192)","input":[{"role":"user","content":"hi"}]}`, + expectField: "thinking.budget_tokens", + expectValue: "8192", + expectErr: false, + }, + + // Same-protocol passthrough tests (80-89) + + // Case 80: OpenAI to OpenAI, level high → passthrough reasoning_effort + { + name: "80", + from: "openai", + to: "openai", + model: "level-model(high)", + inputJSON: `{"model":"level-model(high)","messages":[{"role":"user","content":"hi"}]}`, + expectField: "reasoning_effort", + expectValue: "high", + expectErr: false, + }, + // Case 81: OpenAI to OpenAI, level xhigh → out of range error + { + name: "81", + from: "openai", + to: "openai", + model: "level-model(xhigh)", + inputJSON: `{"model":"level-model(xhigh)","messages":[{"role":"user","content":"hi"}]}`, + expectField: "", + expectErr: true, + }, + // Case 82: Codex to Codex, level high → passthrough reasoning.effort + { + name: "82", + from: "codex", + to: "codex", + model: "level-model(high)", + inputJSON: `{"model":"level-model(high)","input":[{"role":"user","content":"hi"}]}`, + expectField: "reasoning.effort", + expectValue: "high", + expectErr: false, + }, + // Case 83: Codex to Codex, level xhigh → out of range error + { + name: "83", + from: "codex", + to: "codex", + model: "level-model(xhigh)", + inputJSON: `{"model":"level-model(xhigh)","input":[{"role":"user","content":"hi"}]}`, + expectField: "", + expectErr: true, + }, + // Case 84: Gemini to Gemini, budget 8192 → passthrough thinkingBudget + { + name: "84", + from: "gemini", + to: "gemini", + model: "gemini-budget-model(8192)", + inputJSON: `{"model":"gemini-budget-model(8192)","contents":[{"role":"user","parts":[{"text":"hi"}]}]}`, + expectField: "generationConfig.thinkingConfig.thinkingBudget", + expectValue: "8192", + includeThoughts: "true", + expectErr: false, + }, + // Case 85: Gemini to Gemini, budget 64000 → exceeds Max error + { + name: "85", + from: "gemini", + to: "gemini", + model: "gemini-budget-model(64000)", + inputJSON: `{"model":"gemini-budget-model(64000)","contents":[{"role":"user","parts":[{"text":"hi"}]}]}`, + expectField: "", + expectErr: true, + }, + // Case 86: Claude to Claude, budget 8192 → passthrough thinking.budget_tokens + { + name: "86", + from: "claude", + to: "claude", + model: "claude-budget-model(8192)", + inputJSON: `{"model":"claude-budget-model(8192)","messages":[{"role":"user","content":"hi"}]}`, + expectField: "thinking.budget_tokens", + expectValue: "8192", + expectErr: false, + }, + // Case 87: Claude to Claude, budget 200000 → exceeds Max error + { + name: "87", + from: "claude", + to: "claude", + model: "claude-budget-model(200000)", + inputJSON: `{"model":"claude-budget-model(200000)","messages":[{"role":"user","content":"hi"}]}`, + expectField: "", + expectErr: true, + }, + // Case 88: Antigravity to Antigravity, budget 8192 → passthrough thinkingBudget + { + name: "88", + from: "antigravity", + to: "antigravity", + model: "antigravity-budget-model(8192)", + inputJSON: `{"model":"antigravity-budget-model(8192)","request":{"contents":[{"role":"user","parts":[{"text":"hi"}]}]}}`, + expectField: "request.generationConfig.thinkingConfig.thinkingBudget", + expectValue: "8192", + includeThoughts: "true", + expectErr: false, + }, + // Case 89: Antigravity to Antigravity, budget 64000 → exceeds Max error + { + name: "89", + from: "antigravity", + to: "antigravity", + model: "antigravity-budget-model(64000)", + inputJSON: `{"model":"antigravity-budget-model(64000)","request":{"contents":[{"role":"user","parts":[{"text":"hi"}]}]}}`, + expectField: "", + expectErr: true, + }, + + // iflow tests: glm-test and minimax-test (Cases 90-105) + + // glm-test (from: openai, claude) + // Case 90: OpenAI to iflow, no suffix → passthrough + { + name: "90", + from: "openai", + to: "iflow", + model: "glm-test", + inputJSON: `{"model":"glm-test","messages":[{"role":"user","content":"hi"}]}`, + expectField: "", + expectErr: false, + }, + // Case 91: OpenAI to iflow, (medium) → enable_thinking=true + { + name: "91", + from: "openai", + to: "iflow", + model: "glm-test(medium)", + inputJSON: `{"model":"glm-test(medium)","messages":[{"role":"user","content":"hi"}]}`, + expectField: "chat_template_kwargs.enable_thinking", + expectValue: "true", + expectErr: false, + }, + // Case 92: OpenAI to iflow, (auto) → enable_thinking=true + { + name: "92", + from: "openai", + to: "iflow", + model: "glm-test(auto)", + inputJSON: `{"model":"glm-test(auto)","messages":[{"role":"user","content":"hi"}]}`, + expectField: "chat_template_kwargs.enable_thinking", + expectValue: "true", + expectErr: false, + }, + // Case 93: OpenAI to iflow, (none) → enable_thinking=false + { + name: "93", + from: "openai", + to: "iflow", + model: "glm-test(none)", + inputJSON: `{"model":"glm-test(none)","messages":[{"role":"user","content":"hi"}]}`, + expectField: "chat_template_kwargs.enable_thinking", + expectValue: "false", + expectErr: false, + }, + // Case 94: Claude to iflow, no suffix → passthrough + { + name: "94", + from: "claude", + to: "iflow", + model: "glm-test", + inputJSON: `{"model":"glm-test","messages":[{"role":"user","content":"hi"}]}`, + expectField: "", + expectErr: false, + }, + // Case 95: Claude to iflow, (8192) → enable_thinking=true + { + name: "95", + from: "claude", + to: "iflow", + model: "glm-test(8192)", + inputJSON: `{"model":"glm-test(8192)","messages":[{"role":"user","content":"hi"}]}`, + expectField: "chat_template_kwargs.enable_thinking", + expectValue: "true", + expectErr: false, + }, + // Case 96: Claude to iflow, (-1) → enable_thinking=true + { + name: "96", + from: "claude", + to: "iflow", + model: "glm-test(-1)", + inputJSON: `{"model":"glm-test(-1)","messages":[{"role":"user","content":"hi"}]}`, + expectField: "chat_template_kwargs.enable_thinking", + expectValue: "true", + expectErr: false, + }, + // Case 97: Claude to iflow, (0) → enable_thinking=false + { + name: "97", + from: "claude", + to: "iflow", + model: "glm-test(0)", + inputJSON: `{"model":"glm-test(0)","messages":[{"role":"user","content":"hi"}]}`, + expectField: "chat_template_kwargs.enable_thinking", + expectValue: "false", + expectErr: false, + }, + + // minimax-test (from: openai, gemini) + // Case 98: OpenAI to iflow, no suffix → passthrough + { + name: "98", + from: "openai", + to: "iflow", + model: "minimax-test", + inputJSON: `{"model":"minimax-test","messages":[{"role":"user","content":"hi"}]}`, + expectField: "", + expectErr: false, + }, + // Case 99: OpenAI to iflow, (medium) → reasoning_split=true + { + name: "99", + from: "openai", + to: "iflow", + model: "minimax-test(medium)", + inputJSON: `{"model":"minimax-test(medium)","messages":[{"role":"user","content":"hi"}]}`, + expectField: "reasoning_split", + expectValue: "true", + expectErr: false, + }, + // Case 100: OpenAI to iflow, (auto) → reasoning_split=true + { + name: "100", + from: "openai", + to: "iflow", + model: "minimax-test(auto)", + inputJSON: `{"model":"minimax-test(auto)","messages":[{"role":"user","content":"hi"}]}`, + expectField: "reasoning_split", + expectValue: "true", + expectErr: false, + }, + // Case 101: OpenAI to iflow, (none) → reasoning_split=false + { + name: "101", + from: "openai", + to: "iflow", + model: "minimax-test(none)", + inputJSON: `{"model":"minimax-test(none)","messages":[{"role":"user","content":"hi"}]}`, + expectField: "reasoning_split", + expectValue: "false", + expectErr: false, + }, + // Case 102: Gemini to iflow, no suffix → passthrough + { + name: "102", + from: "gemini", + to: "iflow", + model: "minimax-test", + inputJSON: `{"model":"minimax-test","contents":[{"role":"user","parts":[{"text":"hi"}]}]}`, + expectField: "", + expectErr: false, + }, + // Case 103: Gemini to iflow, (8192) → reasoning_split=true + { + name: "103", + from: "gemini", + to: "iflow", + model: "minimax-test(8192)", + inputJSON: `{"model":"minimax-test(8192)","contents":[{"role":"user","parts":[{"text":"hi"}]}]}`, + expectField: "reasoning_split", + expectValue: "true", + expectErr: false, + }, + // Case 104: Gemini to iflow, (-1) → reasoning_split=true + { + name: "104", + from: "gemini", + to: "iflow", + model: "minimax-test(-1)", + inputJSON: `{"model":"minimax-test(-1)","contents":[{"role":"user","parts":[{"text":"hi"}]}]}`, + expectField: "reasoning_split", + expectValue: "true", + expectErr: false, + }, + // Case 105: Gemini to iflow, (0) → reasoning_split=false + { + name: "105", + from: "gemini", + to: "iflow", + model: "minimax-test(0)", + inputJSON: `{"model":"minimax-test(0)","contents":[{"role":"user","parts":[{"text":"hi"}]}]}`, + expectField: "reasoning_split", + expectValue: "false", + expectErr: false, + }, + } + + runThinkingTests(t, cases) +} + +// TestThinkingE2EMatrix_Body tests the thinking configuration transformation using request body parameters. +// Data flow: Input JSON with thinking params → TranslateRequest → ApplyThinking → Validate Output +func TestThinkingE2EMatrix_Body(t *testing.T) { + reg := registry.GetGlobalRegistry() + uid := fmt.Sprintf("thinking-e2e-body-%d", time.Now().UnixNano()) + + reg.RegisterClient(uid, "test", getTestModels()) + defer reg.UnregisterClient(uid) + + cases := []thinkingTestCase{ + // level-model (Levels=minimal/low/medium/high, ZeroAllowed=false, DynamicAllowed=false) + + // Case 1: No param → injected default → medium + { + name: "1", + from: "openai", + to: "codex", + model: "level-model", + inputJSON: `{"model":"level-model","messages":[{"role":"user","content":"hi"}]}`, + expectField: "reasoning.effort", + expectValue: "medium", + expectErr: false, + }, + // Case 2: reasoning_effort=medium → medium + { + name: "2", + from: "openai", + to: "codex", + model: "level-model", + inputJSON: `{"model":"level-model","messages":[{"role":"user","content":"hi"}],"reasoning_effort":"medium"}`, + expectField: "reasoning.effort", + expectValue: "medium", + expectErr: false, + }, + // Case 3: reasoning_effort=xhigh → out of range error + { + name: "3", + from: "openai", + to: "codex", + model: "level-model", + inputJSON: `{"model":"level-model","messages":[{"role":"user","content":"hi"}],"reasoning_effort":"xhigh"}`, + expectField: "", + expectErr: true, + }, + // Case 4: reasoning_effort=none → clamped to minimal + { + name: "4", + from: "openai", + to: "codex", + model: "level-model", + inputJSON: `{"model":"level-model","messages":[{"role":"user","content":"hi"}],"reasoning_effort":"none"}`, + expectField: "reasoning.effort", + expectValue: "minimal", + expectErr: false, + }, + // Case 5: reasoning_effort=auto → medium (DynamicAllowed=false) + { + name: "5", + from: "openai", + to: "codex", + model: "level-model", + inputJSON: `{"model":"level-model","messages":[{"role":"user","content":"hi"}],"reasoning_effort":"auto"}`, + expectField: "reasoning.effort", + expectValue: "medium", + expectErr: false, + }, + // Case 6: No param from gemini → injected default → medium + { + name: "6", + from: "gemini", + to: "codex", + model: "level-model", + inputJSON: `{"model":"level-model","contents":[{"role":"user","parts":[{"text":"hi"}]}]}`, + expectField: "reasoning.effort", + expectValue: "medium", + expectErr: false, + }, + // Case 7: thinkingBudget=8192 → medium + { + name: "7", + from: "gemini", + to: "codex", + model: "level-model", + inputJSON: `{"model":"level-model","contents":[{"role":"user","parts":[{"text":"hi"}]}],"generationConfig":{"thinkingConfig":{"thinkingBudget":8192}}}`, + expectField: "reasoning.effort", + expectValue: "medium", + expectErr: false, + }, + // Case 8: thinkingBudget=64000 → clamped to high + { + name: "8", + from: "gemini", + to: "codex", + model: "level-model", + inputJSON: `{"model":"level-model","contents":[{"role":"user","parts":[{"text":"hi"}]}],"generationConfig":{"thinkingConfig":{"thinkingBudget":64000}}}`, + expectField: "reasoning.effort", + expectValue: "high", + expectErr: false, + }, + // Case 9: thinkingBudget=0 → clamped to minimal + { + name: "9", + from: "gemini", + to: "codex", + model: "level-model", + inputJSON: `{"model":"level-model","contents":[{"role":"user","parts":[{"text":"hi"}]}],"generationConfig":{"thinkingConfig":{"thinkingBudget":0}}}`, + expectField: "reasoning.effort", + expectValue: "minimal", + expectErr: false, + }, + // Case 10: thinkingBudget=-1 → medium (DynamicAllowed=false) + { + name: "10", + from: "gemini", + to: "codex", + model: "level-model", + inputJSON: `{"model":"level-model","contents":[{"role":"user","parts":[{"text":"hi"}]}],"generationConfig":{"thinkingConfig":{"thinkingBudget":-1}}}`, + expectField: "reasoning.effort", + expectValue: "medium", + expectErr: false, + }, + // Case 11: Claude no param → passthrough (no thinking) + { + name: "11", + from: "claude", + to: "openai", + model: "level-model", + inputJSON: `{"model":"level-model","messages":[{"role":"user","content":"hi"}]}`, + expectField: "", + expectErr: false, + }, + // Case 12: thinking.budget_tokens=8192 → medium + { + name: "12", + from: "claude", + to: "openai", + model: "level-model", + inputJSON: `{"model":"level-model","messages":[{"role":"user","content":"hi"}],"thinking":{"type":"enabled","budget_tokens":8192}}`, + expectField: "reasoning_effort", + expectValue: "medium", + expectErr: false, + }, + // Case 13: thinking.budget_tokens=64000 → clamped to high + { + name: "13", + from: "claude", + to: "openai", + model: "level-model", + inputJSON: `{"model":"level-model","messages":[{"role":"user","content":"hi"}],"thinking":{"type":"enabled","budget_tokens":64000}}`, + expectField: "reasoning_effort", + expectValue: "high", + expectErr: false, + }, + // Case 14: thinking.budget_tokens=0 → clamped to minimal + { + name: "14", + from: "claude", + to: "openai", + model: "level-model", + inputJSON: `{"model":"level-model","messages":[{"role":"user","content":"hi"}],"thinking":{"type":"enabled","budget_tokens":0}}`, + expectField: "reasoning_effort", + expectValue: "minimal", + expectErr: false, + }, + // Case 15: thinking.budget_tokens=-1 → medium (DynamicAllowed=false) + { + name: "15", + from: "claude", + to: "openai", + model: "level-model", + inputJSON: `{"model":"level-model","messages":[{"role":"user","content":"hi"}],"thinking":{"type":"enabled","budget_tokens":-1}}`, + expectField: "reasoning_effort", + expectValue: "medium", + expectErr: false, + }, + + // level-subset-model (Levels=low/high, ZeroAllowed=false, DynamicAllowed=false) + + // Case 16: thinkingBudget=8192 → medium → rounded down to low + { + name: "16", + from: "gemini", + to: "openai", + model: "level-subset-model", + inputJSON: `{"model":"level-subset-model","contents":[{"role":"user","parts":[{"text":"hi"}]}],"generationConfig":{"thinkingConfig":{"thinkingBudget":8192}}}`, + expectField: "reasoning_effort", + expectValue: "low", + expectErr: false, + }, + // Case 17: thinking.budget_tokens=1 → minimal → clamped to low + { + name: "17", + from: "claude", + to: "gemini", + model: "level-subset-model", + inputJSON: `{"model":"level-subset-model","messages":[{"role":"user","content":"hi"}],"thinking":{"type":"enabled","budget_tokens":1}}`, + expectField: "generationConfig.thinkingConfig.thinkingLevel", + expectValue: "low", + includeThoughts: "true", + expectErr: false, + }, + + // gemini-budget-model (Min=128, Max=20000, ZeroAllowed=false, DynamicAllowed=true) + + // Case 18: No param → passthrough + { + name: "18", + from: "openai", + to: "gemini", + model: "gemini-budget-model", + inputJSON: `{"model":"gemini-budget-model","messages":[{"role":"user","content":"hi"}]}`, + expectField: "", + expectErr: false, + }, + // Case 19: reasoning_effort=medium → 8192 + { + name: "19", + from: "openai", + to: "gemini", + model: "gemini-budget-model", + inputJSON: `{"model":"gemini-budget-model","messages":[{"role":"user","content":"hi"}],"reasoning_effort":"medium"}`, + expectField: "generationConfig.thinkingConfig.thinkingBudget", + expectValue: "8192", + includeThoughts: "true", + expectErr: false, + }, + // Case 20: reasoning_effort=xhigh → clamped to 20000 + { + name: "20", + from: "openai", + to: "gemini", + model: "gemini-budget-model", + inputJSON: `{"model":"gemini-budget-model","messages":[{"role":"user","content":"hi"}],"reasoning_effort":"xhigh"}`, + expectField: "generationConfig.thinkingConfig.thinkingBudget", + expectValue: "20000", + includeThoughts: "true", + expectErr: false, + }, + // Case 21: reasoning_effort=none → clamped to 128 → includeThoughts=false + { + name: "21", + from: "openai", + to: "gemini", + model: "gemini-budget-model", + inputJSON: `{"model":"gemini-budget-model","messages":[{"role":"user","content":"hi"}],"reasoning_effort":"none"}`, + expectField: "generationConfig.thinkingConfig.thinkingBudget", + expectValue: "128", + includeThoughts: "false", + expectErr: false, + }, + // Case 22: reasoning_effort=auto → -1 (DynamicAllowed=true) + { + name: "22", + from: "openai", + to: "gemini", + model: "gemini-budget-model", + inputJSON: `{"model":"gemini-budget-model","messages":[{"role":"user","content":"hi"}],"reasoning_effort":"auto"}`, + expectField: "generationConfig.thinkingConfig.thinkingBudget", + expectValue: "-1", + includeThoughts: "true", + expectErr: false, + }, + // Case 23: Claude no param → passthrough + { + name: "23", + from: "claude", + to: "gemini", + model: "gemini-budget-model", + inputJSON: `{"model":"gemini-budget-model","messages":[{"role":"user","content":"hi"}]}`, + expectField: "", + expectErr: false, + }, + // Case 24: thinking.budget_tokens=8192 → 8192 + { + name: "24", + from: "claude", + to: "gemini", + model: "gemini-budget-model", + inputJSON: `{"model":"gemini-budget-model","messages":[{"role":"user","content":"hi"}],"thinking":{"type":"enabled","budget_tokens":8192}}`, + expectField: "generationConfig.thinkingConfig.thinkingBudget", + expectValue: "8192", + includeThoughts: "true", + expectErr: false, + }, + // Case 25: thinking.budget_tokens=64000 → clamped to 20000 + { + name: "25", + from: "claude", + to: "gemini", + model: "gemini-budget-model", + inputJSON: `{"model":"gemini-budget-model","messages":[{"role":"user","content":"hi"}],"thinking":{"type":"enabled","budget_tokens":64000}}`, + expectField: "generationConfig.thinkingConfig.thinkingBudget", + expectValue: "20000", + includeThoughts: "true", + expectErr: false, + }, + // Case 26: thinking.budget_tokens=0 → clamped to 128 → includeThoughts=false + { + name: "26", + from: "claude", + to: "gemini", + model: "gemini-budget-model", + inputJSON: `{"model":"gemini-budget-model","messages":[{"role":"user","content":"hi"}],"thinking":{"type":"enabled","budget_tokens":0}}`, + expectField: "generationConfig.thinkingConfig.thinkingBudget", + expectValue: "128", + includeThoughts: "false", + expectErr: false, + }, + // Case 27: thinking.budget_tokens=-1 → -1 (DynamicAllowed=true) + { + name: "27", + from: "claude", + to: "gemini", + model: "gemini-budget-model", + inputJSON: `{"model":"gemini-budget-model","messages":[{"role":"user","content":"hi"}],"thinking":{"type":"enabled","budget_tokens":-1}}`, + expectField: "generationConfig.thinkingConfig.thinkingBudget", + expectValue: "-1", + includeThoughts: "true", + expectErr: false, + }, + + // gemini-mixed-model (Min=128, Max=32768, Levels=low/high, ZeroAllowed=false, DynamicAllowed=true) + + // Case 28: No param → passthrough + { + name: "28", + from: "openai", + to: "gemini", + model: "gemini-mixed-model", + inputJSON: `{"model":"gemini-mixed-model","messages":[{"role":"user","content":"hi"}]}`, + expectField: "", + expectErr: false, + }, + // Case 29: reasoning_effort=high → high + { + name: "29", + from: "openai", + to: "gemini", + model: "gemini-mixed-model", + inputJSON: `{"model":"gemini-mixed-model","messages":[{"role":"user","content":"hi"}],"reasoning_effort":"high"}`, + expectField: "generationConfig.thinkingConfig.thinkingLevel", + expectValue: "high", + includeThoughts: "true", + expectErr: false, + }, + // Case 30: reasoning_effort=xhigh → error (not in low/high) + { + name: "30", + from: "openai", + to: "gemini", + model: "gemini-mixed-model", + inputJSON: `{"model":"gemini-mixed-model","messages":[{"role":"user","content":"hi"}],"reasoning_effort":"xhigh"}`, + expectField: "", + expectErr: true, + }, + // Case 31: reasoning_effort=none → clamped to low → includeThoughts=false + { + name: "31", + from: "openai", + to: "gemini", + model: "gemini-mixed-model", + inputJSON: `{"model":"gemini-mixed-model","messages":[{"role":"user","content":"hi"}],"reasoning_effort":"none"}`, + expectField: "generationConfig.thinkingConfig.thinkingLevel", + expectValue: "low", + includeThoughts: "false", + expectErr: false, + }, + // Case 32: reasoning_effort=auto → -1 (DynamicAllowed=true) + { + name: "32", + from: "openai", + to: "gemini", + model: "gemini-mixed-model", + inputJSON: `{"model":"gemini-mixed-model","messages":[{"role":"user","content":"hi"}],"reasoning_effort":"auto"}`, + expectField: "generationConfig.thinkingConfig.thinkingBudget", + expectValue: "-1", + includeThoughts: "true", + expectErr: false, + }, + // Case 33: Claude no param → passthrough + { + name: "33", + from: "claude", + to: "gemini", + model: "gemini-mixed-model", + inputJSON: `{"model":"gemini-mixed-model","messages":[{"role":"user","content":"hi"}]}`, + expectField: "", + expectErr: false, + }, + // Case 34: thinking.budget_tokens=8192 → 8192 (keeps budget) + { + name: "34", + from: "claude", + to: "gemini", + model: "gemini-mixed-model", + inputJSON: `{"model":"gemini-mixed-model","messages":[{"role":"user","content":"hi"}],"thinking":{"type":"enabled","budget_tokens":8192}}`, + expectField: "generationConfig.thinkingConfig.thinkingBudget", + expectValue: "8192", + includeThoughts: "true", + expectErr: false, + }, + // Case 35: thinking.budget_tokens=64000 → clamped to 32768 (keeps budget) + { + name: "35", + from: "claude", + to: "gemini", + model: "gemini-mixed-model", + inputJSON: `{"model":"gemini-mixed-model","messages":[{"role":"user","content":"hi"}],"thinking":{"type":"enabled","budget_tokens":64000}}`, + expectField: "generationConfig.thinkingConfig.thinkingBudget", + expectValue: "32768", + includeThoughts: "true", + expectErr: false, + }, + // Case 36: thinking.budget_tokens=0 → clamped to low → includeThoughts=false + { + name: "36", + from: "claude", + to: "gemini", + model: "gemini-mixed-model", + inputJSON: `{"model":"gemini-mixed-model","messages":[{"role":"user","content":"hi"}],"thinking":{"type":"enabled","budget_tokens":0}}`, + expectField: "generationConfig.thinkingConfig.thinkingLevel", + expectValue: "low", + includeThoughts: "false", + expectErr: false, + }, + // Case 37: thinking.budget_tokens=-1 → -1 (DynamicAllowed=true) + { + name: "37", + from: "claude", + to: "gemini", + model: "gemini-mixed-model", + inputJSON: `{"model":"gemini-mixed-model","messages":[{"role":"user","content":"hi"}],"thinking":{"type":"enabled","budget_tokens":-1}}`, + expectField: "generationConfig.thinkingConfig.thinkingBudget", + expectValue: "-1", + includeThoughts: "true", + expectErr: false, + }, + + // claude-budget-model (Min=1024, Max=128000, ZeroAllowed=true, DynamicAllowed=false) + + // Case 38: No param → passthrough + { + name: "38", + from: "openai", + to: "claude", + model: "claude-budget-model", + inputJSON: `{"model":"claude-budget-model","messages":[{"role":"user","content":"hi"}]}`, + expectField: "", + expectErr: false, + }, + // Case 39: reasoning_effort=medium → 8192 + { + name: "39", + from: "openai", + to: "claude", + model: "claude-budget-model", + inputJSON: `{"model":"claude-budget-model","messages":[{"role":"user","content":"hi"}],"reasoning_effort":"medium"}`, + expectField: "thinking.budget_tokens", + expectValue: "8192", + expectErr: false, + }, + // Case 40: reasoning_effort=xhigh → clamped to 32768 + { + name: "40", + from: "openai", + to: "claude", + model: "claude-budget-model", + inputJSON: `{"model":"claude-budget-model","messages":[{"role":"user","content":"hi"}],"reasoning_effort":"xhigh"}`, + expectField: "thinking.budget_tokens", + expectValue: "32768", + expectErr: false, + }, + // Case 41: reasoning_effort=none → disabled + { + name: "41", + from: "openai", + to: "claude", + model: "claude-budget-model", + inputJSON: `{"model":"claude-budget-model","messages":[{"role":"user","content":"hi"}],"reasoning_effort":"none"}`, + expectField: "thinking.type", + expectValue: "disabled", + expectErr: false, + }, + // Case 42: reasoning_effort=auto → 64512 (mid-range) + { + name: "42", + from: "openai", + to: "claude", + model: "claude-budget-model", + inputJSON: `{"model":"claude-budget-model","messages":[{"role":"user","content":"hi"}],"reasoning_effort":"auto"}`, + expectField: "thinking.budget_tokens", + expectValue: "64512", + expectErr: false, + }, + // Case 43: Gemini no param → passthrough + { + name: "43", + from: "gemini", + to: "claude", + model: "claude-budget-model", + inputJSON: `{"model":"claude-budget-model","contents":[{"role":"user","parts":[{"text":"hi"}]}]}`, + expectField: "", + expectErr: false, + }, + // Case 44: thinkingBudget=8192 → 8192 + { + name: "44", + from: "gemini", + to: "claude", + model: "claude-budget-model", + inputJSON: `{"model":"claude-budget-model","contents":[{"role":"user","parts":[{"text":"hi"}]}],"generationConfig":{"thinkingConfig":{"thinkingBudget":8192}}}`, + expectField: "thinking.budget_tokens", + expectValue: "8192", + expectErr: false, + }, + // Case 45: thinkingBudget=200000 → clamped to 128000 + { + name: "45", + from: "gemini", + to: "claude", + model: "claude-budget-model", + inputJSON: `{"model":"claude-budget-model","contents":[{"role":"user","parts":[{"text":"hi"}]}],"generationConfig":{"thinkingConfig":{"thinkingBudget":200000}}}`, + expectField: "thinking.budget_tokens", + expectValue: "128000", + expectErr: false, + }, + // Case 46: thinkingBudget=0 → disabled + { + name: "46", + from: "gemini", + to: "claude", + model: "claude-budget-model", + inputJSON: `{"model":"claude-budget-model","contents":[{"role":"user","parts":[{"text":"hi"}]}],"generationConfig":{"thinkingConfig":{"thinkingBudget":0}}}`, + expectField: "thinking.type", + expectValue: "disabled", + expectErr: false, + }, + // Case 47: thinkingBudget=-1 → 64512 (mid-range) + { + name: "47", + from: "gemini", + to: "claude", + model: "claude-budget-model", + inputJSON: `{"model":"claude-budget-model","contents":[{"role":"user","parts":[{"text":"hi"}]}],"generationConfig":{"thinkingConfig":{"thinkingBudget":-1}}}`, + expectField: "thinking.budget_tokens", + expectValue: "64512", + expectErr: false, + }, + + // antigravity-budget-model (Min=128, Max=20000, ZeroAllowed=true, DynamicAllowed=true) + + // Case 48: Gemini no param → passthrough + { + name: "48", + from: "gemini", + to: "antigravity", + model: "antigravity-budget-model", + inputJSON: `{"model":"antigravity-budget-model","contents":[{"role":"user","parts":[{"text":"hi"}]}]}`, + expectField: "", + expectErr: false, + }, + // Case 49: thinkingLevel=medium → 8192 + { + name: "49", + from: "gemini", + to: "antigravity", + model: "antigravity-budget-model", + inputJSON: `{"model":"antigravity-budget-model","contents":[{"role":"user","parts":[{"text":"hi"}]}],"generationConfig":{"thinkingConfig":{"thinkingLevel":"medium"}}}`, + expectField: "request.generationConfig.thinkingConfig.thinkingBudget", + expectValue: "8192", + includeThoughts: "true", + expectErr: false, + }, + // Case 50: thinkingLevel=xhigh → clamped to 20000 + { + name: "50", + from: "gemini", + to: "antigravity", + model: "antigravity-budget-model", + inputJSON: `{"model":"antigravity-budget-model","contents":[{"role":"user","parts":[{"text":"hi"}]}],"generationConfig":{"thinkingConfig":{"thinkingLevel":"xhigh"}}}`, + expectField: "request.generationConfig.thinkingConfig.thinkingBudget", + expectValue: "20000", + includeThoughts: "true", + expectErr: false, + }, + // Case 51: thinkingLevel=none → 0 (ZeroAllowed=true) + { + name: "51", + from: "gemini", + to: "antigravity", + model: "antigravity-budget-model", + inputJSON: `{"model":"antigravity-budget-model","contents":[{"role":"user","parts":[{"text":"hi"}]}],"generationConfig":{"thinkingConfig":{"thinkingLevel":"none"}}}`, + expectField: "request.generationConfig.thinkingConfig.thinkingBudget", + expectValue: "0", + includeThoughts: "false", + expectErr: false, + }, + // Case 52: thinkingBudget=-1 → -1 (DynamicAllowed=true) + { + name: "52", + from: "gemini", + to: "antigravity", + model: "antigravity-budget-model", + inputJSON: `{"model":"antigravity-budget-model","contents":[{"role":"user","parts":[{"text":"hi"}]}],"generationConfig":{"thinkingConfig":{"thinkingBudget":-1}}}`, + expectField: "request.generationConfig.thinkingConfig.thinkingBudget", + expectValue: "-1", + includeThoughts: "true", + expectErr: false, + }, + // Case 53: Claude no param → passthrough + { + name: "53", + from: "claude", + to: "antigravity", + model: "antigravity-budget-model", + inputJSON: `{"model":"antigravity-budget-model","messages":[{"role":"user","content":"hi"}]}`, + expectField: "", + expectErr: false, + }, + // Case 54: thinking.budget_tokens=8192 → 8192 + { + name: "54", + from: "claude", + to: "antigravity", + model: "antigravity-budget-model", + inputJSON: `{"model":"antigravity-budget-model","messages":[{"role":"user","content":"hi"}],"thinking":{"type":"enabled","budget_tokens":8192}}`, + expectField: "request.generationConfig.thinkingConfig.thinkingBudget", + expectValue: "8192", + includeThoughts: "true", + expectErr: false, + }, + // Case 55: thinking.budget_tokens=64000 → clamped to 20000 + { + name: "55", + from: "claude", + to: "antigravity", + model: "antigravity-budget-model", + inputJSON: `{"model":"antigravity-budget-model","messages":[{"role":"user","content":"hi"}],"thinking":{"type":"enabled","budget_tokens":64000}}`, + expectField: "request.generationConfig.thinkingConfig.thinkingBudget", + expectValue: "20000", + includeThoughts: "true", + expectErr: false, + }, + // Case 56: thinking.budget_tokens=0 → 0 (ZeroAllowed=true) + { + name: "56", + from: "claude", + to: "antigravity", + model: "antigravity-budget-model", + inputJSON: `{"model":"antigravity-budget-model","messages":[{"role":"user","content":"hi"}],"thinking":{"type":"enabled","budget_tokens":0}}`, + expectField: "request.generationConfig.thinkingConfig.thinkingBudget", + expectValue: "0", + includeThoughts: "false", + expectErr: false, + }, + // Case 57: thinking.budget_tokens=-1 → -1 (DynamicAllowed=true) + { + name: "57", + from: "claude", + to: "antigravity", + model: "antigravity-budget-model", + inputJSON: `{"model":"antigravity-budget-model","messages":[{"role":"user","content":"hi"}],"thinking":{"type":"enabled","budget_tokens":-1}}`, + expectField: "request.generationConfig.thinkingConfig.thinkingBudget", + expectValue: "-1", + includeThoughts: "true", + expectErr: false, + }, + + // no-thinking-model (Thinking=nil) + + // Case 58: Gemini no param → passthrough + { + name: "58", + from: "gemini", + to: "openai", + model: "no-thinking-model", + inputJSON: `{"model":"no-thinking-model","contents":[{"role":"user","parts":[{"text":"hi"}]}]}`, + expectField: "", + expectErr: false, + }, + // Case 59: thinkingBudget=8192 → stripped + { + name: "59", + from: "gemini", + to: "openai", + model: "no-thinking-model", + inputJSON: `{"model":"no-thinking-model","contents":[{"role":"user","parts":[{"text":"hi"}]}],"generationConfig":{"thinkingConfig":{"thinkingBudget":8192}}}`, + expectField: "", + expectErr: false, + }, + // Case 60: thinkingBudget=0 → stripped + { + name: "60", + from: "gemini", + to: "openai", + model: "no-thinking-model", + inputJSON: `{"model":"no-thinking-model","contents":[{"role":"user","parts":[{"text":"hi"}]}],"generationConfig":{"thinkingConfig":{"thinkingBudget":0}}}`, + expectField: "", + expectErr: false, + }, + // Case 61: thinkingBudget=-1 → stripped + { + name: "61", + from: "gemini", + to: "openai", + model: "no-thinking-model", + inputJSON: `{"model":"no-thinking-model","contents":[{"role":"user","parts":[{"text":"hi"}]}],"generationConfig":{"thinkingConfig":{"thinkingBudget":-1}}}`, + expectField: "", + expectErr: false, + }, + // Case 62: Claude no param → passthrough + { + name: "62", + from: "claude", + to: "openai", + model: "no-thinking-model", + inputJSON: `{"model":"no-thinking-model","messages":[{"role":"user","content":"hi"}]}`, + expectField: "", + expectErr: false, + }, + // Case 63: thinking.budget_tokens=8192 → stripped + { + name: "63", + from: "claude", + to: "openai", + model: "no-thinking-model", + inputJSON: `{"model":"no-thinking-model","messages":[{"role":"user","content":"hi"}],"thinking":{"type":"enabled","budget_tokens":8192}}`, + expectField: "", + expectErr: false, + }, + // Case 64: thinking.budget_tokens=0 → stripped + { + name: "64", + from: "claude", + to: "openai", + model: "no-thinking-model", + inputJSON: `{"model":"no-thinking-model","messages":[{"role":"user","content":"hi"}],"thinking":{"type":"enabled","budget_tokens":0}}`, + expectField: "", + expectErr: false, + }, + // Case 65: thinking.budget_tokens=-1 → stripped + { + name: "65", + from: "claude", + to: "openai", + model: "no-thinking-model", + inputJSON: `{"model":"no-thinking-model","messages":[{"role":"user","content":"hi"}],"thinking":{"type":"enabled","budget_tokens":-1}}`, + expectField: "", + expectErr: false, + }, + + // user-defined-model (UserDefined=true, Thinking=nil) + + // Case 66: Gemini no param → passthrough + { + name: "66", + from: "gemini", + to: "openai", + model: "user-defined-model", + inputJSON: `{"model":"user-defined-model","contents":[{"role":"user","parts":[{"text":"hi"}]}]}`, + expectField: "", + expectErr: false, + }, + // Case 67: thinkingBudget=8192 → medium + { + name: "67", + from: "gemini", + to: "openai", + model: "user-defined-model", + inputJSON: `{"model":"user-defined-model","contents":[{"role":"user","parts":[{"text":"hi"}]}],"generationConfig":{"thinkingConfig":{"thinkingBudget":8192}}}`, + expectField: "reasoning_effort", + expectValue: "medium", + expectErr: false, + }, + // Case 68: thinkingBudget=64000 → xhigh (passthrough) + { + name: "68", + from: "gemini", + to: "openai", + model: "user-defined-model", + inputJSON: `{"model":"user-defined-model","contents":[{"role":"user","parts":[{"text":"hi"}]}],"generationConfig":{"thinkingConfig":{"thinkingBudget":64000}}}`, + expectField: "reasoning_effort", + expectValue: "xhigh", + expectErr: false, + }, + // Case 69: thinkingBudget=0 → none + { + name: "69", + from: "gemini", + to: "openai", + model: "user-defined-model", + inputJSON: `{"model":"user-defined-model","contents":[{"role":"user","parts":[{"text":"hi"}]}],"generationConfig":{"thinkingConfig":{"thinkingBudget":0}}}`, + expectField: "reasoning_effort", + expectValue: "none", + expectErr: false, + }, + // Case 70: thinkingBudget=-1 → auto + { + name: "70", + from: "gemini", + to: "openai", + model: "user-defined-model", + inputJSON: `{"model":"user-defined-model","contents":[{"role":"user","parts":[{"text":"hi"}]}],"generationConfig":{"thinkingConfig":{"thinkingBudget":-1}}}`, + expectField: "reasoning_effort", + expectValue: "auto", + expectErr: false, + }, + // Case 71: Claude no param → injected default → medium + { + name: "71", + from: "claude", + to: "codex", + model: "user-defined-model", + inputJSON: `{"model":"user-defined-model","messages":[{"role":"user","content":"hi"}]}`, + expectField: "reasoning.effort", + expectValue: "medium", + expectErr: false, + }, + // Case 72: thinking.budget_tokens=8192 → medium + { + name: "72", + from: "claude", + to: "codex", + model: "user-defined-model", + inputJSON: `{"model":"user-defined-model","messages":[{"role":"user","content":"hi"}],"thinking":{"type":"enabled","budget_tokens":8192}}`, + expectField: "reasoning.effort", + expectValue: "medium", + expectErr: false, + }, + // Case 73: thinking.budget_tokens=64000 → xhigh (passthrough) + { + name: "73", + from: "claude", + to: "codex", + model: "user-defined-model", + inputJSON: `{"model":"user-defined-model","messages":[{"role":"user","content":"hi"}],"thinking":{"type":"enabled","budget_tokens":64000}}`, + expectField: "reasoning.effort", + expectValue: "xhigh", + expectErr: false, + }, + // Case 74: thinking.budget_tokens=0 → none + { + name: "74", + from: "claude", + to: "codex", + model: "user-defined-model", + inputJSON: `{"model":"user-defined-model","messages":[{"role":"user","content":"hi"}],"thinking":{"type":"enabled","budget_tokens":0}}`, + expectField: "reasoning.effort", + expectValue: "none", + expectErr: false, + }, + // Case 75: thinking.budget_tokens=-1 → auto + { + name: "75", + from: "claude", + to: "codex", + model: "user-defined-model", + inputJSON: `{"model":"user-defined-model","messages":[{"role":"user","content":"hi"}],"thinking":{"type":"enabled","budget_tokens":-1}}`, + expectField: "reasoning.effort", + expectValue: "auto", + expectErr: false, + }, + // Case 76: OpenAI reasoning_effort=medium to Gemini → 8192 + { + name: "76", + from: "openai", + to: "gemini", + model: "user-defined-model", + inputJSON: `{"model":"user-defined-model","messages":[{"role":"user","content":"hi"}],"reasoning_effort":"medium"}`, + expectField: "generationConfig.thinkingConfig.thinkingBudget", + expectValue: "8192", + includeThoughts: "true", + expectErr: false, + }, + // Case 77: OpenAI reasoning_effort=medium to Claude → 8192 + { + name: "77", + from: "openai", + to: "claude", + model: "user-defined-model", + inputJSON: `{"model":"user-defined-model","messages":[{"role":"user","content":"hi"}],"reasoning_effort":"medium"}`, + expectField: "thinking.budget_tokens", + expectValue: "8192", + expectErr: false, + }, + // Case 78: Codex reasoning.effort=medium to Gemini → 8192 + { + name: "78", + from: "codex", + to: "gemini", + model: "user-defined-model", + inputJSON: `{"model":"user-defined-model","input":[{"role":"user","content":"hi"}],"reasoning":{"effort":"medium"}}`, + expectField: "generationConfig.thinkingConfig.thinkingBudget", + expectValue: "8192", + includeThoughts: "true", + expectErr: false, + }, + // Case 79: Codex reasoning.effort=medium to Claude → 8192 + { + name: "79", + from: "codex", + to: "claude", + model: "user-defined-model", + inputJSON: `{"model":"user-defined-model","input":[{"role":"user","content":"hi"}],"reasoning":{"effort":"medium"}}`, + expectField: "thinking.budget_tokens", + expectValue: "8192", + expectErr: false, + }, + + // Same-protocol passthrough tests (80-89) + + // Case 80: OpenAI to OpenAI, reasoning_effort=high → passthrough + { + name: "80", + from: "openai", + to: "openai", + model: "level-model", + inputJSON: `{"model":"level-model","messages":[{"role":"user","content":"hi"}],"reasoning_effort":"high"}`, + expectField: "reasoning_effort", + expectValue: "high", + expectErr: false, + }, + // Case 81: OpenAI to OpenAI, reasoning_effort=xhigh → out of range error + { + name: "81", + from: "openai", + to: "openai", + model: "level-model", + inputJSON: `{"model":"level-model","messages":[{"role":"user","content":"hi"}],"reasoning_effort":"xhigh"}`, + expectField: "", + expectErr: true, + }, + // Case 82: Codex to Codex, reasoning.effort=high → passthrough + { + name: "82", + from: "codex", + to: "codex", + model: "level-model", + inputJSON: `{"model":"level-model","input":[{"role":"user","content":"hi"}],"reasoning":{"effort":"high"}}`, + expectField: "reasoning.effort", + expectValue: "high", + expectErr: false, + }, + // Case 83: Codex to Codex, reasoning.effort=xhigh → out of range error + { + name: "83", + from: "codex", + to: "codex", + model: "level-model", + inputJSON: `{"model":"level-model","input":[{"role":"user","content":"hi"}],"reasoning":{"effort":"xhigh"}}`, + expectField: "", + expectErr: true, + }, + // Case 84: Gemini to Gemini, thinkingBudget=8192 → passthrough + { + name: "84", + from: "gemini", + to: "gemini", + model: "gemini-budget-model", + inputJSON: `{"model":"gemini-budget-model","contents":[{"role":"user","parts":[{"text":"hi"}]}],"generationConfig":{"thinkingConfig":{"thinkingBudget":8192}}}`, + expectField: "generationConfig.thinkingConfig.thinkingBudget", + expectValue: "8192", + includeThoughts: "true", + expectErr: false, + }, + // Case 85: Gemini to Gemini, thinkingBudget=64000 → exceeds Max error + { + name: "85", + from: "gemini", + to: "gemini", + model: "gemini-budget-model", + inputJSON: `{"model":"gemini-budget-model","contents":[{"role":"user","parts":[{"text":"hi"}]}],"generationConfig":{"thinkingConfig":{"thinkingBudget":64000}}}`, + expectField: "", + expectErr: true, + }, + // Case 86: Claude to Claude, thinking.budget_tokens=8192 → passthrough + { + name: "86", + from: "claude", + to: "claude", + model: "claude-budget-model", + inputJSON: `{"model":"claude-budget-model","messages":[{"role":"user","content":"hi"}],"thinking":{"type":"enabled","budget_tokens":8192}}`, + expectField: "thinking.budget_tokens", + expectValue: "8192", + expectErr: false, + }, + // Case 87: Claude to Claude, thinking.budget_tokens=200000 → exceeds Max error + { + name: "87", + from: "claude", + to: "claude", + model: "claude-budget-model", + inputJSON: `{"model":"claude-budget-model","messages":[{"role":"user","content":"hi"}],"thinking":{"type":"enabled","budget_tokens":200000}}`, + expectField: "", + expectErr: true, + }, + // Case 88: Antigravity to Antigravity, thinkingBudget=8192 → passthrough + { + name: "88", + from: "antigravity", + to: "antigravity", + model: "antigravity-budget-model", + inputJSON: `{"model":"antigravity-budget-model","request":{"contents":[{"role":"user","parts":[{"text":"hi"}]}],"generationConfig":{"thinkingConfig":{"thinkingBudget":8192}}}}`, + expectField: "request.generationConfig.thinkingConfig.thinkingBudget", + expectValue: "8192", + includeThoughts: "true", + expectErr: false, + }, + // Case 89: Antigravity to Antigravity, thinkingBudget=64000 → exceeds Max error + { + name: "89", + from: "antigravity", + to: "antigravity", + model: "antigravity-budget-model", + inputJSON: `{"model":"antigravity-budget-model","request":{"contents":[{"role":"user","parts":[{"text":"hi"}]}],"generationConfig":{"thinkingConfig":{"thinkingBudget":64000}}}}`, + expectField: "", + expectErr: true, + }, + + // iflow tests: glm-test and minimax-test (Cases 90-105) + + // glm-test (from: openai, claude) + // Case 90: OpenAI to iflow, no param → passthrough + { + name: "90", + from: "openai", + to: "iflow", + model: "glm-test", + inputJSON: `{"model":"glm-test","messages":[{"role":"user","content":"hi"}]}`, + expectField: "", + expectErr: false, + }, + // Case 91: OpenAI to iflow, reasoning_effort=medium → enable_thinking=true + { + name: "91", + from: "openai", + to: "iflow", + model: "glm-test", + inputJSON: `{"model":"glm-test","messages":[{"role":"user","content":"hi"}],"reasoning_effort":"medium"}`, + expectField: "chat_template_kwargs.enable_thinking", + expectValue: "true", + expectErr: false, + }, + // Case 92: OpenAI to iflow, reasoning_effort=auto → enable_thinking=true + { + name: "92", + from: "openai", + to: "iflow", + model: "glm-test", + inputJSON: `{"model":"glm-test","messages":[{"role":"user","content":"hi"}],"reasoning_effort":"auto"}`, + expectField: "chat_template_kwargs.enable_thinking", + expectValue: "true", + expectErr: false, + }, + // Case 93: OpenAI to iflow, reasoning_effort=none → enable_thinking=false + { + name: "93", + from: "openai", + to: "iflow", + model: "glm-test", + inputJSON: `{"model":"glm-test","messages":[{"role":"user","content":"hi"}],"reasoning_effort":"none"}`, + expectField: "chat_template_kwargs.enable_thinking", + expectValue: "false", + expectErr: false, + }, + // Case 94: Claude to iflow, no param → passthrough + { + name: "94", + from: "claude", + to: "iflow", + model: "glm-test", + inputJSON: `{"model":"glm-test","messages":[{"role":"user","content":"hi"}]}`, + expectField: "", + expectErr: false, + }, + // Case 95: Claude to iflow, thinking.budget_tokens=8192 → enable_thinking=true + { + name: "95", + from: "claude", + to: "iflow", + model: "glm-test", + inputJSON: `{"model":"glm-test","messages":[{"role":"user","content":"hi"}],"thinking":{"type":"enabled","budget_tokens":8192}}`, + expectField: "chat_template_kwargs.enable_thinking", + expectValue: "true", + expectErr: false, + }, + // Case 96: Claude to iflow, thinking.budget_tokens=-1 → enable_thinking=true + { + name: "96", + from: "claude", + to: "iflow", + model: "glm-test", + inputJSON: `{"model":"glm-test","messages":[{"role":"user","content":"hi"}],"thinking":{"type":"enabled","budget_tokens":-1}}`, + expectField: "chat_template_kwargs.enable_thinking", + expectValue: "true", + expectErr: false, + }, + // Case 97: Claude to iflow, thinking.budget_tokens=0 → enable_thinking=false + { + name: "97", + from: "claude", + to: "iflow", + model: "glm-test", + inputJSON: `{"model":"glm-test","messages":[{"role":"user","content":"hi"}],"thinking":{"type":"enabled","budget_tokens":0}}`, + expectField: "chat_template_kwargs.enable_thinking", + expectValue: "false", + expectErr: false, + }, + + // minimax-test (from: openai, gemini) + // Case 98: OpenAI to iflow, no param → passthrough + { + name: "98", + from: "openai", + to: "iflow", + model: "minimax-test", + inputJSON: `{"model":"minimax-test","messages":[{"role":"user","content":"hi"}]}`, + expectField: "", + expectErr: false, + }, + // Case 99: OpenAI to iflow, reasoning_effort=medium → reasoning_split=true + { + name: "99", + from: "openai", + to: "iflow", + model: "minimax-test", + inputJSON: `{"model":"minimax-test","messages":[{"role":"user","content":"hi"}],"reasoning_effort":"medium"}`, + expectField: "reasoning_split", + expectValue: "true", + expectErr: false, + }, + // Case 100: OpenAI to iflow, reasoning_effort=auto → reasoning_split=true + { + name: "100", + from: "openai", + to: "iflow", + model: "minimax-test", + inputJSON: `{"model":"minimax-test","messages":[{"role":"user","content":"hi"}],"reasoning_effort":"auto"}`, + expectField: "reasoning_split", + expectValue: "true", + expectErr: false, + }, + // Case 101: OpenAI to iflow, reasoning_effort=none → reasoning_split=false + { + name: "101", + from: "openai", + to: "iflow", + model: "minimax-test", + inputJSON: `{"model":"minimax-test","messages":[{"role":"user","content":"hi"}],"reasoning_effort":"none"}`, + expectField: "reasoning_split", + expectValue: "false", + expectErr: false, + }, + // Case 102: Gemini to iflow, no param → passthrough + { + name: "102", + from: "gemini", + to: "iflow", + model: "minimax-test", + inputJSON: `{"model":"minimax-test","contents":[{"role":"user","parts":[{"text":"hi"}]}]}`, + expectField: "", + expectErr: false, + }, + // Case 103: Gemini to iflow, thinkingBudget=8192 → reasoning_split=true + { + name: "103", + from: "gemini", + to: "iflow", + model: "minimax-test", + inputJSON: `{"model":"minimax-test","contents":[{"role":"user","parts":[{"text":"hi"}]}],"generationConfig":{"thinkingConfig":{"thinkingBudget":8192}}}`, + expectField: "reasoning_split", + expectValue: "true", + expectErr: false, + }, + // Case 104: Gemini to iflow, thinkingBudget=-1 → reasoning_split=true + { + name: "104", + from: "gemini", + to: "iflow", + model: "minimax-test", + inputJSON: `{"model":"minimax-test","contents":[{"role":"user","parts":[{"text":"hi"}]}],"generationConfig":{"thinkingConfig":{"thinkingBudget":-1}}}`, + expectField: "reasoning_split", + expectValue: "true", + expectErr: false, + }, + // Case 105: Gemini to iflow, thinkingBudget=0 → reasoning_split=false + { + name: "105", + from: "gemini", + to: "iflow", + model: "minimax-test", + inputJSON: `{"model":"minimax-test","contents":[{"role":"user","parts":[{"text":"hi"}]}],"generationConfig":{"thinkingConfig":{"thinkingBudget":0}}}`, + expectField: "reasoning_split", + expectValue: "false", + expectErr: false, + }, + } + + runThinkingTests(t, cases) +} + +// getTestModels returns the shared model definitions for E2E tests. +func getTestModels() []*registry.ModelInfo { + return []*registry.ModelInfo{ { ID: "level-model", Object: "model", @@ -38,11 +2448,16 @@ func TestThinkingE2EMatrix(t *testing.T) { OwnedBy: "test", Type: "openai", DisplayName: "Level Model", - Thinking: ®istry.ThinkingSupport{ - Levels: []string{"minimal", "low", "medium", "high"}, - ZeroAllowed: false, - DynamicAllowed: false, - }, + Thinking: ®istry.ThinkingSupport{Levels: []string{"minimal", "low", "medium", "high"}, ZeroAllowed: false, DynamicAllowed: false}, + }, + { + ID: "level-subset-model", + Object: "model", + Created: 1700000000, + OwnedBy: "test", + Type: "gemini", + DisplayName: "Level Subset Model", + Thinking: ®istry.ThinkingSupport{Levels: []string{"low", "high"}, ZeroAllowed: false, DynamicAllowed: false}, }, { ID: "gemini-budget-model", @@ -51,12 +2466,7 @@ func TestThinkingE2EMatrix(t *testing.T) { OwnedBy: "test", Type: "gemini", DisplayName: "Gemini Budget Model", - Thinking: ®istry.ThinkingSupport{ - Min: 128, - Max: 20000, - ZeroAllowed: false, - DynamicAllowed: true, - }, + Thinking: ®istry.ThinkingSupport{Min: 128, Max: 20000, ZeroAllowed: false, DynamicAllowed: true}, }, { ID: "gemini-mixed-model", @@ -65,13 +2475,7 @@ func TestThinkingE2EMatrix(t *testing.T) { OwnedBy: "test", Type: "gemini", DisplayName: "Gemini Mixed Model", - Thinking: ®istry.ThinkingSupport{ - Min: 128, - Max: 32768, - Levels: []string{"low", "high"}, - ZeroAllowed: false, - DynamicAllowed: true, - }, + Thinking: ®istry.ThinkingSupport{Min: 128, Max: 32768, Levels: []string{"low", "high"}, ZeroAllowed: false, DynamicAllowed: true}, }, { ID: "claude-budget-model", @@ -80,12 +2484,7 @@ func TestThinkingE2EMatrix(t *testing.T) { OwnedBy: "test", Type: "claude", DisplayName: "Claude Budget Model", - Thinking: ®istry.ThinkingSupport{ - Min: 1024, - Max: 128000, - ZeroAllowed: true, - DynamicAllowed: false, - }, + Thinking: ®istry.ThinkingSupport{Min: 1024, Max: 128000, ZeroAllowed: true, DynamicAllowed: false}, }, { ID: "antigravity-budget-model", @@ -94,12 +2493,7 @@ func TestThinkingE2EMatrix(t *testing.T) { OwnedBy: "test", Type: "gemini-cli", DisplayName: "Antigravity Budget Model", - Thinking: ®istry.ThinkingSupport{ - Min: 128, - Max: 20000, - ZeroAllowed: true, - DynamicAllowed: true, - }, + Thinking: ®istry.ThinkingSupport{Min: 128, Max: 20000, ZeroAllowed: true, DynamicAllowed: true}, }, { ID: "no-thinking-model", @@ -120,877 +2514,53 @@ func TestThinkingE2EMatrix(t *testing.T) { UserDefined: true, Thinking: nil, }, - } - - reg.RegisterClient(uid, "test", testModels) - defer reg.UnregisterClient(uid) - - type testCase struct { - name string - from string - to string - modelSuffix string - inputJSON string - expectField string - expectValue string - includeThoughts string - expectErr bool - } - - cases := []testCase{ - // level-model (Levels=minimal/low/medium/high, ZeroAllowed=false, DynamicAllowed=false) - // Case 1: No suffix, translator adds default medium for codex { - name: "1", - from: "openai", - to: "codex", - modelSuffix: "level-model", - inputJSON: `{"model":"level-model","messages":[{"role":"user","content":"hi"}]}`, - expectField: "reasoning.effort", - expectValue: "medium", - expectErr: false, - }, - // Case 2: Explicit medium level - { - name: "2", - from: "openai", - to: "codex", - modelSuffix: "level-model(medium)", - inputJSON: `{"model":"level-model(medium)","messages":[{"role":"user","content":"hi"}]}`, - expectField: "reasoning.effort", - expectValue: "medium", - expectErr: false, - }, - // Case 3: xhigh not in Levels=[minimal,low,medium,high] → ValidateConfig returns error - { - name: "3", - from: "openai", - to: "codex", - modelSuffix: "level-model(xhigh)", - inputJSON: `{"model":"level-model(xhigh)","messages":[{"role":"user","content":"hi"}]}`, - expectField: "", - expectErr: true, - }, - // Case 4: none → ModeNone, ZeroAllowed=false → clamp to min level (minimal) - { - name: "4", - from: "openai", - to: "codex", - modelSuffix: "level-model(none)", - inputJSON: `{"model":"level-model(none)","messages":[{"role":"user","content":"hi"}]}`, - expectField: "reasoning.effort", - expectValue: "minimal", - expectErr: false, - }, - // Case 5: auto → ModeAuto, DynamicAllowed=false → convert to mid-range (medium) - { - name: "5", - from: "openai", - to: "codex", - modelSuffix: "level-model(auto)", - inputJSON: `{"model":"level-model(auto)","messages":[{"role":"user","content":"hi"}]}`, - expectField: "reasoning.effort", - expectValue: "medium", - expectErr: false, - }, - // Case 6: No suffix from gemini → translator injects default reasoning.effort: medium - { - name: "6", - from: "gemini", - to: "codex", - modelSuffix: "level-model", - inputJSON: `{"model":"level-model","contents":[{"role":"user","parts":[{"text":"hi"}]}]}`, - expectField: "reasoning.effort", - expectValue: "medium", - expectErr: false, - }, - // Case 7: 8192 → medium (1025-8192) - { - name: "7", - from: "gemini", - to: "codex", - modelSuffix: "level-model(8192)", - inputJSON: `{"model":"level-model(8192)","contents":[{"role":"user","parts":[{"text":"hi"}]}]}`, - expectField: "reasoning.effort", - expectValue: "medium", - expectErr: false, - }, - // Case 8: 64000 → xhigh → not supported → error - { - name: "8", - from: "gemini", - to: "codex", - modelSuffix: "level-model(64000)", - inputJSON: `{"model":"level-model(64000)","contents":[{"role":"user","parts":[{"text":"hi"}]}]}`, - expectField: "", - expectErr: true, - }, - // Case 9: 0 → ModeNone, ZeroAllowed=false → clamp to min level (minimal) - { - name: "9", - from: "gemini", - to: "codex", - modelSuffix: "level-model(0)", - inputJSON: `{"model":"level-model(0)","contents":[{"role":"user","parts":[{"text":"hi"}]}]}`, - expectField: "reasoning.effort", - expectValue: "minimal", - expectErr: false, - }, - // Case 10: -1 → ModeAuto, DynamicAllowed=false → convert to mid-range (medium) - { - name: "10", - from: "gemini", - to: "codex", - modelSuffix: "level-model(-1)", - inputJSON: `{"model":"level-model(-1)","contents":[{"role":"user","parts":[{"text":"hi"}]}]}`, - expectField: "reasoning.effort", - expectValue: "medium", - expectErr: false, - }, - // Case 11: No suffix from claude → no thinking config - { - name: "11", - from: "claude", - to: "openai", - modelSuffix: "level-model", - inputJSON: `{"model":"level-model","messages":[{"role":"user","content":"hi"}]}`, - expectField: "", - expectErr: false, - }, - // Case 12: 8192 → medium - { - name: "12", - from: "claude", - to: "openai", - modelSuffix: "level-model(8192)", - inputJSON: `{"model":"level-model(8192)","messages":[{"role":"user","content":"hi"}]}`, - expectField: "reasoning_effort", - expectValue: "medium", - expectErr: false, - }, - // Case 13: 64000 → xhigh → not supported → error - { - name: "13", - from: "claude", - to: "openai", - modelSuffix: "level-model(64000)", - inputJSON: `{"model":"level-model(64000)","messages":[{"role":"user","content":"hi"}]}`, - expectField: "", - expectErr: true, - }, - // Case 14: 0 → ModeNone, ZeroAllowed=false → clamp to min level (minimal) - { - name: "14", - from: "claude", - to: "openai", - modelSuffix: "level-model(0)", - inputJSON: `{"model":"level-model(0)","messages":[{"role":"user","content":"hi"}]}`, - expectField: "reasoning_effort", - expectValue: "minimal", - expectErr: false, - }, - // Case 15: -1 → ModeAuto, DynamicAllowed=false → convert to mid-range (medium) - { - name: "15", - from: "claude", - to: "openai", - modelSuffix: "level-model(-1)", - inputJSON: `{"model":"level-model(-1)","messages":[{"role":"user","content":"hi"}]}`, - expectField: "reasoning_effort", - expectValue: "medium", - expectErr: false, - }, - - // gemini-budget-model (Min=128, Max=20000, ZeroAllowed=false, DynamicAllowed=true) - { - name: "16", - from: "openai", - to: "gemini", - modelSuffix: "gemini-budget-model", - inputJSON: `{"model":"gemini-budget-model","messages":[{"role":"user","content":"hi"}]}`, - expectField: "", - expectErr: false, - }, - // medium → 8192 - { - name: "17", - from: "openai", - to: "gemini", - modelSuffix: "gemini-budget-model(medium)", - inputJSON: `{"model":"gemini-budget-model(medium)","messages":[{"role":"user","content":"hi"}]}`, - expectField: "generationConfig.thinkingConfig.thinkingBudget", - expectValue: "8192", - includeThoughts: "true", - expectErr: false, - }, - // xhigh → 32768 → clamp to 20000 - { - name: "18", - from: "openai", - to: "gemini", - modelSuffix: "gemini-budget-model(xhigh)", - inputJSON: `{"model":"gemini-budget-model(xhigh)","messages":[{"role":"user","content":"hi"}]}`, - expectField: "generationConfig.thinkingConfig.thinkingBudget", - expectValue: "20000", - includeThoughts: "true", - expectErr: false, - }, - // none → 0 → ZeroAllowed=false → clamp to 128, includeThoughts=false - { - name: "19", - from: "openai", - to: "gemini", - modelSuffix: "gemini-budget-model(none)", - inputJSON: `{"model":"gemini-budget-model(none)","messages":[{"role":"user","content":"hi"}]}`, - expectField: "generationConfig.thinkingConfig.thinkingBudget", - expectValue: "128", - includeThoughts: "false", - expectErr: false, - }, - // auto → -1 dynamic allowed - { - name: "20", - from: "openai", - to: "gemini", - modelSuffix: "gemini-budget-model(auto)", - inputJSON: `{"model":"gemini-budget-model(auto)","messages":[{"role":"user","content":"hi"}]}`, - expectField: "generationConfig.thinkingConfig.thinkingBudget", - expectValue: "-1", - includeThoughts: "true", - expectErr: false, - }, - { - name: "21", - from: "claude", - to: "gemini", - modelSuffix: "gemini-budget-model", - inputJSON: `{"model":"gemini-budget-model","messages":[{"role":"user","content":"hi"}]}`, - expectField: "", - expectErr: false, - }, - { - name: "22", - from: "claude", - to: "gemini", - modelSuffix: "gemini-budget-model(8192)", - inputJSON: `{"model":"gemini-budget-model(8192)","messages":[{"role":"user","content":"hi"}]}`, - expectField: "generationConfig.thinkingConfig.thinkingBudget", - expectValue: "8192", - includeThoughts: "true", - expectErr: false, - }, - { - name: "23", - from: "claude", - to: "gemini", - modelSuffix: "gemini-budget-model(64000)", - inputJSON: `{"model":"gemini-budget-model(64000)","messages":[{"role":"user","content":"hi"}]}`, - expectField: "generationConfig.thinkingConfig.thinkingBudget", - expectValue: "20000", - includeThoughts: "true", - expectErr: false, - }, - { - name: "24", - from: "claude", - to: "gemini", - modelSuffix: "gemini-budget-model(0)", - inputJSON: `{"model":"gemini-budget-model(0)","messages":[{"role":"user","content":"hi"}]}`, - expectField: "generationConfig.thinkingConfig.thinkingBudget", - expectValue: "128", - includeThoughts: "false", - expectErr: false, - }, - { - name: "25", - from: "claude", - to: "gemini", - modelSuffix: "gemini-budget-model(-1)", - inputJSON: `{"model":"gemini-budget-model(-1)","messages":[{"role":"user","content":"hi"}]}`, - expectField: "generationConfig.thinkingConfig.thinkingBudget", - expectValue: "-1", - includeThoughts: "true", - expectErr: false, - }, - - // gemini-mixed-model (Min=128, Max=32768, Levels=low/high, ZeroAllowed=false, DynamicAllowed=true) - { - name: "26", - from: "openai", - to: "gemini", - modelSuffix: "gemini-mixed-model", - inputJSON: `{"model":"gemini-mixed-model","messages":[{"role":"user","content":"hi"}]}`, - expectField: "", - expectErr: false, - }, - // high → use thinkingLevel - { - name: "27", - from: "openai", - to: "gemini", - modelSuffix: "gemini-mixed-model(high)", - inputJSON: `{"model":"gemini-mixed-model(high)","messages":[{"role":"user","content":"hi"}]}`, - expectField: "generationConfig.thinkingConfig.thinkingLevel", - expectValue: "high", - includeThoughts: "true", - expectErr: false, - }, - // xhigh → not in Levels=[low,high] → error - { - name: "28", - from: "openai", - to: "gemini", - modelSuffix: "gemini-mixed-model(xhigh)", - inputJSON: `{"model":"gemini-mixed-model(xhigh)","messages":[{"role":"user","content":"hi"}]}`, - expectField: "", - expectErr: true, - }, - // none → ModeNone, ZeroAllowed=false → set Level to lowest (low), includeThoughts=false - { - name: "29", - from: "openai", - to: "gemini", - modelSuffix: "gemini-mixed-model(none)", - inputJSON: `{"model":"gemini-mixed-model(none)","messages":[{"role":"user","content":"hi"}]}`, - expectField: "generationConfig.thinkingConfig.thinkingLevel", - expectValue: "low", - includeThoughts: "false", - expectErr: false, - }, - // auto → dynamic allowed, use thinkingBudget=-1 - { - name: "30", - from: "openai", - to: "gemini", - modelSuffix: "gemini-mixed-model(auto)", - inputJSON: `{"model":"gemini-mixed-model(auto)","messages":[{"role":"user","content":"hi"}]}`, - expectField: "generationConfig.thinkingConfig.thinkingBudget", - expectValue: "-1", - includeThoughts: "true", - expectErr: false, - }, - { - name: "31", - from: "claude", - to: "gemini", - modelSuffix: "gemini-mixed-model", - inputJSON: `{"model":"gemini-mixed-model","messages":[{"role":"user","content":"hi"}]}`, - expectField: "", - expectErr: false, - }, - // 8192 → ModeBudget → clamp (in range) → thinkingBudget: 8192 - { - name: "32", - from: "claude", - to: "gemini", - modelSuffix: "gemini-mixed-model(8192)", - inputJSON: `{"model":"gemini-mixed-model(8192)","messages":[{"role":"user","content":"hi"}]}`, - expectField: "generationConfig.thinkingConfig.thinkingBudget", - expectValue: "8192", - includeThoughts: "true", - expectErr: false, - }, - // 64000 → ModeBudget → clamp to 32768 → thinkingBudget: 32768 - { - name: "33", - from: "claude", - to: "gemini", - modelSuffix: "gemini-mixed-model(64000)", - inputJSON: `{"model":"gemini-mixed-model(64000)","messages":[{"role":"user","content":"hi"}]}`, - expectField: "generationConfig.thinkingConfig.thinkingBudget", - expectValue: "32768", - includeThoughts: "true", - expectErr: false, - }, - // 0 → ModeNone, ZeroAllowed=false → set Level to lowest (low), includeThoughts=false - { - name: "34", - from: "claude", - to: "gemini", - modelSuffix: "gemini-mixed-model(0)", - inputJSON: `{"model":"gemini-mixed-model(0)","messages":[{"role":"user","content":"hi"}]}`, - expectField: "generationConfig.thinkingConfig.thinkingLevel", - expectValue: "low", - includeThoughts: "false", - expectErr: false, - }, - // -1 → auto, dynamic allowed - { - name: "35", - from: "claude", - to: "gemini", - modelSuffix: "gemini-mixed-model(-1)", - inputJSON: `{"model":"gemini-mixed-model(-1)","messages":[{"role":"user","content":"hi"}]}`, - expectField: "generationConfig.thinkingConfig.thinkingBudget", - expectValue: "-1", - includeThoughts: "true", - expectErr: false, - }, - - // claude-budget-model (Min=1024, Max=128000, ZeroAllowed=true, DynamicAllowed=false) - { - name: "36", - from: "openai", - to: "claude", - modelSuffix: "claude-budget-model", - inputJSON: `{"model":"claude-budget-model","messages":[{"role":"user","content":"hi"}]}`, - expectField: "", - expectErr: false, - }, - // medium → 8192 - { - name: "37", - from: "openai", - to: "claude", - modelSuffix: "claude-budget-model(medium)", - inputJSON: `{"model":"claude-budget-model(medium)","messages":[{"role":"user","content":"hi"}]}`, - expectField: "thinking.budget_tokens", - expectValue: "8192", - expectErr: false, - }, - // xhigh → 32768 - { - name: "38", - from: "openai", - to: "claude", - modelSuffix: "claude-budget-model(xhigh)", - inputJSON: `{"model":"claude-budget-model(xhigh)","messages":[{"role":"user","content":"hi"}]}`, - expectField: "thinking.budget_tokens", - expectValue: "32768", - expectErr: false, - }, - // none → ZeroAllowed=true → disabled - { - name: "39", - from: "openai", - to: "claude", - modelSuffix: "claude-budget-model(none)", - inputJSON: `{"model":"claude-budget-model(none)","messages":[{"role":"user","content":"hi"}]}`, - expectField: "thinking.type", - expectValue: "disabled", - expectErr: false, - }, - // auto → ModeAuto, DynamicAllowed=false → convert to mid-range - { - name: "40", - from: "openai", - to: "claude", - modelSuffix: "claude-budget-model(auto)", - inputJSON: `{"model":"claude-budget-model(auto)","messages":[{"role":"user","content":"hi"}]}`, - expectField: "thinking.budget_tokens", - expectValue: "64512", - expectErr: false, - }, - { - name: "41", - from: "gemini", - to: "claude", - modelSuffix: "claude-budget-model", - inputJSON: `{"model":"claude-budget-model","contents":[{"role":"user","parts":[{"text":"hi"}]}]}`, - expectField: "", - expectErr: false, - }, - { - name: "42", - from: "gemini", - to: "claude", - modelSuffix: "claude-budget-model(8192)", - inputJSON: `{"model":"claude-budget-model(8192)","contents":[{"role":"user","parts":[{"text":"hi"}]}]}`, - expectField: "thinking.budget_tokens", - expectValue: "8192", - expectErr: false, - }, - { - name: "43", - from: "gemini", - to: "claude", - modelSuffix: "claude-budget-model(200000)", - inputJSON: `{"model":"claude-budget-model(200000)","contents":[{"role":"user","parts":[{"text":"hi"}]}]}`, - expectField: "thinking.budget_tokens", - expectValue: "128000", - expectErr: false, - }, - // 0 → ZeroAllowed=true → disabled - { - name: "44", - from: "gemini", - to: "claude", - modelSuffix: "claude-budget-model(0)", - inputJSON: `{"model":"claude-budget-model(0)","contents":[{"role":"user","parts":[{"text":"hi"}]}]}`, - expectField: "thinking.type", - expectValue: "disabled", - expectErr: false, - }, - // -1 → auto → DynamicAllowed=false → mid-range - { - name: "45", - from: "gemini", - to: "claude", - modelSuffix: "claude-budget-model(-1)", - inputJSON: `{"model":"claude-budget-model(-1)","contents":[{"role":"user","parts":[{"text":"hi"}]}]}`, - expectField: "thinking.budget_tokens", - expectValue: "64512", - expectErr: false, - }, - - // antigravity-budget-model (Min=128, Max=20000, ZeroAllowed=true, DynamicAllowed=true) - { - name: "46", - from: "gemini", - to: "antigravity", - modelSuffix: "antigravity-budget-model", - inputJSON: `{"model":"antigravity-budget-model","contents":[{"role":"user","parts":[{"text":"hi"}]}]}`, - expectField: "", - expectErr: false, - }, - { - name: "47", - from: "gemini", - to: "antigravity", - modelSuffix: "antigravity-budget-model(medium)", - inputJSON: `{"model":"antigravity-budget-model(medium)","contents":[{"role":"user","parts":[{"text":"hi"}]}]}`, - expectField: "request.generationConfig.thinkingConfig.thinkingBudget", - expectValue: "8192", - includeThoughts: "true", - expectErr: false, - }, - { - name: "48", - from: "gemini", - to: "antigravity", - modelSuffix: "antigravity-budget-model(xhigh)", - inputJSON: `{"model":"antigravity-budget-model(xhigh)","contents":[{"role":"user","parts":[{"text":"hi"}]}]}`, - expectField: "request.generationConfig.thinkingConfig.thinkingBudget", - expectValue: "20000", - includeThoughts: "true", - expectErr: false, - }, - { - name: "49", - from: "gemini", - to: "antigravity", - modelSuffix: "antigravity-budget-model(none)", - inputJSON: `{"model":"antigravity-budget-model(none)","contents":[{"role":"user","parts":[{"text":"hi"}]}]}`, - expectField: "request.generationConfig.thinkingConfig.thinkingBudget", - expectValue: "0", - includeThoughts: "false", - expectErr: false, - }, - { - name: "50", - from: "gemini", - to: "antigravity", - modelSuffix: "antigravity-budget-model(auto)", - inputJSON: `{"model":"antigravity-budget-model(auto)","contents":[{"role":"user","parts":[{"text":"hi"}]}]}`, - expectField: "request.generationConfig.thinkingConfig.thinkingBudget", - expectValue: "-1", - includeThoughts: "true", - expectErr: false, - }, - { - name: "51", - from: "claude", - to: "antigravity", - modelSuffix: "antigravity-budget-model", - inputJSON: `{"model":"antigravity-budget-model","messages":[{"role":"user","content":"hi"}]}`, - expectField: "", - expectErr: false, - }, - { - name: "52", - from: "claude", - to: "antigravity", - modelSuffix: "antigravity-budget-model(8192)", - inputJSON: `{"model":"antigravity-budget-model(8192)","messages":[{"role":"user","content":"hi"}]}`, - expectField: "request.generationConfig.thinkingConfig.thinkingBudget", - expectValue: "8192", - includeThoughts: "true", - expectErr: false, - }, - { - name: "53", - from: "claude", - to: "antigravity", - modelSuffix: "antigravity-budget-model(64000)", - inputJSON: `{"model":"antigravity-budget-model(64000)","messages":[{"role":"user","content":"hi"}]}`, - expectField: "request.generationConfig.thinkingConfig.thinkingBudget", - expectValue: "20000", - includeThoughts: "true", - expectErr: false, - }, - { - name: "54", - from: "claude", - to: "antigravity", - modelSuffix: "antigravity-budget-model(0)", - inputJSON: `{"model":"antigravity-budget-model(0)","messages":[{"role":"user","content":"hi"}]}`, - expectField: "request.generationConfig.thinkingConfig.thinkingBudget", - expectValue: "0", - includeThoughts: "false", - expectErr: false, - }, - { - name: "55", - from: "claude", - to: "antigravity", - modelSuffix: "antigravity-budget-model(-1)", - inputJSON: `{"model":"antigravity-budget-model(-1)","messages":[{"role":"user","content":"hi"}]}`, - expectField: "request.generationConfig.thinkingConfig.thinkingBudget", - expectValue: "-1", - includeThoughts: "true", - expectErr: false, - }, - - // no-thinking-model (Thinking=nil) - { - name: "46", - from: "gemini", - to: "openai", - modelSuffix: "no-thinking-model", - inputJSON: `{"model":"no-thinking-model","contents":[{"role":"user","parts":[{"text":"hi"}]}]}`, - expectField: "", - expectErr: false, - }, - { - name: "47", - from: "gemini", - to: "openai", - modelSuffix: "no-thinking-model(8192)", - inputJSON: `{"model":"no-thinking-model(8192)","contents":[{"role":"user","parts":[{"text":"hi"}]}]}`, - expectField: "", - expectErr: false, - }, - { - name: "48", - from: "gemini", - to: "openai", - modelSuffix: "no-thinking-model(0)", - inputJSON: `{"model":"no-thinking-model(0)","contents":[{"role":"user","parts":[{"text":"hi"}]}]}`, - expectField: "", - expectErr: false, - }, - { - name: "49", - from: "gemini", - to: "openai", - modelSuffix: "no-thinking-model(-1)", - inputJSON: `{"model":"no-thinking-model(-1)","contents":[{"role":"user","parts":[{"text":"hi"}]}]}`, - expectField: "", - expectErr: false, - }, - { - name: "50", - from: "claude", - to: "openai", - modelSuffix: "no-thinking-model", - inputJSON: `{"model":"no-thinking-model","messages":[{"role":"user","content":"hi"}]}`, - expectField: "", - expectErr: false, - }, - { - name: "51", - from: "claude", - to: "openai", - modelSuffix: "no-thinking-model(8192)", - inputJSON: `{"model":"no-thinking-model(8192)","messages":[{"role":"user","content":"hi"}]}`, - expectField: "", - expectErr: false, - }, - { - name: "52", - from: "claude", - to: "openai", - modelSuffix: "no-thinking-model(0)", - inputJSON: `{"model":"no-thinking-model(0)","messages":[{"role":"user","content":"hi"}]}`, - expectField: "", - expectErr: false, - }, - { - name: "53", - from: "claude", - to: "openai", - modelSuffix: "no-thinking-model(-1)", - inputJSON: `{"model":"no-thinking-model(-1)","messages":[{"role":"user","content":"hi"}]}`, - expectField: "", - expectErr: false, - }, - - // user-defined-model (UserDefined=true, Thinking=nil) - { - name: "54", - from: "gemini", - to: "openai", - modelSuffix: "user-defined-model", - inputJSON: `{"model":"user-defined-model","contents":[{"role":"user","parts":[{"text":"hi"}]}]}`, - expectField: "", - expectErr: false, - }, - // 8192 → medium (passthrough for UserDefined) - { - name: "55", - from: "gemini", - to: "openai", - modelSuffix: "user-defined-model(8192)", - inputJSON: `{"model":"user-defined-model(8192)","contents":[{"role":"user","parts":[{"text":"hi"}]}]}`, - expectField: "reasoning_effort", - expectValue: "medium", - expectErr: false, - }, - // 64000 → xhigh - { - name: "56", - from: "gemini", - to: "openai", - modelSuffix: "user-defined-model(64000)", - inputJSON: `{"model":"user-defined-model(64000)","contents":[{"role":"user","parts":[{"text":"hi"}]}]}`, - expectField: "reasoning_effort", - expectValue: "xhigh", - expectErr: false, - }, - // 0 → none - { - name: "57", - from: "gemini", - to: "openai", - modelSuffix: "user-defined-model(0)", - inputJSON: `{"model":"user-defined-model(0)","contents":[{"role":"user","parts":[{"text":"hi"}]}]}`, - expectField: "reasoning_effort", - expectValue: "none", - expectErr: false, - }, - // -1 → auto - { - name: "58", - from: "gemini", - to: "openai", - modelSuffix: "user-defined-model(-1)", - inputJSON: `{"model":"user-defined-model(-1)","contents":[{"role":"user","parts":[{"text":"hi"}]}]}`, - expectField: "reasoning_effort", - expectValue: "auto", - expectErr: false, - }, - // Case 59: No suffix from claude → translator injects default reasoning.effort: medium - { - name: "59", - from: "claude", - to: "codex", - modelSuffix: "user-defined-model", - inputJSON: `{"model":"user-defined-model","messages":[{"role":"user","content":"hi"}]}`, - expectField: "reasoning.effort", - expectValue: "medium", - expectErr: false, - }, - // 8192 → medium - { - name: "60", - from: "claude", - to: "codex", - modelSuffix: "user-defined-model(8192)", - inputJSON: `{"model":"user-defined-model(8192)","messages":[{"role":"user","content":"hi"}]}`, - expectField: "reasoning.effort", - expectValue: "medium", - expectErr: false, - }, - // 64000 → xhigh - { - name: "61", - from: "claude", - to: "codex", - modelSuffix: "user-defined-model(64000)", - inputJSON: `{"model":"user-defined-model(64000)","messages":[{"role":"user","content":"hi"}]}`, - expectField: "reasoning.effort", - expectValue: "xhigh", - expectErr: false, - }, - // 0 → none - { - name: "62", - from: "claude", - to: "codex", - modelSuffix: "user-defined-model(0)", - inputJSON: `{"model":"user-defined-model(0)","messages":[{"role":"user","content":"hi"}]}`, - expectField: "reasoning.effort", - expectValue: "none", - expectErr: false, - }, - // -1 → auto - { - name: "63", - from: "claude", - to: "codex", - modelSuffix: "user-defined-model(-1)", - inputJSON: `{"model":"user-defined-model(-1)","messages":[{"role":"user","content":"hi"}]}`, - expectField: "reasoning.effort", - expectValue: "auto", - expectErr: false, - }, - // openai/codex → gemini/claude for user-defined-model - { - name: "64", - from: "openai", - to: "gemini", - modelSuffix: "user-defined-model(8192)", - inputJSON: `{"model":"user-defined-model(8192)","messages":[{"role":"user","content":"hi"}]}`, - expectField: "generationConfig.thinkingConfig.thinkingBudget", - expectValue: "8192", - includeThoughts: "true", - expectErr: false, - }, - { - name: "65", - from: "openai", - to: "claude", - modelSuffix: "user-defined-model(8192)", - inputJSON: `{"model":"user-defined-model(8192)","messages":[{"role":"user","content":"hi"}]}`, - expectField: "thinking.budget_tokens", - expectValue: "8192", - expectErr: false, - }, - { - name: "66", - from: "codex", - to: "gemini", - modelSuffix: "user-defined-model(8192)", - inputJSON: `{"model":"user-defined-model(8192)","input":[{"role":"user","content":"hi"}]}`, - expectField: "generationConfig.thinkingConfig.thinkingBudget", - expectValue: "8192", - includeThoughts: "true", - expectErr: false, - }, - { - name: "67", - from: "codex", - to: "claude", - modelSuffix: "user-defined-model(8192)", - inputJSON: `{"model":"user-defined-model(8192)","input":[{"role":"user","content":"hi"}]}`, - expectField: "thinking.budget_tokens", - expectValue: "8192", - expectErr: false, + ID: "glm-test", + Object: "model", + Created: 1700000000, + OwnedBy: "test", + Type: "iflow", + DisplayName: "GLM Test Model", + Thinking: ®istry.ThinkingSupport{Levels: []string{"none", "auto", "minimal", "low", "medium", "high", "xhigh"}}, + }, + { + ID: "minimax-test", + Object: "model", + Created: 1700000000, + OwnedBy: "test", + Type: "iflow", + DisplayName: "MiniMax Test Model", + Thinking: ®istry.ThinkingSupport{Levels: []string{"none", "auto", "minimal", "low", "medium", "high", "xhigh"}}, }, } +} +// runThinkingTests runs thinking test cases using the real data flow path. +func runThinkingTests(t *testing.T, cases []thinkingTestCase) { for _, tc := range cases { tc := tc - testName := fmt.Sprintf("Case%s_%s->%s_%s", tc.name, tc.from, tc.to, tc.modelSuffix) + testName := fmt.Sprintf("Case%s_%s->%s_%s", tc.name, tc.from, tc.to, tc.model) t.Run(testName, func(t *testing.T) { - // Real data flow path: - // 1. Parse suffix to get base model - suffixResult := thinking.ParseSuffix(tc.modelSuffix) + suffixResult := thinking.ParseSuffix(tc.model) baseModel := suffixResult.ModelName - // 2. Translate request from source format to target format + translateTo := tc.to + applyTo := tc.to + if tc.to == "iflow" { + translateTo = "openai" + applyTo = "iflow" + } + body := sdktranslator.TranslateRequest( sdktranslator.FromString(tc.from), - sdktranslator.FromString(tc.to), + sdktranslator.FromString(translateTo), baseModel, []byte(tc.inputJSON), true, ) - // 3. Apply thinking configuration (main entry point) - body, err := thinking.ApplyThinking(body, tc.modelSuffix, tc.to) + body, err := thinking.ApplyThinking(body, tc.model, tc.from, applyTo) - // Validate results if tc.expectErr { if err == nil { t.Fatalf("expected error but got none, body=%s", string(body)) @@ -1001,18 +2571,23 @@ func TestThinkingE2EMatrix(t *testing.T) { t.Fatalf("unexpected error: %v, body=%s", err, string(body)) } - // Check for expected field absence if tc.expectField == "" { var hasThinking bool switch tc.to { case "gemini": hasThinking = gjson.GetBytes(body, "generationConfig.thinkingConfig").Exists() + case "gemini-cli": + hasThinking = gjson.GetBytes(body, "request.generationConfig.thinkingConfig").Exists() + case "antigravity": + hasThinking = gjson.GetBytes(body, "request.generationConfig.thinkingConfig").Exists() case "claude": hasThinking = gjson.GetBytes(body, "thinking").Exists() case "openai": hasThinking = gjson.GetBytes(body, "reasoning_effort").Exists() case "codex": hasThinking = gjson.GetBytes(body, "reasoning.effort").Exists() || gjson.GetBytes(body, "reasoning").Exists() + case "iflow": + hasThinking = gjson.GetBytes(body, "chat_template_kwargs.enable_thinking").Exists() || gjson.GetBytes(body, "reasoning_split").Exists() } if hasThinking { t.Fatalf("expected no thinking field but found one, body=%s", string(body)) @@ -1020,7 +2595,6 @@ func TestThinkingE2EMatrix(t *testing.T) { return } - // Check expected field value val := gjson.GetBytes(body, tc.expectField) if !val.Exists() { t.Fatalf("expected field %s not found, body=%s", tc.expectField, string(body)) @@ -1034,7 +2608,6 @@ func TestThinkingE2EMatrix(t *testing.T) { t.Fatalf("field %s: expected %q, got %q, body=%s", tc.expectField, tc.expectValue, actualValue, string(body)) } - // Check includeThoughts for Gemini/Antigravity if tc.includeThoughts != "" && (tc.to == "gemini" || tc.to == "antigravity") { path := "generationConfig.thinkingConfig.includeThoughts" if tc.to == "antigravity" { @@ -1049,6 +2622,17 @@ func TestThinkingE2EMatrix(t *testing.T) { t.Fatalf("includeThoughts: expected %s, got %s, body=%s", tc.includeThoughts, actual, string(body)) } } + + // Verify clear_thinking for iFlow GLM models when enable_thinking=true + if tc.to == "iflow" && tc.expectField == "chat_template_kwargs.enable_thinking" && tc.expectValue == "true" { + ctVal := gjson.GetBytes(body, "chat_template_kwargs.clear_thinking") + if !ctVal.Exists() { + t.Fatalf("expected clear_thinking field not found for GLM model, body=%s", string(body)) + } + if ctVal.Bool() != false { + t.Fatalf("clear_thinking: expected false, got %v, body=%s", ctVal.Bool(), string(body)) + } + } }) } }