From 4c83f360890b543dd70e338dbbd42f6e07a0862f Mon Sep 17 00:00:00 2001 From: CJACK Date: Sun, 26 Apr 2026 07:31:19 +0800 Subject: [PATCH] =?UTF-8?q?=E5=BC=BA=E5=88=B6=E5=90=AF=E7=94=A8=E6=96=87?= =?UTF-8?q?=E4=BB=B6=E6=8B=86=E5=88=86=EF=BC=88=E5=AE=9E=E9=99=85=E6=A8=A1?= =?UTF-8?q?=E5=9E=8B=E5=BF=BD=E7=95=A5=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- API.en.md | 4 ++-- API.md | 4 ++-- README.MD | 2 +- README.en.md | 2 +- docs/prompt-compatibility.md | 2 +- internal/config/config.go | 9 +++++++++ internal/config/store_accessors.go | 7 +------ internal/config/store_accessors_test.go | 19 +++++++++++++++++-- .../httpapi/admin/handler_settings_test.go | 4 ++-- .../admin/settings/handler_settings_parse.go | 6 ++---- .../httpapi/openai/history/history_split.go | 3 --- .../features/settings/HistorySplitSection.jsx | 13 ++++--------- .../src/features/settings/useSettingsForm.js | 4 ++-- webui/src/locales/en.json | 4 ++-- webui/src/locales/zh.json | 4 ++-- 15 files changed, 48 insertions(+), 39 deletions(-) diff --git a/API.en.md b/API.en.md index ae9ae04..ca1e7a9 100644 --- a/API.en.md +++ b/API.en.md @@ -711,7 +711,7 @@ Reads runtime settings and status, including: - `compat` (`wide_input_strict_output`, `strip_reference_markers`) - `responses` / `embeddings` - `auto_delete` (`mode`: `none` / `single` / `all`; legacy `sessions=true` is still treated as `all`) -- `history_split` (`enabled`, `trigger_after_turns`) +- `history_split` (`enabled` always returns `true`, `trigger_after_turns`) - `model_aliases` - `env_backed`, `needs_vercel_sync` - `toolcall` policy is fixed to `feature_match + high` and is no longer returned or editable via settings @@ -726,7 +726,7 @@ Hot-updates runtime settings. Supported fields: - `responses.store_ttl_seconds` - `embeddings.provider` - `auto_delete.mode` -- `history_split.enabled` / `history_split.trigger_after_turns` +- `history_split.trigger_after_turns` (`history_split.enabled` is forced on globally; legacy client writes are stored as `true`) - `model_aliases` - `toolcall` policy is fixed and is no longer writable through settings diff --git a/API.md b/API.md index 50299cd..35d97d4 100644 --- a/API.md +++ b/API.md @@ -712,7 +712,7 @@ data: {"type":"message_stop"} - `compat`(`wide_input_strict_output`、`strip_reference_markers`) - `responses` / `embeddings` - `auto_delete`(`mode`:`none` / `single` / `all`;旧配置 `sessions=true` 仍按 `all` 处理) -- `history_split`(`enabled`、`trigger_after_turns`) +- `history_split`(`enabled` 固定返回 `true`、`trigger_after_turns`) - `model_aliases` - `env_backed`、`needs_vercel_sync` - `toolcall` 策略已固定为 `feature_match + high`,不再通过 settings 返回或修改 @@ -727,7 +727,7 @@ data: {"type":"message_stop"} - `responses.store_ttl_seconds` - `embeddings.provider` - `auto_delete.mode` -- `history_split.enabled` / `history_split.trigger_after_turns` +- `history_split.trigger_after_turns`(`history_split.enabled` 已全局强制开启;旧客户端传入时会被保存为 `true`) - `model_aliases` - `toolcall` 策略已固定,不再作为可写入字段 diff --git a/README.MD b/README.MD index b874ec0..a525f02 100644 --- a/README.MD +++ b/README.MD @@ -272,7 +272,7 @@ go run ./cmd/ds2api - `model_aliases`:OpenAI / Claude / Gemini 共用的模型 alias 映射。 - `runtime`:账号并发、队列与 token 刷新策略,可通过 Admin Settings 热更新。 - `auto_delete.mode`:请求结束后的远端会话清理策略,支持 `none` / `single` / `all`。 -- `history_split`:多轮历史拆分策略,默认开启,避免长历史全部内联进 prompt。 +- `history_split`:多轮历史拆分策略,已全局强制开启;可调整触发阈值,避免长历史全部内联进 prompt。 环境变量完整列表见 [部署指南](docs/DEPLOY.md),接口鉴权规则见 [API.md](API.md#鉴权规则)。 diff --git a/README.en.md b/README.en.md index 827a3a0..299d228 100644 --- a/README.en.md +++ b/README.en.md @@ -270,7 +270,7 @@ Common fields: - `model_aliases`: one shared alias map for OpenAI / Claude / Gemini model names. - `runtime`: account concurrency, queueing, and token refresh behavior, hot-reloadable via Admin Settings. - `auto_delete.mode`: remote session cleanup after each request, supporting `none` / `single` / `all`. -- `history_split`: multi-turn history split policy, enabled by default to avoid inlining all long history into the prompt. +- `history_split`: multi-turn history split policy, now forced on globally; tune its trigger threshold to avoid inlining all long history into the prompt. For the full environment variable list, see [docs/DEPLOY.en.md](docs/DEPLOY.en.md). For auth behavior, see [API.en.md](API.en.md#authentication). diff --git a/docs/prompt-compatibility.md b/docs/prompt-compatibility.md index 93128f5..26e1ba8 100644 --- a/docs/prompt-compatibility.md +++ b/docs/prompt-compatibility.md @@ -230,7 +230,7 @@ OpenAI 文件相关实现: ## 9. 多轮历史为什么不会一直完整内联在 prompt -默认情况下,history split 是开启的,且默认从第 2 个 user turn 起就可能触发。 +history split 现在全局强制开启;旧配置中的 `history_split.enabled=false` 会被忽略。默认从第 2 个 user turn 起就可能触发,仍可通过 `history_split.trigger_after_turns` 调整触发阈值。 相关实现: diff --git a/internal/config/config.go b/internal/config/config.go index 43856c6..4053798 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -100,6 +100,7 @@ func (c *Config) NormalizeCredentials() { } c.normalizeModelAliases() + c.forceHistorySplitEnabled() } // DropInvalidAccounts removes accounts that cannot be addressed by admin APIs @@ -140,6 +141,14 @@ func (c *Config) normalizeModelAliases() { } } +func (c *Config) forceHistorySplitEnabled() { + if c == nil { + return + } + enabled := true + c.HistorySplit.Enabled = &enabled +} + type CompatConfig struct { WideInputStrictOutput *bool `json:"wide_input_strict_output,omitempty"` StripReferenceMarkers *bool `json:"strip_reference_markers,omitempty"` diff --git a/internal/config/store_accessors.go b/internal/config/store_accessors.go index 6849b85..4b25284 100644 --- a/internal/config/store_accessors.go +++ b/internal/config/store_accessors.go @@ -164,12 +164,7 @@ func (s *Store) AutoDeleteSessions() bool { } func (s *Store) HistorySplitEnabled() bool { - s.mu.RLock() - defer s.mu.RUnlock() - if s.cfg.HistorySplit.Enabled == nil { - return true - } - return *s.cfg.HistorySplit.Enabled + return true } func (s *Store) HistorySplitTriggerAfterTurns() int { diff --git a/internal/config/store_accessors_test.go b/internal/config/store_accessors_test.go index 6939602..af197ce 100644 --- a/internal/config/store_accessors_test.go +++ b/internal/config/store_accessors_test.go @@ -18,10 +18,25 @@ func TestStoreHistorySplitAccessors(t *testing.T) { TriggerAfterTurns: &turns, } - if store.HistorySplitEnabled() { - t.Fatal("expected history split disabled after override") + if !store.HistorySplitEnabled() { + t.Fatal("expected history split to stay enabled after legacy disabled override") } if got := store.HistorySplitTriggerAfterTurns(); got != 3 { t.Fatalf("history split trigger_after_turns=%d want=3", got) } } + +func TestStoreHistorySplitLegacyDisabledConfigNormalizesToEnabled(t *testing.T) { + t.Setenv("DS2API_CONFIG_JSON", `{"keys":["k1"],"history_split":{"enabled":false,"trigger_after_turns":2}}`) + store := LoadStore() + if !store.HistorySplitEnabled() { + t.Fatal("expected history split enabled when legacy config disables it") + } + snap := store.Snapshot() + if snap.HistorySplit.Enabled == nil || !*snap.HistorySplit.Enabled { + t.Fatalf("expected normalized history_split.enabled=true, got %#v", snap.HistorySplit.Enabled) + } + if got := store.HistorySplitTriggerAfterTurns(); got != 2 { + t.Fatalf("history split trigger_after_turns=%d want=2", got) + } +} diff --git a/internal/httpapi/admin/handler_settings_test.go b/internal/httpapi/admin/handler_settings_test.go index e231739..aefc1bd 100644 --- a/internal/httpapi/admin/handler_settings_test.go +++ b/internal/httpapi/admin/handler_settings_test.go @@ -189,8 +189,8 @@ func TestUpdateSettingsHistorySplit(t *testing.T) { t.Fatalf("expected 200, got %d body=%s", rec.Code, rec.Body.String()) } snap := h.Store.Snapshot() - if snap.HistorySplit.Enabled == nil || *snap.HistorySplit.Enabled { - t.Fatalf("expected history_split.enabled=false, got %#v", snap.HistorySplit.Enabled) + if snap.HistorySplit.Enabled == nil || !*snap.HistorySplit.Enabled { + t.Fatalf("expected history_split.enabled to be forced true, got %#v", snap.HistorySplit.Enabled) } if snap.HistorySplit.TriggerAfterTurns == nil || *snap.HistorySplit.TriggerAfterTurns != 3 { t.Fatalf("expected history_split.trigger_after_turns=3, got %#v", snap.HistorySplit.TriggerAfterTurns) diff --git a/internal/httpapi/admin/settings/handler_settings_parse.go b/internal/httpapi/admin/settings/handler_settings_parse.go index 53507f3..14fb92d 100644 --- a/internal/httpapi/admin/settings/handler_settings_parse.go +++ b/internal/httpapi/admin/settings/handler_settings_parse.go @@ -152,10 +152,8 @@ func parseSettingsUpdateRequest(req map[string]any) (*config.AdminConfig, *confi if raw, ok := req["history_split"].(map[string]any); ok { cfg := &config.HistorySplitConfig{} - if v, exists := raw["enabled"]; exists { - b := boolFrom(v) - cfg.Enabled = &b - } + enabled := true + cfg.Enabled = &enabled if v, exists := raw["trigger_after_turns"]; exists { n := intFrom(v) if err := config.ValidateIntRange("history_split.trigger_after_turns", n, 1, 1000, true); err != nil { diff --git a/internal/httpapi/openai/history/history_split.go b/internal/httpapi/openai/history/history_split.go index c92cd69..96775ef 100644 --- a/internal/httpapi/openai/history/history_split.go +++ b/internal/httpapi/openai/history/history_split.go @@ -27,9 +27,6 @@ func (s Service) Apply(ctx context.Context, a *auth.RequestAuth, stdReq promptco if s.DS == nil || s.Store == nil || a == nil { return stdReq, nil } - if !s.Store.HistorySplitEnabled() { - return stdReq, nil - } promptMessages, historyMessages := SplitOpenAIHistoryMessages(stdReq.Messages, s.Store.HistorySplitTriggerAfterTurns()) if len(historyMessages) == 0 { diff --git a/webui/src/features/settings/HistorySplitSection.jsx b/webui/src/features/settings/HistorySplitSection.jsx index d9db63c..242d687 100644 --- a/webui/src/features/settings/HistorySplitSection.jsx +++ b/webui/src/features/settings/HistorySplitSection.jsx @@ -9,15 +9,10 @@ export default function HistorySplitSection({ t, form, setForm }) {