Skip to content

Commit 4f8a016

Browse files
authored
tools: loosen tool argument parsing (ollama#11509)
1 parent 1e6eab5 commit 4f8a016

File tree

2 files changed

+78
-244
lines changed

2 files changed

+78
-244
lines changed

tools/tools.go

Lines changed: 50 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -120,16 +120,14 @@ func (p *Parser) parseToolCall() *api.ToolCall {
120120
return nil
121121
}
122122

123-
// only look for arguments after the tool name if the tool has parameters
124-
// TODO (jmorganca): while probably uncommon, this doesn't support
125-
// parsing arguments before the tool name, which may be needed in the future
126-
args := map[string]any{}
127-
if len(tool.Function.Parameters.Properties) > 0 {
128-
var i int
129-
if args, i = findArguments(*tool, p.buffer[end:]); args == nil {
130-
return nil
123+
var args map[string]any
124+
if found, i := findArguments(p.buffer); found == nil {
125+
return nil
126+
} else {
127+
args = found
128+
if i > end {
129+
end = i
131130
}
132-
end += i
133131
}
134132

135133
tc := &api.ToolCall{
@@ -217,93 +215,70 @@ func findTool(tools []api.Tool, buf []byte) (*api.Tool, int) {
217215
// objects for functions that have all-optional parameters
218216
// e.g. `{"name": "get_conditions", "arguments": {}}` will work but
219217
// `{"name": "get_conditions"}` will not currently work
220-
func findArguments(tool api.Tool, buffer []byte) (map[string]any, int) {
218+
func findArguments(buffer []byte) (map[string]any, int) {
221219
if len(buffer) == 0 {
222220
return nil, 0
223221
}
224222

225223
var braces int
226224
var start int = -1
227-
var end int
228-
var object []byte
229225

230-
// find any outer json object
231226
for i, c := range buffer {
232227
if c == '{' {
233-
braces++
234-
if start == -1 {
228+
if braces == 0 {
235229
start = i
236230
}
237-
}
238-
239-
if c == '}' {
240-
if start != -1 {
241-
braces--
242-
if braces == 0 {
243-
end = i + 1
244-
object = buffer[start:end]
245-
break
231+
braces++
232+
} else if c == '}' && braces > 0 {
233+
braces--
234+
if braces == 0 && start != -1 {
235+
object := buffer[start : i+1]
236+
237+
var data map[string]any
238+
if err := json.Unmarshal(object, &data); err != nil {
239+
start = -1
240+
continue
246241
}
247-
}
248-
}
249-
}
250242

251-
if braces > 0 {
252-
return nil, 0
253-
}
243+
var findObject func(obj map[string]any) (map[string]any, bool)
244+
findObject = func(obj map[string]any) (map[string]any, bool) {
245+
if _, hasName := obj["name"]; hasName {
246+
if args, ok := obj["arguments"].(map[string]any); ok {
247+
return args, true
248+
}
249+
if args, ok := obj["parameters"].(map[string]any); ok {
250+
return args, true
251+
}
252+
return nil, true
253+
}
254254

255-
var data map[string]any
256-
if err := json.Unmarshal(object, &data); err != nil {
257-
return nil, 0
258-
}
255+
for _, v := range obj {
256+
switch child := v.(type) {
257+
case map[string]any:
258+
if result, found := findObject(child); found {
259+
return result, true
260+
}
261+
case []any:
262+
for _, item := range child {
263+
if childObj, ok := item.(map[string]any); ok {
264+
if result, found := findObject(childObj); found {
265+
return result, true
266+
}
267+
}
268+
}
269+
}
270+
}
259271

260-
var find func(obj any) map[string]any
261-
find = func(obj any) map[string]any {
262-
switch obj := obj.(type) {
263-
case map[string]any:
264-
valid := true
265-
// check if all keys in the object exist in the tool's parameters
266-
for key := range obj {
267-
if _, exists := tool.Function.Parameters.Properties[key]; !exists {
268-
valid = false
269-
break
272+
return nil, false
270273
}
271-
}
272274

273-
// check for required parameters
274-
// TODO (jmorganca): this should error instead of silently failing
275-
if valid {
276-
for _, required := range tool.Function.Parameters.Required {
277-
if _, exists := obj[required]; !exists {
278-
valid = false
279-
break
280-
}
275+
if args, found := findObject(data); found {
276+
return args, i
281277
}
282-
}
283278

284-
if valid {
285-
return obj
286-
}
287-
288-
for _, value := range obj {
289-
if result := find(value); result != nil {
290-
return result
291-
}
292-
}
293-
case []any:
294-
for _, item := range obj {
295-
if result := find(item); result != nil {
296-
return result
297-
}
279+
return data, i
298280
}
299281
}
300-
301-
return nil
302-
}
303-
304-
result := find(data)
305-
if result != nil {
306-
return result, end
307282
}
308283

309284
return nil, 0

0 commit comments

Comments
 (0)