Skip to content
Merged
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
43 changes: 26 additions & 17 deletions core/providers/gemini/uniontype_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,9 +120,8 @@ func TestConvertPropertyToSchema_UnionType(t *testing.T) {
}
}

// TestConvertBifrostToolsToGemini_UnionTypeProperty is the end-to-end test
// that reproduces the Goose+Vertex bug: a tool parameter with
// "type": ["integer", "null"] must produce a non-empty Gemini type field.
// TestConvertBifrostToolsToGemini_UnionTypeProperty verifies that tool parameters
// with JSON Schema union types are passed through unchanged in parametersJsonSchema.
func TestConvertBifrostToolsToGemini_UnionTypeProperty(t *testing.T) {
toolJSON := `{
"type": "function",
Expand All @@ -149,28 +148,37 @@ func TestConvertBifrostToolsToGemini_UnionTypeProperty(t *testing.T) {
var chatTool schemas.ChatTool
require.NoError(t, json.Unmarshal([]byte(toolJSON), &chatTool))

geminiTools := convertBifrostToolsToGemini([]schemas.ChatTool{chatTool})
geminiTools, err := convertBifrostToolsToGemini([]schemas.ChatTool{chatTool})
require.NoError(t, err)
require.Len(t, geminiTools, 1)
require.Len(t, geminiTools[0].FunctionDeclarations, 1)

fd := geminiTools[0].FunctionDeclarations[0]
require.NotNil(t, fd.Parameters)
require.NotNil(t, fd.ParametersJSONSchema)
assert.Nil(t, fd.Parameters, "chat tools use parametersJsonSchema passthrough, not Gemini Schema")

raw, err := json.Marshal(fd.ParametersJSONSchema)
require.NoError(t, err)

timeoutSchema, ok := fd.Parameters.Properties["timeout_secs"]
var paramsSchema map[string]interface{}
require.NoError(t, json.Unmarshal(raw, &paramsSchema))

properties, ok := paramsSchema["properties"].(map[string]interface{})
require.True(t, ok, "parameters must have properties")

timeoutProp, ok := properties["timeout_secs"].(map[string]interface{})
require.True(t, ok, "timeout_secs property must be present")

// Before the fix this was "" — Vertex AI rejected with
// "parameters.timeout_secs schema didn't specify the schema type field"
assert.NotEmpty(t, timeoutSchema.Type, "Type must not be empty for union-typed property")
assert.Equal(t, Type("integer"), timeoutSchema.Type)
require.NotNil(t, timeoutSchema.Nullable)
assert.True(t, *timeoutSchema.Nullable)
timeoutType, ok := timeoutProp["type"].([]interface{})
require.True(t, ok, "timeout_secs type must be a JSON Schema union array")
assert.Equal(t, "integer", timeoutType[0])
assert.Equal(t, "null", timeoutType[1])
Comment thread
BearTS marked this conversation as resolved.
assert.Equal(t, "Timeout in seconds", timeoutProp["description"])

// The non-union "command" property must be unaffected
commandSchema, ok := fd.Parameters.Properties["command"]
commandProp, ok := properties["command"].(map[string]interface{})
require.True(t, ok)
assert.Equal(t, Type("string"), commandSchema.Type)
assert.Nil(t, commandSchema.Nullable)
assert.Equal(t, "string", commandProp["type"])
assert.Equal(t, "Command to run", commandProp["description"])
}

func boolPtr(b bool) *bool { return &b }
Expand Down Expand Up @@ -321,7 +329,8 @@ func TestConvertBifrostToolsToGemini_WirePayload(t *testing.T) {
var chatTool schemas.ChatTool
require.NoError(t, json.Unmarshal([]byte(toolJSON), &chatTool))

geminiTools := convertBifrostToolsToGemini([]schemas.ChatTool{chatTool})
geminiTools, err := convertBifrostToolsToGemini([]schemas.ChatTool{chatTool})
require.NoError(t, err)
require.Len(t, geminiTools, 1)

// Serialize to the exact bytes that would be sent to Vertex
Expand Down
Loading