feat: Improve tool sieve to correctly preserve trailing text within the same chunk as a tool call.

This commit is contained in:
CJACK
2026-02-18 17:24:43 +08:00
parent ce74b124d2
commit 7fc10573ab
4 changed files with 55 additions and 2 deletions

View File

@@ -220,7 +220,7 @@ function consumeToolCapture(state, toolNames) {
}
const prefixPart = captured.slice(0, start);
const suffixPart = captured.slice(obj.end);
if (!state.toolNameSent && (hasMeaningfulText(prefixPart) || hasMeaningfulText(suffixPart) || looksLikeToolExampleContext(state.recentTextTail))) {
if (!state.toolNameSent && (hasMeaningfulText(prefixPart) || looksLikeToolExampleContext(state.recentTextTail) || looksLikeToolExampleContext(suffixPart))) {
return {
ready: true,
prefix: captured,

View File

@@ -183,3 +183,15 @@ test('sieve still intercepts tool call after leading plain text without suffix',
assert.equal(leakedText.includes('我将调用工具。'), true);
assert.equal(leakedText.toLowerCase().includes('tool_calls'), false);
});
test('sieve intercepts tool call and preserves trailing same-chunk text', () => {
const events = runSieve(
['{"tool_calls":[{"name":"read_file","input":{"path":"README.MD"}}]}然后继续解释。'],
['read_file'],
);
const hasTool = events.some((evt) => (evt.type === 'tool_calls' && evt.calls?.length > 0) || (evt.type === 'tool_call_deltas' && evt.deltas?.length > 0));
const leakedText = collectText(events);
assert.equal(hasTool, true);
assert.equal(leakedText.includes('然后继续解释。'), true);
assert.equal(leakedText.toLowerCase().includes('tool_calls'), false);
});