Commit Graph
5411 Commits
Author SHA1 Message Date
SPHINX998andClaude Opus 5.5 7a767d5ce8 fix(apicompat): pair legacy chat function calls with their results
Legacy Chat function calling has no call IDs: the assistant turn carries
function_call and the role=function result refers to it only by name.
ChatCompletionsToResponses ignored the assistant function_call and used
the function name as the result's call_id, so the Responses upstream got
a function_call_output with no matching function_call and rejected the
replayed history.

Emit a function_call item for each legacy assistant call and give the
following same-name result the same call_id. IDs follow message order
(call_legacy_N), so the same history converts to the same input across
turns, and skip IDs already used by tool_calls. A result with no
preceding call keeps the previous name-based call_id.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 21:55:31 +08:00
SPHINX998andClaude Opus 5.5 9a8ab4f88e fix(apicompat): keep chat developer messages as developer role
chatMessageToResponsesItems only mapped "system" explicitly and sent every
other unknown role through the user converter, so Chat Completions
developer messages reached the Responses upstream as user turns and lost
their instruction priority.

The Responses API accepts role "developer" in input messages; convert it
like a system message and keep the original role. Downstream converters
already understand it (ResponsesToAnthropicRequest folds developer into
the system prompt, the Codex transform forwards it unchanged).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 21:53:30 +08:00
SPHINX998andClaude Opus 5.5 f3d752693f fix(apicompat): flatten named chat tool_choice for Responses
Chat Completions names a forced function as
{"type":"function","function":{"name":"X"}}, while the Responses API
expects {"type":"function","name":"X"}. ChatCompletionsToResponses passed
the Chat shape through unchanged, so OpenAI API-key accounts on
/v1/chat/completions forwarded the nested shape to /v1/responses and the
upstream rejected the request.

Flatten the named Chat choice; strings and objects already in the
Responses shape are kept as-is.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 21:51:37 +08:00
lyy0709 71e97d1353 fix(models): enforce account mappings for remote Codex discovery 2026-10-01 03:30:56 -07:00
lyen1688 ad05eda157 依据正常交互式 CLI 抓包修正 Grok 身份头 2026-10-01 12:17:27 +08:00
lyen1688 fdce5b8d01 修复 Grok CLI 版本门槛并对齐官方无界面请求头 2026-10-01 12:04:39 +08:00
SPHINX998andClaude Opus 5.5 73381fc9c7 fix(openai-ws): bill each later turn with the current group pricing
The Responses WebSocket handler reads the API key auth snapshot once at
connect and reuses it for every turn: RecordUsage takes the group pricing
from that snapshot, and the profit gate reinstalled in BeforeTurn takes
its selling price from the connect-time ctxkey.Group. A group price
change therefore never reaches an open connection until the client
reconnects, while HTTP requests pick it up once the auth cache entry is
invalidated.

Each later turn now re-reads the key through APIKeyService.GetByKey (the
auth cache the middleware uses) in BeforeTurn. When the key ID, group ID,
group platform and subscription type are unchanged, the fresh group is
swapped into ctxkey.Group for the turn's profit gate and passed to that
turn's RecordUsage. A failed lookup or a key moved to another group keeps
the connection snapshot.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 11:27:35 +08:00
YahooYuan666 27f4a1780f test(grok): cover empty response.completed failover for PlatformGrok
Adds two regression cases mirroring the #5009 OpenAI ones for Grok
platform accounts (issue #7774):

- TestOpenAIResponsesEmptyCompletedFailsOverGrok: an empty
  response.completed on the forwardGrokResponses streaming path must
  produce UpstreamFailoverError with an empty client body. Verified to
  FAIL on main without the guard extension and PASS with it.
- TestOpenAIResponsesEmptyCompletedWithOutputSucceedsGrok: normal Grok
  streams with semantic output and usage are untouched.

Fixes #7774
2026-10-01 03:01:43 +08:00
YahooYuan666 3613174cf6 fix(grok): fail over silent-refusal empty response.completed on Grok Responses streams
Issue #5009 added a failover guard for OpenAI Responses streams that
terminate with an empty response.completed (no output, no usage, no
error), but the guard is gated on PlatformOpenAI. Grok platform
accounts reach the same handler (forwardGrokResponses ->
handleStreamingResponseWithReasoning) and exhibit the same upstream
silent-refusal shape, so their empty completions are relayed to the
client as successful empty turns: Responses SDK clients map the empty
completed to finishReason stop and accept the turn with no retry.

Extend the guard to PlatformGrok, matching the unconditional guard on
the passthrough path (openai_gateway_passthrough.go).

Fixes #7774
2026-10-01 02:16:22 +08:00
Wesley Liddick 96f4c115c9 Merge pull request #7726 from william-drakemond/feat/claude-reset-redeem
feat(claude): 手动兑换 Claude 重置次数 / Manually redeem Claude limit resets
2026-09-30 14:33:23 +08:00
Wesley Liddick 327c322184 Merge pull request #7730 from lyy0709/codex/gpt61-sol-codex-plans
feat(openai): support GPT-6.1 Sol, Codex plans and Astra Ultrafast
2026-09-30 14:32:59 +08:00
Wesley Liddick 4f0c079e27 Merge pull request #7681 from william-drakemond/fix/balance-inflight-reservation
fix(billing): Redis 在途预留,防止余额模式并发透支
2026-09-30 14:31:04 +08:00
shaw 017bbcb901 chore(api-key): 创建频率默认值调整为每小时 60 次 2026-09-30 13:55:37 +08:00
shaw 9ecb340822 feat(api-key): 增加 API Key 创建数量与频率限制
- 新增 api_key_create.max_active_per_user(默认 200)与
  api_key_create.max_per_user_per_hour(默认 20),0 表示不限制
- 创建频率按固定一小时窗口统计,自定义与自动生成的 Key 统一计入
- 创建计数独立于 Key 生命周期,删除或修改 Key 不再重置计数
- 移除不再使用的 DeleteCreateAttemptCount 缓存方法
2026-09-30 13:44:02 +08:00
Wesley Liddick a0f41f95a0 Merge pull request #7579 from feeeei/fix/claude-code-only-fallback-openai-compat
fix(gateway): Claude Code 限制分组的 OpenAI 兼容入口改走降级分组,不再直接 403
2026-09-30 10:47:08 +08:00
lyy0709 e6d191a83f fix(openai): support Astra Ultrafast capabilities and pricing 2026-09-29 11:50:08 -07:00
lyy0709 c91bb6124a feat(codex): recognize current subscription plans 2026-09-29 11:50:08 -07:00
lyy0709 9688571a83 feat(models): support GPT-6.1 Sol 2026-09-29 11:49:26 -07:00
William DrakeandClaude Opus 5.5 dc2efc6b9a fix(claude): tighten reset redeem outcome handling (review R1)
- R1-1: an explicit upstream "unavailable" answer now fences the organization
  for 15 minutes (CLAUDE_RESET_UPSTREAM_UNAVAILABLE) instead of 24h; ambiguous
  outcomes keep the 24h fence. Frontend tells the operator to retry later.
- R1-2: rotate the idempotency key after definite pre-claim refusals (busy,
  not available, unresolved, upstream unavailable); map IDEMPOTENCY_IN_PROGRESS
  and IDEMPOTENCY_RETRY_BACKOFF to clear zh/en messages.
- R1-3: redeem reason and cleared windows pass only through explicit
  allowlists, so upstream values can never echo a grant ID to clients.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-29 14:21:10 -04:00
196b47757a feat(claude): redeem native limit resets with server-selected grant
POST /admin/accounts/:id/claude/reset-credits/redeem (Idempotency-Key
required, same middleware chain as the Codex reset-quota route). The server
re-queries eligibility and claims only the upstream next_grant_id when it is
redeemable; grant/org IDs never reach the client. Claims are serialized by
account and organization Redis leases, and a durable per-organization fence
in the idempotency table blocks further redemption after an unconfirmed
outcome (settles after 24h). Ported from upstream PR #7591.

Co-authored-by: korkin25 <eugeny.kuzakov@gmail.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-29 11:41:33 -04:00
wucm667 27de27ac64 fix(risk-control): remove deleted groups from moderation scope 2026-09-29 23:24:45 +08:00
wucm667 a4ea8059b7 fix(openai): recover rejected encrypted reasoning signatures 2026-09-29 16:42:02 +08:00
Wesley Liddick fe9dadfc7d Merge pull request #7679 from PMExtra/feat/risk-control-allowlist
feat: 新增可信用户风控白名单,保留审计并免除本地自动处罚
2026-09-29 15:53:18 +08:00
Wesley Liddick 052e08e764 Merge pull request #7684 from william-drakemond/feat/claude-reset-credits-ui
feat(claude): 查询 Claude 重置次数(只读),UI 复用 Codex 次数按钮 / Read-only Claude reset credit count, reusing Codex count UI
2026-09-29 15:50:53 +08:00
Wesley Liddick 1f955af353 Merge pull request #7380 from wucm667/fix/issue-7377-forward-stream-usage
fix(gateway): forward received chat stream usage
2026-09-29 15:44:10 +08:00
Wesley Liddick ccb027b515 Merge pull request #7378 from wucm667/fix/issue-7368-composite-ws-routing
fix(gateway): resolve composite routes for websocket aliases
2026-09-29 15:43:52 +08:00
Wesley Liddick 883a53fea0 Merge pull request #7643 from wucm667/fix/issue-7637-antigravity-compat-keepalive
fix(antigravity): keep compat streams alive before first content
2026-09-29 15:43:19 +08:00
Wesley Liddick b35a611538 Merge pull request #7646 from wucm667/feat/issue-7642-dashboard-spending-trend
feat(dashboard): toggle recent usage between tokens and spending
2026-09-29 15:43:07 +08:00
Wesley Liddick 1dca86f287 Merge pull request #7701 from wucm667/fix/issue-7699-tool-rewrite
fix(anthropic): rewrite tool names in one body pass
2026-09-29 15:42:45 +08:00
wucm667 27a9421e6a fix(anthropic): rewrite tool names in one body pass 2026-09-29 15:00:16 +08:00
William DrakeandClaude Opus 5.5 860782e6fb fix(claude): harden reset credit projection and cell state (review R2)
- Drop selection_token (unsalted hash of a short grant ID, reversible) and
  claudeGrantSelection from the read-only DTO; assert it never serializes.
- query() returns only the projected result; no raw upstream block escapes.
- Clear a cooldown_until that is already past, using the same `now` as the
  redeemable check; the cell also shows the cooldown only when in the future.
- Invalidate in-flight queries when account id, platform or type changes.
- Sort the earliest-expiry fallback by parsed timestamp, invalid/missing last.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-28 20:59:39 -04:00
korkin25 48970f61b4 fix(claude): explicitly close status HTTP response 2026-09-28 18:04:14 -04:00
korkin25 6a8f921650 feat(claude): expose sanitized native reset credit status 2026-09-28 18:04:13 -04:00
haruka 2cc459e24f style: satisfy Sonnet 5.5 lint checks 2026-09-29 05:20:04 +08:00
William DrakeandClaude Opus 5.5 475c0839c6 style: gofmt
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-28 17:08:27 -04:00
William DrakeandClaude Opus 5.5 b886fb8edb fix(billing): read account-level mappings from scheduler snapshot only when unpriced
- only consult account mappings after primary/channel candidates fail to price (or composite)
- source mappings from the scheduler snapshot (same pool as account selection), no direct DB query and no per-model cache
- ungrouped API keys use the scheduler's ungrouped pool
- no negative caching: new mappings take effect on the next request

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-28 17:08:13 -04:00
haruka 8490a81867 feat: support Claude Sonnet 5.5 2026-09-29 04:55:07 +08:00
William DrakeandClaude Opus 5.5 652a7d74f9 fix(billing): estimate unpriced aliases via billing fallback models; don't renew expired reservations
- Inflight estimate now mirrors billableModelWithFallback: when the primary
  billing model has no price, fall back to the channel-mapped model (also for
  billing_model_source=requested) and to account-level mapped models (max over
  schedulable accounts in the group, cached 30s).
- Composite groups also consider concrete account-mapped models (max) so the
  alias' family fuzzy price cannot underestimate.
- Renew Lua refuses (and purges) members whose score <= now.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-28 16:49:29 -04:00
William DrakeandClaude Opus 5.5 52c123e6f4 fix(billing): hold inflight reservation until billing lands; price estimate like billing; wire remaining endpoints
Address gaps found in adversarial verification of the in-flight balance
reservation:

- Early release: the reservation is now a ref-counted handle carried on the
  request context. Billing tasks submitted via submit*UsageRecordTask take a
  ref and return it only after RecordUsage has run; with reservation enabled
  the balance cache is deducted synchronously inside the billing task, so the
  reservation is released only after admission can see the deduction. Dropped
  tasks return their ref immediately. The reservation is renewed every ttl/3
  while the handler runs; after the handler returns it is no longer renewed,
  so the post-handler hold is bounded by one TTL.
- Estimate: use the same billing model (channel mapping + BillingModelSource,
  max over requested/mapped when ambiguous) and ModelPricingResolver (group
  ModelPricing incl. wildcards, channel per_request/image/video/token
  overrides), default/group/user rate + peak multiplier, plus image, video,
  audio and search surcharge kinds. Unpriced requests fail open with a
  throttled log; new billing.inflight_reservation.fail_closed_on_unpriced
  (default false) rejects them instead.
- Wire images, embeddings, web/x search, alpha search, grok media generation,
  grok realtime/voice, OpenAI-platform /v1/messages and ResponsesWebSocket
  (session-level reservation with renewal).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-28 16:14:43 -04:00
William DrakeandClaude Opus 5.5 43f88c99aa fix(billing): reserve in-flight balance to prevent concurrent overdraft
Balance-mode admission only checked cached balance > threshold, so N
concurrent expensive requests could all pass and overdraw the user.
Add a Redis in-flight reservation: at admission estimate a conservative
per-request cost (input estimate + output price x max_tokens, times rate
multiplier) and atomically require cached_balance - inflight >= estimate
(first in-flight request always admitted). Reservations are released via
defer on every path and expire by TTL. Fails open on estimation / Redis
errors. Configurable via billing.inflight_reservation (enabled by default).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-28 14:20:49 -04:00
PMExtra 6a69dd051a feat: add risk control user allowlist 2026-09-29 01:50:21 +08:00
William DrakeandClaude Opus 5.5 b1ce3c6d59 fix(email): atomic verify-code attempts and hashed single-use reset tokens
- Verification code attempts (register + notify email) are now reserved via
  an atomic Redis INCR (Lua) before comparing, so concurrent wrong guesses
  cannot exceed the 5-attempt cap.
- Password reset tokens are stored as SHA-256 hashes and consumed with an
  atomic Lua compare-and-delete, so a token can only be used once even under
  concurrent requests.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-28 12:26:28 -04:00
William DrakeandClaude Opus 5.5 d359196adc fix(antigravity): sanitize upstream error body before returning to client
The antigravity Gemini forward path returned the raw upstream Google error
body to end users, which can contain consumer project numbers, GCP project
IDs and service account emails. Return a Gemini-style error body with only
code/status and a scrubbed message; raw body stays in ops logging.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-28 12:23:13 -04:00
William DrakeandClaude Opus 5.5 cf19f30d26 fix(payment): rate-limit anonymous public order verify endpoint
POST /api/v1/payment/public/orders/verify is unauthenticated and still used
by PaymentResultView as a fallback, so keep it but cap it at 20 req/min per
client IP (fail-open on Redis errors) to make out_trade_no enumeration
impractical without affecting users mid-payment.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-28 12:03:17 -04:00
Yuan LiuandClaude Opus 5.5 f48866958b fix(openai): only inject the Codex image generation bridge for OpenAI accounts
The hosted image_generation tool is OpenAI-only. With the global switch or
a channel's openai bridge flag enabled, isCodexImageGenerationBridgeEnabled
also returned true for Kimi / DeepSeek / MiniMax accounts that forward
natively over Responses, so their upstream requests carried the tool,
tool_choice and the bridge instructions and were rejected with 400.

Return false for nil and non-OpenAI accounts before evaluating the
account / channel / global settings.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 14:59:35 +08:00
wucm667 aa3a3ea787 feat(dashboard): toggle recent usage between tokens and spending 2026-09-28 14:48:42 +08:00
wucm667 bd48b2b2d2 fix(antigravity): keep compat streams alive before first content 2026-09-28 12:15:27 +08:00
Wesley Liddick 4c00df2e01 Merge pull request #7638 from Achordchan/fix/anthropic-structured-output-beta
fix(anthropic): preserve requested structured outputs beta
2026-09-28 10:52:52 +08:00
Wesley Liddick b5298fdde8 Merge pull request #7538 from TouHouQing/fix/claude-thinking-disabled-openai
fix(openai): honor disabled thinking in Claude Messages bridge
2026-09-28 10:51:30 +08:00
Wesley Liddick 5a350108c4 Merge pull request #7607 from ResidualBlood/fix/antigravity-malformed-function-call-retry
fix(antigravity): trigger failover retry on empty stream caused by MALFORMED_FUNCTION_CALL
2026-09-28 10:50:53 +08:00