Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions admin/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -9262,6 +9262,7 @@ type settingsResponse struct {
CodexWSBusyOverflowEnabled bool `json:"codex_ws_busy_overflow_enabled"`
CodexWSBusyPatienceSec int `json:"codex_ws_busy_patience_sec"`
CodexWSStatelessSlots int `json:"codex_ws_stateless_slots"`
CodexWSDownstreamKeepaliveSlots int `json:"codex_ws_downstream_keepalive_slots"`
GithubTokenConfigured bool `json:"github_token_configured"`
GithubProxyURL string `json:"github_proxy_url"`
CodexOverloadPauseEnabled bool `json:"codex_overload_pause_enabled"`
Expand Down Expand Up @@ -9457,6 +9458,7 @@ type updateSettingsReq struct {
CodexWSBusyOverflowEnabled *bool `json:"codex_ws_busy_overflow_enabled"`
CodexWSBusyPatienceSec *int `json:"codex_ws_busy_patience_sec"`
CodexWSStatelessSlots *int `json:"codex_ws_stateless_slots"`
CodexWSDownstreamKeepaliveSlots *int `json:"codex_ws_downstream_keepalive_slots"`
GithubToken *string `json:"github_token"`
GithubProxyURL *string `json:"github_proxy_url"`
CodexOverloadPauseEnabled *bool `json:"codex_overload_pause_enabled"`
Expand Down Expand Up @@ -10294,6 +10296,7 @@ func (h *Handler) GetSettings(c *gin.Context) {
CodexWSBusyOverflowEnabled: h.store.CodexWSBusyOverflowEnabled(),
CodexWSBusyPatienceSec: h.store.CodexWSBusyPatienceSec(),
CodexWSStatelessSlots: h.store.CodexWSStatelessSlots(),
CodexWSDownstreamKeepaliveSlots: h.store.CodexWSDownstreamKeepaliveSlots(),
GithubTokenConfigured: h.store.GithubToken() != "",
GithubProxyURL: h.store.GithubProxyURL(),
CodexOverloadPauseEnabled: runtimeCfg.CodexOverloadPauseEnabled,
Expand Down Expand Up @@ -11101,6 +11104,13 @@ func (h *Handler) UpdateSettings(c *gin.Context) {
log.Printf("设置已更新: codex_ws_stateless_slots = %d", v)
}

if req.CodexWSDownstreamKeepaliveSlots != nil {
v := database.NormalizeCodexWSDownstreamKeepaliveSlots(*req.CodexWSDownstreamKeepaliveSlots)
h.store.SetCodexWSDownstreamKeepaliveSlots(v)
runtimeCfg.CodexWSDownstreamKeepaliveSlots = v
log.Printf("设置已更新: codex_ws_downstream_keepalive_slots = %d", v)
}

// GitHub 访问设置(issue #522)。token 不回显也不落日志,空串表示清除。
if req.GithubToken != nil {
v := strings.TrimSpace(*req.GithubToken)
Expand Down Expand Up @@ -11840,6 +11850,7 @@ func (h *Handler) UpdateSettings(c *gin.Context) {
CodexWSBusyOverflowEnabled: h.store.CodexWSBusyOverflowEnabled(),
CodexWSBusyPatienceSec: h.store.CodexWSBusyPatienceSec(),
CodexWSStatelessSlots: h.store.CodexWSStatelessSlots(),
CodexWSDownstreamKeepaliveSlots: h.store.CodexWSDownstreamKeepaliveSlots(),
GithubToken: h.store.GithubToken(),
GithubProxyURL: h.store.GithubProxyURL(),
CodexOverloadPauseEnabled: runtimeCfg.CodexOverloadPauseEnabled,
Expand Down Expand Up @@ -12177,6 +12188,7 @@ func (h *Handler) UpdateSettings(c *gin.Context) {
CodexWSBusyOverflowEnabled: h.store.CodexWSBusyOverflowEnabled(),
CodexWSBusyPatienceSec: h.store.CodexWSBusyPatienceSec(),
CodexWSStatelessSlots: h.store.CodexWSStatelessSlots(),
CodexWSDownstreamKeepaliveSlots: h.store.CodexWSDownstreamKeepaliveSlots(),
GithubTokenConfigured: h.store.GithubToken() != "",
GithubProxyURL: h.store.GithubProxyURL(),
CodexOverloadPauseEnabled: runtimeCfg.CodexOverloadPauseEnabled,
Expand Down
18 changes: 18 additions & 0 deletions auth/codex_ws_settings.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package auth

import "github.com/codex2api/database"

// SetCodexWSDownstreamKeepaliveSlots 设置每账号独立的空闲聊天连接保留上限。
func (s *Store) SetCodexWSDownstreamKeepaliveSlots(slots int) {
if s != nil {
s.codexWSDownstreamKeepaliveSlots.Store(int64(database.NormalizeCodexWSDownstreamKeepaliveSlots(slots)))
}
}

// CodexWSDownstreamKeepaliveSlots 返回每账号独立的空闲聊天连接保留上限。
func (s *Store) CodexWSDownstreamKeepaliveSlots() int {
if s == nil {
return database.DefaultCodexWSDownstreamKeepaliveSlots
}
return database.NormalizeCodexWSDownstreamKeepaliveSlots(int(s.codexWSDownstreamKeepaliveSlots.Load()))
}
35 changes: 19 additions & 16 deletions auth/store.go
Original file line number Diff line number Diff line change
Expand Up @@ -3611,20 +3611,21 @@ type Store struct {
codexForceWebsocket atomic.Bool // 强制 Codex 上游走 WebSocket(复用连接池)
// codexRequestCompression HTTP /responses 请求体 zstd 压缩,默认开启(对齐真实客户端)。
// 与上面几项 WS 设置正交:WS 走 permessage-deflate,本项只作用于 HTTP 路径。
codexRequestCompression atomic.Bool
codexWSKeepaliveEnabled atomic.Bool // 启用上游 WS 空闲连接保活(仅 Ping)
codexWSKeepaliveIntervalSec atomic.Int64 // WS 保活 Ping 间隔(秒),默认 60
codexWSHideUpstreamErrors atomic.Bool // 隐藏上游 WS 原始错误,默认开启
codexWSSilentRetryEnabled atomic.Bool // 首包前上游 WS 错误静默换号重试,默认开启
codexWSSilentMaxRetries atomic.Int64 // WS 静默换号最大重试次数,默认 2
codexWSSizeRouterEnabled atomic.Bool // 1009 自学习体积路由,默认开启
codexWSBusyMaxWaitSec atomic.Int64 // busy session 等待上限(秒),默认 30(issue #413)
codexWSBusyOverflowEnabled atomic.Bool // busy session 溢出到同账号兄弟连接,默认关闭
codexWSBusyPatienceSec atomic.Int64 // 触发溢出前的短等待(秒),默认 2
codexWSStatelessSlots atomic.Int64 // 无状态请求每 (账号, cacheKey) 的连接槽位数,默认 8(issue #522)
overflowAutoCompactEnabled atomic.Bool // 上下文超窗自动摘要重试(实验性,默认关闭,issue #415)
compactViaResponsesEnabled atomic.Bool // /v1/responses/compact 改写为 /responses body-signal 压缩,默认关闭
firstTokenExcludesWsAcquire atomic.Bool // 落库 first_token_ms 扣除 WS 取连耗时,默认关闭
codexRequestCompression atomic.Bool
codexWSKeepaliveEnabled atomic.Bool // 启用上游 WS 空闲连接保活(仅 Ping)
codexWSKeepaliveIntervalSec atomic.Int64 // WS 保活 Ping 间隔(秒),默认 60
codexWSHideUpstreamErrors atomic.Bool // 隐藏上游 WS 原始错误,默认开启
codexWSSilentRetryEnabled atomic.Bool // 首包前上游 WS 错误静默换号重试,默认开启
codexWSSilentMaxRetries atomic.Int64 // WS 静默换号最大重试次数,默认 2
codexWSSizeRouterEnabled atomic.Bool // 1009 自学习体积路由,默认开启
codexWSBusyMaxWaitSec atomic.Int64 // busy session 等待上限(秒),默认 30(issue #413)
codexWSBusyOverflowEnabled atomic.Bool // busy session 溢出到同账号兄弟连接,默认关闭
codexWSBusyPatienceSec atomic.Int64 // 触发溢出前的短等待(秒),默认 2
codexWSStatelessSlots atomic.Int64 // 每账号空白上游 WS 槽位预算,默认 8
codexWSDownstreamKeepaliveSlots atomic.Int64
overflowAutoCompactEnabled atomic.Bool // 上下文超窗自动摘要重试(实验性,默认关闭,issue #415)
compactViaResponsesEnabled atomic.Bool // /v1/responses/compact 改写为 /responses body-signal 压缩,默认关闭
firstTokenExcludesWsAcquire atomic.Bool // 落库 first_token_ms 扣除 WS 取连耗时,默认关闭

// 前置元数据 SSE 事件立即透传下游(旧版兼容,默认关闭,issue #425)
codexPreflightSSEPassthroughEnabled atomic.Bool
Expand Down Expand Up @@ -4212,6 +4213,7 @@ func NewStore(db *database.DB, tc cache.TokenCache, settings *database.SystemSet
CodexWSBusyAcquireMaxWaitSec: 30,
CodexWSBusyPatienceSec: 2,
CodexWSStatelessSlots: 8,
CodexWSDownstreamKeepaliveSlots: database.DefaultCodexWSDownstreamKeepaliveSlots,
CodexContinueMaxRounds: 8,
AutoPause5hGuardBandPercent: defaultAutoPause5hGuardBandPercent,
AutoPause5hGuardConcurrency: defaultAutoPause5hGuardConcurrency,
Expand Down Expand Up @@ -4332,6 +4334,7 @@ func NewStore(db *database.DB, tc cache.TokenCache, settings *database.SystemSet
s.codexWSBusyOverflowEnabled.Store(settings.CodexWSBusyOverflowEnabled)
s.codexWSBusyPatienceSec.Store(int64(database.NormalizeCodexWSBusyPatienceSec(settings.CodexWSBusyPatienceSec)))
s.codexWSStatelessSlots.Store(int64(database.NormalizeCodexWSStatelessSlots(settings.CodexWSStatelessSlots)))
s.SetCodexWSDownstreamKeepaliveSlots(settings.CodexWSDownstreamKeepaliveSlots)
s.overflowAutoCompactEnabled.Store(settings.OverflowAutoCompactEnabled)
s.compactViaResponsesEnabled.Store(settings.CompactViaResponsesEnabled)
s.codexPreflightSSEPassthroughEnabled.Store(settings.CodexPreflightSSEPassthroughEnabled)
Expand Down Expand Up @@ -4780,15 +4783,15 @@ func (s *Store) GithubProxyURL() string {
return ""
}

// SetCodexWSStatelessSlots 设置无状态请求每 (账号, cacheKey) 的连接槽位数。
// SetCodexWSStatelessSlots 设置每个账号的空白上游 WS 连接槽位预算。
func (s *Store) SetCodexWSStatelessSlots(slots int) {
if s == nil {
return
}
s.codexWSStatelessSlots.Store(int64(database.NormalizeCodexWSStatelessSlots(slots)))
}

// CodexWSStatelessSlots 返回无状态请求每 (账号, cacheKey) 的连接槽位数。
// CodexWSStatelessSlots 返回每个账号的空白上游 WS 连接槽位预算。
func (s *Store) CodexWSStatelessSlots() int {
if s == nil {
return 8
Expand Down
5 changes: 4 additions & 1 deletion cache/auth_invalidation.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ type BoundedRuntimeReader interface {
GetRuntimeBounded(context.Context, string, string, int64) ([]byte, bool, error)
}

// ErrRuntimeSnapshotTooLarge 表示后端值在反序列化前超过读取上限。
var ErrRuntimeSnapshotTooLarge = errors.New("runtime snapshot exceeds byte limit")

type AuthSnapshotWriter interface {
SetAuthSnapshot(context.Context, string, string, json.RawMessage, time.Duration) error
}
Expand Down Expand Up @@ -73,7 +76,7 @@ func (tc *redisTokenCache) GetRuntimeBounded(ctx context.Context, namespace, key
return nil, false, err
}
if int64(len(raw)) > maxBytes {
return nil, false, errors.New("runtime snapshot exceeds byte limit")
return nil, false, ErrRuntimeSnapshotTooLarge
}
return raw, len(raw) > 0, nil
}
Expand Down
17 changes: 17 additions & 0 deletions database/codex_ws_settings.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package database

const (
DefaultCodexWSDownstreamKeepaliveSlots = 8
MaxCodexWSDownstreamKeepaliveSlots = 32
)

// NormalizeCodexWSDownstreamKeepaliveSlots 归一化空闲聊天连接保留上限,0 表示不保留。
func NormalizeCodexWSDownstreamKeepaliveSlots(slots int) int {
if slots < 0 {
return DefaultCodexWSDownstreamKeepaliveSlots
}
if slots > MaxCodexWSDownstreamKeepaliveSlots {
return MaxCodexWSDownstreamKeepaliveSlots
}
return slots
}
Loading
Loading