mirror of
https://github.com/CJackHwang/ds2api.git
synced 2026-05-04 00:15:28 +08:00
强制启用文件拆分(实际模型忽略)
This commit is contained in:
@@ -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
|
||||
|
||||
|
||||
4
API.md
4
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` 策略已固定,不再作为可写入字段
|
||||
|
||||
|
||||
@@ -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#鉴权规则)。
|
||||
|
||||
|
||||
@@ -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).
|
||||
|
||||
|
||||
@@ -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` 调整触发阈值。
|
||||
|
||||
相关实现:
|
||||
|
||||
|
||||
@@ -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"`
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -9,15 +9,10 @@ export default function HistorySplitSection({ t, form, setForm }) {
|
||||
<label className="flex items-start gap-3 rounded-lg border border-border bg-background/60 p-4">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={Boolean(form.history_split?.enabled ?? true)}
|
||||
onChange={(e) => setForm((prev) => ({
|
||||
...prev,
|
||||
history_split: {
|
||||
...prev.history_split,
|
||||
enabled: e.target.checked,
|
||||
},
|
||||
}))}
|
||||
className="mt-1 h-4 w-4 rounded border-border"
|
||||
checked
|
||||
disabled
|
||||
readOnly
|
||||
className="mt-1 h-4 w-4 rounded border-border disabled:opacity-70"
|
||||
/>
|
||||
<div className="space-y-1">
|
||||
<span className="text-sm font-medium block">{t('settings.historySplitEnabled')}</span>
|
||||
|
||||
@@ -71,7 +71,7 @@ function fromServerForm(data) {
|
||||
mode: normalizeAutoDeleteMode(data.auto_delete),
|
||||
},
|
||||
history_split: {
|
||||
enabled: data.history_split?.enabled ?? true,
|
||||
enabled: true,
|
||||
trigger_after_turns: Number(data.history_split?.trigger_after_turns || 1),
|
||||
},
|
||||
model_aliases_text: JSON.stringify(data.model_aliases || {}, null, 2),
|
||||
@@ -94,7 +94,7 @@ function toServerPayload(form) {
|
||||
embeddings: { provider: String(form.embeddings.provider || '').trim() },
|
||||
auto_delete: { mode: normalizeAutoDeleteMode(form.auto_delete) },
|
||||
history_split: {
|
||||
enabled: Boolean(form.history_split?.enabled ?? true),
|
||||
enabled: true,
|
||||
trigger_after_turns: Number(form.history_split?.trigger_after_turns || 1),
|
||||
},
|
||||
}
|
||||
|
||||
@@ -375,8 +375,8 @@
|
||||
"embeddingsProvider": "Embeddings provider",
|
||||
"historySplitTitle": "History Split",
|
||||
"historySplitDesc": "Pack earlier turns into an attached HISTORY.txt so the model reads the file first and then continues from the latest user request.",
|
||||
"historySplitEnabled": "Enable history split",
|
||||
"historySplitEnabledDesc": "Enabled by default. Turning this off falls back to normal full-context requests.",
|
||||
"historySplitEnabled": "History split is forced on",
|
||||
"historySplitEnabledDesc": "This capability is now enabled globally; legacy disabled values are ignored.",
|
||||
"historySplitTriggerAfterTurns": "Trigger threshold (user turns)",
|
||||
"historySplitTriggerHelp": "Default is 1, which means history split starts from the second turn.",
|
||||
"compatibilityTitle": "Compatibility",
|
||||
|
||||
@@ -375,8 +375,8 @@
|
||||
"embeddingsProvider": "Embeddings Provider",
|
||||
"historySplitTitle": "历史拆分",
|
||||
"historySplitDesc": "将更早的对话整理成 HISTORY.txt 上传,让模型优先读取历史文件,再结合最新一轮继续回答。",
|
||||
"historySplitEnabled": "启用历史拆分",
|
||||
"historySplitEnabledDesc": "默认开启。关闭后会恢复为普通的完整上下文提交。",
|
||||
"historySplitEnabled": "历史拆分已强制启用",
|
||||
"historySplitEnabledDesc": "该能力现在全局开启;旧配置里的关闭值会被忽略。",
|
||||
"historySplitTriggerAfterTurns": "触发阈值(用户回合数)",
|
||||
"historySplitTriggerHelp": "默认值为 1,表示从第二轮开始拆分历史。",
|
||||
"compatibilityTitle": "兼容性设置",
|
||||
|
||||
Reference in New Issue
Block a user