Repository navigation
chore: merge upstream v0.2.11 preserving downstream contracts - #220
Merged
Merged
Conversation
分组开启 claude_code_only 并配置降级分组时,/v1/messages 会在选号入口的 checkClaudeCodeRestriction 中把非 Claude Code 请求解析到降级分组;但 GatewayHandler.ChatCompletions / Responses 在 handler 层只要分组开了 claude_code_only 就直接返回 403,不看降级分组,请求到不了调度层:只会发 Chat Completions / Responses 的第三方客户端拿这类分组的 Key 一律 403。 - handler 层提前拒绝只在未配置降级分组时生效,拒绝的状态码与文案不变 - 配置了降级分组时请求继续,由 SelectAccountWithLoadAwareness 解析到降级分组 选号,再经已有的 Chat Completions / Responses ⇄ Anthropic Messages 转换转发; service 层无改动 - 与 /v1/messages 的降级语义一致:选号与渠道限制预检按降级分组,计费与用量 记录仍按 API Key 所属分组 新增 handler 单测覆盖两个入口在有/无降级分组时的行为。
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>
…timate 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>
…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>
…y 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>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…t button The Claude reset credit query rendered as a bare "Claude 重置额度" link, which reads like a redeem action, and dumped raw ISO timestamps and window keys. Render it like OpenAIQuotaResetCell instead: a read-only "次数 N" button sharing one row with the local query button via a #pre-actions slot, plus an expiry chip whose tooltip carries the grant label, cleared windows and limit requirement. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
- Show resets held (sum of resets_left) on the count button, matching the Codex count; flag "not usable now" when none is redeemable yet, instead of showing 0 while a grant exists. - Pick the redeemable (next) grant for the expiry chip, falling back to the earliest expiry, rather than assuming credits[0] is next. - Render a single ClaudeResetCreditsCell across usage states so a query started while usage loads is not lost on remount. - Drop an unrelated google/subcommands go.sum entry. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
- 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>
…tream feat: support Claude Sonnet 5.5
…rite fix(anthropic): rewrite tool names in one body pass
…stream-usage fix(gateway): forward received chat stream usage
…eset-credits-ui feat(claude): 查询 Claude 重置次数(只读),UI 复用 Codex 次数按钮 / Read-only Claude reset credit count, reusing Codex count UI
fix: 移除 OpenAI 分组 Codex 配置中的模型目录依赖
…list feat: 新增可信用户风控白名单,保留审计并免除本地自动处罚
…lient-tabs feat: Claude Code 专用分组仅展示受支持的客户端入口
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 Wei-Shaw#7591. Co-authored-by: korkin25 <eugeny.kuzakov@gmail.com> Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Orange 「重置」 button mirrors the Codex cell: enabled only after a query shows a redeemable credit, danger confirm, one idempotency key per confirmation reused on retry, outcome-specific messages, then refreshes credits and the usage row. Ported from upstream PR Wei-Shaw#7591. Co-authored-by: korkin25 <eugeny.kuzakov@gmail.com> Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
- 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>
… R1-4)
- Map five_hour/seven_day/seven_day_overage_included to 5h/7d/7d overage
(raw key fallback) in the confirm message, credit tooltip and success message.
- Align zh/en confirm copy with the Codex dialog; {count} is the count
remaining after this use.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…lback-openai-compat fix(gateway): Claude Code 限制分组的 OpenAI 兼容入口改走降级分组,不再直接 403
- 新增 api_key_create.max_active_per_user(默认 200)与 api_key_create.max_per_user_per_hour(默认 20),0 表示不限制 - 创建频率按固定一小时窗口统计,自定义与自动生成的 Key 统一计入 - 创建计数独立于 Key 生命周期,删除或修改 Key 不再重置计数 - 移除不再使用的 DeleteCreateAttemptCount 缓存方法
feat(api-key): 增加 API Key 创建数量与频率限制
…nflight-reservation fix(billing): Redis 在途预留,防止余额模式并发透支
…l-catalog feat(codex): 支持 model_catalog_url 远程模型目录配置
…lans feat(openai): support GPT-6.1 Sol, Codex plans and Astra Ultrafast
…eset-redeem feat(claude): 手动兑换 Claude 重置次数 / Manually redeem Claude limit resets
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Merge official Sub2API v0.2.11 (96f4c11; upstream/main 42bc7f6 adds the VERSION sync) into downstream main 3fa9b52 (v0.2.10-codexrip.4). The preceding ours-merge records the squashed v0.2.10 integration, so the merge base is v0.2.10 and 18 files conflicted. The decision record is .downstream/upstream-review-v0.2.11.md. - Adopt the in-flight balance reservation (enabled by default), API key creation limits (200 active, 60 per hour), remote Codex model catalogs and the Claude Code-only fallback for OpenAI-compatible entries. - Compose the reservation hand-off with the downstream quota-activity receipt in every usage-task submitter. - Keep capacity manifest-derived: the GPT-6.1 Sol client descriptor carries no context window, and dated GPT-6.x snapshots pass through. - Keep the version-free /backend-api/codex/models manifest download; model_catalog_url uses upstream's /v1/models. - Adopt upstream Codex plan names, GPT-6.1 Sol and Astra Ultrafast pricing alongside the GPT-5.6 fallbacks and DeepSeek pricing identity. - VERSION 0.2.11 and upstream-base v0.2.11. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- OpenCode config: drop the static GPT-6.1 Sol limit that auto-merged from upstream Wei-Shaw#7730. OpenCode limits come only from resolver-tagged manifest rows (v0.2.9 capacity contract); the spec asserts it stays unset. - Claude limit reset (Wei-Shaw#7726): the irreversible reset button and its confirmation follow canInteract like OpenAIQuotaResetCell. Read-only table and card cells keep only the reset-count query they already had, so `redeemed` can no longer start an unbatched active usage query from the table. - Pricing: unrecognized gpt-6.x names (dated GPT-6.1 Sol snapshots, gpt-6.1-luna, gpt-6.1) stay unpriced like unknown gpt-6-* names instead of falling back to the gpt-5.4 default card. - Claude reset claim: log what the sanitized response drops (transport error, upstream HTTP status, bounded raw body, unknown result or reason) so administrators can see why a claim stayed unconfirmed. The response contract is unchanged. - Record the fixes in .downstream/upstream-review-v0.2.11.md. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Integrates upstream v0.2.11 (24 commits, about 90 files). The decisions are recorded in
.downstream/upstream-review-v0.2.11.md. v0.2.10 had been integrated by a squash (#215), so it is first recorded as merged (merge -s ours), which makes the real base v0.2.10; that leaves 18 text conflicts, all resolved.Owner decisions (2026-10-01): adopt upstream for all of them.
billing.inflight_reservation.enabled=true). Every billed entry, including the fork's Image Studio/async images,/backend-api/codex, Seedance and the extra platforms, reserves through the upstream handlers. Exempt: count_tokens, models, usage, live/realtime calls, batch images and media polling, all as upstream. The fork's quota-activity receipt and the reservation hand-off are combined inwrapTrackedUsageRecordTask.model_catalog_urlin the Codex CLI config. The manifest is still served by the fork's endpoint and capacity stays manifest-derived. The opencode tab keeps its manifest fetch.Fork contracts kept: manifest-derived capacity (the hard-coded 1,050,000 for gpt-6.1-sol in the opencode config is removed, and the offline catalog test asserts no context fields); the GPT-5.6 family price fallbacks and DeepSeek identity; the v0.2.10 WebSocket routing; the cyber log-only gate; full admin visibility (redeem failures log the upstream status and body to the server and Ops logs); the stacked tables, palette C,
#overlay-rootand static data-ui.Independent review found 2 major and 2 minor issues, all fixed in
df0df96c2:Validation
GOOS=linux go build ./...passes.vue-tscexit 0. Specs pass: UseKeyModal 31, ClaudeResetCreditsCell 27, AccountUsageCell 56 + 15, codex api, PlatformTypeBadge plans, credentialsBuilder. eslint passes on changed files.model_catalog_url.🤖 Generated with Claude Code