Skip to content

chore: merge upstream v0.2.11 preserving downstream contracts - #220

Merged
HTExplicit merged 61 commits into
mainfrom
sync/upstream-0.2.11
Oct 1, 2026
Merged

HTExplicit merged 61 commits into
mainfrom
sync/upstream-0.2.11

Conversation

@HTExplicit

Copy link
Copy Markdown
Owner

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.

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-root and static data-ui.

Independent review found 2 major and 2 minor issues, all fixed in df0df96c2:

  • the hard-coded 6.1 Sol capacity
  • the irreversible redeem button showing in read-only cells
  • dated 6.1 snapshot names falling back to the gpt-5.4 default price, now fail-closed like the other unknown GPT-6 names
  • redeem failures dropping the upstream detail

Validation

  • GOOS=linux go build ./... passes.
  • Focused unit tests pass: in-flight reservation and usage-record hand-off, Claude Code-only fallback, GPT-6.1 Sol and GPT-6/5.6 pricing, Codex catalog, API key create limits, Claude reset redeem.
  • golangci-lint 2.13 on the changed packages: 0 issues.
  • vue-tsc exit 0. Specs pass: UseKeyModal 31, ClaudeResetCreditsCell 27, AccountUsageCell 56 + 15, codex api, PlatformTypeBadge plans, credentialsBuilder. eslint passes on changed files.
  • Not run: full suites, real model calls, a real Codex 0.156+ client against model_catalog_url.

🤖 Generated with Claude Code

wucm667 and others added 30 commits September 21, 2026 12:58
分组开启 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>
…rite

fix(anthropic): rewrite tool names in one body pass
Wei-Shaw and others added 27 commits September 29, 2026 15:44
…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>
@HTExplicit HTExplicit added the upstream-reviewed Human upstream overlap review completed label Oct 1, 2026
@HTExplicit
HTExplicit merged commit af647ec into main Oct 1, 2026
21 of 22 checks passed
@HTExplicit
HTExplicit deleted the sync/upstream-0.2.11 branch October 1, 2026 04:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

upstream-reviewed Human upstream overlap review completed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

9 participants