From eeccc967f534a885e5cd43b16ca26c5d2f11d768 Mon Sep 17 00:00:00 2001 From: CJACK Date: Tue, 17 Feb 2026 01:02:21 +0800 Subject: [PATCH] feat: Improve tool detection by implementing a new content splitting strategy that identifies suspicious prefixes. --- api/chat-stream.js | 35 +++++++++++++++++++++++++----- internal/adapter/openai/handler.go | 33 +++++++++++++++++++++++----- 2 files changed, 57 insertions(+), 11 deletions(-) diff --git a/api/chat-stream.js b/api/chat-stream.js index 8879a47..93cb05f 100644 --- a/api/chat-stream.js +++ b/api/chat-stream.js @@ -751,7 +751,7 @@ function processToolSieveChunk(state, chunk, toolNames) { continue; } - const [safe, hold] = splitSafeContent(state.pending, 64); + const [safe, hold] = splitSafeContentForToolDetection(state.pending); if (!safe) { break; } @@ -791,12 +791,35 @@ function flushToolSieve(state, toolNames) { return events; } -function splitSafeContent(s, holdChars) { - const chars = Array.from(s || ''); - if (chars.length <= holdChars) { - return ['', s]; +function splitSafeContentForToolDetection(s) { + const text = s || ''; + if (!text) { + return ['', '']; } - return [chars.slice(0, chars.length - holdChars).join(''), chars.slice(chars.length - holdChars).join('')]; + const suspiciousStart = findSuspiciousPrefixStart(text); + if (suspiciousStart < 0) { + return [text, '']; + } + if (suspiciousStart > 0) { + return [text.slice(0, suspiciousStart), text.slice(suspiciousStart)]; + } + const chars = Array.from(text); + const maxHold = 128; + if (chars.length <= maxHold) { + return ['', text]; + } + return [chars.slice(0, chars.length - maxHold).join(''), chars.slice(chars.length - maxHold).join('')]; +} + +function findSuspiciousPrefixStart(s) { + let start = -1; + for (const needle of ['{', '[', '```']) { + const idx = s.lastIndexOf(needle); + if (idx > start) { + start = idx; + } + } + return start; } function findToolSegmentStart(s) { diff --git a/internal/adapter/openai/handler.go b/internal/adapter/openai/handler.go index 78e9164..e9d4bde 100644 --- a/internal/adapter/openai/handler.go +++ b/internal/adapter/openai/handler.go @@ -810,7 +810,7 @@ func processToolSieveChunk(state *toolStreamSieveState, chunk string, toolNames continue } - safe, hold := splitSafeContent(pending, 64) + safe, hold := splitSafeContentForToolDetection(pending) if safe == "" { break } @@ -842,15 +842,38 @@ func flushToolSieve(state *toolStreamSieveState, toolNames []string) []toolStrea return events } -func splitSafeContent(s string, holdRunes int) (safe, hold string) { - if s == "" || holdRunes <= 0 { +func splitSafeContentForToolDetection(s string) (safe, hold string) { + if s == "" { + return "", "" + } + suspiciousStart := findSuspiciousPrefixStart(s) + if suspiciousStart < 0 { return s, "" } + if suspiciousStart > 0 { + return s[:suspiciousStart], s[suspiciousStart:] + } runes := []rune(s) - if len(runes) <= holdRunes { + const maxHold = 128 + if len(runes) <= maxHold { return "", s } - return string(runes[:len(runes)-holdRunes]), string(runes[len(runes)-holdRunes:]) + return string(runes[:len(runes)-maxHold]), string(runes[len(runes)-maxHold:]) +} + +func findSuspiciousPrefixStart(s string) int { + start := -1 + indices := []int{ + strings.LastIndex(s, "{"), + strings.LastIndex(s, "["), + strings.LastIndex(s, "```"), + } + for _, idx := range indices { + if idx > start { + start = idx + } + } + return start } func findToolSegmentStart(s string) int {