Skip to content

Commit c74734e

Browse files
committed
Fix schema handling for Google provider
1 parent 8fdcb90 commit c74734e

3 files changed

Lines changed: 77 additions & 59 deletions

File tree

anthropic.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,12 @@ func ToolsToAnthropic(tools []Tool) []anthropic.ToolUnionParam {
1313
anthropicTools := []anthropic.ToolUnionParam{}
1414
for _, tool := range tools {
1515
// Construct the ToolInputSchemaParam struct directly
16+
properties := tool.Schema.Properties
17+
if properties == nil {
18+
properties = make(map[string]interface{})
19+
}
1620
inputSchema := anthropic.ToolInputSchemaParam{
17-
Properties: tool.Schema.Properties, // Assuming Properties is map[string]interface{}
21+
Properties: properties, // Assuming Properties is map[string]interface{}
1822
// Type defaults to "object" via omitempty / SDK marshalling if needed
1923
}
2024
// Add required fields if they exist

google.go

Lines changed: 53 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"encoding/json"
55
"fmt"
66
"iter"
7+
"strings"
78

89
"github.com/google/uuid"
910
"google.golang.org/genai"
@@ -17,6 +18,51 @@ type GoogleStreamIterator interface {
1718

1819
func ToolsToGoogle(tools []Tool) ([]*genai.Tool, error) {
1920
googleTools := []*genai.Tool{}
21+
22+
var propertyToSchema func(property map[string]any) (*genai.Schema, error)
23+
propertyToSchema = func(property map[string]any) (*genai.Schema, error) {
24+
schema := &genai.Schema{}
25+
26+
typeRaw, ok := property["type"]
27+
if ok {
28+
typ, ok := typeRaw.(string)
29+
if !ok {
30+
return nil, fmt.Errorf("type is not a string: %T", typeRaw)
31+
}
32+
schema.Type = genai.Type(strings.ToUpper(typ))
33+
}
34+
35+
descriptionRaw, ok := property["description"]
36+
if ok {
37+
description, ok := descriptionRaw.(string)
38+
if !ok {
39+
return nil, fmt.Errorf("description is not a string: %T", descriptionRaw)
40+
}
41+
schema.Description = description
42+
}
43+
44+
propertiesRaw, ok := property["properties"]
45+
if ok {
46+
properties, ok := propertiesRaw.(map[string]any)
47+
if !ok {
48+
return nil, fmt.Errorf("properties is not a map[string]any: %T", propertiesRaw)
49+
}
50+
for key, value := range properties {
51+
propMap, ok := value.(map[string]any)
52+
if !ok {
53+
return nil, fmt.Errorf("property %q is not a map[string]any: %T", key, value)
54+
}
55+
subschema, err := propertyToSchema(propMap)
56+
if err != nil {
57+
return nil, fmt.Errorf("property %q has non-object properties: %w", key, err)
58+
}
59+
schema.Properties[key] = subschema
60+
}
61+
}
62+
63+
return schema, nil
64+
}
65+
2066
for _, tool := range tools {
2167
var schema *genai.Schema
2268
if tool.Schema.Properties != nil {
@@ -26,62 +72,16 @@ func ToolsToGoogle(tools []Tool) ([]*genai.Tool, error) {
2672
Required: tool.Schema.Required,
2773
}
2874

29-
// Convert properties
30-
for propName, propSchema := range tool.Schema.Properties {
31-
// Handle both simple type strings and complex objects
32-
var propMap map[string]any
33-
switch ps := propSchema.(type) {
34-
case map[string]any:
35-
propMap = ps
36-
case map[string]string:
37-
// Convert map[string]string to map[string]any
38-
propMap = make(map[string]any, len(ps))
39-
for k, v := range ps {
40-
propMap[k] = v
41-
}
42-
case string:
43-
// Handle simple type string (convert to a type definition)
44-
propMap = map[string]any{"type": ps}
45-
default:
46-
return nil, fmt.Errorf("property %q has unsupported schema type %T", propName, propSchema)
47-
}
48-
49-
// Get the type
50-
typeStr, ok := propMap["type"].(string)
75+
for key, value := range tool.Schema.Properties {
76+
propMap, ok := value.(map[string]any)
5177
if !ok {
52-
return nil, fmt.Errorf("property %q missing type field", propName)
53-
}
54-
55-
prop := &genai.Schema{}
56-
switch typeStr {
57-
case "string":
58-
prop.Type = genai.TypeString
59-
case "number":
60-
prop.Type = genai.TypeNumber
61-
case "integer":
62-
prop.Type = genai.TypeInteger
63-
case "boolean":
64-
prop.Type = genai.TypeBoolean
65-
case "array":
66-
prop.Type = genai.TypeArray
67-
case "object":
68-
prop.Type = genai.TypeObject
69-
default:
70-
return nil, fmt.Errorf("property %q has unsupported type %q", propName, typeStr)
71-
}
72-
73-
// Copy over common fields if they exist
74-
if desc, ok := propMap["description"].(string); ok {
75-
prop.Description = desc
78+
return nil, fmt.Errorf("property %q is not a map[string]any: %T", key, value)
7679
}
77-
if enum, ok := propMap["enum"].([]any); ok {
78-
prop.Enum = make([]string, len(enum))
79-
for i, e := range enum {
80-
prop.Enum[i] = fmt.Sprintf("%v", e)
81-
}
80+
subschema, err := propertyToSchema(propMap)
81+
if err != nil {
82+
return nil, fmt.Errorf("property %q has non-object properties: %w", key, err)
8283
}
83-
84-
schema.Properties[propName] = prop
84+
schema.Properties[key] = subschema
8585
}
8686
}
8787

@@ -323,10 +323,6 @@ func GoogleToDataStream(stream iter.Seq2[*genai.GenerateContentResponse, error])
323323
}, nil) {
324324
return
325325
}
326-
if !yield(ErrorStreamPart{Content: fmt.Sprintf("failed to marshal function call args for %s: %s", fc.Name, err)}, nil) {
327-
return
328-
}
329-
continue
330326
}
331327

332328
finalReason = FinishReasonToolCalls

openai.go

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,9 @@ func ToolsToOpenAI(tools []Tool) []openai.ChatCompletionToolParam {
1818
schemaParams = map[string]any{
1919
"type": "object",
2020
"properties": tool.Schema.Properties,
21-
"required": tool.Schema.Required,
21+
}
22+
if len(tool.Schema.Required) > 0 {
23+
schemaParams["required"] = tool.Schema.Required
2224
}
2325
}
2426
openaiTools = append(openaiTools, openai.ChatCompletionToolParam{
@@ -195,6 +197,22 @@ func OpenAIToDataStream(stream *ssestream.Stream[openai.ChatCompletionChunk]) Da
195197
}
196198
}
197199
}
200+
201+
if choice.FinishReason != "" {
202+
var finishReason FinishReason
203+
switch choice.FinishReason {
204+
case "tool_calls":
205+
finishReason = FinishReasonToolCalls
206+
default:
207+
finishReason = FinishReasonStop
208+
}
209+
if !yield(FinishStepStreamPart{
210+
IsContinued: false,
211+
FinishReason: finishReason,
212+
}, nil) {
213+
return
214+
}
215+
}
198216
}
199217

200218
var finishReason FinishReason

0 commit comments

Comments
 (0)