mirror of
https://github.com/router-for-me/CLIProxyAPI.git
synced 2026-02-19 04:40:52 +08:00
fix(thinking): map budgets to effort for level models
This commit is contained in:
@@ -39,7 +39,7 @@ func ConvertOpenAIRequestToAntigravity(modelName string, inputRawJSON []byte, _
|
|||||||
// Note: OpenAI official fields take precedence over extra_body.google.thinking_config
|
// Note: OpenAI official fields take precedence over extra_body.google.thinking_config
|
||||||
re := gjson.GetBytes(rawJSON, "reasoning_effort")
|
re := gjson.GetBytes(rawJSON, "reasoning_effort")
|
||||||
hasOfficialThinking := re.Exists()
|
hasOfficialThinking := re.Exists()
|
||||||
if hasOfficialThinking && util.ModelSupportsThinking(modelName) {
|
if hasOfficialThinking && util.ModelSupportsThinking(modelName) && !util.ModelUsesThinkingLevels(modelName) {
|
||||||
switch re.String() {
|
switch re.String() {
|
||||||
case "none":
|
case "none":
|
||||||
out, _ = sjson.DeleteBytes(out, "request.generationConfig.thinkingConfig.include_thoughts")
|
out, _ = sjson.DeleteBytes(out, "request.generationConfig.thinkingConfig.include_thoughts")
|
||||||
@@ -63,7 +63,8 @@ func ConvertOpenAIRequestToAntigravity(modelName string, inputRawJSON []byte, _
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Cherry Studio extension extra_body.google.thinking_config (effective only when official fields are absent)
|
// Cherry Studio extension extra_body.google.thinking_config (effective only when official fields are absent)
|
||||||
if !hasOfficialThinking && util.ModelSupportsThinking(modelName) {
|
// Only apply for models that use numeric budgets, not discrete levels.
|
||||||
|
if !hasOfficialThinking && util.ModelSupportsThinking(modelName) && !util.ModelUsesThinkingLevels(modelName) {
|
||||||
if tc := gjson.GetBytes(rawJSON, "extra_body.google.thinking_config"); tc.Exists() && tc.IsObject() {
|
if tc := gjson.GetBytes(rawJSON, "extra_body.google.thinking_config"); tc.Exists() && tc.IsObject() {
|
||||||
var setBudget bool
|
var setBudget bool
|
||||||
var budget int
|
var budget int
|
||||||
|
|||||||
@@ -114,7 +114,8 @@ func ConvertGeminiRequestToClaude(modelName string, inputRawJSON []byte, stream
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Include thoughts configuration for reasoning process visibility
|
// Include thoughts configuration for reasoning process visibility
|
||||||
if thinkingConfig := genConfig.Get("thinkingConfig"); thinkingConfig.Exists() && thinkingConfig.IsObject() {
|
// Only apply for models that use numeric budgets, not discrete levels.
|
||||||
|
if thinkingConfig := genConfig.Get("thinkingConfig"); thinkingConfig.Exists() && thinkingConfig.IsObject() && !util.ModelUsesThinkingLevels(modelName) {
|
||||||
if includeThoughts := thinkingConfig.Get("include_thoughts"); includeThoughts.Exists() {
|
if includeThoughts := thinkingConfig.Get("include_thoughts"); includeThoughts.Exists() {
|
||||||
if includeThoughts.Type == gjson.True {
|
if includeThoughts.Type == gjson.True {
|
||||||
out, _ = sjson.Set(out, "thinking.type", "enabled")
|
out, _ = sjson.Set(out, "thinking.type", "enabled")
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/router-for-me/CLIProxyAPI/v6/internal/misc"
|
"github.com/router-for-me/CLIProxyAPI/v6/internal/misc"
|
||||||
|
"github.com/router-for-me/CLIProxyAPI/v6/internal/util"
|
||||||
"github.com/tidwall/gjson"
|
"github.com/tidwall/gjson"
|
||||||
"github.com/tidwall/sjson"
|
"github.com/tidwall/sjson"
|
||||||
)
|
)
|
||||||
@@ -214,7 +215,22 @@ func ConvertClaudeRequestToCodex(modelName string, inputRawJSON []byte, _ bool)
|
|||||||
|
|
||||||
// Add additional configuration parameters for the Codex API.
|
// Add additional configuration parameters for the Codex API.
|
||||||
template, _ = sjson.Set(template, "parallel_tool_calls", true)
|
template, _ = sjson.Set(template, "parallel_tool_calls", true)
|
||||||
template, _ = sjson.Set(template, "reasoning.effort", "medium")
|
|
||||||
|
// Convert thinking.budget_tokens to reasoning.effort for level-based models
|
||||||
|
reasoningEffort := "medium" // default
|
||||||
|
if thinking := rootResult.Get("thinking"); thinking.Exists() && thinking.IsObject() {
|
||||||
|
if thinking.Get("type").String() == "enabled" {
|
||||||
|
if util.ModelUsesThinkingLevels(modelName) {
|
||||||
|
if budgetTokens := thinking.Get("budget_tokens"); budgetTokens.Exists() {
|
||||||
|
budget := int(budgetTokens.Int())
|
||||||
|
if effort, ok := util.OpenAIThinkingBudgetToEffort(modelName, budget); ok && effort != "" {
|
||||||
|
reasoningEffort = effort
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
template, _ = sjson.Set(template, "reasoning.effort", reasoningEffort)
|
||||||
template, _ = sjson.Set(template, "reasoning.summary", "auto")
|
template, _ = sjson.Set(template, "reasoning.summary", "auto")
|
||||||
template, _ = sjson.Set(template, "stream", true)
|
template, _ = sjson.Set(template, "stream", true)
|
||||||
template, _ = sjson.Set(template, "store", false)
|
template, _ = sjson.Set(template, "store", false)
|
||||||
|
|||||||
@@ -245,7 +245,22 @@ func ConvertGeminiRequestToCodex(modelName string, inputRawJSON []byte, _ bool)
|
|||||||
|
|
||||||
// Fixed flags aligning with Codex expectations
|
// Fixed flags aligning with Codex expectations
|
||||||
out, _ = sjson.Set(out, "parallel_tool_calls", true)
|
out, _ = sjson.Set(out, "parallel_tool_calls", true)
|
||||||
out, _ = sjson.Set(out, "reasoning.effort", "medium")
|
|
||||||
|
// Convert thinkingBudget to reasoning.effort for level-based models
|
||||||
|
reasoningEffort := "medium" // default
|
||||||
|
if genConfig := root.Get("generationConfig"); genConfig.Exists() {
|
||||||
|
if thinkingConfig := genConfig.Get("thinkingConfig"); thinkingConfig.Exists() && thinkingConfig.IsObject() {
|
||||||
|
if util.ModelUsesThinkingLevels(modelName) {
|
||||||
|
if thinkingBudget := thinkingConfig.Get("thinkingBudget"); thinkingBudget.Exists() {
|
||||||
|
budget := int(thinkingBudget.Int())
|
||||||
|
if effort, ok := util.OpenAIThinkingBudgetToEffort(modelName, budget); ok && effort != "" {
|
||||||
|
reasoningEffort = effort
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
out, _ = sjson.Set(out, "reasoning.effort", reasoningEffort)
|
||||||
out, _ = sjson.Set(out, "reasoning.summary", "auto")
|
out, _ = sjson.Set(out, "reasoning.summary", "auto")
|
||||||
out, _ = sjson.Set(out, "stream", true)
|
out, _ = sjson.Set(out, "stream", true)
|
||||||
out, _ = sjson.Set(out, "store", false)
|
out, _ = sjson.Set(out, "store", false)
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ func ConvertOpenAIRequestToGeminiCLI(modelName string, inputRawJSON []byte, _ bo
|
|||||||
// Note: OpenAI official fields take precedence over extra_body.google.thinking_config
|
// Note: OpenAI official fields take precedence over extra_body.google.thinking_config
|
||||||
re := gjson.GetBytes(rawJSON, "reasoning_effort")
|
re := gjson.GetBytes(rawJSON, "reasoning_effort")
|
||||||
hasOfficialThinking := re.Exists()
|
hasOfficialThinking := re.Exists()
|
||||||
if hasOfficialThinking && util.ModelSupportsThinking(modelName) {
|
if hasOfficialThinking && util.ModelSupportsThinking(modelName) && !util.ModelUsesThinkingLevels(modelName) {
|
||||||
switch re.String() {
|
switch re.String() {
|
||||||
case "none":
|
case "none":
|
||||||
out, _ = sjson.DeleteBytes(out, "request.generationConfig.thinkingConfig.include_thoughts")
|
out, _ = sjson.DeleteBytes(out, "request.generationConfig.thinkingConfig.include_thoughts")
|
||||||
@@ -63,7 +63,8 @@ func ConvertOpenAIRequestToGeminiCLI(modelName string, inputRawJSON []byte, _ bo
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Cherry Studio extension extra_body.google.thinking_config (effective only when official fields are absent)
|
// Cherry Studio extension extra_body.google.thinking_config (effective only when official fields are absent)
|
||||||
if !hasOfficialThinking && util.ModelSupportsThinking(modelName) {
|
// Only apply for models that use numeric budgets, not discrete levels.
|
||||||
|
if !hasOfficialThinking && util.ModelSupportsThinking(modelName) && !util.ModelUsesThinkingLevels(modelName) {
|
||||||
if tc := gjson.GetBytes(rawJSON, "extra_body.google.thinking_config"); tc.Exists() && tc.IsObject() {
|
if tc := gjson.GetBytes(rawJSON, "extra_body.google.thinking_config"); tc.Exists() && tc.IsObject() {
|
||||||
var setBudget bool
|
var setBudget bool
|
||||||
var budget int
|
var budget int
|
||||||
|
|||||||
@@ -154,7 +154,8 @@ func ConvertClaudeRequestToGemini(modelName string, inputRawJSON []byte, _ bool)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Map Anthropic thinking -> Gemini thinkingBudget/include_thoughts when enabled
|
// Map Anthropic thinking -> Gemini thinkingBudget/include_thoughts when enabled
|
||||||
if t := gjson.GetBytes(rawJSON, "thinking"); t.Exists() && t.IsObject() && util.ModelSupportsThinking(modelName) {
|
// Only apply for models that use numeric budgets, not discrete levels.
|
||||||
|
if t := gjson.GetBytes(rawJSON, "thinking"); t.Exists() && t.IsObject() && util.ModelSupportsThinking(modelName) && !util.ModelUsesThinkingLevels(modelName) {
|
||||||
if t.Get("type").String() == "enabled" {
|
if t.Get("type").String() == "enabled" {
|
||||||
if b := t.Get("budget_tokens"); b.Exists() && b.Type == gjson.Number {
|
if b := t.Get("budget_tokens"); b.Exists() && b.Type == gjson.Number {
|
||||||
budget := int(b.Int())
|
budget := int(b.Int())
|
||||||
|
|||||||
@@ -37,9 +37,11 @@ func ConvertOpenAIRequestToGemini(modelName string, inputRawJSON []byte, _ bool)
|
|||||||
|
|
||||||
// Reasoning effort -> thinkingBudget/include_thoughts
|
// Reasoning effort -> thinkingBudget/include_thoughts
|
||||||
// Note: OpenAI official fields take precedence over extra_body.google.thinking_config
|
// Note: OpenAI official fields take precedence over extra_body.google.thinking_config
|
||||||
|
// Only convert for models that use numeric budgets (not discrete levels) to avoid
|
||||||
|
// incorrectly applying thinkingBudget for level-based models like gpt-5.
|
||||||
re := gjson.GetBytes(rawJSON, "reasoning_effort")
|
re := gjson.GetBytes(rawJSON, "reasoning_effort")
|
||||||
hasOfficialThinking := re.Exists()
|
hasOfficialThinking := re.Exists()
|
||||||
if hasOfficialThinking && util.ModelSupportsThinking(modelName) {
|
if hasOfficialThinking && util.ModelSupportsThinking(modelName) && !util.ModelUsesThinkingLevels(modelName) {
|
||||||
switch re.String() {
|
switch re.String() {
|
||||||
case "none":
|
case "none":
|
||||||
out, _ = sjson.DeleteBytes(out, "generationConfig.thinkingConfig.include_thoughts")
|
out, _ = sjson.DeleteBytes(out, "generationConfig.thinkingConfig.include_thoughts")
|
||||||
@@ -63,7 +65,8 @@ func ConvertOpenAIRequestToGemini(modelName string, inputRawJSON []byte, _ bool)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Cherry Studio extension extra_body.google.thinking_config (effective only when official fields are absent)
|
// Cherry Studio extension extra_body.google.thinking_config (effective only when official fields are absent)
|
||||||
if !hasOfficialThinking && util.ModelSupportsThinking(modelName) {
|
// Only apply for models that use numeric budgets, not discrete levels.
|
||||||
|
if !hasOfficialThinking && util.ModelSupportsThinking(modelName) && !util.ModelUsesThinkingLevels(modelName) {
|
||||||
if tc := gjson.GetBytes(rawJSON, "extra_body.google.thinking_config"); tc.Exists() && tc.IsObject() {
|
if tc := gjson.GetBytes(rawJSON, "extra_body.google.thinking_config"); tc.Exists() && tc.IsObject() {
|
||||||
var setBudget bool
|
var setBudget bool
|
||||||
var budget int
|
var budget int
|
||||||
|
|||||||
@@ -389,8 +389,9 @@ func ConvertOpenAIResponsesRequestToGemini(modelName string, inputRawJSON []byte
|
|||||||
}
|
}
|
||||||
|
|
||||||
// OpenAI official reasoning fields take precedence
|
// OpenAI official reasoning fields take precedence
|
||||||
|
// Only convert for models that use numeric budgets (not discrete levels).
|
||||||
hasOfficialThinking := root.Get("reasoning.effort").Exists()
|
hasOfficialThinking := root.Get("reasoning.effort").Exists()
|
||||||
if hasOfficialThinking && util.ModelSupportsThinking(modelName) {
|
if hasOfficialThinking && util.ModelSupportsThinking(modelName) && !util.ModelUsesThinkingLevels(modelName) {
|
||||||
reasoningEffort := root.Get("reasoning.effort")
|
reasoningEffort := root.Get("reasoning.effort")
|
||||||
switch reasoningEffort.String() {
|
switch reasoningEffort.String() {
|
||||||
case "none":
|
case "none":
|
||||||
@@ -418,7 +419,8 @@ func ConvertOpenAIResponsesRequestToGemini(modelName string, inputRawJSON []byte
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Cherry Studio extension (applies only when official fields are missing)
|
// Cherry Studio extension (applies only when official fields are missing)
|
||||||
if !hasOfficialThinking && util.ModelSupportsThinking(modelName) {
|
// Only apply for models that use numeric budgets, not discrete levels.
|
||||||
|
if !hasOfficialThinking && util.ModelSupportsThinking(modelName) && !util.ModelUsesThinkingLevels(modelName) {
|
||||||
if tc := root.Get("extra_body.google.thinking_config"); tc.Exists() && tc.IsObject() {
|
if tc := root.Get("extra_body.google.thinking_config"); tc.Exists() && tc.IsObject() {
|
||||||
var setBudget bool
|
var setBudget bool
|
||||||
var budget int
|
var budget int
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/router-for-me/CLIProxyAPI/v6/internal/util"
|
||||||
"github.com/tidwall/gjson"
|
"github.com/tidwall/gjson"
|
||||||
"github.com/tidwall/sjson"
|
"github.com/tidwall/sjson"
|
||||||
)
|
)
|
||||||
@@ -60,6 +61,18 @@ func ConvertClaudeRequestToOpenAI(modelName string, inputRawJSON []byte, stream
|
|||||||
// Stream
|
// Stream
|
||||||
out, _ = sjson.Set(out, "stream", stream)
|
out, _ = sjson.Set(out, "stream", stream)
|
||||||
|
|
||||||
|
// Thinking: Convert Claude thinking.budget_tokens to OpenAI reasoning_effort
|
||||||
|
if thinking := root.Get("thinking"); thinking.Exists() && thinking.IsObject() {
|
||||||
|
if thinkingType := thinking.Get("type"); thinkingType.Exists() && thinkingType.String() == "enabled" {
|
||||||
|
if budgetTokens := thinking.Get("budget_tokens"); budgetTokens.Exists() {
|
||||||
|
budget := int(budgetTokens.Int())
|
||||||
|
if effort, ok := util.OpenAIThinkingBudgetToEffort(modelName, budget); ok && effort != "" {
|
||||||
|
out, _ = sjson.Set(out, "reasoning_effort", effort)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Process messages and system
|
// Process messages and system
|
||||||
var messagesJSON = "[]"
|
var messagesJSON = "[]"
|
||||||
|
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import (
|
|||||||
"math/big"
|
"math/big"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/router-for-me/CLIProxyAPI/v6/internal/util"
|
||||||
"github.com/tidwall/gjson"
|
"github.com/tidwall/gjson"
|
||||||
"github.com/tidwall/sjson"
|
"github.com/tidwall/sjson"
|
||||||
)
|
)
|
||||||
@@ -76,6 +77,18 @@ func ConvertGeminiRequestToOpenAI(modelName string, inputRawJSON []byte, stream
|
|||||||
out, _ = sjson.Set(out, "stop", stops)
|
out, _ = sjson.Set(out, "stop", stops)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Convert thinkingBudget to reasoning_effort for level-based models
|
||||||
|
if thinkingConfig := genConfig.Get("thinkingConfig"); thinkingConfig.Exists() && thinkingConfig.IsObject() {
|
||||||
|
if util.ModelUsesThinkingLevels(modelName) {
|
||||||
|
if thinkingBudget := thinkingConfig.Get("thinkingBudget"); thinkingBudget.Exists() {
|
||||||
|
budget := int(thinkingBudget.Int())
|
||||||
|
if effort, ok := util.OpenAIThinkingBudgetToEffort(modelName, budget); ok && effort != "" {
|
||||||
|
out, _ = sjson.Set(out, "reasoning_effort", effort)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stream parameter
|
// Stream parameter
|
||||||
|
|||||||
Reference in New Issue
Block a user