| | package parser
|
| |
|
| | import "strings"
|
| |
|
| |
|
| | type BlockInfo struct {
|
| | StartIndex int
|
| | TagType string
|
| | }
|
| |
|
| |
|
| | func FindToolCallBlockAtEnd(text string) *BlockInfo {
|
| | trimmed := strings.TrimRight(text, " \t\n\r")
|
| |
|
| |
|
| | firstFunctionCalls := strings.Index(trimmed, "<function_calls>")
|
| | firstTool := strings.Index(trimmed, "<tool>")
|
| | firstTools := strings.Index(trimmed, "<tools>")
|
| |
|
| |
|
| | firstOpenTag := -1
|
| | tagType := ""
|
| |
|
| | if firstFunctionCalls != -1 {
|
| | firstOpenTag = firstFunctionCalls
|
| | tagType = "function_calls"
|
| | }
|
| | if firstTool != -1 && (firstOpenTag == -1 || firstTool < firstOpenTag) {
|
| | firstOpenTag = firstTool
|
| | tagType = "tool"
|
| | }
|
| | if firstTools != -1 && (firstOpenTag == -1 || firstTools < firstOpenTag) {
|
| | firstOpenTag = firstTools
|
| | tagType = "tools"
|
| | }
|
| |
|
| | if firstOpenTag == -1 {
|
| | return nil
|
| | }
|
| |
|
| |
|
| | openTag := "<" + tagType + ">"
|
| | closeTag := "</" + tagType + ">"
|
| |
|
| | depth := 0
|
| | pos := firstOpenTag
|
| | lastClosePos := -1
|
| |
|
| | for pos < len(trimmed) {
|
| | nextOpen := strings.Index(trimmed[pos:], openTag)
|
| | nextClose := strings.Index(trimmed[pos:], closeTag)
|
| |
|
| | if nextOpen == -1 && nextClose == -1 {
|
| | break
|
| | }
|
| |
|
| | if nextOpen != -1 && (nextClose == -1 || nextOpen < nextClose) {
|
| |
|
| | depth++
|
| | pos = pos + nextOpen + len(openTag)
|
| | } else {
|
| |
|
| | depth--
|
| | if depth == 0 {
|
| | lastClosePos = pos + nextClose + len(closeTag)
|
| | }
|
| | pos = pos + nextClose + len(closeTag)
|
| | }
|
| | }
|
| |
|
| |
|
| | if lastClosePos != -1 {
|
| |
|
| |
|
| | } else if depth > 0 {
|
| |
|
| |
|
| | } else {
|
| |
|
| | return nil
|
| | }
|
| |
|
| | return &BlockInfo{
|
| | StartIndex: firstOpenTag,
|
| | TagType: tagType,
|
| | }
|
| | }
|
| |
|
| |
|
| | func HasCompleteToolCall(text string) bool {
|
| | trimmed := strings.TrimRight(text, " \t\n\r")
|
| | return strings.HasSuffix(trimmed, "</function_calls>") ||
|
| | strings.HasSuffix(trimmed, "</tool>") ||
|
| | strings.HasSuffix(trimmed, "</tools>")
|
| | }
|
| |
|
| |
|
| | func HasIncompleteToolCall(text string) bool {
|
| | trimmed := strings.TrimRight(text, " \t\n\r")
|
| |
|
| |
|
| | firstFunctionCalls := strings.Index(trimmed, "<function_calls>")
|
| | firstTool := strings.Index(trimmed, "<tool>")
|
| | firstTools := strings.Index(trimmed, "<tools>")
|
| |
|
| | firstOpenTag := -1
|
| | tagType := ""
|
| |
|
| | if firstFunctionCalls != -1 {
|
| | firstOpenTag = firstFunctionCalls
|
| | tagType = "function_calls"
|
| | }
|
| | if firstTool != -1 && (firstOpenTag == -1 || firstTool < firstOpenTag) {
|
| | firstOpenTag = firstTool
|
| | tagType = "tool"
|
| | }
|
| | if firstTools != -1 && (firstOpenTag == -1 || firstTools < firstOpenTag) {
|
| | firstOpenTag = firstTools
|
| | tagType = "tools"
|
| | }
|
| |
|
| | if firstOpenTag == -1 {
|
| | return false
|
| | }
|
| |
|
| |
|
| | closeTag := "</" + tagType + ">"
|
| | closeIndex := strings.LastIndex(trimmed, closeTag)
|
| |
|
| |
|
| | return closeIndex == -1 || closeIndex < firstOpenTag
|
| | } |