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
5 changes: 0 additions & 5 deletions core/providers/gemini/responses.go
Original file line number Diff line number Diff line change
Expand Up @@ -1457,11 +1457,6 @@ func convertGeminiContentsToResponsesMessages(contents []Content) []schemas.Resp
},
}

// Also set the tool name if present (Gemini associates on name)
if name := strings.TrimSpace(part.FunctionResponse.Name); name != "" {
msg.ResponsesToolMessage.Name = schemas.Ptr(name)
}

messages = append(messages, msg)

case part.Thought && part.Text != "":
Expand Down
64 changes: 51 additions & 13 deletions core/providers/gemini/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ func (r *GeminiGenerationRequest) convertGenerationConfigToResponsesParameters()
if config.ResponseMIMEType != "" {
switch config.ResponseMIMEType {
case "application/json":
params.Text = buildOpenAIResponseFormat(config.ResponseJSONSchema)
params.Text = buildOpenAIResponseFormat(config.ResponseJSONSchema, config.ResponseSchema)
case "text/plain":
params.Text = &schemas.ResponsesTextConfig{
Format: &schemas.ResponsesTextConfigFormat{
Expand Down Expand Up @@ -1065,22 +1065,60 @@ func buildJSONSchemaFromMap(schemaMap map[string]interface{}) *schemas.Responses
}

// buildOpenAIResponseFormat builds OpenAI response_format for JSON types
func buildOpenAIResponseFormat(responseJsonSchema interface{}) *schemas.ResponsesTextConfig {
func buildOpenAIResponseFormat(responseJsonSchema interface{}, responseSchema *Schema) *schemas.ResponsesTextConfig {
name := "json_response"

// No schema provided - use json_object mode
if responseJsonSchema == nil {
return &schemas.ResponsesTextConfig{
Format: &schemas.ResponsesTextConfigFormat{
Type: "json_object",
},
var schemaMap map[string]interface{}

// Try to use responseJsonSchema first
if responseJsonSchema != nil {
// Use responseJsonSchema directly if it's a map
var ok bool
schemaMap, ok = responseJsonSchema.(map[string]interface{})
if !ok {
// If not a map, fall back to json_object mode
return &schemas.ResponsesTextConfig{
Format: &schemas.ResponsesTextConfigFormat{
Type: "json_object",
},
}
}
} else if responseSchema != nil {
// Convert responseSchema to map using JSON marshaling and type normalization
data, err := sonic.Marshal(responseSchema)
if err != nil {
// If marshaling fails, fall back to json_object mode
return &schemas.ResponsesTextConfig{
Format: &schemas.ResponsesTextConfigFormat{
Type: "json_object",
},
}
}
}

// Use responseJsonSchema directly if it's a map
schemaMap, ok := responseJsonSchema.(map[string]interface{})
if !ok {
// If not a map, fall back to json_object mode
var rawMap map[string]interface{}
if err := sonic.Unmarshal(data, &rawMap); err != nil {
// If unmarshaling fails, fall back to json_object mode
return &schemas.ResponsesTextConfig{
Format: &schemas.ResponsesTextConfigFormat{
Type: "json_object",
},
}
}

// Apply type normalization (convert types to lowercase)
normalized := convertTypeToLowerCase(rawMap)
var ok bool
schemaMap, ok = normalized.(map[string]interface{})
if !ok {
// If type assertion fails, fall back to json_object mode
return &schemas.ResponsesTextConfig{
Format: &schemas.ResponsesTextConfigFormat{
Type: "json_object",
},
}
}
} else {
// No schema provided - use json_object mode
return &schemas.ResponsesTextConfig{
Format: &schemas.ResponsesTextConfigFormat{
Type: "json_object",
Expand Down
58 changes: 55 additions & 3 deletions tests/integrations/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ bifrost:
endpoints:
openai: "openai"
anthropic: "anthropic"
cohere: "cohere"
google: "genai"
litellm: "litellm"
langchain: "langchain"
Expand All @@ -36,7 +37,7 @@ providers:
openai:
chat: "gpt-4o"
vision: "gpt-4o"
file: "gpt-5"
file: "gpt-4o"
tools: "gpt-4o-mini"
speech: "tts-1"
transcription: "whisper-1"
Expand Down Expand Up @@ -111,7 +112,17 @@ providers:
- "gemini-1.5-flash"
- "gemini-1.0-pro"
- "gemini-2.0-flash-001"


vertex:
chat: "gemini-2.5-flash"
vision: "claude-sonnet-4-5"
tools: "gemini-2.5-flash"
file: "claude-sonnet-4-5"
thinking: "gemini-3-pro-preview"
embeddings: "gemini-embedding-001"
streaming: "gemini-2.5-flash"
count_tokens: "claude-sonnet-4-5"

bedrock:
chat: "global.anthropic.claude-sonnet-4-20250514-v1:0"
vision: "global.anthropic.claude-sonnet-4-20250514-v1:0"
Expand All @@ -120,7 +131,7 @@ providers:
streaming: "global.anthropic.claude-sonnet-4-20250514-v1:0"
thinking: "us.anthropic.claude-opus-4-5-20251101-v1:0"
text_completion: "mistral.mistral-7b-instruct-v0:2"
embeddings: "cohere.embed-v4:0"
embeddings: "global.cohere.embed-v4:0"
batch_inline: "anthropic.claude-3-5-sonnet-20240620-v1:0"
batch_list: "anthropic.claude-3-5-sonnet-20240620-v1:0"
batch_retrieve: "anthropic.claude-3-5-sonnet-20240620-v1:0"
Expand Down Expand Up @@ -150,6 +161,7 @@ provider_api_keys:
openai: "OPENAI_API_KEY"
anthropic: "ANTHROPIC_API_KEY"
gemini: "GEMINI_API_KEY"
vertex: "VERTEX_API_KEY"
bedrock: "AWS_ACCESS_KEY_ID"
cohere: "COHERE_API_KEY"

Expand Down Expand Up @@ -276,6 +288,46 @@ provider_scenarios:
file_content: false # Gemini doesn't support direct file download
count_tokens: true

vertex:
simple_chat: true
multi_turn_conversation: true
streaming: true
tool_calls: true
multiple_tool_calls: true
end2end_tool_calling: true
automatic_function_calling: true
image_url: false # Gemini requires base64 or file upload
image_base64: true
file_input: true
multiple_images: false
speech_synthesis: false
speech_synthesis_streaming: false
transcription: false
transcription_streaming: false
embeddings: true
thinking: true
prompt_caching: false
list_models: true
responses: true
responses_image: true
text_completion: false
langchain_structured_output: true
pydantic_structured_output: false # PydanticAI structured output unreliable via Bifrost for Gemini
pydanticai_streaming: false # PydanticAI GoogleModel streaming has asyncio issues
batch_file_upload: false # Gemini supports file upload via Files API
batch_create: false
batch_list: false
batch_retrieve: false
batch_cancel: false
batch_inline: false # Gemini uses inline requests for batch (synchronous)
batch_s3: false # Gemini does not use S3 for batch
file_upload: false
file_list: false
file_retrieve: false
file_delete: false
file_content: false # Gemini doesn't support direct file download
count_tokens: false

bedrock:
simple_chat: true
multi_turn_conversation: true
Expand Down
3 changes: 3 additions & 0 deletions tests/integrations/tests/test_anthropic.py
Original file line number Diff line number Diff line change
Expand Up @@ -796,6 +796,9 @@ def test_16_extended_thinking_streaming(self, anthropic_client, test_config, pro
},
messages=messages,
stream=True,
extra_body={
"reasoning_summary": "detailed"
},
)

# Collect streaming content
Expand Down
Loading