Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 12 additions & 6 deletions internal/translator/gemini_helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -603,12 +603,21 @@ func isGeminiFlashModel(model internalapi.RequestModel) bool {
return strings.Contains(strings.ToLower(model), "flash")
}

// supportsMediumThinkingLevel reports whether the model accepts the "medium" thinking level.
// Gemini 3 Pro only lists "low" and "high", and Gemini 3.1 Flash-Lite Image only "minimal" and "high";
// Flash and Gemini 3.1 Pro also list "medium".
// https://ai.google.dev/gemini-api/docs/thinking#thinking-levels
func supportsMediumThinkingLevel(model internalapi.RequestModel) bool {
m := strings.ToLower(model)
return !strings.Contains(m, "gemini-3-pro") && !strings.Contains(m, "flash-lite-image")
}

// mapReasoningEffortToThinkingLevel converts OpenAI reasoning effort levels to Gemini thinking levels.
// The mapping depends on the model type:
// - "none" → ThinkingLevelMinimal (Gemini Flash only)
// - "low" → ThinkingLevelLow
// - "medium" → ThinkingLevelMedium for Flash, ThinkingLevelHigh for Pro
// - "high" → ThinkingLevelHigh
// - "medium" → ThinkingLevelMedium, except ThinkingLevelHigh for Gemini 3 Pro and 3.1 Flash-Lite Image, which have no medium level
// - "high" → ThinkingLevelHigh (every Gemini 3 model; it is the Pro default)
// https://docs.cloud.google.com/vertex-ai/generative-ai/docs/start/get-started-with-gemini-3#openai-example
func mapReasoningEffortToThinkingLevel(reasonEffort openai.ReasoningEffort, model internalapi.RequestModel) (genai.ThinkingLevel, error) {
isFlash := isGeminiFlashModel(model)
Expand All @@ -622,14 +631,11 @@ func mapReasoningEffortToThinkingLevel(reasonEffort openai.ReasoningEffort, mode
case openai.ReasoningEffortLow:
return genai.ThinkingLevelLow, nil
case openai.ReasoningEffortMedium:
if isFlash {
if supportsMediumThinkingLevel(model) {
return genai.ThinkingLevelMedium, nil
}
return genai.ThinkingLevelHigh, nil
case openai.ReasoningEffortHigh:
if !isFlash {
return "", fmt.Errorf("%w: reasoning effort 'high' is only supported for Gemini Flash models", internalapi.ErrInvalidRequestBody)
}
return genai.ThinkingLevelHigh, nil
default:
return "", fmt.Errorf("%w: unsupported reasoning effort level: %q (supported: none, low, medium, high)", internalapi.ErrInvalidRequestBody, reasonEffort)
Expand Down
41 changes: 39 additions & 2 deletions internal/translator/gemini_helper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1577,6 +1577,19 @@ func TestOpenAIReqToGeminiGenerationConfig(t *testing.T) {
expectedResponseMode: responseModeNone,
requestModel: "gemini-3-flash",
},
{
name: "high reasoning effort on Pro maps to ThinkingLevelHigh",
input: &openai.ChatCompletionRequest{
ReasoningEffort: openai.ReasoningEffortHigh,
},
expectedGenerationConfig: &genai.GenerationConfig{
ThinkingConfig: &genai.ThinkingConfig{
ThinkingLevel: genai.ThinkingLevelHigh,
},
},
expectedResponseMode: responseModeNone,
requestModel: "gemini-3-pro",
},
{
name: "reasoning effort unsupported value",
input: &openai.ChatCompletionRequest{
Expand Down Expand Up @@ -3448,17 +3461,41 @@ func TestMapReasoningEffortToThinkingLevel(t *testing.T) {
expectedThinking: genai.ThinkingLevelMedium,
},
{
name: "medium effort on Pro maps to ThinkingLevelHigh",
name: "medium effort on Gemini 3 Pro maps to ThinkingLevelHigh",
reasoningEffort: openai.ReasoningEffortMedium,
model: "gemini-3-pro",
expectedThinking: genai.ThinkingLevelHigh,
},
{
name: "high effort maps to ThinkingLevelHigh",
name: "medium effort on Gemini 3 Pro preview maps to ThinkingLevelHigh",
reasoningEffort: openai.ReasoningEffortMedium,
model: "gemini-3-pro-preview",
expectedThinking: genai.ThinkingLevelHigh,
},
{
name: "medium effort on Gemini 3.1 Pro preview maps to ThinkingLevelMedium",
reasoningEffort: openai.ReasoningEffortMedium,
model: "gemini-3.1-pro-preview",
expectedThinking: genai.ThinkingLevelMedium,
},
{
name: "medium effort on Gemini 3.1 Flash-Lite Image maps to ThinkingLevelHigh",
reasoningEffort: openai.ReasoningEffortMedium,
model: "gemini-3.1-flash-lite-image",
expectedThinking: genai.ThinkingLevelHigh,
},
{
name: "high effort on Flash maps to ThinkingLevelHigh",
reasoningEffort: openai.ReasoningEffortHigh,
model: "gemini-3-flash",
expectedThinking: genai.ThinkingLevelHigh,
},
{
name: "high effort on Pro maps to ThinkingLevelHigh",
reasoningEffort: openai.ReasoningEffortHigh,
model: "gemini-3-pro",
expectedThinking: genai.ThinkingLevelHigh,
},
{
name: "empty effort - not supported",
reasoningEffort: "",
Expand Down
Loading