diff --git a/internal/adapter/openai/tool_history_sanitize.go b/internal/adapter/openai/tool_history_sanitize.go index 6a2e80a..126414a 100644 --- a/internal/adapter/openai/tool_history_sanitize.go +++ b/internal/adapter/openai/tool_history_sanitize.go @@ -5,10 +5,13 @@ import ( ) var leakedToolHistoryPattern = regexp.MustCompile(`(?is)\[TOOL_CALL_HISTORY\][\s\S]*?\[/TOOL_CALL_HISTORY\]|\[TOOL_RESULT_HISTORY\][\s\S]*?\[/TOOL_RESULT_HISTORY\]`) +var emptyJSONFencePattern = regexp.MustCompile("(?is)```json\\s*```") func sanitizeLeakedToolHistory(text string) string { if text == "" { return text } - return leakedToolHistoryPattern.ReplaceAllString(text, "") + out := leakedToolHistoryPattern.ReplaceAllString(text, "") + out = emptyJSONFencePattern.ReplaceAllString(out, "") + return out } diff --git a/internal/adapter/openai/tool_history_sanitize_test.go b/internal/adapter/openai/tool_history_sanitize_test.go index 02128c9..7c10ad2 100644 --- a/internal/adapter/openai/tool_history_sanitize_test.go +++ b/internal/adapter/openai/tool_history_sanitize_test.go @@ -43,6 +43,14 @@ func TestSanitizeLeakedToolHistoryPreservesChunkWhitespace(t *testing.T) { } } +func TestSanitizeLeakedToolHistoryRemovesEmptyJSONFence(t *testing.T) { + raw := "before\n```json\n```\nafter" + got := sanitizeLeakedToolHistory(raw) + if got != "before\n\nafter" { + t.Fatalf("unexpected sanitized empty json fence: %q", got) + } +} + func TestFlushToolSieveDropsToolHistoryLeak(t *testing.T) { var state toolStreamSieveState chunk := "[TOOL_CALL_HISTORY]\nstatus: already_called\nfunction.name: exec\nfunction.arguments: {}\n[/TOOL_CALL_HISTORY]"