wucm667
c91fbeb0c0
chore: remove unrelated test refactors
2026-08-11 16:57:39 +08:00
wucm667
c3fc4331c1
fix(lint): make compatibility test assertions fatal
2026-08-11 16:57:39 +08:00
wucm667
e087f1b736
fix(lint): use fatal pointer assertions in usage tests
2026-08-11 16:57:39 +08:00
wucm667
145b0ac35e
fix(lint): make remaining pointer assertions explicit
2026-08-11 16:57:39 +08:00
wucm667
5ea03c178d
fix(lint): resolve remaining nil dereference warnings
2026-08-11 16:57:39 +08:00
wucm667
1afb8264e5
fix(lint): use require.NotNil for staticcheck SA5011
...
Replace if-then-t.Fatal nil checks with testify require.NotNil so
staticcheck can prove the pointer is non-nil before field access.
Fixes SA5011 in payment_handler_test, parse_test, and
openai_images_incomplete_test.
2026-08-11 16:57:38 +08:00
wucm667
2321c0e771
chore: retry CI checks
2026-08-11 16:57:38 +08:00
wucm667
0b35370a7a
fix(claude): support top-level deferred tools
2026-08-11 16:57:38 +08:00
wucm667
9c36b75a7d
fix(claude): strip cache control from deferred tools
2026-08-11 16:57:38 +08:00
Wesley Liddick
1e618dbc29
Merge pull request #5054 from wucm667/fix/issue-5029-openai-passthrough-pool-auth-retry
...
fix(openai): retry pool auth failures before failover
2026-08-11 14:21:45 +08:00
shaw
a3bbf35cbd
Merge branch 'main' into fix/issue-5029-openai-passthrough-pool-auth-retry
...
Resolve conflict in backend/internal/handler/openai_gateway_handler_test.go.
main and this branch each appended a passthrough upstream stub plus a test at
the same two insertion points:
main openAIHTTPPassthroughSSERateLimitUpstream
TestOpenAIResponses_APIKeyPassthroughSSERateLimitUsesConfiguredPoolRetry
branch openAIHTTPPassthroughAuthFailoverUpstream
TestOpenAIResponses_APIKeyPassthroughPoolAuthFailureRetriesThenSwitchesToHealthyAccount
Both sides are kept verbatim; the only edit is giving each stub its own
calls() body instead of sharing the trailing one. No assertion was changed.
openai_gateway_passthrough.go and openai_oauth_passthrough_test.go merged
automatically.
2026-08-11 14:09:07 +08:00
Wesley Liddick
caa1abb13a
Merge pull request #5404 from wucm667/fix/issue-5400-oauth-image-stream-error
...
fix(openai): fail over OAuth image stream errors
2026-08-11 14:04:42 +08:00
Wesley Liddick
574dfad2dc
Merge pull request #5488 from wucm667/fix/issue-5482-stale-codex-threshold
...
fix(openai): skip stale and reset Codex snapshots in scheduling threshold evaluator
2026-08-11 14:00:33 +08:00
Wesley Liddick
bc0a6a7039
Merge pull request #5480 from scp-planet/fix/admin-usage-request-id-column
...
修复管理端使用记录请求 ID 列显示 / Restore admin usage request ID column visibility
2026-08-11 14:00:07 +08:00
Wesley Liddick
6876477371
Merge pull request #5304 from wucm667/fix/issue-5302-chat-reasoning-alias
...
fix(apicompat): accept chat reasoning alias
2026-08-11 13:59:49 +08:00
Wesley Liddick
b918874f81
Merge pull request #5403 from cyhhao/fix/codex-capacity-exponential-backoff
...
fix(openai): back off capacity retries exponentially
2026-08-11 13:58:06 +08:00
Wesley Liddick
20a2d12dde
Merge pull request #5415 from Yuxin-Qiao/fix/openai-responses-empty-completed-failover
...
fix(openai): fail over empty response.completed streams instead of recording 0/0 success
2026-08-11 13:53:43 +08:00
Wesley Liddick
ca9e2b48ee
Merge pull request #5413 from Yuxin-Qiao/fix/openai-responses-reasoning-item-id
...
fix(openai): strip invalid reasoning item IDs in API key passthrough
2026-08-11 13:53:05 +08:00
Wesley Liddick
e499a54a9d
Merge pull request #5449 from hongheshan-svg/docs/fix-stale-go-golangci-versions
...
docs: sync Go 1.26.5 / golangci-lint v2.9 versions with CI
2026-08-11 13:52:35 +08:00
Wesley Liddick
0f73203e35
Merge pull request #5277 from Pluviobyte/agent/fix-grok-missing-usage-billing
...
fix: reject Grok responses without billable usage
2026-08-11 09:28:43 +08:00
Wesley Liddick
e2d9034d3d
Merge pull request #5327 from fengshao1227/fix/fingerprint-user-agent-validation
...
fix(identity): validate user-agent before persisting account fingerprint
2026-08-11 09:28:27 +08:00
Wesley Liddick
ba4bd0c0b4
Merge pull request #5481 from fengshao1227/fix/responses-deterministic-400-passthrough
...
fix(openai): 原生 Responses 路径不再把上游确定性 400 归一成可重试 502
2026-08-11 09:27:34 +08:00
Wesley Liddick
61acf29125
Merge pull request #5501 from wucm667/fix/issue-5500-unset-scheduling-thresholds
...
fix(settings): cache unset scheduling thresholds
2026-08-11 09:27:22 +08:00
wucm667
3e1674a060
fix(settings): cache unset scheduling thresholds
2026-08-11 04:34:49 +08:00
Wesley Liddick
0b3fe95afd
Merge pull request #5439 from pigzwy/feat/response-model-billing
...
feat(billing): support safe billing by upstream response model
2026-08-10 19:47:22 +08:00
anya
e5b325e481
fix(billing): harden response-model billing admission
...
Three guards on the response_model billing basis, all scoped to the opt-in
channel mode so existing channels are unaffected.
1. Per-unit billing gate was stale. Audio (AudioUsage) and the search
surcharge (SearchCount) reached the billing paths after this branch was
cut; both are priced per unit rather than per token, so they must be
excluded like image/video/web-search already are. Audio pricing ignores
the model entirely, so the previous code "adopted" a basis switch that
changed nothing and emitted a misleading audit log for it.
2. Never zero out a billable request. A catalog entry whose token prices are
explicitly 0 still passes the identified-pricing gate (TokenPricingAbsent
only means both prices are missing), so an upstream could declare a free
model name and drop the bill to zero. Reject a zero (or negative)
recomputation whenever the baseline was billable; an already-zero baseline
is unaffected.
3. Never cross from channel pricing to the global table. Channel pricing
matches exact keys and prefix wildcards and does not strip date suffixes,
while the global table's identified lookup does. Upstreams routinely
declare dated model IDs (claude-opus-4-5-20251101), so allowing a
cross-source comparison would silently bypass an administrator's channel
markup on essentially every request. Admins who want a downgrade target
discounted can price it explicitly on the channel.
Also skip the recomputation entirely when the declared model equals the
baseline: it is provably the same cost and only burned a pricing resolve.
The identified-pricing helpers now return whether the model resolved to
channel pricing so the third guard costs no extra resolve.
2026-08-10 19:11:47 +08:00
shaw
33351c7bc7
fix(billing): gofmt channel.go and drop the redundant response-model hint
...
- channel.go: 常量块里插入注释后 gofmt 会把 BillingModelSourceResponse 单独成组,
原写法沿用了上一组的对齐空格,CI 的 gofmt 检查因此失败
(internal/service/channel.go:45: File is not properly formatted)。
- 撤掉渠道表单里新加的那条提示:说明本就多余,且"只降不升"只在"相对基线收费"
这个口径下成立,容易被读成"上游返回更贵的模型也不会多收",反而误导。
2026-08-10 18:45:16 +08:00
shaw
b689e5b401
fix(billing): harden response-model billing and repair its test fixtures
...
按上游响应模型计费的准入过宽、且自带用例必然失败,本次一并修复。
严格化准入
- 新增 PricingService.GetIdentifiedModelPricing / BillingService.HasIdentifiedTokenPricing:
只接受价格表中能被确定性识别的条目(精确名、已知拼写变体、去掉日期版本后缀),
不再接受 getFallbackPricing / matchByModelFamily 按子串猜出的系列兜底价。
此前上游只要自报一个含 "haiku" 的编造名字就能被判定"已定价",把账单压到最便宜的
系列价(实测 claude-opus-4.8 基线 $0.0019250 → $0.0000963,20 倍少收)。
GetModelPricing 的对外行为不变,仅把前三步查找抽成共用函数。
- 图片 / 视频 / 网页搜索请求不再走响应模型覆盖:这些路径按张、按秒、按次定价,
与准入检查所验的 token 价不是同一套价格表。
- 准入判断抽成 responseModelBillingDeclaration,两条计费主干共用同一套规则。
正确性与可观测性
- 去掉 recordUsageCore 中 billingModel 的无效赋值(ineffassign 已启用,会让
golangci-lint 直接失败),改由日志表达实际生效的计费基准。
- 补 cost != nil 守卫,与 OpenAI 侧及本文件既有写法对齐。
- 每次实际生效的基准切换记一条 billing.response_model_applied,少收可审计。
- 修正 upstreamResponseModelObserver 上"冲突仅用于诊断、永不影响计费"的过期注释。
测试
- gpt-5.1 与 gpt-5.5 实际共用同一条 gpt-5.4 价格,夹具"价格必须不同"的前置断言
必然失败,OpenAI 侧 3 个用例(含 4 个子用例)从未跑通;改用 gpt-5.4-nano /
gpt-5.5。Anthropic 侧 claude-opus-4 不是价格表精确条目,改用 claude-opus-4.8。
- 夹具增加"必须可被确定性识别"的前置断言,避免用例被更靠前的门挡掉而失去判别力。
- 新增:准入规则表驱动用例、可识别性判定用例、编造家族名在两条主干上均被拒的用例。
前端
- 选择该模式时提示"计费基准以上游自报模型为准,只降不升,仅对可信上游启用"。
2026-08-10 18:45:15 +08:00
pigzwy
9096492b55
feat(billing): support safe upstream response model billing
2026-08-10 18:45:14 +08:00
wucm667
3d3aee2e72
fix(openai): skip stale and reset Codex snapshots in scheduling threshold evaluator
2026-08-10 14:27:04 +08:00
li
591d47fb9b
fix(openai): 原生 Responses 路径不再把上游确定性 400 归一成可重试 502
...
上游返回 400(invalid_function_parameters、missing_required_parameter 等)时,
`/v1/responses` 原生路径把它包成 `502 upstream_error / "Upstream request failed"`,
真实的 message/code/param 全部丢弃。
502 属于可重试类,下游网关会把这个永远不会成功的请求反复重放(issue #5479 实测
30 个失败请求被放大成 60 次上游调用),客户端也拿不到「哪个字段非法」的线索。
同一个 service 上的兄弟路径早已做对:
- `handleCompatErrorResponse`(ChatCompletions / Anthropic)回真实状态码 +
invalid_request_error + 真实 message;
- `/v1/images/generations` 还额外透传 code/param。
原生 Responses 是唯一漏掉的一条,本次对齐。
改动只放行 400:走到该分支说明 `shouldFailoverOpenAIUpstreamResponse` 已判定这个
400 不可 failover,即 server_is_overloaded / at capacity 这类可重试的 400 不会到达。
401/402/403(运营方凭据问题)、404/405(可能是 base_url 配错)、429 一律维持原状。
Fixes #5479
2026-08-10 11:25:46 +08:00
scp-planet
5350b3d98e
fix(usage): restore request ID column visibility
2026-08-10 10:54:39 +08:00
Wesley Liddick
10a4c6e3ad
Merge pull request #5234 from wucm667/fix/issue-5230-deduplicate-latest-turn-audit
...
fix(security-audit): deduplicate websocket turn audits
2026-08-10 10:53:17 +08:00
Wesley Liddick
f3c7a1a8c4
Merge pull request #5295 from wucm667/fix/issue-5289-streaming-upstream-error
...
fix: emit response.failed when compact keepalive commits headers but no SSE payload
2026-08-10 10:52:49 +08:00
Wesley Liddick
f19e3816d0
Merge pull request #5316 from wucm667/fix/issue-5313-legacy-scheduler-diagnostics
...
fix(scheduler): diagnose legacy OpenAI exclusions
2026-08-10 10:52:14 +08:00
Wesley Liddick
30d0405388
Merge pull request #5464 from wucm667/fix/issue-5455-api-key-input-validation
...
fix(api-key): validate quota and expiry inputs
2026-08-10 10:52:04 +08:00
Wesley Liddick
5deeb7ef15
Merge pull request #5376 from wucm667/fix/issue-5369-composite-image-permission
...
fix(admin): enable image generation permission for Composite groups
2026-08-10 10:51:54 +08:00
Wesley Liddick
895e8247af
Merge pull request #5472 from wucm667/fix/issue-5468-openai-threshold-percent
...
fix(openai): preserve Codex usage percentages for scheduling thresholds
2026-08-10 10:51:43 +08:00
Wesley Liddick
2d0976ac2c
Merge pull request #5475 from fengshao1227/fix/oauth-personal-subscription-expiry
...
fix(openai-oauth): 个人订阅到期时间不再被 POID workspace 的 entitlement 覆盖
2026-08-10 10:23:05 +08:00
Wesley Liddick
4667f9012f
Merge pull request #5477 from Wei-Shaw/revert-5394-fix/issue-5388-risk-control-fail-closed
...
Revert "fix(risk-control): 风控后端异常时阻断提示词请求"
2026-08-10 10:00:02 +08:00
Wesley Liddick
af6928a268
Revert "fix(risk-control): 风控后端异常时阻断提示词请求"
2026-08-10 09:59:42 +08:00
li
358e4a89a1
fix(openai-oauth): 个人订阅到期时间不再被 POID workspace 的 entitlement 覆盖
...
enrichTokenInfo 里 plan_type 与 subscription_expires_at 的取值口径不一致:
- plan_type 有 shouldApplyChatGPTAccountInfoPlanType 护着(#3641),
id_token 里的个人套餐优先,accounts/check 只在个人值为空时补位;
- subscription_expires_at 却是无条件覆盖。
accounts/check 是多账号/工作区端点,命中的记录由 access_token JWT 的 poid 决定。
当 poid 指向默认 Personal workspace、而 chatgpt_account_id 指向个人 ChatGPT 账号时
(两者是不同标识),拿到的 entitlement.expires_at 描述的是 workspace 权益。
配上被保护下来的个人 plan_type,账号页就显示成「个人 Pro + workspace 到期时间」。
代码里本来已经有正确的来源——用个人 chatgpt_account_id 查
/backend-api/subscriptions 的 active_until——但它被
`if TrimSpace(SubscriptionExpiresAt) == ""` 挡着,workspace 值一旦写进去就永远不触发。
改为让两个字段始终描述同一份订阅:
- 套餐取自 accounts/check 时(id_token 没带 chatgpt_plan_type),到期时间跟着取
同一条记录,行为不变;
- 套餐保留了 id_token 的个人值时,只有该记录确实属于个人账号才用它的
entitlement.expires_at;不属于就跳过,并强制回落到个人订阅端点。
为此给 ChatGPTAccountInfo 补 AccountID:优先读 account.account_id,缺失时退回
accounts 的 map key(key 可能是 "default" 这类别名)。两侧任一缺 ID 时判定为
无法区分并沿用旧行为,poid == chatgpt_account_id 的单人账号完全不受影响。
Fixes #5459
2026-08-10 09:29:14 +08:00
Wesley Liddick
b5e83156d9
Merge pull request #5394 from wucm667/fix/issue-5388-risk-control-fail-closed
...
fix(risk-control): 风控后端异常时阻断提示词请求
2026-08-10 08:51:46 +08:00
Wesley Liddick
fddc806db2
Merge pull request #5458 from lyen1688/feat/backup-large-file-parts
...
完善大文件备份分卷上传与恢复
2026-08-10 08:36:08 +08:00
wucm667
99b31067f7
fix(openai): preserve Codex usage percentages in scheduling threshold
2026-08-10 04:29:08 +08:00
wucm667
f5c108c836
fix(api-key): validate quota and expiry inputs
2026-08-09 22:42:51 +08:00
lyen1688
bbc8b6e906
完善大文件备份分卷上传与恢复
2026-08-09 20:58:07 +08:00
hongheshan-svg
118c1e8e72
docs: sync Go 1.26.5 / golangci-lint v2.9 versions with CI
...
DEV_GUIDE.md and the three READMEs still advertise Go 1.25.7 and
golangci-lint v2.7, but CI has since moved on:
- backend/go.mod declares go 1.26.5, and backend-ci.yml / release.yml /
security-scan.yml all resolve the toolchain via
`go-version-file: backend/go.mod` and then hard-assert
`go version | grep -q 'go1.26.5'`.
- backend-ci.yml pins golangci-lint to v2.9.
So a contributor following DEV_GUIDE.md installs a linter two minor
versions behind CI (different findings locally vs. in CI) and expects a
Go version that the workflow's own assertion step rejects.
Update all ten stale references, and note in the CI section which files
the Go version assertion lives in so future bumps don't miss one.
Docs only, no code or workflow changes.
2026-08-09 18:10:46 +08:00
github-actions[bot]
48eb3766d2
chore: sync VERSION to 0.1.173 [skip ci]
2026-08-09 08:26:22 +00:00
Wesley Liddick
29009f0b2e
Merge pull request #5446 from Wei-Shaw/feat/email-domain-quota-switch
...
feat: 邮箱域名限量注册增加独立开关(默认关闭)
v0.1.173
2026-08-09 16:09:17 +08:00