From b54b418f96378febb122b17999a2767fa0af8eec Mon Sep 17 00:00:00 2001 From: "CJACK." Date: Mon, 30 Mar 2026 23:39:47 +0800 Subject: [PATCH] fix(sse): globally strip leaked CONTENT_FILTER suffix from output --- internal/sse/content_filter_leak.go | 30 +++++++++++++++++++++++++++++ internal/sse/line.go | 1 + internal/sse/line_test.go | 20 +++++++++++++++++++ 3 files changed, 51 insertions(+) create mode 100644 internal/sse/content_filter_leak.go diff --git a/internal/sse/content_filter_leak.go b/internal/sse/content_filter_leak.go new file mode 100644 index 0000000..87dff7b --- /dev/null +++ b/internal/sse/content_filter_leak.go @@ -0,0 +1,30 @@ +package sse + +import "strings" + +func filterLeakedContentFilterParts(parts []ContentPart) []ContentPart { + if len(parts) == 0 { + return parts + } + out := make([]ContentPart, 0, len(parts)) + for _, p := range parts { + cleaned := stripLeakedContentFilterSuffix(p.Text) + if strings.TrimSpace(cleaned) == "" { + continue + } + p.Text = cleaned + out = append(out, p) + } + return out +} + +func stripLeakedContentFilterSuffix(text string) string { + if text == "" { + return text + } + idx := strings.Index(strings.ToUpper(text), "CONTENT_FILTER") + if idx < 0 { + return text + } + return strings.TrimRight(text[:idx], " \t\r\n") +} diff --git a/internal/sse/line.go b/internal/sse/line.go index b71b2b0..e63f378 100644 --- a/internal/sse/line.go +++ b/internal/sse/line.go @@ -40,6 +40,7 @@ func ParseDeepSeekContentLine(raw []byte, thinkingEnabled bool, currentType stri } } parts, finished, nextType := ParseSSEChunkForContent(chunk, thinkingEnabled, currentType) + parts = filterLeakedContentFilterParts(parts) return LineResult{ Parsed: true, Stop: finished, diff --git a/internal/sse/line_test.go b/internal/sse/line_test.go index 3292a54..8e27f73 100644 --- a/internal/sse/line_test.go +++ b/internal/sse/line_test.go @@ -35,3 +35,23 @@ func TestParseDeepSeekContentLineContent(t *testing.T) { t.Fatalf("unexpected parts: %#v", res.Parts) } } + +func TestParseDeepSeekContentLineStripsLeakedContentFilterSuffix(t *testing.T) { + res := ParseDeepSeekContentLine([]byte(`data: {"p":"response/content","v":"正常输出CONTENT_FILTER你好,这个问题我暂时无法回答"}`), false, "text") + if !res.Parsed || res.Stop { + t.Fatalf("expected parsed non-stop result: %#v", res) + } + if len(res.Parts) != 1 || res.Parts[0].Text != "正常输出" { + t.Fatalf("unexpected parts after filter: %#v", res.Parts) + } +} + +func TestParseDeepSeekContentLineDropsPureLeakedContentFilterChunk(t *testing.T) { + res := ParseDeepSeekContentLine([]byte(`data: {"p":"response/content","v":"CONTENT_FILTER你好,这个问题我暂时无法回答"}`), false, "text") + if !res.Parsed || res.Stop { + t.Fatalf("expected parsed non-stop result: %#v", res) + } + if len(res.Parts) != 0 { + t.Fatalf("expected empty parts, got %#v", res.Parts) + } +}