WIP: stable ordering for tool args
Right now we deserialize tool call definitions' arguments into golang maps. These purposefully don't have a predictable iteration order, whereas we want to maintain the order the user originally provided. Unstable rendering of arguments means that we break the kv cache, which this change fixes. There's no way to build this in a fully backwards compatible way when executing existing templates exactly as they are. We get around this by rewriting templates dynamically just before they're rendered. This is fragile, but perhaps the least bad option?
This commit is contained in:
@@ -268,17 +268,17 @@ func parseToolCall(raw qwenEventRawToolCall, tools []api.Tool) (api.ToolCall, er
|
||||
}
|
||||
}
|
||||
|
||||
toolCall.Function.Arguments = make(api.ToolCallFunctionArguments)
|
||||
toolCall.Function.Arguments = api.NewToolCallFunctionArguments()
|
||||
for _, parameter := range functionCall.Parameters {
|
||||
// Look up the parameter type if we found the tool
|
||||
var paramType api.PropertyType
|
||||
if matchedTool != nil && matchedTool.Function.Parameters.Properties != nil {
|
||||
if prop, ok := matchedTool.Function.Parameters.Properties[parameter.Name]; ok {
|
||||
if matchedTool != nil && matchedTool.Function.Parameters.GetProperties() != nil {
|
||||
if prop, ok := matchedTool.Function.Parameters.GetProperties().Get(parameter.Name); ok {
|
||||
paramType = prop.Type
|
||||
}
|
||||
}
|
||||
|
||||
toolCall.Function.Arguments[parameter.Name] = parseValue(parameter.Value, paramType)
|
||||
toolCall.Function.Arguments.Set(parameter.Name, parseValue(parameter.Value, paramType))
|
||||
}
|
||||
|
||||
return toolCall, nil
|
||||
|
||||
@@ -11,10 +11,25 @@ import (
|
||||
func tool(name string, props map[string]api.ToolProperty) api.Tool {
|
||||
t := api.Tool{Type: "function", Function: api.ToolFunction{Name: name}}
|
||||
t.Function.Parameters.Type = "object"
|
||||
t.Function.Parameters.Properties = props
|
||||
p := api.NewToolProperties()
|
||||
for k, v := range props {
|
||||
p.Set(k, v)
|
||||
}
|
||||
t.Function.Parameters.SetProperties(p)
|
||||
return t
|
||||
}
|
||||
|
||||
// Helper function to create ordered arguments for tests
|
||||
func makeArgs(pairs ...any) api.ToolCallFunctionArguments {
|
||||
args := api.NewToolCallFunctionArguments()
|
||||
for i := 0; i < len(pairs); i += 2 {
|
||||
key := pairs[i].(string)
|
||||
value := pairs[i+1]
|
||||
args.Set(key, value)
|
||||
}
|
||||
return args
|
||||
}
|
||||
|
||||
func TestQwenParserStreaming(t *testing.T) {
|
||||
type step struct {
|
||||
input string
|
||||
@@ -354,10 +369,7 @@ celsius
|
||||
wantToolCall: api.ToolCall{
|
||||
Function: api.ToolCallFunction{
|
||||
Name: "get_current_temperature",
|
||||
Arguments: map[string]any{
|
||||
"location": "San Francisco",
|
||||
"unit": "celsius",
|
||||
},
|
||||
Arguments: makeArgs("location", "San Francisco", "unit", "celsius"),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -375,10 +387,10 @@ celsius
|
||||
wantToolCall: api.ToolCall{
|
||||
Function: api.ToolCallFunction{
|
||||
Name: "get current temperature",
|
||||
Arguments: map[string]any{
|
||||
"location with spaces": "San Francisco",
|
||||
"unit with spaces": "celsius",
|
||||
},
|
||||
Arguments: makeArgs(
|
||||
"location with spaces", "San Francisco",
|
||||
"unit with spaces", "celsius",
|
||||
),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -400,10 +412,10 @@ San Francisco
|
||||
wantToolCall: api.ToolCall{
|
||||
Function: api.ToolCallFunction{
|
||||
Name: "\"get current temperature\"",
|
||||
Arguments: map[string]any{
|
||||
"\"location with spaces\"": "San Francisco",
|
||||
"\"unit with spaces\"": "\"celsius\"",
|
||||
},
|
||||
Arguments: makeArgs(
|
||||
"\"location with spaces\"", "San Francisco",
|
||||
"\"unit with spaces\"", "\"celsius\"",
|
||||
),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -434,12 +446,12 @@ true
|
||||
wantToolCall: api.ToolCall{
|
||||
Function: api.ToolCallFunction{
|
||||
Name: "calculate",
|
||||
Arguments: map[string]any{
|
||||
"x": 3.14,
|
||||
"y": 42,
|
||||
"enabled": true,
|
||||
"items": []any{"a", "b", "c"},
|
||||
},
|
||||
Arguments: makeArgs(
|
||||
"x", 3.14,
|
||||
"y", 42,
|
||||
"enabled", true,
|
||||
"items", []any{"a", "b", "c"},
|
||||
),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -455,9 +467,7 @@ ls && echo "done"
|
||||
wantToolCall: api.ToolCall{
|
||||
Function: api.ToolCallFunction{
|
||||
Name: "exec",
|
||||
Arguments: map[string]any{
|
||||
"command": "ls && echo \"done\"",
|
||||
},
|
||||
Arguments: makeArgs("command", "ls && echo \"done\""),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -472,9 +482,7 @@ ls && echo "a > b and a < b"
|
||||
wantToolCall: api.ToolCall{
|
||||
Function: api.ToolCallFunction{
|
||||
Name: "exec",
|
||||
Arguments: map[string]any{
|
||||
"command": "ls && echo \"a > b and a < b\"",
|
||||
},
|
||||
Arguments: makeArgs("command", "ls && echo \"a > b and a < b\""),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -492,10 +500,7 @@ Hello! 你好! 🌟 مرحبا
|
||||
wantToolCall: api.ToolCall{
|
||||
Function: api.ToolCallFunction{
|
||||
Name: "获取天气",
|
||||
Arguments: map[string]any{
|
||||
"城市": "北京",
|
||||
"message": "Hello! 你好! 🌟 مرحبا",
|
||||
},
|
||||
Arguments: makeArgs("城市", "北京", "message", "Hello! 你好! 🌟 مرحبا"),
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user