fix(gemini): resolve latency and error issues with model normalization and v1beta tool routing
This commit is contained in:
+2
-2
@@ -209,9 +209,9 @@ func (db *DB) RunMigrations() error {
|
|||||||
}{
|
}{
|
||||||
{"deepseek-auto", "heuristic", `["deepseek-chat","deepseek-reasoner"]`, "", nil, nil, nil},
|
{"deepseek-auto", "heuristic", `["deepseek-chat","deepseek-reasoner"]`, "", nil, nil, nil},
|
||||||
{"openai-auto", "heuristic", `["gpt-4o-mini","gpt-4o"]`, "", nil, nil, nil},
|
{"openai-auto", "heuristic", `["gpt-4o-mini","gpt-4o"]`, "", nil, nil, nil},
|
||||||
{"gemini-auto", "heuristic", `["gemini-2.5-flash","gemini-2.5-pro"]`, "", nil, nil, nil},
|
{"gemini-auto", "heuristic", `["gemini-3.5-flash-lite","gemini-3.1-flash-lite","gemini-2.5-flash"]`, "", nil, nil, nil},
|
||||||
{"heavy-logic", "heuristic", `["grok-4.3","kimi-k2.6","deepseek-v4-pro"]`, "", nil, intPtr(9), strPtr("Complex Coding, Logic, Agents.")},
|
{"heavy-logic", "heuristic", `["grok-4.3","kimi-k2.6","deepseek-v4-pro"]`, "", nil, intPtr(9), strPtr("Complex Coding, Logic, Agents.")},
|
||||||
{"standard-pro", "heuristic", `["gpt-5.4-mini","gemini-3-flash-preview"]`, "", nil, intPtr(5), strPtr("General Assistant, Long Docs.")},
|
{"standard-pro", "heuristic", `["gpt-5.4-mini","gemini-3.5-flash-lite"]`, "", nil, intPtr(5), strPtr("General Assistant, Long Docs.")},
|
||||||
{"fast-flow", "heuristic", `["deepseek-v4-flash","gpt-5.4-nano"]`, "", nil, intPtr(2), strPtr("Classification, JSON, Basic Q&A.")},
|
{"fast-flow", "heuristic", `["deepseek-v4-flash","gpt-5.4-nano"]`, "", nil, intPtr(2), strPtr("Classification, JSON, Basic Q&A.")},
|
||||||
{"dispatcher", "classifier", `["fast-flow","standard-pro","heavy-logic"]`, "gpt-5.4-nano", intPtr(10), nil, strPtr("Auto-dispatches to tier groups by complexity.")},
|
{"dispatcher", "classifier", `["fast-flow","standard-pro","heavy-logic"]`, "gpt-5.4-nano", intPtr(10), nil, strPtr("Auto-dispatches to tier groups by complexity.")},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -202,15 +202,7 @@ func (p *GeminiProvider) ResponsesStream(ctx context.Context, req *models.Respon
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (p *GeminiProvider) ChatCompletion(ctx context.Context, req *models.UnifiedRequest) (*models.ChatCompletionResponse, error) {
|
func (p *GeminiProvider) ChatCompletion(ctx context.Context, req *models.UnifiedRequest) (*models.ChatCompletionResponse, error) {
|
||||||
// Map deprecated or preview model names to active equivalents
|
req.Model = normalizeGeminiModel(req.Model)
|
||||||
switch req.Model {
|
|
||||||
case "gemini-2.0-flash":
|
|
||||||
req.Model = "gemini-2.5-flash"
|
|
||||||
case "gemini-3-flash":
|
|
||||||
req.Model = "gemini-3-flash-preview"
|
|
||||||
case "gemini-3-pro":
|
|
||||||
req.Model = "gemini-3-pro-preview"
|
|
||||||
}
|
|
||||||
|
|
||||||
// Gemini mapping
|
// Gemini mapping
|
||||||
var contents []GeminiContent
|
var contents []GeminiContent
|
||||||
@@ -273,7 +265,7 @@ func (p *GeminiProvider) ChatCompletion(ctx context.Context, req *models.Unified
|
|||||||
}
|
}
|
||||||
|
|
||||||
if foundAny {
|
if foundAny {
|
||||||
contents = append(contents, GeminiContent{Role: "function", Parts: functionParts})
|
contents = append(contents, GeminiContent{Role: "user", Parts: functionParts})
|
||||||
i = j - 1 // Advance outer loop past the tool messages we consumed
|
i = j - 1 // Advance outer loop past the tool messages we consumed
|
||||||
} else {
|
} else {
|
||||||
// If no tool results found but assistant made calls, Gemini WILL error.
|
// If no tool results found but assistant made calls, Gemini WILL error.
|
||||||
@@ -364,8 +356,9 @@ func (p *GeminiProvider) ChatCompletion(ctx context.Context, req *models.Unified
|
|||||||
if strings.Contains(lowerModel, "preview") ||
|
if strings.Contains(lowerModel, "preview") ||
|
||||||
strings.Contains(lowerModel, "thinking") ||
|
strings.Contains(lowerModel, "thinking") ||
|
||||||
strings.Contains(lowerModel, "gemini-") ||
|
strings.Contains(lowerModel, "gemini-") ||
|
||||||
hasMappedTools {
|
hasMappedTools ||
|
||||||
// Use v1beta for preview, newer models, or when using tools
|
hasHistoryToolCalls(contents) {
|
||||||
|
// Use v1beta for preview, newer models, tool use, or historical tool calls
|
||||||
if !strings.Contains(baseURL, "v1beta") {
|
if !strings.Contains(baseURL, "v1beta") {
|
||||||
baseURL = strings.Replace(baseURL, "/v1", "/v1beta", 1)
|
baseURL = strings.Replace(baseURL, "/v1", "/v1beta", 1)
|
||||||
}
|
}
|
||||||
@@ -478,15 +471,7 @@ func (p *GeminiProvider) ChatCompletion(ctx context.Context, req *models.Unified
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (p *GeminiProvider) ChatCompletionStream(ctx context.Context, req *models.UnifiedRequest) (<-chan *models.ChatCompletionStreamResponse, error) {
|
func (p *GeminiProvider) ChatCompletionStream(ctx context.Context, req *models.UnifiedRequest) (<-chan *models.ChatCompletionStreamResponse, error) {
|
||||||
// Map deprecated or preview model names to active equivalents
|
req.Model = normalizeGeminiModel(req.Model)
|
||||||
switch req.Model {
|
|
||||||
case "gemini-2.0-flash":
|
|
||||||
req.Model = "gemini-2.5-flash"
|
|
||||||
case "gemini-3-flash":
|
|
||||||
req.Model = "gemini-3-flash-preview"
|
|
||||||
case "gemini-3-pro":
|
|
||||||
req.Model = "gemini-3-pro-preview"
|
|
||||||
}
|
|
||||||
|
|
||||||
// Simplified Gemini mapping
|
// Simplified Gemini mapping
|
||||||
var contents []GeminiContent
|
var contents []GeminiContent
|
||||||
@@ -535,7 +520,7 @@ func (p *GeminiProvider) ChatCompletionStream(ctx context.Context, req *models.U
|
|||||||
}
|
}
|
||||||
|
|
||||||
if foundAny {
|
if foundAny {
|
||||||
contents = append(contents, GeminiContent{Role: "function", Parts: functionParts})
|
contents = append(contents, GeminiContent{Role: "user", Parts: functionParts})
|
||||||
i = j - 1
|
i = j - 1
|
||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
@@ -617,8 +602,9 @@ func (p *GeminiProvider) ChatCompletionStream(ctx context.Context, req *models.U
|
|||||||
if strings.Contains(lowerModel, "preview") ||
|
if strings.Contains(lowerModel, "preview") ||
|
||||||
strings.Contains(lowerModel, "thinking") ||
|
strings.Contains(lowerModel, "thinking") ||
|
||||||
strings.Contains(lowerModel, "gemini-") ||
|
strings.Contains(lowerModel, "gemini-") ||
|
||||||
hasMappedTools {
|
hasMappedTools ||
|
||||||
// Use v1beta for preview, newer models, or when using tools
|
hasHistoryToolCalls(contents) {
|
||||||
|
// Use v1beta for preview, newer models, tool use, or historical tool calls
|
||||||
if !strings.Contains(baseURL, "v1beta") {
|
if !strings.Contains(baseURL, "v1beta") {
|
||||||
baseURL = strings.Replace(baseURL, "/v1", "/v1beta", 1)
|
baseURL = strings.Replace(baseURL, "/v1", "/v1beta", 1)
|
||||||
}
|
}
|
||||||
@@ -748,3 +734,30 @@ func resolveToolName(toolMsg models.UnifiedMessage, toolCalls []models.ToolCall,
|
|||||||
return "unknown_function"
|
return "unknown_function"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func normalizeGeminiModel(model string) string {
|
||||||
|
switch model {
|
||||||
|
case "gemini-2.0-flash", "gemini-1.5-flash":
|
||||||
|
return "gemini-3.5-flash-lite"
|
||||||
|
case "gemini-3-flash", "gemini-3-flash-preview":
|
||||||
|
return "gemini-3.5-flash-lite"
|
||||||
|
case "gemini-3-pro", "gemini-3-pro-preview", "gemini-1.5-pro":
|
||||||
|
return "gemini-2.5-pro"
|
||||||
|
default:
|
||||||
|
return model
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func hasHistoryToolCalls(contents []GeminiContent) bool {
|
||||||
|
for _, c := range contents {
|
||||||
|
if c.Role == "function" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
for _, p := range c.Parts {
|
||||||
|
if p.FunctionCall != nil || p.FunctionResponse != nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user