mirror of
https://github.com/CJackHwang/ds2api.git
synced 2026-05-05 00:45:29 +08:00
Expand history-sanitize boundary coverage for stream chunks
This commit is contained in:
@@ -11,10 +11,35 @@ func TestSanitizeLeakedToolHistoryRemovesMarkerBlocks(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestSanitizeLeakedToolHistoryPreservesChunkWhitespace(t *testing.T) {
|
||||
raw := "Hello "
|
||||
got := sanitizeLeakedToolHistory(raw)
|
||||
if got != "Hello " {
|
||||
t.Fatalf("expected trailing whitespace to be preserved, got %q", got)
|
||||
cases := []struct {
|
||||
name string
|
||||
raw string
|
||||
want string
|
||||
}{
|
||||
{
|
||||
name: "trailing space kept",
|
||||
raw: "Hello ",
|
||||
want: "Hello ",
|
||||
},
|
||||
{
|
||||
name: "leading newline kept",
|
||||
raw: "\nworld",
|
||||
want: "\nworld",
|
||||
},
|
||||
{
|
||||
name: "surrounding whitespace around marker is preserved",
|
||||
raw: "A \n[TOOL_RESULT_HISTORY]\nfunction.name: exec\nfunction.arguments: {}\n[/TOOL_RESULT_HISTORY]\n B",
|
||||
want: "A \n\n B",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
got := sanitizeLeakedToolHistory(tc.raw)
|
||||
if got != tc.want {
|
||||
t.Fatalf("unexpected sanitize result, want %q got %q", tc.want, got)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,3 +68,31 @@ func TestFlushToolSieveDropsToolResultHistoryLeak(t *testing.T) {
|
||||
t.Fatalf("expected result history block to be swallowed, got %+v", flushed)
|
||||
}
|
||||
}
|
||||
|
||||
func TestProcessToolSieveChunkSplitsResultHistoryBoundary(t *testing.T) {
|
||||
var state toolStreamSieveState
|
||||
parts := []string{
|
||||
"Hello ",
|
||||
"[TOOL_RESULT_HISTORY]\nstatus: already_called\n",
|
||||
"function.name: exec\nfunction.arguments: {}\n[/TOOL_RESULT_HISTORY]",
|
||||
"world",
|
||||
}
|
||||
var events []toolStreamEvent
|
||||
for _, p := range parts {
|
||||
events = append(events, processToolSieveChunk(&state, p, []string{"exec"})...)
|
||||
}
|
||||
events = append(events, flushToolSieve(&state, []string{"exec"})...)
|
||||
|
||||
var text string
|
||||
for _, evt := range events {
|
||||
if evt.Content != "" {
|
||||
text += evt.Content
|
||||
}
|
||||
if len(evt.ToolCalls) > 0 {
|
||||
t.Fatalf("did not expect parsed tool calls from history leak: %+v", evt.ToolCalls)
|
||||
}
|
||||
}
|
||||
if text != "Hello world" {
|
||||
t.Fatalf("expected clean text output preserving boundary spaces, got %q", text)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -260,6 +260,22 @@ test('sieve swallows leaked TOOL_RESULT_HISTORY marker blocks', () => {
|
||||
assert.equal(leakedText.includes('[TOOL_RESULT_HISTORY]'), false);
|
||||
});
|
||||
|
||||
test('sieve preserves text spacing when TOOL_RESULT_HISTORY spans chunks', () => {
|
||||
const events = runSieve(
|
||||
[
|
||||
'Hello ',
|
||||
'[TOOL_RESULT_HISTORY]\nstatus: already_called\n',
|
||||
'function.name: exec\nfunction.arguments: {}\n[/TOOL_RESULT_HISTORY]',
|
||||
'world',
|
||||
],
|
||||
['exec'],
|
||||
);
|
||||
const leakedText = collectText(events);
|
||||
const hasToolCall = events.some((evt) => evt.type === 'tool_calls' && evt.calls?.length > 0);
|
||||
assert.equal(hasToolCall, false);
|
||||
assert.equal(leakedText, 'Hello world');
|
||||
});
|
||||
|
||||
test('sieve intercepts rejected unknown tool payload (no args) without raw leak', () => {
|
||||
const events = runSieve(
|
||||
['{"tool_calls":[{"name":"not_in_schema"}]}', '后置正文G。'],
|
||||
|
||||
Reference in New Issue
Block a user