From 122be712b790c434ad020072e30b26121bb9cec0 Mon Sep 17 00:00:00 2001 From: "CJACK." Date: Tue, 31 Mar 2026 01:21:14 +0800 Subject: [PATCH] Fix config writeback fallback and tighten content_filter trimming --- internal/config/config_test.go | 33 +++++++++++++++++++++++++++++ internal/config/store.go | 11 ++++++++++ internal/sse/content_filter_leak.go | 26 ++++++++++++++++++++++- internal/sse/line_test.go | 10 +++++++++ 4 files changed, 79 insertions(+), 1 deletion(-) diff --git a/internal/config/config_test.go b/internal/config/config_test.go index 30a2e80..da8b0e2 100644 --- a/internal/config/config_test.go +++ b/internal/config/config_test.go @@ -154,6 +154,39 @@ func TestEnvBackedStoreWritebackDoesNotBootstrapOnInvalidEnvJSON(t *testing.T) { } } +func TestEnvBackedStoreWritebackFallsBackToPersistedFileWhenEnvMalformed(t *testing.T) { + tmp, err := os.CreateTemp(t.TempDir(), "config-*.json") + if err != nil { + t.Fatalf("create temp config: %v", err) + } + path := tmp.Name() + _ = tmp.Close() + + seed := `{"keys":["file-k"],"accounts":[{"email":"file@example.com","password":"p"}]}` + if err := os.WriteFile(path, []byte(seed), 0o644); err != nil { + t.Fatalf("write seed config: %v", err) + } + + t.Setenv("DS2API_CONFIG_JSON", "{invalid-json") + t.Setenv("CONFIG_JSON", "") + t.Setenv("DS2API_CONFIG_PATH", path) + t.Setenv("DS2API_ENV_WRITEBACK", "1") + + cfg, fromEnv, loadErr := loadConfig() + if loadErr == nil { + t.Fatalf("expected loadConfig error for invalid env json") + } + if fromEnv { + t.Fatalf("expected fromEnv=false when persisted config file fallback succeeds") + } + if len(cfg.Keys) != 1 || cfg.Keys[0] != "file-k" { + t.Fatalf("expected keys from persisted file, got %#v", cfg.Keys) + } + if len(cfg.Accounts) != 1 || cfg.Accounts[0].Email != "file@example.com" { + t.Fatalf("expected accounts from persisted file, got %#v", cfg.Accounts) + } +} + func TestRuntimeTokenRefreshIntervalHoursDefaultsToSix(t *testing.T) { t.Setenv("DS2API_CONFIG_JSON", `{ "keys":["k1"], diff --git a/internal/config/store.go b/internal/config/store.go index 9375bcf..e2317b9 100644 --- a/internal/config/store.go +++ b/internal/config/store.go @@ -41,6 +41,17 @@ func loadConfig() (Config, bool, error) { if rawCfg != "" { cfg, err := parseConfigString(rawCfg) if err != nil { + if IsVercel() || !envWritebackEnabled() { + return cfg, true, err + } + content, fileErr := os.ReadFile(ConfigPath()) + if fileErr == nil { + var fileCfg Config + if unmarshalErr := json.Unmarshal(content, &fileCfg); unmarshalErr == nil { + fileCfg.DropInvalidAccounts() + return fileCfg, false, err + } + } return cfg, true, err } cfg.ClearAccountTokens() diff --git a/internal/sse/content_filter_leak.go b/internal/sse/content_filter_leak.go index 87dff7b..2333096 100644 --- a/internal/sse/content_filter_leak.go +++ b/internal/sse/content_filter_leak.go @@ -2,6 +2,13 @@ package sse import "strings" +var leakedContentFilterSuffixPrefixes = []string{ + "你好,这个问题我暂时无法回答", + "您好,这个问题我暂时无法回答", + ",这个问题我暂时无法回答", + ",这个问题我暂时无法回答", +} + func filterLeakedContentFilterParts(parts []ContentPart) []ContentPart { if len(parts) == 0 { return parts @@ -22,9 +29,26 @@ func stripLeakedContentFilterSuffix(text string) string { if text == "" { return text } - idx := strings.Index(strings.ToUpper(text), "CONTENT_FILTER") + upper := strings.ToUpper(text) + idx := strings.Index(upper, "CONTENT_FILTER") if idx < 0 { return text } + suffix := strings.TrimSpace(text[idx+len("CONTENT_FILTER"):]) + if !looksLikeLeakedContentFilterSuffix(suffix) { + return text + } return strings.TrimRight(text[:idx], " \t\r\n") } + +func looksLikeLeakedContentFilterSuffix(suffix string) bool { + if suffix == "" { + return false + } + for _, p := range leakedContentFilterSuffixPrefixes { + if strings.HasPrefix(suffix, p) { + return true + } + } + return false +} diff --git a/internal/sse/line_test.go b/internal/sse/line_test.go index 8e27f73..6b6923b 100644 --- a/internal/sse/line_test.go +++ b/internal/sse/line_test.go @@ -55,3 +55,13 @@ func TestParseDeepSeekContentLineDropsPureLeakedContentFilterChunk(t *testing.T) t.Fatalf("expected empty parts, got %#v", res.Parts) } } + +func TestParseDeepSeekContentLineKeepsLegitContentFilterMentions(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 != "字符串 CONTENT_FILTER 用于说明上游审核信号" { + t.Fatalf("unexpected parts for legit mention: %#v", res.Parts) + } +}