package agentcapabilities import ( "regexp" "strings" ) // Compiled regexes for structural provider tool-call artifacts from common LLM // providers. These guards belong to the shared provider boundary so Assistant // streaming and external adapter bridges do not diverge on what counts as raw // tool-call leakage. var ( providerDSMLMarkers = []string{ "<|DSML|", "`) providerPipeMarkerRe = regexp.MustCompile(`<\|(?:plugin|interpreter|tool_call)\|>`) providerMiniMaxRe = regexp.MustCompile(`(?m)^minimax:tool_call\b`) providerDSMLRe = regexp.MustCompile(`= 0 { record(idx) } } if loc := providerDSMLRe.FindStringIndex(content); loc != nil { record(loc[0]) } if loc := providerXMLToolCallRe.FindStringIndex(content); loc != nil { record(loc[0]) } if loc := providerPipeMarkerRe.FindStringIndex(content); loc != nil { record(loc[0]) } if loc := providerMiniMaxRe.FindStringIndex(content); loc != nil { record(loc[0]) } record(providerJSONToolCallLeakIndex(content, catalog)) record(providerPlainFunctionToolCallLeakIndex(content, catalog)) return first } // SplitTrailingProviderToolNamePrefix holds a trailing token fragment when it // can still become a known provider tool name. Streaming callers can keep the // held suffix private until the next chunk proves whether it is prose or a raw // provider tool call. func SplitTrailingProviderToolNamePrefix(content string, catalog ProviderToolNameCatalog) (visible string, held string) { if content == "" { return "", "" } start := len(content) for start > 0 { ch := content[start-1] if (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || (ch >= '0' && ch <= '9') || ch == '_' { start-- continue } break } if start == len(content) { return content, "" } token := content[start:] if catalog.HasPrefix(token) { return content[:start], token } return content, "" } func providerJSONToolCallLeakIndex(content string, catalog ProviderToolNameCatalog) int { if content == "" { return -1 } matches := providerJSONToolCallRe.FindAllStringSubmatchIndex(content, -1) for _, m := range matches { if len(m) < 4 || m[2] < 0 || m[3] < 0 { continue } name := content[m[2]:m[3]] if catalog.Has(name) { return m[0] } } return -1 } func providerPlainFunctionToolCallLeakIndex(content string, catalog ProviderToolNameCatalog) int { if content == "" { return -1 } matches := providerPlainFunctionToolCallRe.FindAllStringSubmatchIndex(content, -1) for _, m := range matches { if len(m) < 4 || m[2] < 0 || m[3] < 0 { continue } name := content[m[2]:m[3]] if catalog.Has(name) { return m[2] } } return -1 }