| 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
|
| } |