| package toolstream |
|
|
| import ( |
| "ds2api/internal/toolcall" |
| "strings" |
| ) |
|
|
| |
| func consumeXMLToolCapture(captured string, toolNames []string) (prefix string, calls []toolcall.ParsedToolCall, suffix string, ready bool) { |
| anyOpenFound := false |
| type candidate struct { |
| start int |
| prefix string |
| calls []toolcall.ParsedToolCall |
| suffix string |
| } |
| type rejectedBlock struct { |
| start int |
| prefix string |
| suffix string |
| } |
| var best *candidate |
| var rejected *rejectedBlock |
|
|
| |
| |
| |
| for searchFrom := 0; searchFrom < len(captured); { |
| tag, ok := toolcall.FindToolMarkupTagOutsideIgnored(captured, searchFrom) |
| if !ok { |
| break |
| } |
| if tag.Closing || tag.Name != "tool_calls" { |
| searchFrom = tag.End + 1 |
| continue |
| } |
| closeTag, ok := toolcall.FindMatchingToolMarkupClose(captured, tag) |
| if !ok { |
| anyOpenFound = true |
| searchFrom = tag.End + 1 |
| continue |
| } |
|
|
| xmlBlock := captured[tag.Start : closeTag.End+1] |
| prefixPart := captured[:tag.Start] |
| suffixPart := captured[closeTag.End+1:] |
| parsed := toolcall.ParseStandaloneToolCallsDetailed(xmlBlock, toolNames) |
| if len(parsed.Calls) > 0 { |
| prefixPart, suffixPart = trimWrappingJSONFence(prefixPart, suffixPart) |
| if best == nil || tag.Start < best.start { |
| best = &candidate{start: tag.Start, prefix: prefixPart, calls: parsed.Calls, suffix: suffixPart} |
| } |
| break |
| } |
| if parsed.SawToolCallSyntax { |
| if rejected == nil || tag.Start < rejected.start { |
| rejected = &rejectedBlock{start: tag.Start, prefix: prefixPart, suffix: suffixPart} |
| } |
| searchFrom = tag.End + 1 |
| continue |
| } |
| if rejected == nil || tag.Start < rejected.start { |
| rejected = &rejectedBlock{start: tag.Start, prefix: prefixPart + xmlBlock, suffix: suffixPart} |
| } |
| searchFrom = tag.End + 1 |
| } |
| if best != nil { |
| return best.prefix, best.calls, best.suffix, true |
| } |
| if anyOpenFound { |
| |
| |
| return "", nil, "", false |
| } |
| if rejected != nil { |
| |
| return rejected.prefix, nil, rejected.suffix, true |
| } |
| if invokeTag, ok := findFirstToolMarkupTagByName(captured, 0, "invoke"); ok { |
| if wrapperOpen, ok := findFirstToolMarkupTagByName(captured, 0, "tool_calls"); !ok || wrapperOpen.Start > invokeTag.Start { |
| if closeTag, ok := findFirstToolMarkupTagByNameFrom(captured, invokeTag.Start+1, "tool_calls", true); ok && closeTag.Start > invokeTag.Start { |
| xmlBlock := "<tool_calls>" + captured[invokeTag.Start:closeTag.End+1] |
| prefixPart := captured[:invokeTag.Start] |
| suffixPart := captured[closeTag.End+1:] |
| parsed := toolcall.ParseStandaloneToolCallsDetailed(xmlBlock, toolNames) |
| if len(parsed.Calls) > 0 { |
| prefixPart, suffixPart = trimWrappingJSONFence(prefixPart, suffixPart) |
| return prefixPart, parsed.Calls, suffixPart, true |
| } |
| if parsed.SawToolCallSyntax { |
| return prefixPart, nil, suffixPart, true |
| } |
| return prefixPart + captured[invokeTag.Start:closeTag.End+1], nil, suffixPart, true |
| } |
| } |
| } |
| return "", nil, "", false |
| } |
|
|
| |
| |
| func hasOpenXMLToolTag(captured string) bool { |
| for searchFrom := 0; searchFrom < len(captured); { |
| tag, ok := toolcall.FindToolMarkupTagOutsideIgnored(captured, searchFrom) |
| if !ok { |
| return false |
| } |
| if tag.Closing || tag.Name != "tool_calls" { |
| searchFrom = tag.End + 1 |
| continue |
| } |
| if _, ok := toolcall.FindMatchingToolMarkupClose(captured, tag); !ok { |
| return true |
| } |
| searchFrom = tag.End + 1 |
| } |
| return false |
| } |
|
|
| func shouldKeepBareInvokeCapture(captured string) bool { |
| invokeTag, ok := findFirstToolMarkupTagByName(captured, 0, "invoke") |
| if !ok { |
| return false |
| } |
| if wrapperOpen, ok := findFirstToolMarkupTagByName(captured, 0, "tool_calls"); ok && wrapperOpen.Start <= invokeTag.Start { |
| return false |
| } |
| if closeTag, ok := findFirstToolMarkupTagByNameFrom(captured, invokeTag.Start+1, "tool_calls", true); ok && closeTag.Start > invokeTag.Start { |
| return true |
| } |
| startEnd := invokeTag.End |
| if startEnd < 0 { |
| return true |
| } |
| body := captured[startEnd+1:] |
| trimmedBody := strings.TrimLeft(body, " \t\r\n") |
| if trimmedBody == "" { |
| return true |
| } |
|
|
| if invokeCloseTag, ok := findFirstToolMarkupTagByNameFrom(captured, startEnd+1, "invoke", true); ok { |
| return strings.TrimSpace(captured[invokeCloseTag.End+1:]) == "" |
| } |
|
|
| trimmedLower := strings.ToLower(trimmedBody) |
| return strings.HasPrefix(trimmedLower, "<parameter") || |
| strings.HasPrefix(trimmedLower, "{") || |
| strings.HasPrefix(trimmedLower, "[") |
| } |
|
|
| func findPartialXMLToolTagStart(s string) int { |
| lastLT := strings.LastIndex(s, "<") |
| if lastLT < 0 { |
| return -1 |
| } |
| start := includeDuplicateLeadingLessThan(s, lastLT) |
| tail := s[start:] |
| |
| if strings.Contains(tail, ">") { |
| return -1 |
| } |
| if toolcall.IsPartialToolMarkupTagPrefix(tail) { |
| return start |
| } |
| return -1 |
| } |
|
|