Commit Graph
4188 Commits
Author SHA1 Message Date
hsn 65fa728921 fix(openai): fail over on chat transport errors 2026-06-23 22:53:20 +08:00
feitianbubu 82576e0a38 fix(auth): stop swallowing email auth identity create error via shadowed err 2026-06-23 19:29:17 +08:00
zichuanwangcloud-gifandClaude Opus 4.8 9707dedca2 fix(ops): prevent monitoring trend cards from growing unbounded
The concurrency / switch-rate / throughput cards on the ops dashboard
sit in grid cells that only set `min-h-[360px]` (no definite height).
Their inner card uses `h-full`, which resolves to `auto` when the parent
height is `auto`. Combined with the Chart.js `responsive` +
`maintainAspectRatio: false` charts, this forms a height feedback loop:
the canvas reads the parent height to size itself, the content then grows,
the next ResizeObserver tick reads an even larger height, and the cards
stretch downward without bound.

On wide screens (`lg:grid-cols-4`) a sibling card usually fixes the row
height via `align-items: stretch`, masking the issue. It surfaces when no
sibling bounds the row height — e.g. the single-column (`grid-cols-1`)
stacked layout on narrow viewports, or when the concurrency card collapses
to little content. `min-h` only sets a floor, not a ceiling.

Fix: give the two Chart.js canvas cells a definite height (`h-[360px]`) so
the responsive resize has a fixed reference and the loop cannot run. The
concurrency card is not a responsive canvas, so it keeps `min-h-[360px]`
to avoid clipping its content.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-23 10:46:17 +00:00
feitianbubu ae5e980dd1 fix(gateway): enforce codex_cli_only restriction on /v1/chat/completions 2026-06-23 18:38:52 +08:00
feitianbubu 28e7adef09 fix(keys): add CLAUDE_CODE_ATTRIBUTION_HEADER=0 to Claude Code terminal templates 2026-06-23 18:27:49 +08:00
404QAQ 9491de0a39 fix(images): pass content-moderation refusals through as 400 instead of retrying
When gpt-image upstream returns response.completed with no image but a text
refusal (content moderation, e.g. the model replies "this request was judged
unsafe to generate"), the soft-failure path treated it as a probabilistic
upstream failure and returned a retryable UpstreamFailoverError (502). Retrying
or switching accounts is futile for a content-policy block — it just burns
other accounts' quota and still surfaces an opaque 502 to the client.

Distinguish the two no-image cases:
  (A) model text refusal  -> 400 content_policy_violation, no retry, refusal
      reason passed through to the client.
  (B) truly empty response -> unchanged: retryable UpstreamFailoverError (502).

Adds extractOpenAIImagesModelRefusal (extracts the refusal text from
output_text.delta / message output_text, capped at 600 chars) and two unit
tests; the existing empty-response retry test is unaffected.
2026-06-23 16:57:16 +08:00
milesians 147c1879d9 fix(payment): support plural subscription validity units 2026-06-23 15:22:55 +08:00
syx0310 32df33a1c3 feat: add codex personal access token auth 2026-06-22 16:07:41 +00:00
shaw 85a3b12254 chore: update sponsors 2026-06-22 16:37:56 +08:00
wucm667 c6f375d3ab fix(payment): 订阅订单应用充值汇率换算 2026-06-22 10:33:26 +08:00
wucm667 9f5b57fc96 fix(billing): 防止余额计费持续透支 2026-06-22 10:30:27 +08:00
shaw e5f38a6f60 chore: update sponsors 2026-06-22 09:55:31 +08:00
github-actions[bot] d430343f51 chore: sync VERSION to 0.1.138 [skip ci] 2026-06-22 01:24:13 +00:00
harukaandClaude Opus 4.8 6239e395b8 i18n(channel): explain case-insensitive matching in pricing conflict messages (#3394)
Update the modelConflict / mappingConflict strings (en + zh) to state that
model names are matched case-insensitively, so an existing entry (e.g.
"GLM-5.2") already covers all case variants and the lowercase variant does not
need to be added. This addresses the confusion in #3394 where the rejection of
a case-only duplicate looked like inconsistent behavior.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 07:47:49 -07:00
harukaandClaude Opus 4.8 7c2fee6c99 fix(billing): dedup fallback pricing warn to stop per-request log spam (#3394)
The "[Billing] Using fallback pricing for model: X" warn was emitted on
every request for any model not in LiteLLM but matched by getFallbackPricing
(e.g. "glm-5.2" substring-matches the "glm-5" fallback price). Via the stdlib
log bridge it is inferred as WARN and persisted to ops_system_logs, producing
tens of thousands of rows/day. It fired even when channel pricing already
overrides the price, and from several call sites (resolver, account-stats
pricing, non-channel billing, admin lookup).

Dedup the warn at the source (BillingService.GetModelPricing) via a sync.Map
keyed by the already-lowercased model name, so each model logs at most once
per process while keeping one audit line. Billing amounts are unchanged.

Also clarify the model/mapping pattern conflict error to state that names are
matched case-insensitively, so an existing entry (e.g. "GLM-5.2") already
covers all case variants and the lowercase variant need not be added — the
behavior the issue mistook for a case-sensitivity bug.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 07:46:52 -07:00
shaw 6936687870 fix(lint): check WriteString return in summarizeOpenAIImagesNoOutputBody
修复 PR #3381 引入的 errcheck lint 错误,satisfy golangci-lint
对 strings.Builder.WriteString 返回值的检查要求。
v0.1.138
2026-06-21 21:43:58 +08:00
404QAQ f4b51b0f2b fix(lint): check WriteString return value in summarizeOpenAIImagesNoOutputBody
golangci-lint (errcheck) flagged the unchecked (*strings.Builder).WriteString
return value at openai_images_responses.go:632. Align with the existing
convention used elsewhere in the package (e.g. gateway_service.go) by
explicitly discarding the return values with `_, _ =`.
2026-06-21 21:39:44 +08:00
Wesley Liddick 8105846f33 Merge pull request #3326 from tairan/fix/selinux-bind-mount-labels
fix(deploy): add :Z SELinux labels to bind mounts in compose files
2026-06-21 21:23:27 +08:00
Wesley Liddick f079742bab Merge pull request #3371 from cugxuan/feat/subscription-affiliate-rebate
feat: apply affiliate rebate to subscription payments
2026-06-21 21:18:50 +08:00
Wesley Liddick a560d2f91e Merge pull request #3363 from kangjwme/feat/prefer-soonest-reset-scheduling
feat(scheduling): opt-in "prefer soonest reset" account selection
2026-06-21 21:17:44 +08:00
Wesley Liddick d2ff9c0c4c Merge pull request #3369 from skyswordw/codex/default-ccswitch-openai-gpt-55
fix(ccswitch): default OpenAI import model to gpt-5.5
2026-06-21 21:16:07 +08:00
Wesley Liddick 92ce5bfe24 Merge pull request #3347 from SavitarC/fix/usage-cache-token-tooltip
fix(usage): 显示缓存 Token 明细
2026-06-21 21:15:45 +08:00
Wesley Liddick febfe538b7 Merge pull request #3344 from Milesians/main
fix(frontend): refresh custom page document title
2026-06-21 21:15:17 +08:00
Wesley Liddick 6e27b82335 Merge pull request #3381 from 404QAQ/fix/images-incomplete-failover
fix(images): 识别 response.incomplete 触发 failover + 记录软失败上游响应
2026-06-21 21:14:48 +08:00
Wesley Liddick f6d734ec23 Merge pull request #3308 from a0yark/fix/gemini-tool-schema-cleanup
fix(gemini): clean unsupported tool schema fields
2026-06-21 21:14:09 +08:00
Wesley Liddick af1a032d35 Merge pull request #3375 from StarryKira/fix/3358-vertex-beta-and-cch
fix(gateway): filter anthropic-beta on the Vertex Anthropic path + drop cch sign (#3358)
2026-06-21 21:13:14 +08:00
Wesley Liddick 93de19665b Merge pull request #3359 from alfadb/fix/glm-effort-mapping
修复 GLM 推理强度映射
2026-06-21 21:12:46 +08:00
Wesley Liddick bed12b7016 Merge pull request #3360 from feitianbubu/fix/auto-mode-cc-entrypoint-ide
fix(gateway): in auto mode recognize Claude Code IDE clients via any cc_entrypoint
2026-06-21 21:12:33 +08:00
Wesley Liddick f597e926da Merge pull request #3335 from FjlI5/fix/openai-upstream-endpoint-logging
fix: 修正 chat-only API-key 账号上游端点被误记为 /v1/responses
2026-06-21 21:12:15 +08:00
Wesley Liddick 7b5fbe5197 Merge pull request #3364 from feitianbubu/fix/promo-clear-expiry
fix(promo): allow clearing promo code expiry on edit
2026-06-21 21:12:02 +08:00
Wesley Liddick 04494fd0d9 Merge pull request #3312 from 315944211/codex/update-pnpm-action-setup-node24
chore: update pnpm action setup
2026-06-21 21:11:43 +08:00
Wesley Liddick 945b9b208b Merge pull request #3374 from wucm667/fix/email-binding-enforce-suffix-whitelist
fix(auth): 邮箱身份绑定流程同样校验注册后缀白名单,堵住绕过
2026-06-21 12:56:53 +08:00
404QAQ b0d5592ae2 fix(images): 识别 response.incomplete + 记录软失败上游响应
修复 gpt-image-2 大图/编辑请求 502 报错(社区 issue #2232/#3135/#2516,
现象:upstream did not return image output)。根因是上游生成超时/截断时返回
response.incomplete,旧逻辑只认 error/response.failed,导致:
1) 软失败报成模糊 502 且不触发 failover 换账号重试
2) 上游真实响应未记录,ops_error_logs 里上游信息全空,无法排查

- openAIImagesUpstreamErrorFromSSEPayload 增加 response.incomplete 识别:
  生成超时/截断(max_output_tokens 等) → 可重试 502 触发 failover;
  content_filter/moderation → 400 不重试
- 软失败兜底(无图无标准错误)记录上游诊断摘要到 ops(last_event/status/
  incomplete_reason/body 片段),非流式+流式两条路径一致
- summarizeOpenAIImagesNoOutputBody 提取诊断信息,body 截断上限 1KB

基于官方 v0.1.137 净分支。测试: 5 个新单测 + 图片回归通过 (-tags unit)
2026-06-21 08:34:12 +08:00
harukaandClaude Opus 4.8 5cb8cdd36c test(claude-code): detection recognizes the new-CLI billing block (no cch)
Locks in that Claude Code detection keys on the billing block prefix +
cc_entrypoint=cli, not on the cch field that the new CLI (and now our own
mimicry) no longer sends:

- BillingBlockRecognizedWithoutCCH: an identity-prose-less sub-request whose
  system block is `x-anthropic-billing-header: cc_version=...; cc_entrypoint=cli;`
  (no cch) is still detected as Claude Code.
- NoCCHBlockStillRequiresClaudeCodeUA: dropping cch did not loosen detection —
  a non-claude-cli UA is still rejected, so ClaudeCodeOnly groups can't be spoofed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 08:02:44 -07:00
harukaandClaude Opus 4.8 6cfb7898df fix(claude-mimicry): drop the cch sign to match new Claude Code CLI
Recent Claude Code CLI versions no longer emit the cch=... signature field in
their x-anthropic-billing-header system block (issue #3358). sub2api still
injected cch=00000 when mimicking Claude Code for OAuth accounts and optionally
signed it, so mimicked requests now diverge from real CLI traffic — the opposite
of what the mimicry is for.

- buildBillingAttributionText emits the block without the cch=00000 segment;
  cc_version + cc_entrypoint=cli are kept (detection and Anthropic's first-party
  signal rely on the block, not on cch).
- Retire signing: remove the two enableCCH signBillingHeaderCCH call sites in
  buildUpstreamRequest / buildCountTokensRequest and delete the now-dead
  signBillingHeaderCCH, cchPlaceholderRe, cchSeed, xxHash64Seeded helpers.
- enable_cch_signing is now a documented no-op (kept for backward compat).
- Drop the obsolete signing tests (TestSignBillingHeaderCCH, TestXXHash64Seeded,
  TestSanitizeMustBeBeforeCCHSigning_HashConsistency) and update the prompt test
  to assert the injected block no longer carries cch=.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 08:01:29 -07:00
harukaandClaude Opus 4.8 efffd5d791 test(gateway): Vertex anthropic-beta filtering
Covers the #3358 fix:
- StripsUnsupportedClaudeCodeTokens reproduces the prod 400 — the four Vertex-
  rejected tokens (advisor-tool, prompt-caching-scope, redact-thinking,
  thinking-token-count) plus the identity betas are stripped while whitelisted
  tokens survive. Fails before the builder fix, passes after.
- DropsHeaderWhenAllUnsupported: no anthropic-beta header is sent when every
  client token is filtered out.
- BodySanitizeKeysOnFinalBeta: body.context_management is stripped based on the
  final beta, not the raw client value.
- BlocksViaBetaPolicy: an admin block rule on a Vertex account returns BetaBlockedError.
- TestFilterVertexBetaTokens unit-tests whitelist/drop-set/dedupe/empty.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 07:53:32 -07:00
harukaandClaude Opus 4.8 40e1cc14b3 fix(gateway): filter anthropic-beta on the Vertex Anthropic path (#3358)
Vertex AI's Anthropic endpoint rejects unknown anthropic-beta tokens with
HTTP 400. buildUpstreamRequestAnthropicVertex forwarded the client header
verbatim via the allowedHeaders whitelist, so recent Claude Code CLIs that
send advisor-tool-2026-03-01, prompt-caching-scope-2026-01-05,
redact-thinking-2026-02-12 and thinking-token-count-2026-05-13 broke every
Vertex service_account request, even though plain account-test requests passed.

This is the only upstream builder that bypassed beta filtering: the
OAuth/API-key path uses computeFinalAnthropicBeta and the Bedrock path uses
filterBedrockBetaTokens. Close the gap with a Vertex-specific whitelist
(vertexSupportedBetaTokens) mirroring bedrockSupportedBetaTokens, plus the
existing BetaPolicy block check:

- evaluateBetaPolicy block check (symmetric to resolveBedrockBetaTokensForRequest)
- filterVertexBetaTokens strips policy-filtered + non-whitelisted tokens
- body context_management sanitize now keys on the final beta, not the raw client value
- overwrite the anthropic-beta header after the whitelist copy loop with the final value

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 07:51:24 -07:00
wucm667 ecedc7c8d3 fix(auth): enforce email bind suffix whitelist 2026-06-19 21:17:45 +08:00
cugxuan 0fa604ba71 feat: apply affiliate rebate to subscription payments 2026-06-19 17:47:10 +08:00
skywalker d3dfa28f8e Update CC Switch OpenAI default model 2026-06-19 17:11:25 +08:00
feitianbubu 2dc1387b59 fix(promo): allow clearing promo code expiry on edit 2026-06-18 23:07:25 +08:00
kangjwme 510adf703c feat(scheduling): add opt-in "prefer soonest reset" account selection
Adds a use-it-or-lose-it scheduling strategy: prefer accounts whose
session window resets soonest, so near-reset accounts get drained first
instead of accounts whose reset is still far away.

Both schedulers, opt-in, default behavior unchanged:

- Anthropic (gateway_service.go): new GatewaySchedulingConfig
  .PreferSoonestReset flag. When on, the layered load-aware selection
  inserts a filterBySoonestReset stage (priority -> soonest-reset ->
  load -> LRU). Accounts with no active SessionWindowEnd are treated as
  lowest priority; ties fall through to LRU.

- OpenAI/Codex (openai_account_scheduler.go): new "reset" score weight
  in GatewayOpenAIWSSchedulerScoreWeights. Soonest-reset accounts score
  higher; weight defaults to 0 (no effect).

SessionWindowEnd (upstream 5h/quota ResetsAt) is already carried in the
scheduler snapshot, so no snapshot changes are needed.

Documented in deploy/config.example.yaml. Adds unit tests for the
Anthropic filter and the OpenAI reset factor.
2026-06-18 22:50:46 +08:00
feitianbubu e3e31bd4c5 fix(gateway): auto mode recognize Claude Code IDE clients via any cc_entrypoint 2026-06-18 19:22:10 +08:00
alfadb 89cfe24a0c fix(openai): normalize glm reasoning effort 2026-06-18 19:07:08 +08:00
SavitarC 51d722906c fix(usage): 显示缓存 Token 明细 2026-06-18 14:34:13 +08:00
milesians 952be8711b fix(frontend): refresh custom page document title 2026-06-18 12:30:21 +08:00
欧阳丹 df51edfb06 Preserve OAuth instructions while keeping developer input 2026-06-18 03:15:31 +08:00
FjlI5 bab8a9a93e fix(openai): log /v1/chat/completions upstream endpoint for chat-only API-key accounts
DeriveUpstreamEndpoint maps every OpenAI-platform request to /v1/responses, but
API-key accounts whose upstream only speaks Chat Completions
(!ShouldUseResponsesAPI) are forwarded directly to /v1/chat/completions. The
messages, responses and cyber-policy recording sites derived the endpoint via the
bare GetUpstreamEndpoint, so usage/ops records mislabeled those requests as
/v1/responses. Generalize the existing resolveRawCCUpstreamEndpoint into
resolveOpenAIUpstreamEndpoint and use it at every OpenAI recording site, matching
the already-correct chat-completions client path.
2026-06-18 00:29:04 +08:00
WANG TAIRAN 31640363fc fix(deploy): add :Z SELinux labels to bind mounts
Add the :Z (private unshared) SELinux label to all bind-mounted
directories in docker-compose.local.yml and docker-compose.dev.yml.

On systems with SELinux in Enforcing mode (e.g. Fedora, RHEL, CentOS),
containers using bind mounts are denied access to host directories
because the default 'user_home_t' context is not accessible to
container processes. The :Z label tells the container runtime to
relabel the mount point with 'container_file_t' so the container
can read/write it.

Named volumes in docker-compose.yml are not affected because the
runtime already handles their labels automatically.

Fixes permission-denied errors on:
- ./data:/app/data
- ./postgres_data:/var/lib/postgresql/data
- ./redis_data:/data
2026-06-17 12:38:40 +08:00
a0yarkandCursor 8c4a43cf72 fix(gemini): satisfy schema cleanup test lint
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-16 23:19:45 +08:00