mirror of
https://github.com/YspCoder/clawgo.git
synced 2026-05-16 15:57:35 +08:00
fix(provider): support dsml tool call markup
This commit is contained in:
@@ -415,17 +415,25 @@ func codexCompatRequestBody(requestBody map[string]interface{}) map[string]inter
|
||||
}
|
||||
|
||||
func parseCompatFunctionCalls(content string) ([]ToolCall, string) {
|
||||
if strings.TrimSpace(content) == "" || !strings.Contains(content, "<function_call>") {
|
||||
if strings.TrimSpace(content) == "" || !containsCompatFunctionCallMarkup(content) {
|
||||
return nil, content
|
||||
}
|
||||
blockRe := regexp.MustCompile(`(?is)<function_call>\s*(.*?)\s*</function_call>`)
|
||||
blocks := blockRe.FindAllStringSubmatch(content, -1)
|
||||
blockRe := regexp.MustCompile(`(?is)<function_call>\s*(.*?)\s*</function_call>|<||DSML||tool_calls>\s*(.*?)\s*</||DSML||tool_calls>`)
|
||||
matches := blockRe.FindAllStringSubmatch(content, -1)
|
||||
blocks := make([]string, 0, len(matches))
|
||||
for _, match := range matches {
|
||||
switch {
|
||||
case len(match) > 1 && strings.TrimSpace(match[1]) != "":
|
||||
blocks = append(blocks, match[1])
|
||||
case len(match) > 2 && strings.TrimSpace(match[2]) != "":
|
||||
blocks = append(blocks, match[2])
|
||||
}
|
||||
}
|
||||
if len(blocks) == 0 {
|
||||
return nil, content
|
||||
}
|
||||
toolCalls := make([]ToolCall, 0, len(blocks))
|
||||
for i, block := range blocks {
|
||||
raw := block[1]
|
||||
for i, raw := range blocks {
|
||||
invoke := extractTag(raw, "invoke")
|
||||
if invoke != "" {
|
||||
raw = invoke
|
||||
@@ -434,6 +442,9 @@ func parseCompatFunctionCalls(content string) ([]ToolCall, string) {
|
||||
if strings.TrimSpace(name) == "" {
|
||||
name = extractTag(raw, "tool_name")
|
||||
}
|
||||
if strings.TrimSpace(name) == "" {
|
||||
name = extractInvokeNameAttr(raw)
|
||||
}
|
||||
name = strings.TrimSpace(name)
|
||||
if name == "" {
|
||||
continue
|
||||
@@ -466,6 +477,14 @@ func parseCompatFunctionCalls(content string) ([]ToolCall, string) {
|
||||
return toolCalls, cleaned
|
||||
}
|
||||
|
||||
func containsCompatFunctionCallMarkup(content string) bool {
|
||||
trimmed := strings.TrimSpace(content)
|
||||
if trimmed == "" {
|
||||
return false
|
||||
}
|
||||
return strings.Contains(trimmed, "<function_call>") || strings.Contains(trimmed, "<||DSML||tool_calls>")
|
||||
}
|
||||
|
||||
func extractTag(src string, tag string) string {
|
||||
re := regexp.MustCompile(fmt.Sprintf(`(?is)<%s>\s*(.*?)\s*</%s>`, regexp.QuoteMeta(tag), regexp.QuoteMeta(tag)))
|
||||
m := re.FindStringSubmatch(src)
|
||||
@@ -474,3 +493,12 @@ func extractTag(src string, tag string) string {
|
||||
}
|
||||
return strings.TrimSpace(m[1])
|
||||
}
|
||||
|
||||
func extractInvokeNameAttr(src string) string {
|
||||
re := regexp.MustCompile(`(?is)<(?:invoke|||DSML||invoke)\b[^>]*\bname\s*=\s*"([^"]+)"[^>]*>`)
|
||||
m := re.FindStringSubmatch(src)
|
||||
if len(m) < 2 {
|
||||
return ""
|
||||
}
|
||||
return strings.TrimSpace(m[1])
|
||||
}
|
||||
|
||||
@@ -312,3 +312,16 @@ func TestHTTPProviderChatConfiguredCompatBackfillsReasoningContentForToolHistory
|
||||
t.Fatalf("reasoning_content = %#v, want thinking content", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseCompatFunctionCallsSupportsDSMLToolCalls(t *testing.T) {
|
||||
calls, cleaned := parseCompatFunctionCalls(`<||DSML||tool_calls><||DSML||invoke name="read_file"></||DSML||invoke></||DSML||tool_calls>`)
|
||||
if len(calls) != 1 {
|
||||
t.Fatalf("calls = %#v, want one tool call", calls)
|
||||
}
|
||||
if calls[0].Name != "read_file" {
|
||||
t.Fatalf("tool name = %q, want read_file", calls[0].Name)
|
||||
}
|
||||
if strings.TrimSpace(cleaned) != "" {
|
||||
t.Fatalf("cleaned = %q, want empty", cleaned)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user