Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
129 commits
Select commit Hold shift + click to select a range
ef51690
fix(antigravity): preserve string const constraints in tool schemas
Achordchan Sep 20, 2026
b13200d
fix(gateway): forward received chat stream usage
wucm667 Sep 19, 2026
ece1ecc
fix(gateway): normalize streamed Anthropic usage
wucm667 Sep 21, 2026
f867e3e
test(antigravity): check schema properties type assertion
Achordchan Sep 22, 2026
1af3269
fix(groups): clean up modal listeners and pending searches
ouchihao Sep 22, 2026
b5b5f1f
fix(model-plaza): apply independent video billing multipliers
Achordchan Sep 23, 2026
17ce21c
test(scheduler): pass explicit multiplier pointer to snapshot
Achordchan Sep 23, 2026
ecba49f
fix(gateway): let OpenAI passthrough accounts supplement model discov…
HuntercodeT Sep 23, 2026
0416c9e
fix(gateway): avoid ambiguous late-cache input subtraction
wucm667 Sep 23, 2026
13fcfc0
fix(gateway): resolve composite routes for websocket aliases
wucm667 Sep 19, 2026
e878ab7
fix(gateway): constrain account model route ownership
wucm667 Sep 21, 2026
453b796
fix(gateway): enforce composite model ownership in legacy scheduling
wucm667 Sep 23, 2026
2fc9166
fix(apicompat): honor disabled thinking in OpenAI bridges
TouHouQing Sep 23, 2026
a3eb7ef
chore: sync VERSION to 0.2.8 [skip ci]
github-actions[bot] Sep 23, 2026
e2c35cf
fix(account): prevent whitelist model mapping conflicts
wucm667 Sep 23, 2026
00f6f4e
fix(ccswitch): stop usage query from requesting /v1/v1/usage
helianthus1 Sep 23, 2026
1ca7b2a
fix(scheduler): keep auto reset-credit fields in the scheduler projec…
Yukinoshita-Yukino Sep 23, 2026
5979521
fix(openai): inject DeepSeek reasoning placeholder for OpenCode Zen u…
william-drakemond Sep 22, 2026
82b092f
fix(apicompat): treat every GPT generation from 5 on as a reasoning m…
SPHINX998 Sep 24, 2026
090b2cc
fix(apicompat): recover streamed text when the final output message i…
SPHINX998 Sep 24, 2026
97bdde3
fix(apicompat): keep tool arguments sent on content_block_start
SPHINX998 Sep 24, 2026
a2007db
fix(billing): keep catalog image prices when channel pricing leaves t…
Yukinoshita-Yukino Sep 24, 2026
9461196
fix(openai): keep Responses support unknown when the probe model is m…
SPHINX998 Sep 24, 2026
1d126c8
fix(openai): require a successful completion before billing an alpha …
SPHINX998 Sep 24, 2026
b31b435
fix(gateway): Claude Code 限制分组的 OpenAI 兼容入口改走降级分组,不再直接 403
feeeei Sep 24, 2026
ca2746f
fix(antigravity): forward chat PDF file parts as Gemini inlineData
ResidualBlood Sep 24, 2026
f0ebc18
feat(groups): allow wildcard positions in model allowlist
wucm667 Sep 25, 2026
8581036
fix(auto-reset): stop re-querying confirmed no-credit accounts and ba…
Yukinoshita-Yukino Sep 25, 2026
11ffee5
fix: recognize OpenRouter Claude Opus 5.5 alias for billing
far5man Sep 25, 2026
630639f
test: preserve Opus 5.5 thinking for OpenRouter alias
far5man Sep 25, 2026
52c2f4c
fix(antigravity): trigger failover retry on empty stream caused by MA…
ResidualBlood Sep 26, 2026
afbe51a
Merge upstream main and retain string const enum intersection
Achordchan Sep 26, 2026
510b680
Merge upstream main and retain upstream scheduler test fix
Achordchan Sep 26, 2026
f4f8ff0
fix(gateway): 客户端断开统一标记 499,不再被记成 502/200 上游错误
feeeei Sep 26, 2026
c802c45
fix(deploy): 用 exec 列表形式重写 redis command
nhirsama Sep 26, 2026
994048e
fix(ui): show reset countdown for idle usage windows with known reset…
ZeroClover Sep 26, 2026
3c5ea29
fix(billing): keep zero-cost usage log when Free Fast has no pricing
yujunkim Sep 26, 2026
7626110
fix(openai): break websocket chain on context rollover
UnlastingR Sep 26, 2026
8b95dc9
fix(openai): preserve Responses multi-agent beta
korkin25 Sep 27, 2026
fd7af1d
fix(billing): honor long-context gate in account stats pricing
SavitarC Sep 27, 2026
14483c9
fix(ccswitch): preserve Codex provider root endpoint
Sep 27, 2026
02088be
fix(openai): retain quota pause until known future reset
wucm667 Sep 27, 2026
6b40449
fix(antigravity): exclude Signature-only events from meaningful data …
ResidualBlood Sep 27, 2026
f652d0e
fix(keys): use ~/ for the Codex model catalog path on Windows
Sterbennicht Sep 27, 2026
82d2552
fix(apicompat): mark converted role items as messages
wucm667 Sep 28, 2026
f45126c
Merge pull request #7622 from Anti2077/fix/ccswitch-codex-root-endpoint
Wei-Shaw Sep 28, 2026
512aa8b
fix(setup): omit obsolete rate limit defaults
wucm667 Sep 28, 2026
35d2ef0
Merge pull request #7636 from wucm667/fix/issue-7631-remove-unused-ra…
Wei-Shaw Sep 28, 2026
ec334fd
Merge pull request #7628 from Sterbennicht/fix/codex-windows-catalog-…
Wei-Shaw Sep 28, 2026
99d86e4
Merge pull request #7611 from ZeroClover/fix/usage-bar-idle-reset-time
Wei-Shaw Sep 28, 2026
89e9976
Merge pull request #7497 from ouchihao/codex/bug-group-modal-cleanup
Wei-Shaw Sep 28, 2026
66f07ef
Merge pull request #7549 from helianthus1/fix/ccswitch-usage-v1-path
Wei-Shaw Sep 28, 2026
be64b55
Merge pull request #7635 from wucm667/fix/issue-7629-responses-messag…
Wei-Shaw Sep 28, 2026
6655f4e
Merge pull request #7393 from Achordchan/fix/antigravity-string-const
Wei-Shaw Sep 28, 2026
51a10e4
Merge pull request #7585 from ResidualBlood/fix/antigravity-pdf-inlin…
Wei-Shaw Sep 28, 2026
00ae7c3
Merge pull request #7570 from SPHINX998/fix/apicompat-tool-input-on-b…
Wei-Shaw Sep 28, 2026
8aa7fb8
Merge pull request #7569 from SPHINX998/fix/apicompat-recover-empty-f…
Wei-Shaw Sep 28, 2026
11608c5
Merge pull request #7568 from SPHINX998/fix/apicompat-gpt6-reasoning-…
Wei-Shaw Sep 28, 2026
dc4874a
Merge pull request #7613 from yujunkim/fix/openai-free-fast-missing-p…
Wei-Shaw Sep 28, 2026
6906062
Merge pull request #7597 from far5man/codex/opus55-openrouter-alias-main
Wei-Shaw Sep 28, 2026
06f70a6
Merge pull request #7572 from SPHINX998/fix/openai-alpha-search-requi…
Wei-Shaw Sep 28, 2026
9375e28
Merge pull request #7571 from SPHINX998/fix/openai-probe-model-unavai…
Wei-Shaw Sep 28, 2026
90ae81e
Merge pull request #7624 from wucm667/fix/issue-7620-future-quota-reset
Wei-Shaw Sep 28, 2026
e4b3444
Merge pull request #7619 from SavitarC/fix/account-stats-long-context…
Wei-Shaw Sep 28, 2026
37f35b1
Merge pull request #7524 from Achordchan/fix/plaza-video-rate
Wei-Shaw Sep 28, 2026
f592b33
Merge pull request #7595 from wucm667/feat/issue-7587-allowlist-glob
Wei-Shaw Sep 28, 2026
1ff2bd6
Merge pull request #7573 from Yukinoshita-Yukino/fix/channel-image-pr…
Wei-Shaw Sep 28, 2026
790723e
Merge pull request #7610 from nhirsama/fix/redis-command-exec-form
Wei-Shaw Sep 28, 2026
79c18ec
Merge pull request #7609 from feeeei/fix/client-disconnect-499
Wei-Shaw Sep 28, 2026
ff6b85e
Merge pull request #7615 from UnlastingR/fix/codex-ws-context-rollove…
Wei-Shaw Sep 28, 2026
68c2f5a
fix(anthropic): preserve requested structured outputs beta
Achordchan Sep 28, 2026
93c3da0
Merge pull request #7555 from Yukinoshita-Yukino/fix/openai-auto-rese…
Wei-Shaw Sep 28, 2026
8616d4e
Merge pull request #7617 from korkin25/fix/responses-beta-upstream
Wei-Shaw Sep 28, 2026
b3c7038
Merge pull request #7526 from HuntercodeT/fix/passthrough-model-disco…
Wei-Shaw Sep 28, 2026
1df5c39
Merge pull request #7562 from william-drakemond/fix/opencode-zen-deep…
Wei-Shaw Sep 28, 2026
5a35010
Merge pull request #7607 from ResidualBlood/fix/antigravity-malformed…
Wei-Shaw Sep 28, 2026
b5298fd
Merge pull request #7538 from TouHouQing/fix/claude-thinking-disabled…
Wei-Shaw Sep 28, 2026
4c00df2
Merge pull request #7638 from Achordchan/fix/anthropic-structured-out…
Wei-Shaw Sep 28, 2026
9a62841
chore: sync VERSION to 0.2.9 [skip ci]
github-actions[bot] Sep 28, 2026
bd48b2b
fix(antigravity): keep compat streams alive before first content
wucm667 Sep 28, 2026
aa3a3ea
feat(dashboard): toggle recent usage between tokens and spending
wucm667 Sep 28, 2026
2840cde
feat(frontend): hide unsupported clients for Claude Code-only groups
PMExtra Sep 23, 2026
6a69dd0
feat: add risk control user allowlist
PMExtra Sep 24, 2026
6073704
fix: omit Codex model catalog for OpenAI groups
PMExtra Sep 23, 2026
43f88c9
fix(billing): reserve in-flight balance to prevent concurrent overdraft
william-drakemond Sep 28, 2026
52c123e
fix(billing): hold inflight reservation until billing lands; price es…
william-drakemond Sep 28, 2026
652a7d7
fix(billing): estimate unpriced aliases via billing fallback models; …
william-drakemond Sep 28, 2026
8490a81
feat: support Claude Sonnet 5.5
StarryKira Sep 28, 2026
b886fb8
fix(billing): read account-level mappings from scheduler snapshot onl…
william-drakemond Sep 28, 2026
475c083
style: gofmt
william-drakemond Sep 28, 2026
2cc459e
style: satisfy Sonnet 5.5 lint checks
StarryKira Sep 28, 2026
6a8f921
feat(claude): expose sanitized native reset credit status
korkin25 Sep 24, 2026
f8984b8
feat(accounts): show Claude reset credit status on demand
korkin25 Sep 24, 2026
48970f6
fix(claude): explicitly close status HTTP response
korkin25 Sep 24, 2026
421da3f
feat(accounts): align Claude reset credit query with Codex reset coun…
william-drakemond Sep 28, 2026
35232c3
fix(accounts): address review of Claude reset credit count
william-drakemond Sep 28, 2026
860782e
fix(claude): harden reset credit projection and cell state (review R2)
william-drakemond Sep 29, 2026
27a9421
fix(anthropic): rewrite tool names in one body pass
wucm667 Sep 29, 2026
dd6cb41
Merge pull request #7683 from StarryKira/codex/sonnet-5-5-upstream
Wei-Shaw Sep 29, 2026
1dca86f
Merge pull request #7701 from wucm667/fix/issue-7699-tool-rewrite
Wei-Shaw Sep 29, 2026
b35a611
Merge pull request #7646 from wucm667/feat/issue-7642-dashboard-spend…
Wei-Shaw Sep 29, 2026
883a53f
Merge pull request #7643 from wucm667/fix/issue-7637-antigravity-comp…
Wei-Shaw Sep 29, 2026
f50b99b
Merge pull request #7542 from wucm667/fix/model-whitelist-mapping-con…
Wei-Shaw Sep 29, 2026
ccb027b
Merge pull request #7378 from wucm667/fix/issue-7368-composite-ws-rou…
Wei-Shaw Sep 29, 2026
1f955af
Merge pull request #7380 from wucm667/fix/issue-7377-forward-stream-u…
Wei-Shaw Sep 29, 2026
052e08e
Merge pull request #7684 from william-drakemond/feat/claude-reset-cre…
Wei-Shaw Sep 29, 2026
41dcfec
Merge pull request #7680 from PMExtra/fix/openai-model-catalog
Wei-Shaw Sep 29, 2026
fe9dadf
Merge pull request #7679 from PMExtra/feat/risk-control-allowlist
Wei-Shaw Sep 29, 2026
2f3fed2
Merge pull request #7678 from PMExtra/feat/claude-code-only-client-tabs
Wei-Shaw Sep 29, 2026
a60a295
chore: sync VERSION to 0.2.10 [skip ci]
github-actions[bot] Sep 29, 2026
196b477
feat(claude): redeem native limit resets with server-selected grant
william-drakemond Sep 29, 2026
1872d44
feat(accounts): add confirmed Claude reset button next to reset count
william-drakemond Sep 29, 2026
dc2efc6
fix(claude): tighten reset redeem outcome handling (review R1)
william-drakemond Sep 29, 2026
514ad0a
fix(accounts): readable window labels in Claude reset confirm (review…
william-drakemond Sep 29, 2026
9688571
feat(models): support GPT-6.1 Sol
lyy0709 Sep 29, 2026
c91bb61
feat(codex): recognize current subscription plans
lyy0709 Sep 29, 2026
e6d191a
fix(openai): support Astra Ultrafast capabilities and pricing
lyy0709 Sep 29, 2026
b5efbe3
feat(codex): support remote model catalogs in client config
lyy0709 Sep 29, 2026
a0f41f9
Merge pull request #7579 from feeeei/fix/claude-code-only-fallback-op…
Wei-Shaw Sep 30, 2026
9ecb340
feat(api-key): 增加 API Key 创建数量与频率限制
Wei-Shaw Sep 30, 2026
017bbcb
chore(api-key): 创建频率默认值调整为每小时 60 次
Wei-Shaw Sep 30, 2026
b0cfff9
Merge pull request #7752 from Wei-Shaw/fix/api-key-create-limits
Wei-Shaw Sep 30, 2026
4f0c079
Merge pull request #7681 from william-drakemond/fix/balance-inflight-…
Wei-Shaw Sep 30, 2026
48b7109
Merge pull request #7736 from lyy0709/codex/remote-codex-model-catalog
Wei-Shaw Sep 30, 2026
327c322
Merge pull request #7730 from lyy0709/codex/gpt61-sol-codex-plans
Wei-Shaw Sep 30, 2026
96f4c11
Merge pull request #7726 from william-drakemond/feat/claude-reset-redeem
Wei-Shaw Sep 30, 2026
9d9aecb
merge: integrate Sub2API v0.2.11 while preserving Zero One contracts
XiaoSiKe Oct 1, 2026
be36f46
fix: preserve product pricing and verify v0.2.11 integration
XiaoSiKe Oct 1, 2026
3e292bf
docs(ui): pin reviewed v0.2.11 version snapshots
XiaoSiKe Oct 1, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/scripts/ui-baseline.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"schema_version": 1,
"baseline_ref": "ui-approved-2026-09-24-v028-version-badge-v2",
"baseline_commit": "d50f5d0d9d7b7eb5c69614ef3c91239aba72d75b",
"baseline_ref": "ui-approved-2026-10-01-v0211-version-badge",
"baseline_commit": "be36f46044301526977db2a2800dfb6be0f01de7",
"protected_paths": [
"landing/",
"frontend/",
Expand Down
38 changes: 36 additions & 2 deletions .github/scripts/verify-ui-boundary.test.mjs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import assert from 'node:assert/strict'
import { execFileSync } from 'node:child_process'
import { readFileSync } from 'node:fs'
import { execFileSync, spawnSync } from 'node:child_process'
import { chmodSync, copyFileSync, mkdirSync, mkdtempSync, readFileSync, rmSync, symlinkSync, writeFileSync } from 'node:fs'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import test from 'node:test'
import {
evaluateChangedPaths,
Expand Down Expand Up @@ -292,3 +294,35 @@ test('requires the console entry to keep the approved asset references', () => {
['console/index.html asset references differ from approved baseline'],
)
})

test('compatibility builds accept evolved output and reject changes to published assets', () => {
const root = mkdtempSync(join(tmpdir(), 'frozen-build-contract-'))
try {
const scripts = join(root, 'deploy/zero-one')
const assets = join(scripts, 'recovered-frontend/console/assets')
const bin = join(root, 'bin')
mkdirSync(assets, { recursive: true })
mkdirSync(bin)
writeFileSync(join(assets, 'published.js'), 'historical output')
symlinkSync('.', join(assets, 'historical-alias'))
copyFileSync(new URL('../../deploy/zero-one/verify-frozen-console-build.mjs', import.meta.url), join(scripts, 'verify-frozen-console-build.mjs'))
writeFileSync(join(bin, 'pnpm'), `#!/usr/bin/env node
const fs = require('node:fs')
fs.writeFileSync(process.env.ZERO_ONE_FROZEN_BUILD_ROOT + '/different-output.js', 'evolved API caller source')
if (process.env.FROZEN_TEST_MUTATION === 'true') fs.writeFileSync(process.env.FROZEN_TEST_ASSET, 'unexpected rewrite')
`)
chmodSync(join(bin, 'pnpm'), 0o755)
const run = (mutation) => spawnSync(process.execPath, [join(scripts, 'verify-frozen-console-build.mjs'), 'cn-provider-admin'], {
encoding: 'utf8',
env: { ...process.env, PATH: `${bin}:${process.env.PATH}`, FROZEN_TEST_ASSET: join(assets, 'published.js'), FROZEN_TEST_MUTATION: String(mutation) },
})
const compatible = run(false)
assert.equal(compatible.status, 0, compatible.stderr)
assert.equal(readFileSync(join(assets, 'published.js'), 'utf8'), 'historical output')
const mutation = run(true)
assert.notEqual(mutation.status, 0)
assert.match(mutation.stderr, /modified frozen production assets/)
} finally {
rmSync(root, { recursive: true, force: true })
}
})
46 changes: 30 additions & 16 deletions .github/upstream-baseline.json
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
{
"schema_version": 5,
"repository": "Wei-Shaw/sub2api",
"release": "v0.2.8",
"commit": "fd80b08c90b55edcad5b00171b53f08721d30da1",
"release": "v0.2.11",
"commit": "96f4c115c9749078f90cbf210a01d39baf3f53b6",
"upstream_sync": {
"previous_release": "v0.2.7",
"previous_commit": "aea725f2ea644d5592d0bbb1d63b607efa7e200a",
"product_commit": "a8da9f14efcf8ac8e82730088b249071beb52ed5",
"merge_commit": "f0ed107cfd1ce9111b01551998e5b3d4a80675f1"
"previous_release": "v0.2.8",
"previous_commit": "fd80b08c90b55edcad5b00171b53f08721d30da1",
"product_commit": "ce899e5ae03cf4265855022a54d2d1ef5b5c5a68",
"merge_commit": "9d9aecbb860b8bceb1ae0c031a7c77f08c7b3d83"
},
"approved_backports": [],
"preserve_bytes_on_upstream_sync": [
Expand Down Expand Up @@ -1308,7 +1308,14 @@
"frontend/src/views/user/__tests__/ChannelStatusV1View.refresh.spec.ts",
"frontend/src/api/admin/ops.ts",
"docs/upgrades/v0.2.8.md",
"docs/upgrades/v0.2.8-change-map.json"
"docs/upgrades/v0.2.8-change-map.json",
"frontend/src/utils/planType.ts",
"frontend/src/views/admin/__tests__/groupModelAllowlist.spec.ts",
"frontend/src/views/admin/groupModelAllowlist.ts",
"docs/upgrades/v0.2.11.md",
"docs/upgrades/v0.2.11-change-map.json",
"backend/internal/service/account_stats_pricing_test.go",
"backend/internal/service/billing_inflight_reservation_test.go"
],
"retired_preserved_paths": [
{
Expand Down Expand Up @@ -1867,7 +1874,7 @@
"legacy_hotfixes": [
{
"name": "approved-v0.1.179-production-correctness-and-billing-compatibility",
"valid_for_release": "v0.2.8",
"valid_for_release": "v0.2.11",
"exit_condition": "Remove each path when the first stable upstream release containing its equivalent production-correctness fix becomes the baseline; remove the entire block when no listed path remains.",
"paths": [
"backend/internal/handler/admin/admin_basic_handlers_test.go",
Expand Down Expand Up @@ -1897,15 +1904,15 @@
},
{
"name": "approved-v0.1.179-version-metadata-alignment",
"valid_for_release": "v0.2.8",
"valid_for_release": "v0.2.11",
"exit_condition": "Remove this metadata alignment when the first stable upstream release makes backend/cmd/server/VERSION match the baseline release.",
"paths": [
"backend/cmd/server/VERSION"
]
},
{
"name": "approved-v0.1.179-race-safety",
"valid_for_release": "v0.2.8",
"valid_for_release": "v0.2.11",
"exit_condition": "Remove each path when the first stable upstream release contains the equivalent race-safe runtime cache and asynchronous test fixture handling.",
"paths": [
"backend/internal/service/content_moderation.go",
Expand All @@ -1920,23 +1927,23 @@
},
{
"name": "approved-v0.1.179-remove-unconditional-sticky-debug-logs",
"valid_for_release": "v0.2.8",
"valid_for_release": "v0.2.11",
"exit_condition": "Remove this hotfix when the first stable upstream release removes or explicitly gates the equivalent per-request sticky-session debug logs.",
"paths": [
"backend/internal/service/gateway_scheduling.go"
]
},
{
"name": "approved-v0.1.181-grok-mapping-test-isolation",
"valid_for_release": "v0.2.8",
"valid_for_release": "v0.2.11",
"exit_condition": "Remove this hotfix when the first stable upstream release contains equivalent runtime model-mapping isolation in the canonical scheduling test.",
"paths": [
"backend/internal/service/openai_model_mapping_test.go"
]
},
{
"name": "approved-v0.1.182-go-dependency-security-updates",
"valid_for_release": "v0.2.8",
"valid_for_release": "v0.2.11",
"exit_condition": "Remove this hotfix when the first stable upstream release uses golang.org/x/image v0.45.0 or later, Testcontainers v0.44.0 or later, github.com/moby/go-archive v0.3.0 or later, and no longer depends on github.com/docker/docker.",
"paths": [
"backend/go.mod",
Expand All @@ -1948,7 +1955,7 @@
},
{
"name": "approved-v0.2.7-sse-keepalive-test-determinism",
"valid_for_release": "v0.2.8",
"valid_for_release": "v0.2.11",
"exit_condition": "Remove this hotfix when the first stable upstream release makes the ordinary-client SSE keepalive test tolerate a comment payload and at least two ticker periods of runner scheduling delay.",
"paths": [
"backend/internal/service/gemini_sse_comment_compat_test.go"
Expand Down Expand Up @@ -2168,7 +2175,10 @@
"frontend/src/views/admin/__tests__/BackupView.spec.ts",
"frontend/src/views/admin/__tests__/RiskControlView.spec.ts",
"frontend/src/views/admin/__tests__/UsersView.spec.ts",
"frontend/src/views/user/__tests__/ChannelStatusV1View.refresh.spec.ts"
"frontend/src/views/user/__tests__/ChannelStatusV1View.refresh.spec.ts",
"frontend/src/utils/planType.ts",
"frontend/src/views/admin/__tests__/groupModelAllowlist.spec.ts",
"frontend/src/views/admin/groupModelAllowlist.ts"
]
},
{
Expand Down Expand Up @@ -2580,7 +2590,11 @@
"backend/migrations/opencode_go_platform_migration_test.go",
"backend/migrations/purge_unlimited_user_platform_quotas_migration_test.go",
"backend/internal/repository/purge_unlimited_user_platform_quotas_migration_integration_test.go",
"frontend/src/api/admin/ops.ts"
"frontend/src/api/admin/ops.ts",
"docs/upgrades/v0.2.11.md",
"docs/upgrades/v0.2.11-change-map.json",
"backend/internal/service/account_stats_pricing_test.go",
"backend/internal/service/billing_inflight_reservation_test.go"
]
},
{
Expand Down
2 changes: 1 addition & 1 deletion backend/cmd/server/VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0.2.8
0.2.11
7 changes: 4 additions & 3 deletions backend/cmd/server/wire_gen.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

53 changes: 53 additions & 0 deletions backend/internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ type Config struct {
Pricing PricingConfig `mapstructure:"pricing"`
Gateway GatewayConfig `mapstructure:"gateway"`
APIKeyAuth APIKeyAuthCacheConfig `mapstructure:"api_key_auth_cache"`
APIKeyCreate APIKeyCreateConfig `mapstructure:"api_key_create"`
SubscriptionCache SubscriptionCacheConfig `mapstructure:"subscription_cache"`
SubscriptionMaintenance SubscriptionMaintenanceConfig `mapstructure:"subscription_maintenance"`
Dashboard DashboardCacheConfig `mapstructure:"dashboard_cache"`
Expand Down Expand Up @@ -921,6 +922,30 @@ type BillingConfig struct {
// UserPlatformQuotaSentinelTTLSeconds sentinel(无 limit 占位)entry 的 TTL,
// 显著短于 quota cache 默认 86400s 以控 Redis 内存;默认 3600=1h。
UserPlatformQuotaSentinelTTLSeconds int `mapstructure:"user_platform_quota_sentinel_ttl_seconds"`
// InflightReservation 余额模式在途请求预留(Redis),防止并发请求在预检时看到同一份余额而集体透支。
InflightReservation InflightReservationConfig `mapstructure:"inflight_reservation"`
}

// InflightReservationConfig 余额模式在途预留配置。
// 准入时按 输入估算 + 输出单价 × max_tokens 估算单请求费用,在 Redis 中原子地
// 校验 缓存余额 - 在途预留合计 >= 估算 后登记预留;handler 和异步计费任务
// 均结束后释放,长请求期间续期,进程崩溃时由 TTL 回收。
// 估算失败或 Redis 不可用时 fail-open,退回旧的仅余额 > 阈值检查。
type InflightReservationConfig struct {
Enabled bool `mapstructure:"enabled"`
// TTLSeconds 单条预留的最长存活时间;进程崩溃等泄漏的预留到期自动失效。
TTLSeconds int `mapstructure:"ttl_seconds"`
// DefaultMaxTokens 请求未携带 max_tokens 时用于估算的输出 token 数。
DefaultMaxTokens int `mapstructure:"default_max_tokens"`
// MaxOutputTokens 估算输出 token 的上限(max_tokens 超出时截断)。
MaxOutputTokens int `mapstructure:"max_output_tokens"`
// MaxInputTokens 输入 token 估算(请求体字节数 / 4)的上限。
MaxInputTokens int `mapstructure:"max_input_tokens"`
// MaxReservationUSD 单请求预留金额上限;0 表示不设上限。
MaxReservationUSD float64 `mapstructure:"max_reservation_usd"`
// FailClosedOnUnpriced 无法为请求估算费用(模型/分组/渠道均无定价)时是否拒绝请求。
// 默认 false:放行且不预留(fail-open,节流告警日志)。
FailClosedOnUnpriced bool `mapstructure:"fail_closed_on_unpriced"`
}

type CircuitBreakerConfig struct {
Expand Down Expand Up @@ -1696,6 +1721,14 @@ type APIKeyAuthCacheConfig struct {
InvalidAbuse InvalidAuthAbuseConfig `mapstructure:"invalid_abuse"`
}

// APIKeyCreateConfig 用户创建 API Key 的防滥用限制(0 表示不限制)
type APIKeyCreateConfig struct {
// MaxActivePerUser 单个用户同时存在(未删除)的 API Key 上限
MaxActivePerUser int `mapstructure:"max_active_per_user"`
// MaxPerUserPerHour 单个用户每小时可创建的 API Key 次数(删除不返还次数)
MaxPerUserPerHour int `mapstructure:"max_per_user_per_hour"`
}

type InvalidAuthAbuseConfig struct {
Enabled bool `mapstructure:"enabled"`
Threshold int `mapstructure:"threshold"`
Expand Down Expand Up @@ -2093,6 +2126,13 @@ func setDefaults() {
viper.SetDefault("billing.minimum_balance_reserve", 0.000001)
viper.SetDefault("billing.user_platform_quota_cache_ttl_seconds", 86400)
viper.SetDefault("billing.user_platform_quota_sentinel_ttl_seconds", 3600)
viper.SetDefault("billing.inflight_reservation.enabled", true)
viper.SetDefault("billing.inflight_reservation.ttl_seconds", 900)
viper.SetDefault("billing.inflight_reservation.default_max_tokens", 8192)
viper.SetDefault("billing.inflight_reservation.max_output_tokens", 128000)
viper.SetDefault("billing.inflight_reservation.max_input_tokens", 200000)
viper.SetDefault("billing.inflight_reservation.max_reservation_usd", 0)
viper.SetDefault("billing.inflight_reservation.fail_closed_on_unpriced", false)

// Turnstile
viper.SetDefault("turnstile.required", false)
Expand Down Expand Up @@ -2320,6 +2360,8 @@ func setDefaults() {
viper.SetDefault("api_key_auth_cache.invalid_abuse.window_seconds", 60)
viper.SetDefault("api_key_auth_cache.invalid_abuse.block_seconds", 60)
viper.SetDefault("api_key_auth_cache.invalid_abuse.capacity", 16384)
viper.SetDefault("api_key_create.max_active_per_user", 200)
viper.SetDefault("api_key_create.max_per_user_per_hour", 60)

// Subscription auth L1 cache
viper.SetDefault("subscription_cache.l1_size", 16384)
Expand Down Expand Up @@ -2699,6 +2741,12 @@ func (c *Config) Validate() error {
return fmt.Errorf("server.h2c.max_upload_buffer_per_stream must be positive")
}
}
if c.APIKeyCreate.MaxActivePerUser < 0 {
return fmt.Errorf("api_key_create.max_active_per_user must be non-negative")
}
if c.APIKeyCreate.MaxPerUserPerHour < 0 {
return fmt.Errorf("api_key_create.max_per_user_per_hour must be non-negative")
}
if c.APIKeyAuth.InvalidAbuse.Enabled {
if c.APIKeyAuth.InvalidAbuse.Threshold < 10 {
return fmt.Errorf("api_key_auth_cache.invalid_abuse.threshold must be at least 10")
Expand Down Expand Up @@ -3050,6 +3098,11 @@ func (c *Config) Validate() error {
if c.Billing.MinimumBalanceReserve < 0 {
return fmt.Errorf("billing.minimum_balance_reserve must be non-negative")
}
if c.Billing.InflightReservation.TTLSeconds < 0 || c.Billing.InflightReservation.DefaultMaxTokens < 0 ||
c.Billing.InflightReservation.MaxOutputTokens < 0 || c.Billing.InflightReservation.MaxInputTokens < 0 ||
c.Billing.InflightReservation.MaxReservationUSD < 0 {
return fmt.Errorf("billing.inflight_reservation values must be non-negative")
}
if c.Database.MaxOpenConns <= 0 {
return fmt.Errorf("database.max_open_conns must be positive")
}
Expand Down
Loading
Loading