feat: adaptor reason effort convert#539
Conversation
There was a problem hiding this comment.
Pull request overview
This PR introduces a unified “thinking / reasoning” compatibility layer across request modes (OpenAI Chat/Completions, OpenAI Responses, Gemini, Claude/Anthropic) and updates multiple adaptors to normalize and convert reasoning parameters consistently (with origin-model-first matching), backed by expanded documentation and tests.
Changes:
- Add normalized reasoning model + parsing/application helpers, and wire them into OpenAI/Gemini/Claude conversion paths (including Responses
reasoning.effort). - Update several adaptors (Ali/Doubao/DeepSeek/Zhipu/VertexAI/AWS/Azure/Baidu/Moonshot, etc.) to use origin-first model matching and to apply reasoning conversions/hooks.
- Add comprehensive docs (EN/ZH) describing the compatibility rules and conversion examples.
Reviewed changes
Copilot reviewed 50 out of 50 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| docs/REASONING_COMPATIBILITY.md | New English spec for reasoning/thinking normalization + conversion rules and examples. |
| docs/REASONING_COMPATIBILITY.zh.md | New Chinese spec mirroring the compatibility behavior and examples. |
| core/relay/utils/reasoning.go | Core implementation: parse/normalize/apply reasoning across OpenAI/Responses/Gemini/Claude + model-name resolution helpers. |
| core/relay/model/reasoning.go | Adds NormalizedReasoning + ReasoningEffort enum + normalization function. |
| core/relay/model/response.go | Adds reasoning to CreateResponseRequest for Responses API output. |
| core/relay/model/gemini.go | Extends Gemini thinking config to include thinkingLevel. |
| core/relay/model/completions.go | Adds OpenAI-compatible reasoning fields (reasoning_effort) and Ali fields to GeneralOpenAIRequest. |
| core/relay/model/claude.go | Adds reasoning fields and Claude adaptive/output_config structures. |
| core/relay/adaptor/openai/gemini.go | Adds hook support + maps Gemini thinkingConfig into OpenAI reasoning fields (and Responses). |
| core/relay/adaptor/openai/gemini_test.go | Adds tests verifying Gemini thinking -> OpenAI reasoning_effort. |
| core/relay/adaptor/openai/claude.go | Adds hook support + maps Claude thinking/output_config into OpenAI reasoning (and Responses). |
| core/relay/adaptor/openai/chat.go | Ensures Chat -> Responses conversion writes reasoning.effort. |
| core/relay/adaptor/openai/constants.go | Adds IsResponsesOnlyModelAny (origin-first, actual-fallback). |
| core/relay/adaptor/openai/chat_test.go | Tests for IsResponsesOnlyModelAny. |
| core/relay/adaptor/openai/adaptor.go | Uses IsResponsesOnlyModelAny across routing/conversion/response handling. |
| core/relay/adaptor/azure/main.go | Uses origin-first IsResponsesOnlyModelAny for Azure Responses routing. |
| core/relay/adaptor/zhipu/adaptor.go | Applies reasoning conversion into Zhipu’s simplified thinking.type. |
| core/relay/adaptor/zhipu/adaptor_test.go | Tests Chat/Gemini reasoning -> Zhipu thinking. |
| core/relay/adaptor/deepseek/adaptor.go | Adds Chat/Gemini reasoning conversion into DeepSeek thinking.type. |
| core/relay/adaptor/deepseek/adaptor_test.go | Tests reasoning conversions for DeepSeek. |
| core/relay/adaptor/doubao/main.go | Adds reasoning hook for Gemini/Anthropic -> Doubao simplified thinking + origin-first routing helper. |
| core/relay/adaptor/doubao/chat.go | Converts Chat reasoning_effort -> thinking + origin/actual deepseek-reasoner detection via AST callbacks. |
| core/relay/adaptor/doubao/main_test.go | Tests origin-first routing and reasoning conversions + deepseek-reasoner prompt injection. |
| core/relay/adaptor/ali/chat.go | Replaces legacy thinking patch with reasoning-based conversion; adds Gemini->Ali conversion with model-specific hooks. |
| core/relay/adaptor/ali/adaptor.go | Routes Gemini mode through Ali’s new Gemini conversion function. |
| core/relay/adaptor/ali/adaptor_test.go | Adds tests for Gemini->Ali reasoning and budget behavior. |
| core/relay/adaptor/gemini/openai.go | Applies normalized reasoning to Gemini thinkingConfig; origin-first feature model resolution. |
| core/relay/adaptor/gemini/adaptor.go | Uses origin-first logic for deciding Gemini API version selection. |
| core/relay/adaptor/gemini/openai_test.go | Adds tests for reasoning_effort -> Gemini thinkingConfig behavior. |
| core/relay/adaptor/vertexai/adaptor.go | Uses origin-first “feature model” to pick inner adaptor and route publishers/paths. |
| core/relay/adaptor/vertexai/adaptor_test.go | Tests origin-first routing decisions for VertexAI publisher endpoints. |
| core/relay/adaptor/vertexai/claude/adapter.go | Uses origin-first resolved model name when fixing betas. |
| core/relay/adaptor/vertexai/claude/adapter_test.go | Tests native-thinking preservation behavior. |
| core/relay/adaptor/aws/adaptor.go | Uses origin-first model selection with fallback for AWS inner adaptor selection. |
| core/relay/adaptor/aws/claude/adapter.go | Uses origin-first resolved model name for betas, context mgmt edits, and model ID selection. |
| core/relay/adaptor/aws/claude/adapter_test.go | Tests native-thinking preservation behavior. |
| core/relay/adaptor/antling/adaptor.go | Uses origin-first resolved model name when fixing Anthropic betas. |
| core/relay/adaptor/baidu/adaptor.go | Uses origin-first model selection with endpoint-map fallback. |
| core/relay/adaptor/baidu/adaptor_test.go | Tests origin-alias fallback to actual model for endpoint routing. |
| core/relay/adaptor/moonshot/adaptor.go | Explicitly rejects Responses modes while delegating other modes to OpenAI-compatible adaptor. |
| core/relay/adaptor/moonshot/adaptor_test.go | Tests Responses-mode rejection + supported modes. |
| core/relay/adaptor/anthropic/utils.go | Adds origin-first model resolver + adaptive-thinking detection and normalization logic. |
| core/relay/adaptor/anthropic/openai.go | Switches OpenAI->Claude conversion to normalized reasoning + adaptive normalization. |
| core/relay/adaptor/anthropic/main.go | Uses resolved model for default max_tokens; simplifies thinking handling. |
| core/relay/adaptor/anthropic/gemini.go | Applies Gemini thinkingConfig via normalized reasoning when converting Gemini->Claude. |
| core/relay/adaptor/anthropic/gemini_test.go | Tests adaptive normalization for Gemini->Claude path. |
| core/relay/adaptor/anthropic/adaptor.go | Uses resolved model name when fixing betas. |
| core/relay/adaptor/anthropic/openai_test.go | Adds coverage for adaptive-thinking model behavior and origin-first matching. |
| README.md | Adds link to new reasoning/thinking compatibility docs; removes contributing section. |
| README.zh.md | Adds link to new reasoning/thinking compatibility docs; removes contributing section. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| name: "gemini 2.5 uses thinking budget", | ||
| modelName: "gemini-2.5-pro", | ||
| reasoningEffort: "high", | ||
| expectedBudget: new(16384), | ||
| expectedThoughts: true, | ||
| }, | ||
| { | ||
| name: "gemini 3 pro uses thinking level", | ||
| modelName: "gemini-3-pro", | ||
| reasoningEffort: "high", | ||
| expectedLevel: "high", | ||
| expectedThoughts: false, | ||
| }, | ||
| { | ||
| name: "gemini 2.5 flash disables thinking with none", | ||
| modelName: "gemini-2.5-flash", | ||
| reasoningEffort: "none", | ||
| expectedBudget: new(0), | ||
| expectedThoughts: false, | ||
| }, | ||
| { | ||
| name: "gemini 2.5 pro none uses minimum budget", | ||
| modelName: "gemini-2.5-pro", | ||
| reasoningEffort: "none", | ||
| expectedBudget: new(128), | ||
| expectedThoughts: false, |
There was a problem hiding this comment.
These test cases use new(16384), new(0), etc. In Go the built-in new takes a type, not a value, so this won’t compile. Use a small helper like intPtr(v int) *int (or assign to a local and take &local) when you need *int literals in the table.
| reqBody := relaymodel.GeneralOpenAIRequest{ | ||
| Model: "claude-opus-4-7", | ||
| Messages: []relaymodel.Message{ | ||
| {Role: relaymodel.RoleUser, Content: "hello"}, | ||
| }, | ||
| ReasoningEffort: new("low"), | ||
| } | ||
|
|
||
| data, err := sonic.Marshal(reqBody) | ||
| require.NoError(t, err) |
There was a problem hiding this comment.
This test uses new("low") / new("medium") to create *string values. The built-in new takes a type (e.g. new(string)), so new("...") won’t compile. Prefer a helper like stringPtr(v string) *string (or a local variable + &local) for pointer literals.
No description provided.