Commit Graph
3866 Commits
Author SHA1 Message Date
Wesley Liddick 1cecd2716c Merge pull request #2972 from northya/fix/chat-completions-response-failed
fix: 修复 Chat Completions 兼容层误将 response.failed 返回为成功响应
2026-06-05 21:51:23 +08:00
Wesley Liddick 8775047f84 Merge pull request #3051 from wucm667/fix/openai-messages-missing-terminal-event-failover
fix(openai): /v1/messages 流式缺终止事件时纳入 failover 与 ops 错误归因
2026-06-05 21:34:14 +08:00
Wesley Liddick 872e56aed7 Merge pull request #3052 from wucm667/feat/openai-image-ratelimit-cooldown-failover
feat(openai): gpt-image 图片限流按能力维度冷却并 failover,不再误伤文字请求
2026-06-05 21:25:40 +08:00
CoolCoolTomato 8a56c9fa0c fix(setup): bootstrap postgres connection with maintenance db 2026-06-05 20:48:04 +08:00
Wesley Liddick b5b68f86bd Merge pull request #3037 from feitianbubu/pr/fix-auto-mode-invalid-by-security-monitor
fix: cc开启auto mode后无法通过cc客户端验证的问题
2026-06-05 20:43:37 +08:00
wucm667 36721d35a8 feat(openai): cool down image rate limits by capability 2026-06-05 18:12:33 +08:00
wucm667 8e27ff20af fix(openai): handle missing messages stream terminal 2026-06-05 18:11:23 +08:00
erio b8c89c34d8 fix(ci): add missing fields to pass frontend typecheck and contract test
- Add `service_quota_enabled` to PublicSettings default in app.ts
- Add `image_output_tokens` and `image_output_cost` to usage contract test
2026-06-05 17:55:22 +08:00
方程 705fe7d880 fix(admin): delete user api keys with user 2026-06-05 16:23:56 +08:00
Wesley Liddick d895d765b6 Merge pull request #3042 from DaydreamCoding/feat/usage-error-requests
feat(usage/ops): 失败请求记录与展示(用户端 + 管理端)+ 错误日志 Key 归因
2026-06-05 15:42:25 +08:00
feitianbubuandClaude Opus 4.8 d626ccce1d fix: recognize claude code clients via billing block, not just prompt
Genuine Claude Code CLI sub-requests (e.g. the security monitor) carry
no identity system prompt, so claude_code_only groups wrongly rejected
them with "this group only allows Claude Code clients". Detect the
x-anthropic-billing-header block with cc_entrypoint=cli as a stable
client signal, while keeping the existing header/metadata checks.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-05 14:09:03 +08:00
DaydreamCodingandClaude Opus 4.8 fe8952733a fix(usage): 管理端错误请求页过滤与分列完善
- 错误请求标签补传 model/account_id/group_id 过滤(此前 loadAdminErrors 丢弃),admin handler
  读取 model 查询参数走精确匹配
- 错误表格拆成 用户/API Key/账号 三独立列(上游行也显示用户),补 api_key_name/api_key_deleted,
  已删除 key 显示红色「已删除」标记;i18n keyDeletedBadge 补入 errorLog 命名空间

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-05 14:00:57 +08:00
DaydreamCodingandClaude Opus 4.8 cf12bc521b fix(usage): 用量明细虚拟表对可空字段渲染崩溃致整表空白
- formatDuration 兜底 null(duration_ms 后端为 *int,count_tokens/失败等请求无耗时)
- tokens/cost 单元格与 CSV 导出加 (x ?? 0) 防御
- duration_ms 前端类型改 number|null,对齐后端指针,编译器从此拦未保护用法
- DataTable 虚拟化加固:initialRect 一屏兜底 + 过滤 0 高度读数

根因:渲染对 null 调 .toFixed() 抛错→Vue 弃整个 tbody→空白;虚拟化只渲可视行故呈概率性。
非 b3847fa 引入(formatDuration 自首个 commit 即存在)。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-05 14:00:57 +08:00
DaydreamCodingandClaude Opus 4.8 cfb195c7b2 feat(usage): 记录并展示失败请求(用户端+管理端)
- 记录失败请求并在用户端/管理端展示;分类下拉改用统一 Select 组件
- 模型过滤改后端 ILIKE 模糊匹配;新增「Key 名称」列(含已删除标记)与按 Key 过滤;时间列移至末列
- 用户可见「已删除 key 失败请求」:OpsErrorLogFilter 加 MatchDeletedKeyOwner,用户侧归属
  放宽为 (user_id OR deleted_key_owner_user_id),让 key 原所有者能看到删除 key 后继续请求
  导致的认证失败记录(他人仍 NotFound,不泄露存在性)
- 迁移 148:ops_error_logs 用户+时间索引

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-05 14:00:57 +08:00
DaydreamCodingandClaude Opus 4.8 ddf063352a feat(ops): 错误日志 key 归因与早退字段补全
让 /admin/ops 错误详情正确归因 API key 并补全早退场景字段,合并三项改动:

- 鉴权早退补全用户/分组/平台字段:引入 ops fallback key(ContextKeyOpsFallbackAPIKey),
  apiKey 一加载成功即写入,覆盖分组停用/删除、Key 停用/过期/额度、用户停用、IP 限制等早退
  路径;ops 错误日志改用 getOpsAPIKey(正式 key 优先、回退键兜底),不改「已鉴权」语义。
- 已删除 key 归因(迁移 145):删除 key 时同一事务写 deleted_api_key_audits 映射,认证失败
  时用明文反查命中原所有者,错误详情展示「已删除 Key 所有者」「尝试的 Key 前缀」。
- 有效 key 报错快照前缀(迁移 147):对绑定有效 key 的错误,落库时快照明文前 8 位到
  api_key_prefix(与 attempted_key_prefix 互斥),key 之后被删仍保留报错当时真实前缀。

均仅对上线后新产生的错误/删除生效。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-05 14:00:57 +08:00
Wesley Liddick 57bae985a7 Merge pull request #2711 from wucm667/fix/db-pool-enforce-conn-lifetime-floor
fix(db-pool): 强制连接生命期下限,缓解 lib/pq watchCancel 协程泄漏
2026-06-05 13:55:07 +08:00
Wesley Liddick e080d3f966 Merge pull request #2975 from wucm667/fix/easypay-queryorder-trade-status
fix(payment): EasyPay 查单以 trade_status 判定支付成功,避免未付订单误判到账
2026-06-05 13:54:54 +08:00
Wesley Liddick f332e0a83c Merge pull request #2872 from wucm667/fix/scheduler-sticky-health-escape
fix(scheduler): session_hash sticky 引入健康度逃逸,慢账号不再独占用户会话
2026-06-05 13:54:37 +08:00
Wesley Liddick 83cce858a5 Merge pull request #3039 from StarryKira/fix/issue-2994-codex-5h-used-percent-selfheal
fix(openai): self-heal stale Codex used% snapshots + lock semantics (#2994)
2026-06-05 13:54:18 +08:00
Wesley Liddick bebeaf57c5 Merge pull request #2854 from ttt132/fix/responses-stream-completed-output
fix: normalize responses streaming terminal output
2026-06-05 13:54:05 +08:00
Wesley Liddick a879f254c8 Merge pull request #3012 from visa2/fix/responses-anthropic-tool-pairing
fix(apicompat): repair tool_use/tool_result pairing on the Responses→Anthropic path
2026-06-05 13:53:31 +08:00
Wesley Liddick fbd25acae0 Merge pull request #3035 from ghostg00/fix/admin-group-clear-description
fix(group): 管理员清空分组描述时正确持久化
2026-06-05 13:53:20 +08:00
Wesley Liddick 05f0326e99 Merge pull request #2988 from wucm667: content audit auto-ban exempts admin accounts
fix(risk-control): 内容审计 auto-ban 豁免管理员账号,避免封禁管理员/超管
2026-06-05 13:47:10 +08:00
Wesley Liddick b34480df68 Merge pull request #3001 from Pluviobyte: bump Go patch version to 1.26.4
chore: bump Go patch version to 1.26.4
2026-06-05 13:46:37 +08:00
bwlcandClaude Opus 4.8 69b4654510 fix(ops): weight TTFT percentiles by streaming sample count
TTFT (first_token_ms) is only recorded for streaming requests, but the
ops dashboard weighted merged TTFT percentiles by success_count (all
successful requests, streaming + non-streaming). When non-streaming
traffic was present this diluted/skewed the merged TTFT figures shown
for longer (pre-aggregated) time ranges; the realtime path was exact.

Add a per-bucket ttft_sample_count (rows that actually recorded
first_token_ms) to ops_metrics_hourly / ops_metrics_daily and weight all
TTFT percentile merges by it instead of success_count:

- hourly/daily pre-agg upserts populate and propagate ttft_sample_count;
  daily TTFT p50/p90/avg now weighted by ttft_sample_count.
- dashboard hourly-row merge and cross-segment combine weight TTFT by
  the streaming sample count; queryUsageLatency returns it for raw
  head/tail fragments.

duration stays weighted by success_count (recorded for every request);
p95/p99/max keep the conservative MAX merge (weight-independent).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-05 10:58:44 +08:00
harukaandClaude Opus 4.8 86d9b6bff9 fix(openai): self-heal stale Codex used% snapshots + lock semantics (#2994)
The OpenAI/Codex 5h "used %" inversion that caused fresh accounts to show
~96-99% used (PR #2918, commit b65dde63) was already reverted in #2993, so the
stored value is now the correct "used %" again. This commit hardens that fix:

1. Regression test locking in direct "used %" semantics. The semantics have
   flip-flopped twice (#2918 -> #2993) with no value-level guard — a fresh
   account (secondary_used_percent=1, 5h window) must store
   codex_5h_used_percent=1, not 99.

2. Stale-bounded self-heal in resolveOpenAIQuotaUtilization (the single
   auto-pause chokepoint). An account poisoned with an inflated used% gets
   excluded from scheduling, and a paused account never receives traffic to
   refresh its snapshot — so it stayed stuck until the window's reset_at passed
   (up to 5h/7d). When codex_usage_updated_at is older than 2h, the account is
   no longer auto-paused on that snapshot; it gets one request whose response
   headers refresh the snapshot and self-heal it. A missing timestamp is treated
   as fresh (stays paused), and an actively-served exhausted account refreshes
   the timestamp every response so it never crosses the bound — it cannot escape
   auto-pause.

No change to Normalize(); no 100-x reintroduced; no new dependency wiring.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-04 22:07:36 +08:00
ghostg00 bc7ce18574 fix(group): 管理员清空分组描述时正确持久化
UpdateGroup 之前用 `if input.Description != ""` 判空,
把"未提供"和"显式置空"混为一谈,导致管理员在分组编辑表单
里清空备注后保存无效。

将 UpdateGroupRequest / UpdateGroupInput 的 Description 改为
*string:nil 表示未提供(保持原值),"" 表示显式清空。
2026-06-04 19:48:03 +08:00
whatIsNextToTheMoon 5bd3d90434 fix(openai): preserve upstream response.failed errors 2026-06-04 11:17:22 +00:00
Wesley Liddick f1aa589646 Merge pull request #2993 from ghostg00/fix/openai-5h-used-percent-direct
fix(usage): revert OpenAI 5h used_percent inversion (#2918 regression)
2026-06-04 16:02:52 +08:00
Zbl1007 9b99f6c1f3 fix(apicompat): surface DeepSeek reasoning-only replies 2026-06-04 11:34:54 +08:00
Zbl1007 fb0195f3dc fix(account): normalize fixed quota windows on edit 2026-06-04 01:06:41 +08:00
Fool0ntheHill 55655b8654 fix(apicompat): surface reasoning-only chat streams 2026-06-03 20:52:11 +08:00
visa2andClaude Opus 4.8 60867022b6 fix(apicompat): repair tool_use/tool_result pairing on the Responses→Anthropic path
When an OpenAI Chat Completions client targets an Anthropic-platform group,
ForwardAsChatCompletions converts the request CC → Responses → Anthropic
(ChatCompletionsToResponses → ResponsesToAnthropicRequest) before forwarding it
upstream. The Responses→Anthropic converter emits each function_call as its own
assistant message and each function_call_output as its own user message and
relies solely on mergeConsecutiveMessages to alternate roles. That is not enough
to satisfy Anthropic's tool-pairing invariants, so a trimmed or partial tool
history produces an upstream 400, e.g.:

    tool_use_id found in tool_result blocks: call_00_...
    Each tool_result block must have a corresponding tool_use block in the
    previous message.

The failures this leaves unrepaired:

  - orphan tool_result — a client that does sliding-window context management
    keeps a recent tool result but drops the assistant tool_calls message that
    announced it, so the tool_result has no matching tool_use;
  - unanswered/dangling tool_use — a parallel call whose sibling result never
    came back, or a call left dangling, which Anthropic also rejects.

Add normalizeAnthropicToolPairing, run between two merge passes: the first merge
groups parallel calls and their results; the pairing pass indexes every
tool_result by its tool_use id, keeps only answered tool_use blocks (dropping
unanswered/dangling calls, and the assistant message entirely when nothing else
remains) and re-emits the matching tool_result blocks as the immediately
following user message; standalone/orphan tool_results are dropped from their
original position; the second merge restores alternation. This mirrors
normalizeChatMessages on the Responses→Chat path.

Tested two ways: responses_to_anthropic_tool_pairing_test.go covers the repair
on direct Responses input (developer message between call and output, parallel
both-answered kept grouped, parallel one-unanswered dropped, orphan tool_result,
dangling call, single-call baseline); responses_to_anthropic_cc_chain_test.go
drives the real ChatCompletionsToResponses → ResponsesToAnthropicRequest chain
and reproduces the production 400 (orphan and unanswered-parallel) — both fail
without the repair and pass with it. The full apicompat suite stays green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-03 17:26:05 +08:00
Liu LinhuaiandClaude Opus 4.8 381d1d6d6c fix(images): surface real upstream error instead of generic 502
The /v1/images/generations and /v1/images/edits paths routed every
non-failover upstream error through the generic handleErrorResponse,
whose final switch collapses anything that isn't 401/402/403/429 into a
hardcoded 502 "Upstream request failed" — discarding the actual upstream
status code, type, code, message, and param. So a gpt-image-2 400
(invalid_request_error, moderation, unsupported parameter, ...) reached
the client as an opaque 502.

The sibling Chat Completions and Messages compat paths already avoid this
via handleCompatErrorResponse, which preserves the real status and
message. Add the equivalent for images: handleOpenAIImagesErrorResponse
keeps all existing side-effects (ops logging, error-passthrough rules,
ShouldHandleErrorCode, account-disable/secondary-failover) but surfaces
the real upstream status + type/code/message/param by reusing the
existing OpenAIImagesUpstreamError machinery. Both forward paths now call
it instead of handleErrorResponse for non-failover errors.

Failover behavior (5xx / 401 / 403 / 429 / 529) is unchanged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-03 17:23:13 +08:00
Pluviobyte 5634cc83eb chore: bump Go patch version 2026-06-03 14:24:50 +08:00
Pluviobyte 3571b082fb fix: validate stream field type 2026-06-03 14:18:12 +08:00
Pluviobyte b6c0706e30 fix: sync scheduler snapshots after account state clears 2026-06-03 14:18:12 +08:00
b605166577 7513b7ea69 Bind OpenAI HTTP response IDs to selected accounts 2026-06-03 13:07:16 +08:00
feitianbubuandClaude Opus 4.8 32ef471103 fix: treat allowed proxy quality statuses as pass not warn
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-03 12:06:05 +08:00
ghostg00 a8ffb052ca Revert "fix(usage): 修正 OpenAI 5h 用量百分比语义"
This reverts commit b65dde634b.
2026-06-03 11:46:31 +08:00
wucm667 134687782c build(go): bump toolchain to 1.26.4 2026-06-03 09:48:46 +08:00
wucm667 c40a74d983 fix(risk-control): exempt admins from moderation auto-ban 2026-06-03 09:33:37 +08:00
wucm667 04deb819b0 fix(payment): use trade_status for EasyPay query 2026-06-02 14:59:18 +08:00
northya 2e212d18e3 fix: handle failed responses in chat completions compat 2026-06-02 14:00:16 +08:00
ghostg00 585ff09443 compat(redis): replicate effects for Lua scripts using TIME on Redis 3.2-4.x
Call redis.replicate_commands() at the top of every Lua script that
reads redis.call('TIME'), so the resulting writes (ZADD, ZREMRANGEBYSCORE,
SET, DEL, EXPIRE) replicate correctly on Redis 3.2-4.x.

This is a no-op on Redis 5.0+, where effects replication is the default.
The function remains a public API on Redis 7.0+ (deprecated but supported)
and is still the documented way to opt into effects replication on older
versions, so calling it unconditionally is safe and forward-compatible.

Affected scripts (8 in total, all already using redis.call('TIME') by
design to avoid client-side clock skew across multiple app instances):
- concurrency_cache.go: acquireScript, getCountScript,
  cleanupExpiredSlotsScript
- session_limit_cache.go: registerSessionScript, refreshSessionScript,
  getActiveSessionCountScript, isSessionActiveScript
- user_msg_queue_cache.go: releaseLockScript

Scripts that do not invoke non-deterministic commands are intentionally
left untouched.
2026-06-02 12:46:46 +08:00
Cheri Wen 11b6017171 fix: return 404 instead of 403 for unauthorized key access to prevent ID oracle (CWE-204)
GET /api/v1/keys/:id previously returned distinct HTTP status
codes for 'key not found' (404) vs 'key exists but belongs to
another user' (403). This oracle allowed attackers to enumerate
valid API key IDs by observing response differences.

Now returns 404 in both cases so the response is identical
regardless of whether a key exists.

Fixes: CWE-204 (Information Disclosure via ID Oracle)
2026-06-02 00:46:50 +08:00
Cheri Wen 0ae3329613 fix: sanitize API key name with html.EscapeString to prevent stored XSS (CWE-79)
HTML-encode user-supplied key names in both Create and Update
endpoints. Previously, names were stored verbatim — an attacker
could inject script tags that would execute in admin panels or
any view using innerHTML/v-html rendering.

Fixes: CWE-79 (Stored Cross-Site Scripting)
2026-06-02 00:46:29 +08:00
paraline bf1a2d6dc2 Align Codex usage stats with reset windows 2026-06-01 11:37:45 +00:00
EricLi404 bd0b1ff557 Add codex-auto-review default model
Include codex-auto-review in the OpenAI fallback models list so /v1/models exposes it when no account mapping is configured. Keep the entry aligned with the existing default model catalog.
2026-06-01 18:46:15 +08:00
Wesley Liddick aa69e3947d Merge pull request #2926 from visa2/feat/codex-responses-bridge-redesign
refactor(apicompat): redesign the Codex Responses ↔ Chat Completions …
2026-06-01 15:07:44 +08:00