Commit Graph
4045 Commits
Author SHA1 Message Date
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
Wesley Liddick 5f63fe1945 Merge pull request #2927 from moonagic/main
fix antigravity gemini rate limit and account scheduling
2026-06-01 14:50:36 +08:00
Wesley Liddick 3ba4a51355 Merge pull request #2937 from Kanshan03/fix/ws-codex-image-bridge
fix(openai): inject WS Codex image bridge tool
2026-06-01 14:18:58 +08:00
visa2andClaude Opus 4.8 003b2786da test(apicompat): check type assertions in responses stream wire tests
errcheck (check-type-assertions) flagged unchecked single-value type
assertions; switch to the comma-ok form so golangci-lint passes.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-01 12:03:15 +08:00
Wesley Liddick a0057f44f2 Merge pull request #2932 from fatelei/issue-2917
fix: change balance to pointer type
2026-06-01 11:37:33 +08:00
Kanshan03 2a075a85bd fix(openai): 注入 WS Codex 生图桥接工具 2026-06-01 11:25:28 +08:00
Wesley Liddick bc5813f023 Merge pull request #2936 from xlx0852/codex/ws-http-bridge-recovery
fix(openai): bridge oversized websocket requests
2026-06-01 11:11:11 +08:00
Wesley Liddick 80fa2f28c5 Merge pull request #2920 from wucm667/fix/openai-failover-stale-cached-body
test(openai): 覆盖 responses failover 请求体重映射,防止 alias 模型泄漏
2026-06-01 10:47:48 +08:00
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
wucm667 c8cd91e3ce test(openai): 覆盖 failover 请求体重映射 2026-06-01 10:19:24 +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 ed8f5666c6 Merge pull request #2895 from wucm667/feat/usage-window-tooltip
feat(admin): 账号用量窗口 5h/7d 增加说明 tooltip
2026-06-01 09:57:03 +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
Wesley Liddick c596b0a294 Merge pull request #2908 from DaydreamCoding/feat/admin-usage
feat(usage): /admin/usage 支持查看已删除用户 + 打开速度与刷新优化
2026-06-01 09:40:22 +08:00
fatelei 0560340bd4 fix: change balance to pointer type 2026-06-01 08:41:35 +08:00
erio 1e2e8b1d6b fix(billing): channel pricing fully overrides image output token price
When channel pricing is configured, image output token price now
correctly overrides all other sources (LiteLLM, group config).
Add ImageOutputPriceExplicit flag to distinguish intentional zero
price from missing price, preventing fallback to output price.

Affects Gemini, OpenAI inline image gen, and Images API paths.
2026-05-31 22:57:56 +08:00
erio ef5ad0fb10 fix(frontend): display image_output_tokens breakdown in usage pages
When image generation models are billed by token (channel pricing mode=token),
the usage pages previously showed only image count format instead of detailed
token breakdown. This fixes the display to properly show image output tokens
separately from text output tokens.
2026-05-31 22:53:22 +08:00
moonagic a01686c637 fix antigravity gemini rate limit and account scheduling
Squash of 4 commits:
- Fix Gemini rate limit scheduling
- fix antigravity gemini rate limit scheduling
- fix antigravity gemini limited account scheduling
- fix antigravity test stubs for default lint
2026-05-31 22:00:03 +08:00