2697 Commits

Author SHA1 Message Date
xlx0852
08e19bb15c fix(openai): bridge oversized websocket requests 2026-06-01 10:42:55 +08:00
Wesley Liddick
7ad2dc74dd
Merge pull request #2922 from gsh20040816/codex/fix-openai-ws-usage-dedup
fix: avoid OpenAI WS usage dedup conflicts
2026-06-01 10:23:01 +08:00
Wesley Liddick
418d09be56
Merge pull request #2881 from xiaoyiluck666/fix/openai-oauth-refresh-enrichment
修复 OpenAI OAuth 刷新未补全账号信息
2026-06-01 10:04:16 +08:00
Wesley Liddick
910ff3cd58
Merge pull request #2892 from Arron196/feat/sync-upstream-models-on-create
feat: 添加账号时支持同步上游支持的模型
2026-06-01 09:58:33 +08:00
Wesley Liddick
52ef848432
Merge pull request #2919 from wucm667/fix/claude-code-only-allow-count-tokens
fix(gateway): claude_code_only 不再误拦截官方 Claude Code 的 count_tokens 与非流式 messages 请求
2026-06-01 09:44:40 +08:00
Wesley Liddick
29c8b8aa4b
Merge pull request #2918 from wucm667/fix/openai-5h-usage-window-inverted
fix(usage): 修正 OpenAI 5h 用量窗口 used%/remaining% 颠倒
2026-06-01 09:42:20 +08:00
Wesley Liddick
0f70f84182
Merge pull request #2925 from is7Qin/feat/openai-oom
refactor(gateway): 降低大请求体内存保留
2026-06-01 09:40:31 +08:00
name
1afae0a4cd fix(gateway): address OpenAI OOM review lint 2026-05-31 16:38:42 +08:00
name
8ac2e23fb3 refactor(gateway): defer OpenAI request map decoding
Keep the Responses hot path on raw request bytes until complex mutation or retry branches need a decoded map, reducing large-body retention under high concurrency.
2026-05-31 16:00:35 +08:00
gsh
1e2193c3d2 fix: avoid websocket usage dedup conflicts 2026-05-31 15:09:06 +08:00
wucm667
bf3787de1f fix(gateway): allow Claude Code count_tokens 2026-05-31 08:43:20 +08:00
wucm667
b65dde634b fix(usage): 修正 OpenAI 5h 用量百分比语义 2026-05-31 08:39:37 +08:00
name
6a5f6b96b6 refactor(gateway): introduce OpenAI request view
Cache hot-path request scalars before full body decoding so later branches can avoid repeated map work while preserving current decode behavior.
2026-05-31 01:01:58 +08:00
name
34de99ee0e refactor(gateway): cap upstream error body reads 2026-05-30 20:01:40 +08:00
name
09af6ebd40 refactor(gateway): avoid extra OpenAI WS body map copy 2026-05-30 20:01:40 +08:00
name
5a3e193b53 refactor(gateway): shorten OpenAI request body retention 2026-05-30 20:01:39 +08:00
name
2caee9d884 refactor(gateway): snapshot usage worker inputs 2026-05-30 20:01:39 +08:00
name
619e5ae619 refactor(gateway): isolate anthropic body rewrites
Keep Anthropic request body rewrites attempt-local and synchronize the accepted wire body only after upstream success so failover and retry paths do not reuse stale parsed state.
2026-05-30 20:01:39 +08:00
name
b1c4be4ac8 refactor(gateway): remove parsed request object graphs
Keep large gateway payloads as raw body ranges and bind OpenAI parsed-body caches to the body bytes so failover and mapping do not reuse stale mutable state.
2026-05-30 20:01:38 +08:00
name
d8cbf9ab5c refactor(gateway): introduce request body refs 2026-05-30 20:01:38 +08:00
DaydreamCoding
bf24b61139 perf(usage): 优化 /admin/usage 打开速度与刷新响应
根因:页面 mount 并发 6 个请求,其中 5 个在原始 usage_logs 上 live 聚合,
且有 1 个重复 getModelStats。优化(不引入预聚合):

前端
- UsageView 经 :model-options 下传 model 列表,移除 UsageFilters 重复的
  getModelStats(mount 请求 6→5,少一次 usage_logs 全表 GROUP BY model)
- 刷新/换筛选保留旧模型数据(invalidateModelStatsCache 只失效标记不清空数据),
  图表不再闪空,刷新期间页面保持可交互

后端
- GetStatsWithFilters 4 条聚合 errgroup 并行(仅 *sql.DB 连接池路径,ent.Tx
  顺序回退以保事务内不并发),endpoint 明细 best-effort;抑制取消级联噪声日志
- /admin/usage/stats 复用 dashboard 的 newSnapshotCache 30s 处理器层缓存,按
  filters+窗口为 key;前端手动刷新带 nocache=1 强制回源(刷新=最新)

注:自合并提交 4c8396c 迁移而来,仅取"列表打开速度与刷新响应"部分;原提交的
"审计查看弹窗大 body 渲染"改动(AuditLogModal / audit-log-format / 审计 i18n)
依赖尚未迁移的审计功能(d8389ade),本次已排除。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-30 18:56:08 +08:00
DaydreamCoding
b60d8bb4cc feat(usage): 在 /admin/usage 支持查看已删除用户的历史使用情况
用户软删除后使用记录仍在,但身份(邮箱)被 ent 软删除拦截器隐藏。本次在
三条管理员只读路径定点穿透软删除过滤,并把删除状态传播到前端标记,零新表/
迁移/回填:

- 后端穿透:富化 usage 日志(loadUsers)、用户搜索(ListWithFilters +
  UserListFilters.IncludeDeleted)、点击详情(GetByIDIncludeDeleted /
  GetUserIncludeDeleted + getById ?include_deleted 分支)
- 状态传播:service.User / dto.User 新增 DeletedAt;SearchUsers 标记 deleted
- 前端:表格与余额弹窗展示"已删除"徽标、筛选下拉标注并排序、点击走
  include_deleted;新增 i18n admin.usage.userDeletedBadge
- 安全:普通用户 usage 仅查本人(无 PII 泄漏);主用户列表与默认 getById
  行为不变(已删用户仍 404);仅 admin 搜索设 IncludeDeleted

后端 build / 三态 vet / unit 全量 / 仓储集成全绿;前端 typecheck / vitest /
改动文件 eslint 全清。

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-30 17:52:18 +08:00
benjamin
57d9e15e04 feat: 添加账号时支持同步上游模型
新增 POST /admin/accounts/models/sync-upstream-preview 端点,
允许在创建账号流程中直接使用凭证同步上游支持的模型,
无需先保存账号获取 ID。

- 后端: 新增 SyncUpstreamModelsPreview handler
- 前端: 新增 syncUpstreamModelsPreview API 函数
- 前端: ModelWhitelistSelector 支持 syncCredentials prop
- 前端: CreateAccountModal 传递凭证给 ModelWhitelistSelector
2026-05-30 12:19:38 +08:00
DaydreamCoding
5fd9a35093 test(pricing): 修复 codex-auto-review 定价测试断言
68901cbff 批量同步定价数据后,codex-auto-review 的 input_cost_per_token 从 2.5e-6 更新为 5e-6,output 从 1.5e-5 更新为 3e-5,cache_read 从 2.5e-7 更新为 5e-7。测试断言需要同步更新以匹配当前定价数据。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-29 18:48:49 +08:00
DaydreamCoding
f7f5e33830 feat(quota): user×platform 配额 DB 写聚合 flusher
Redis 同步权威 + DB 镜像,不在进程内维护 delta:
- 写入点 HasUserPlatformQuotaLimit 守卫:无 limit 跳过 Redis 写与持久化
- 累加 usage 的 Lua 在 flusher_enabled 时 SADD 脏集 billing:upq:dirty
- UserPlatformQuotaUsageFlusher 定时 SPOP 脏集 → 批量 HGETALL 读当前窗口 usage 快照
  → BatchSnapshotUsage 绝对值 UPSERT 覆盖 DB(去 SELECT FOR UPDATE 行锁)
  → 失败 SADD 回 / FK(23503)整批丢弃
- flusher 单批 clamp 到 ≤6000,保证一次 flush 只生成一条 UPSERT(单事务原子)
- flusher_enabled 默认 false(降级=旧异步直写 DB)

效果:DB 写连接从 O(QPS) 收敛到 O(副本)。
循环依赖:service 层独立 Snapshot/FK 类型,repository adapter 转换 + %w 映射 FK error。
admin reset/upsert 后失效 cache(脏残留被 flusher 当 MISS 跳过)。

健壮性与可观测性:
- flusher_enabled=false 时 Start 不注册定时器;flush_interval_ms 非法回退 2s
- Readd 回填失败单独计 dirty_lost(不再误记 dirty_readd)并 ALERT;脏集 Readd 补兜底 TTL
- 单 tick 达 max batches 上限仍有积压时记 log
- admin 失效 cache 失败升级为 ALERT(提示 enforcement 可能延迟至 sentinel TTL)
- BatchGet 单条命令失败 / usage 字段损坏均记 log,避免静默以 0 覆写 DB

三态 go vet + 单测/集成测全绿。
已知取舍(默认 flusher_enabled=false 不触发):
- FK 整批丢弃牵连同批正常 key(活跃 key 靠下次 SADD+绝对值快照自愈;Redis 仍权威)
- admin reset/upsert 直写 DB 与 flusher 异步刷存在覆盖竞态:flusher 持旧快照在途时可能覆盖
  admin 刚写值(limit 列不受影响;usage 有 preflight windowExpired 兜底;低频)。彻底消除需 version OCC。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-29 17:19:15 +08:00
DaydreamCoding
06fca66273 feat(quota): sentinel 回填消除无配额行用户 preflight 每请求回源 DB
无 user×platform 配额行的用户,preflight 每次 cache MISS 后回源 DB 查得"无行"
却不缓存该结论,导致每请求一次 DB 往返。本 PR 回填 sentinel 占位 entry,使后续
请求命中 Redis 后稳定判"无 limit",TTL 内不再查 DB。

- config: 加 UserPlatformQuotaSentinelTTLSeconds(默认 3600s,短于普通 quota
  cache 的 86400s 以控 Redis 内存)
- metrics: 加 userPlatformQuotaSentinelSetCacheErrorTotal,并入
  GatewayUserPlatformQuotaIncrStats 暴露
- billing_cache: checkUserPlatformQuotaEligibility 在 cache MISS + DB 无行且
  cacheErr==nil 时回填 sentinel(三 limit nil、三 window_start non-nil、SchemaV1);
  TTL<=0 fallback 1h 防 EXPIRE 立即删 key 击穿;SET 失败 fail-open + 计 metric
- billing_cache: HIT 路径对 sentinel(三 limit nil)跳过 windowExpired refresh,
  避免短 sentinel TTL 被误升级为 86400s

有配额 limit 的用户 enforcement 行为不变(rec!=nil 不回填、isSentinel=false 不跳过 refresh)。

测试:扩展 fakeFullCache 夹具(setCalls/lastSetTTL/getErr/setErr);新增回填正确性 /
Redis-GET-故障不回填 / SET-失败 fail-open / sentinel 跨窗口不 refresh 四个单测。
go build、quota+billing unit、三态 go vet 全绿。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-29 17:19:15 +08:00
github-actions[bot]
7321e4dea8 chore: sync VERSION to 0.1.133 [skip ci] 2026-05-29 08:50:18 +00:00
shaw
68901cbfff chore(pricing): update model pricing metadata 2026-05-29 16:29:29 +08:00
Wesley Liddick
0a8c77f8c3
Merge pull request #2884 from Pluviobyte/fix/gemini-messages-tool-text-block-ordering
fix(gemini): close tool_use block before text in messages streaming
2026-05-29 16:22:34 +08:00
Wesley Liddick
69e7c4db30
Merge pull request #2865 from wey-gu/feat/usage-request-context
fix(gateway): preserve usage request context
2026-05-29 16:21:59 +08:00
Wesley Liddick
52292741cb
Merge pull request #2849 from Pluviobyte/fix/count-tokens-payload-filter
fix(gateway): filter count_tokens generation fields
2026-05-29 16:11:01 +08:00
Wesley Liddick
f68d351158
Merge pull request #2873 from wucm667/feat/account-quota-threshold-auto-pause
feat(account): 支持按 5h/7d 用量阈值自动暂停账号调度
2026-05-29 15:40:33 +08:00
Pluviobyte
0a521f09fb
fix(gemini): close tool_use block before text in messages streaming
When the Gemini->Anthropic streaming bridge for the /v1/messages endpoint
receives a functionCall part followed by a text part, the text branch in
handleStreamingResponse opened a new text content block without closing the
already-open tool_use block. The tool block's content_block_stop was only
emitted at end-of-stream, after the text block's content_block_start, so the
Anthropic SSE stream contained overlapping/unterminated content blocks. Clients
that assemble messages by block index (e.g. Claude Code) can drop the tool
input or mis-parse the response.

The functionCall branch already closes an open text block before opening a tool
block, and the chat-completions sibling closes the tool block in its text branch
via closeOpenTool(). This applies the same symmetric handling to the messages
variant: close any open tool_use block (resetting openToolIndex/openToolName/
seenToolJSON) before starting text.

Adds a regression test that replays a tool->text Gemini stream and asserts the
Anthropic content-block lifecycle never overlaps.
2026-05-29 06:47:23 +00:00
wucm667
c9caadb378 fix(account): address second-round review on quota auto-pause
- TopK initial filter now drops quota-paused accounts: fold the quota check
  into isAccountRequestCompatible so session-hash, TopK pool, and per-candidate
  rechecks all skip paused accounts. Previously the candidate pool was built
  without the quota check, so paused accounts could fill TopK and leave the
  scheduler returning "no available accounts" even with healthy ones available.
- Add per-account explicit disable flags auto_pause_5h_disabled /
  auto_pause_7d_disabled with toggles in EditAccountModal. Without these,
  leaving the account threshold blank silently falls back to the global default,
  so admins could not exempt a single account once a global default existed.
  Disable is per-window: an account can opt out of 5h auto-pause while still
  honoring 7d. Schedule snapshot whitelist includes the new fields, i18n EN/ZH
  updated, threshold-hint text revised to explain "blank = global default".
- Move quota auto-pause settings off the request hot path: replace the per-repo
  TTL+singleflight sync DB read with a per-SettingService stale-while-revalidate
  in-memory snapshot. Get is non-blocking (atomic.Pointer load + async refresh
  on staleness); writes via UpdateOpsAdvancedSettings push directly into the
  cache through an injected sink; wire warms the cache at startup. Adds Warm
  (sync) for tests/init and SetOpenAIQuotaAutoPauseSettings (sink target).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-29 14:32:45 +08:00
xiaoyiluck666
eba2046320 fix: enrich OpenAI OAuth token refresh 2026-05-29 14:01:39 +08:00
wucm667
8b7a822706 fix(account): address review on OpenAI quota auto-pause
- gate previous_response_id sticky path with quota auto-pause check at both
  the snapshot and DB-recheck stages (previously bypassed, #1)
- skip pausing when the usage window already reset to avoid a stale stuck-pause;
  carry codex_*_reset_at / reset_after_seconds / codex_usage_updated_at through
  the scheduler snapshot whitelist (#2)
- remove the incomplete limit mode; percentage threshold only (#3)
- add global default 5h/7d threshold inputs to the Ops settings dialog with
  validation and en/zh i18n (#4)
- downgrade account_auto_paused_by_quota log from Info to Debug; it fires
  per-candidate on the scheduling hot path (#5)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-29 12:20:30 +08:00
Wesley Liddick
1d46be02ae
Merge pull request #2830 from stabey/fix/anthropic-to-responses-cache-tokens
fix(apicompat): Anthropic 转 Responses 时按 OpenAI 语义汇总 input_tokens
2026-05-29 11:00:19 +08:00
wucm667
ead471d64b feat(account): 支持按 5h/7d 用量阈值自动暂停账号调度 2026-05-29 10:47:47 +08:00
Wesley Liddick
16842c2f8b
Merge pull request #2836 from siyuan-123/fix/openai-ws-compat-usage
修复 OpenAI WS 兼容性与 usage 统计
2026-05-29 10:47:12 +08:00
Wesley Liddick
21cd382f39
Merge pull request #2855 from gaoren002/fix/concurrency-error-classification
fix: classify concurrency acquire failures
2026-05-29 10:33:40 +08:00
Wesley Liddick
e6a3f1e12b
Merge pull request #2869 from Pluviobyte/fix/ws-first-token-terminal-event
fix(ws): exclude terminal events from first-token detection
2026-05-29 10:32:16 +08:00
Wesley Liddick
fc9d79d96b
Merge pull request #2835 from JIA-ss/fix/responses-passthrough-token-details
fix(apicompat): Responses→Chat 转换补齐 completion_tokens_details 透传
2026-05-29 10:31:52 +08:00
Wesley Liddick
d3a8db8084
Merge pull request #2833 from alfadb/fix/haiku-strip-context-management
fix(gateway): 按最终 anthropic-beta header 对 body.context_management 做能力维度 sanitize
2026-05-29 10:31:12 +08:00
Wesley Liddick
433f8dcd13
Merge pull request #2834 from DaydreamCoding/pr/openai-codex-cli-allow-claude-code
feat(openai): codex_cli_only 新增放行 Claude Code Codex 插件的机制
2026-05-29 10:30:33 +08:00
Wesley Liddick
6bc1983506
Merge pull request #2853 from wucm667/fix/system-update-already-up-to-date-response
fix(admin): system/update 在无更新时返回 200 + already_up_to_date 而非 500
2026-05-29 10:29:49 +08:00
shaw
6010c3cca9 test: 修复内容审计日志异步断言 2026-05-29 09:57:02 +08:00
shaw
514ac5c6a1 feat: 适配 claude-opus-4-8 2026-05-29 09:56:48 +08:00
Pluviobyte
8a999f438d
fix(ws): exclude terminal events from first-token detection
isOpenAIWSTokenEvent classified response.completed / response.done as
token events. When upstream finishes a request without ever emitting
a recognizable delta (e.g. cached completions or models that skip
incremental output), firstTokenMs was then filled at the terminal
event's timestamp, so the first-token latency metric effectively
reported total request duration.

Terminal events are already handled separately by
isOpenAIWSTerminalEvent. Treating them as token events makes the two
classifiers overlap, which violates the implicit invariant that the
token-event and terminal-event sets are disjoint.

The metric only affects ForwardResult.FirstTokenMs (logging and
observability) — billing and routing are unchanged.

Add regression tests for both directions:

* TestIsOpenAIWSTokenEvent_TerminalEventsExcluded covers each
  classification branch.
* TestIsOpenAIWSTokenEvent_DisjointWithTerminal asserts the
  disjoint-set invariant for every known terminal event.

Both new tests fail when the old `return eventType == "response.completed"
|| eventType == "response.done"` is restored.

Fixes #2651

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-29 01:33:42 +00:00
shaw
ed1b57c597 fix(openai): gate routing by endpoint capability 2026-05-29 08:58:10 +08:00
Wey Gu
2bd3125d0f Preserve usage request context 2026-05-28 22:44:25 +08:00