server: collect nested tool call objects when parsing (#5824)
This commit is contained in:
parent
80ee9b5e47
commit
b3e5491e41
5 changed files with 120 additions and 13 deletions
|
@ -344,6 +344,10 @@ func (m *Model) parseToolCalls(s string) ([]api.ToolCall, bool) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if name == "" || arguments == "" {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
|
||||||
var objs []map[string]any
|
var objs []map[string]any
|
||||||
for offset := 0; offset < len(s); {
|
for offset := 0; offset < len(s); {
|
||||||
var obj map[string]any
|
var obj map[string]any
|
||||||
|
@ -361,24 +365,41 @@ func (m *Model) parseToolCalls(s string) ([]api.ToolCall, bool) {
|
||||||
return nil, false
|
return nil, false
|
||||||
} else {
|
} else {
|
||||||
offset += int(decoder.InputOffset())
|
offset += int(decoder.InputOffset())
|
||||||
objs = append(objs, obj)
|
|
||||||
|
// collect all nested objects
|
||||||
|
var collect func(any) []map[string]any
|
||||||
|
collect = func(obj any) (all []map[string]any) {
|
||||||
|
switch o := obj.(type) {
|
||||||
|
case map[string]any:
|
||||||
|
all = append(all, o)
|
||||||
|
for _, v := range o {
|
||||||
|
all = append(all, collect(v)...)
|
||||||
|
}
|
||||||
|
case []any:
|
||||||
|
for _, v := range o {
|
||||||
|
all = append(all, collect(v)...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return all
|
||||||
|
}
|
||||||
|
objs = append(objs, collect(obj)...)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var toolCalls []api.ToolCall
|
var toolCalls []api.ToolCall
|
||||||
for _, kv := range objs {
|
for _, kv := range objs {
|
||||||
var call api.ToolCall
|
n, nok := kv[name].(string)
|
||||||
for k, v := range kv {
|
a, aok := kv[arguments].(map[string]any)
|
||||||
switch k {
|
if nok && aok {
|
||||||
case name:
|
toolCalls = append(toolCalls, api.ToolCall{
|
||||||
call.Function.Name = v.(string)
|
Function: api.ToolCallFunction{
|
||||||
case arguments:
|
Name: n,
|
||||||
call.Function.Arguments = v.(map[string]any)
|
Arguments: a,
|
||||||
|
},
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
toolCalls = append(toolCalls, call)
|
|
||||||
}
|
|
||||||
|
|
||||||
return toolCalls, len(toolCalls) > 0
|
return toolCalls, len(toolCalls) > 0
|
||||||
}
|
}
|
||||||
|
|
|
@ -166,6 +166,7 @@ The temperature in San Francisco, CA is 70°F and in Toronto, Canada is 20°C.`,
|
||||||
{"name": "get_current_weather", "arguments": {"format":"fahrenheit","location":"San Francisco, CA"}}
|
{"name": "get_current_weather", "arguments": {"format":"fahrenheit","location":"San Francisco, CA"}}
|
||||||
{"name": "get_current_weather", "arguments": {"format":"celsius","location":"Toronto, Canada"}}
|
{"name": "get_current_weather", "arguments": {"format":"celsius","location":"Toronto, Canada"}}
|
||||||
</tool_call>`, true},
|
</tool_call>`, true},
|
||||||
|
{"xlam", `{"tool_calls": [{"name": "get_current_weather", "arguments": {"format":"fahrenheit","location":"San Francisco, CA"}},{"name": "get_current_weather", "arguments": {"format":"celsius","location":"Toronto, Canada"}}]}`, true},
|
||||||
}
|
}
|
||||||
|
|
||||||
var tools []api.Tool
|
var tools []api.Tool
|
||||||
|
|
45
server/testdata/tools/xlam.gotmpl
vendored
Normal file
45
server/testdata/tools/xlam.gotmpl
vendored
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
{{- if .System }}{{ .System }}
|
||||||
|
{{ end }}
|
||||||
|
{{- range $i, $_ := .Messages }}
|
||||||
|
{{- if eq .Role "user" }}### Instruction:
|
||||||
|
{{- if and $.Tools (le (len (slice $.Messages $i)) 2) }}
|
||||||
|
[BEGIN OF TASK INSTRUCTION]
|
||||||
|
You are an expert in composing functions. You are given a question and a set of possible functions.
|
||||||
|
Based on the question, you will need to make one or more function/tool calls to achieve the purpose.
|
||||||
|
If none of the functions can be used, point it out and refuse to answer.
|
||||||
|
If the given question lacks the parameters required by the function, also point it out.
|
||||||
|
[END OF TASK INSTRUCTION]
|
||||||
|
|
||||||
|
[BEGIN OF AVAILABLE TOOLS]
|
||||||
|
{{ $.Tools }}
|
||||||
|
[END OF AVAILABLE TOOLS]
|
||||||
|
|
||||||
|
[BEGIN OF FORMAT INSTRUCTION]
|
||||||
|
The output MUST strictly adhere to the following JSON format, and NO other text MUST be included.
|
||||||
|
The example format is as follows. Please make sure the parameter type is correct. If no function call is needed, please make tool_calls an empty list '[]'.
|
||||||
|
```
|
||||||
|
{
|
||||||
|
"tool_calls": [
|
||||||
|
{"name": "func_name1", "arguments": {"argument1": "value1", "argument2": "value2"}},
|
||||||
|
... (more tool calls as required)
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
[END OF FORMAT INSTRUCTION]
|
||||||
|
|
||||||
|
[BEGIN OF QUERY]
|
||||||
|
{{ .Content }}
|
||||||
|
[END OF QUERY]
|
||||||
|
|
||||||
|
|
||||||
|
{{ else }}
|
||||||
|
{{ .Content }}
|
||||||
|
{{ end }}
|
||||||
|
{{- else if .ToolCalls }}### Response:
|
||||||
|
{"tool_calls": [{{ range .ToolCalls }}{"name": "{{ .Function.Name }}", "arguments": {{ .Function.Arguments }}}{{ end }}]}
|
||||||
|
<|EOT|>
|
||||||
|
{{ else if eq .Role "assistant" }}### Response:
|
||||||
|
{{ .Content }}
|
||||||
|
<|EOT|>
|
||||||
|
{{ end }}
|
||||||
|
{{- end }}### Response:
|
40
server/testdata/tools/xlam.out
vendored
Normal file
40
server/testdata/tools/xlam.out
vendored
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
You are a knowledgable assistant. You can answer questions and perform tasks.
|
||||||
|
### Instruction:
|
||||||
|
What's the weather like today in Paris?
|
||||||
|
### Response:
|
||||||
|
{"tool_calls": [{"name": "get_current_weather", "arguments": {"format":"celsius","location":"Paris, France"}}]}
|
||||||
|
<|EOT|>
|
||||||
|
### Response:
|
||||||
|
The current temperature in Paris, France is 22 degrees Celsius.
|
||||||
|
<|EOT|>
|
||||||
|
### Instruction:
|
||||||
|
[BEGIN OF TASK INSTRUCTION]
|
||||||
|
You are an expert in composing functions. You are given a question and a set of possible functions.
|
||||||
|
Based on the question, you will need to make one or more function/tool calls to achieve the purpose.
|
||||||
|
If none of the functions can be used, point it out and refuse to answer.
|
||||||
|
If the given question lacks the parameters required by the function, also point it out.
|
||||||
|
[END OF TASK INSTRUCTION]
|
||||||
|
|
||||||
|
[BEGIN OF AVAILABLE TOOLS]
|
||||||
|
[{"type":"function","function":{"name":"get_current_weather","description":"Get the current weather","parameters":{"type":"object","required":["location","format"],"properties":{"format":{"type":"string","description":"The temperature unit to use. Infer this from the users location.","enum":["celsius","fahrenheit"]},"location":{"type":"string","description":"The city and state, e.g. San Francisco, CA"}}}}}]
|
||||||
|
[END OF AVAILABLE TOOLS]
|
||||||
|
|
||||||
|
[BEGIN OF FORMAT INSTRUCTION]
|
||||||
|
The output MUST strictly adhere to the following JSON format, and NO other text MUST be included.
|
||||||
|
The example format is as follows. Please make sure the parameter type is correct. If no function call is needed, please make tool_calls an empty list '[]'.
|
||||||
|
```
|
||||||
|
{
|
||||||
|
"tool_calls": [
|
||||||
|
{"name": "func_name1", "arguments": {"argument1": "value1", "argument2": "value2"}},
|
||||||
|
... (more tool calls as required)
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
[END OF FORMAT INSTRUCTION]
|
||||||
|
|
||||||
|
[BEGIN OF QUERY]
|
||||||
|
What's the weather like today in San Francisco and Toronto?
|
||||||
|
[END OF QUERY]
|
||||||
|
|
||||||
|
|
||||||
|
### Response:
|
Loading…
Reference in a new issue