mirror of
https://github.com/CJackHwang/ds2api.git
synced 2026-05-11 03:37:40 +08:00
116 lines
2.9 KiB
Go
116 lines
2.9 KiB
Go
package toolcall
|
|
|
|
import (
|
|
"encoding/json"
|
|
"html"
|
|
"regexp"
|
|
"strings"
|
|
)
|
|
|
|
var toolCallMarkupKVPattern = regexp.MustCompile(`(?is)<(?:[a-z0-9_:-]+:)?([a-z0-9_\-.]+)\b[^>]*>(.*?)</(?:[a-z0-9_:-]+:)?([a-z0-9_\-.]+)>`)
|
|
|
|
// cdataPattern matches a standalone CDATA section.
|
|
var cdataPattern = regexp.MustCompile(`(?is)^<!\[CDATA\[(.*?)]]>$`)
|
|
|
|
func parseMarkupKVObject(text string) map[string]any {
|
|
matches := toolCallMarkupKVPattern.FindAllStringSubmatch(strings.TrimSpace(text), -1)
|
|
if len(matches) == 0 {
|
|
return nil
|
|
}
|
|
out := map[string]any{}
|
|
for _, m := range matches {
|
|
if len(m) < 4 {
|
|
continue
|
|
}
|
|
key := strings.TrimSpace(m[1])
|
|
endKey := strings.TrimSpace(m[3])
|
|
if key == "" {
|
|
continue
|
|
}
|
|
if !strings.EqualFold(key, endKey) {
|
|
continue
|
|
}
|
|
value := parseMarkupValue(m[2])
|
|
if value == nil {
|
|
continue
|
|
}
|
|
appendMarkupValue(out, key, value)
|
|
}
|
|
if len(out) == 0 {
|
|
return nil
|
|
}
|
|
return out
|
|
}
|
|
|
|
func parseMarkupValue(inner string) any {
|
|
if value, ok := extractStandaloneCDATA(inner); ok {
|
|
return value
|
|
}
|
|
value := strings.TrimSpace(extractRawTagValue(inner))
|
|
if value == "" {
|
|
return ""
|
|
}
|
|
|
|
if strings.Contains(value, "<") && strings.Contains(value, ">") {
|
|
if parsed := parseStructuredToolCallInput(value); len(parsed) > 0 {
|
|
if len(parsed) == 1 {
|
|
if raw, ok := parsed["_raw"].(string); ok {
|
|
return raw
|
|
}
|
|
}
|
|
return parsed
|
|
}
|
|
}
|
|
|
|
var jsonValue any
|
|
if json.Unmarshal([]byte(value), &jsonValue) == nil {
|
|
return jsonValue
|
|
}
|
|
return value
|
|
}
|
|
|
|
func appendMarkupValue(out map[string]any, key string, value any) {
|
|
if existing, ok := out[key]; ok {
|
|
switch current := existing.(type) {
|
|
case []any:
|
|
out[key] = append(current, value)
|
|
default:
|
|
out[key] = []any{current, value}
|
|
}
|
|
return
|
|
}
|
|
out[key] = value
|
|
}
|
|
|
|
// extractRawTagValue treats the inner content of a tag robustly.
|
|
// It detects CDATA and strips it, otherwise it unescapes standard HTML entities.
|
|
// It avoids over-aggressive tag stripping that might break user content.
|
|
func extractRawTagValue(inner string) string {
|
|
trimmed := strings.TrimSpace(inner)
|
|
if trimmed == "" {
|
|
return ""
|
|
}
|
|
|
|
// 1. Check for CDATA - if present, it's the ultimate "safe" container.
|
|
if value, ok := extractStandaloneCDATA(trimmed); ok {
|
|
return value // Return raw content between CDATA brackets
|
|
}
|
|
|
|
// 2. If no CDATA, we still want to be robust.
|
|
// We unescape standard HTML entities (like < > &)
|
|
// but we DON'T recursively strip tags unless they are actually valid XML tags
|
|
// at the start/end (which should have been handled by the outer matcher anyway).
|
|
|
|
// If it contains what looks like a single tag and no other text, it might be nested XML
|
|
// but for KV objects we usually want the value.
|
|
return html.UnescapeString(inner)
|
|
}
|
|
|
|
func extractStandaloneCDATA(inner string) (string, bool) {
|
|
trimmed := strings.TrimSpace(inner)
|
|
if cdataMatches := cdataPattern.FindStringSubmatch(trimmed); len(cdataMatches) >= 2 {
|
|
return cdataMatches[1], true
|
|
}
|
|
return "", false
|
|
}
|