5519 Commits
Author SHA1 Message Date
wucm667 f5ae32e53f fix(gateway): normalize GLM-5.3 thinking effort 2026-09-04 08:15:41 +08:00
Ritel-T a3a6a85b73 test(admin): assert nested credential redaction in lite lists 2026-09-03 23:32:34 +09:00
Ritel-T 3e60c3b86b fix: add compact admin account list DTO 2026-09-03 21:20:08 +09:00
feeeei de27905e88 feat(usage): 用量记录落库上游请求标识并支持按账户配置响应头名
- usage_logs 新增 upstream_request_id 列(迁移 232)与仅索引非空行的 CONCURRENTLY 部分索引(迁移 233,runner 登记失效索引清理)
- 账户 extra.upstream_request_id_header 指定要读取的上游响应头,只读该头;留空不记录;创建/更新/批量入口校验头名合法且不超过 64 字节
- ForwardResult/OpenAIForwardResult 携带 UpstreamHeaders,中央计费按账户解析落库,WS 轮次保持 NULL
- 管理端用量明细新增默认隐藏的"上游ID"列,带与请求ID列一致的复制按钮,并同步导出
- 创建/编辑账户弹窗新增"上游ID"输入(UpstreamRequestIdHeaderField),提示按接入类型与平台分条列出常见头名
- HelpTooltip 悬停模式下指针移入提示框时保持打开,便于选中复制提示内容
2026-09-03 18:02:15 +08:00
wucm667 28dde982cb fix(openai): normalize automation heartbeat bootstrap 2026-09-03 17:29:04 +08:00
weiness de8d756afa fix(payment): reconcile pending Alipay orders 2026-09-03 16:33:30 +08:00
Wei-Shaw abc07bb079 test(ops): satisfy errcheck and gofmt in proxy attribution tests 2026-09-03 15:23:22 +08:00
wucm667 78fc7b171e test: skip live token comparison on network errors 2026-09-03 15:16:18 +08:00
Wei-Shaw 4c1f920d58 fix(ops): close proxy attribution gaps and bound queued upstream events
Follow-up to the event-time proxy snapshot: every upstream attempt now
carries attribution, the direct/unknown contract is enforced everywhere,
and the queued upstream_errors array has hard bounds again.

Attribution coverage
- compact fallback retry helper and the streaming compact failover event
  now stamp proxy_id/proxy_name; the non-streaming SSE compact retry
  previously dropped its first attempt on `continue` and now records it
- raw chat-completions stream truncation events (new on main) attributed
- Anthropic/Bedrock transport errors get attribution inside the shared
  handleUpstreamTransportError helper, so all five callers inherit it
- OpenAI first-output timeout takes attribution from the caller: HTTP
  callers pass the HTTP rule, the WS passthrough relay passes the WS rule
  (no managed proxy => unknown, never direct, because http.DefaultClient
  may honor environment proxies)

Contract hardening
- single opsUpstreamProxyAttribution returns (id, name) from one decision;
  a hydrated Proxy without a durable ID is unknown, not id=null+name
- both normalizers (struct at append time, JSON on detail reads) enforce
  proxy_id==null <=> sentinel name; blank managed name => "proxy"
- detail-read normalization edits only the attribution keys via sjson, so
  keys from older struct versions and stored key order are preserved

Queue bounds
- keep at most 256 attempts and ~512 KiB serialized per entry, newest
  first; the oldest retained event records dropped_earlier_attempts
- attempts outside the 16-event body window trim url/message to 512 bytes
- restore the empty-event drop rule, judged before the body window clears
  detail so older real attempts survive

Tests cover each site above (managed proxy, direct, WS unknown), the
normalizer invariants, key/order preservation, and the count/byte bounds.
2026-09-03 15:11:36 +08:00
wwcchh0123 e9e3c46cb7 fix(ops): snapshot proxy on upstream errors 2026-09-03 14:20:39 +08:00
wucm667 62198286e9 [verified] fix(handler): distinguish gateway admission rejections 2026-09-03 12:48:02 +08:00
NellPoi af90a9bd1f fix(openai): 兼容历史任务委派续聊 2026-09-03 11:39:23 +08:00
wucm667 5e4958c889 fix(openai): schedule accounts with mapped model 2026-09-03 10:51:12 +08:00
feeeei fdc2ec17e6 feat(pricing): fallback/override 定价文件按内容哈希随定时器热重载
PricingService 的定时器(hash_check_interval_minutes,默认 10 分钟)每轮先做
远程目录哈希同步,再比对 fallback_file/override_file 两个文件内容的联合 sha256
(customFilesHash),变化时从本地 model_pricing.json 重建叠加层,只替换内存数据
与指纹,不改远程同步锚点 localHash。只配置了自定义文件而 remote_url 为空时
调度器同样启动。

- 三条重建路径(启动加载、远程下载、热重载)统一走 buildPricingData,指纹在合并
  读取之前采样,并发改文件只会落后于数据、下一轮补重建
- 删除文件视为该层清空,下一轮生效;文件存在但不可读或不是 JSON 对象时保留旧数据、
  不更新指纹,每轮重试并告警
- config.example.yaml 同步生效说明;新增 pricing_service_hot_reload_test.go
2026-09-03 10:35:20 +08:00
wucm667 b6740c937f fix(openai): recover unavailable continuation responses 2026-09-03 10:26:12 +08:00
wucm667 10c8f523c4 fix(openai): preserve passthrough reasoning effort 2026-09-03 09:42:50 +08:00
haruka 8249ab37db feat: add Anthropic reasoning effort pricing controls 2026-09-02 20:16:57 +08:00
wall-wxkandClaude Opus 4.6 9be9c0b68c fix(gateway): 转发失败的请求立即释放会话槽,不再占用整个空闲超时窗口
会话数量限制(max_sessions)此前只有空闲超时自动过期,没有任何主动释放
机制:请求在选号阶段注册会话后,即使转发失败(上游错误/客户端断开/换号
耗尽),该 session hash 也会占住槽位直到空闲超时(默认 5 分钟),导致
max_sessions 受限的账号在窗口内拒绝所有新会话。

修复:
- SessionLimitCache 新增 UnregisterSession(Redis ZREM),立即移除会话注册
- GatewayService 新增 ReleaseAccountSession,适用条件与 checkAndRegisterSession 对齐
- Messages 主路径:记录 failover 链上注册过会话槽的账号,请求最终失败时
  统一释放;failover continue / profit veto / 兜底分组切换时按次即时释放;
  成功及"上游已计量部分结果"(#5148)保持既有空闲超时语义
- CountTokens:转发失败时同样释放选号时注册的会话槽

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-09-02 18:45:20 +08:00
Soulxyz 8ed48e27a6 fix(setup): avoid hardcoded postgres connection 2026-09-02 15:30:59 +08:00
aofee a7f1ed8fa4 fix(openai-ws): track pending passthrough turns 2026-09-02 14:59:13 +08:00
WUJI-Labs f5e4da5e72 style(upstream): gofmt http_upstream_profile.go 2026-09-02 12:40:23 +08:00
WUJI-Labs 9dc4c40bff test(upstream): align HTTP/2 keepalive tests with long-stream profile API 2026-09-02 12:28:37 +08:00
WUJI-Labs 81fd853005 feat(upstream): add HTTP/2 PING keepalive for long LLM generation streams
Long-running LLM inferences (such as high-thinking Gemini models) may experience
silent TCP disconnections across intermediate proxies, NAT gateways, or firewalls
when no data frames are emitted for tens of seconds to minutes.

Go's default http2.Transport sets ReadIdleTimeout=0 (disabling active PING checks).
This commit introduces HTTPUpstreamProfileLongStream with:
- ReadIdleTimeout = 10s
- PingTimeout = 5s

When a pooled H2 connection stays idle for 10s during long streams, active PING frames
are dispatched. Dead connections failing to respond within 5s are proactively evicted,
preventing client generation requests from hanging on dead sockets or failing with EOF/502.

Signed-off-by: WUJI-Labs <zoujincool@gmail.com>
2026-09-02 12:11:17 +08:00
aofee 01bd9b71a2 feat: add Grok media eligibility controls 2026-09-02 11:50:33 +08:00
feeeei 9ad3865690 fix(gateway): 负载感知调度补齐渠道模型限制的逐账号检查
渠道 billing_model_source=upstream 时,限制模型只能按账号映射后的上游模型
逐账号判定。GatewayService.SelectAccountWithLoadAwareness 的负载感知各层
(模型路由候选、路由内粘性 gate、无路由粘性 gate、负载感知候选及其兜底)
此前都没有这一判定,load_batch_enabled 默认开启,anthropic/gemini/antigravity
分组的渠道白名单实际不生效。

现在入口算一次 needsUpstreamChannelRestrictionCheck,各层共用同一判定;
候选因渠道限制筛空时记 warn 并返回带 channel pricing restriction 的
ErrNoAvailableAccounts,与预检路径一致。
2026-09-02 11:46:37 +08:00
cxx-cod 6f9fde093f style(openai): apply De Morgan's law to satisfy staticcheck QF1001 2026-09-02 03:30:14 +00:00
aofee a774230661 fix(grok): keep inconclusive billing accounts eligible 2026-09-02 11:09:21 +08:00
aofee 222181efd6 fix(anthropic): scope Fable credits errors to model 2026-09-02 11:02:38 +08:00
刘佳林 77729e2723 merge: resolve conflicts with upstream/main
Keep model-scoped reasoning effort mappings together with the
deny-or-downgrade ceiling control that landed on main.
2026-09-02 10:01:42 +08:00
Wesley Liddick 6566039bc8 Merge pull request #6470 from aofee/codex/fix-ws-passthrough-missing-terminal
fix(openai-ws): reject passthrough close before terminal event
2026-09-02 09:42:13 +08:00
Wesley Liddick e50bffb7e3 Merge pull request #6469 from wucm667/fix/issue-6465-apikey-chat-cache-identity
fix(openai): preserve API key chat cache identity
2026-09-02 09:25:02 +08:00
Wesley Liddick f2804eb2c8 Merge pull request #6444 from Ritel-T/pr/group-free-openai-fast
Add group-level Standard billing for OpenAI Fast requests
2026-09-02 09:23:49 +08:00
Wesley Liddick 559960865a Merge pull request #6447 from DuskLin/feat/openai-reasoning-effort-over-limit
feat(groups): 推理强度上限增加超限自动降档与拒绝访问
2026-09-02 09:07:53 +08:00
Wesley Liddick a4fb58e428 Merge pull request #6443 from Ritel-T/pr/group-openai-fast
Add group-level OpenAI Fast enforcement
2026-09-02 09:07:05 +08:00
Wesley Liddick a668aa8b36 Merge pull request #6460 from HuntercodeT/fix/model-not-found-not-overridden-by-rate-limit
fix(gateway): stop a per-model cooldown from turning 404 model_not_found into 429
2026-09-02 09:06:17 +08:00
Wesley Liddick 8d0b5ede28 Merge pull request #6458 from HuntercodeT/fix/scheduler-projection-keeps-openai-passthrough
fix(scheduler): keep OpenAI passthrough flag in the scheduler snapshot projection
2026-09-02 09:05:51 +08:00
haruka 34b8bf1a6e feat: support Claude Fable 5.1 2026-09-02 05:47:01 +08:00
cxx-cod 277aa1411d fix(openai): end streaming at terminal event instead of waiting for upstream EOF
The streaming forward loops treated upstream EOF as the only normal exit
condition; response.completed/[DONE] only set a flag. With keep-alive /
HTTP/2 connection reuse to the upstream, the upstream may hold the
stream open for 8-46s after the terminal event, during which the
gateway could only emit downstream keepalives, inflating tail latency.

Finish the stream as soon as the terminal event frame has been fully
written downstream (usage is already parsed from the terminal event),
and close the upstream body to unblock the reader goroutine. Applied
to the guarded/async, sync, and passthrough streaming paths. The WS
HTTP bridge already behaved this way.

Adds a regression test with an upstream that hangs after
response.completed.
2026-09-01 19:32:24 +00:00
Ritel-T 52c7d88340 chore: merge upstream v0.1.185 2026-09-02 03:29:52 +09:00
Ritel-T 9e2d97f250 chore: merge upstream v0.1.185 2026-09-02 03:28:24 +09:00
aofee bfe0a5a873 fix(openai-ws): reject close before terminal event 2026-09-01 20:44:11 +08:00
wucm667 504919a054 fix(openai): preserve API key chat cache identity 2026-09-01 19:10:37 +08:00
Wesley Liddick 0d27f45ead Merge pull request #6454 from alfadb/fix/anthropic-fallbacks-sanitize
fix(gateway): 缺 server-side-fallback beta 时剥离 Anthropic fallbacks 字段
2026-09-01 17:47:34 +08:00
Wesley Liddick 65380be9c8 Merge pull request #6463 from DuskLin/feat/kimi-native-responses
feat(kimi): 原生转发 OpenAI Responses API
2026-09-01 17:46:36 +08:00
刘佳林 e377c4358c feat(kimi): forward native OpenAI Responses API
Moonshot pay-as-you-go and Kimi For Coding both expose native
/v1/responses. Stop converting Kimi Responses traffic to Chat
Completions, and treat Kimi like DeepSeek for protocol selection while
keeping /v1/responses URL construction.
2026-09-01 16:50:04 +08:00
HuntercodeT 3438580213 fix(gateway): stop a per-model cooldown from turning 404 model_not_found into 429
classifySelectionFailureError upgrades a selection failure to 429
rate_limit_error whenever the scheduler's compact reason string matches
model_rate_limited=(\d+) with a positive count. It does so unconditionally,
including when the fallback it was handed is the authoritative 404
model_not_found classification.

Selection failure reasons are additive, so both can appear at once, e.g.
"pool=9, filtered: model_not_supported=8 model_rate_limited=1": eight accounts
cannot serve the model at all and the ninth happens to sit in a per-model
cooldown. classifyNoAccountError only produces ModelNotFound through
DiagnoseModelAvailabilityForPlatform, a dedicated database query over
persistent eligibility (active + schedulable + model_mapping), so it has
already established that no account in the group can serve the model. One
transient cooldown does not make "All available accounts are currently
rate-limited" true.

The mislabel is not cosmetic:

- Retrying can never succeed, yet 429 explicitly invites a retry.
- Clients that treat 429 as a rate limit retry hard and swallow the body;
  Codex surfaces only "exceeded retry limit, last status: 429", discarding the
  one message that names the real problem. Operators then look for capacity
  and rate limits instead of a model whitelist.
- Ops attribution becomes self-contradictory: classifyNoAccountErrorFromGin
  has already marked the request business-limited for local model
  configuration, while call sites gate markOpsRoutingCapacityLimitedIfNoAvailable
  on ModelNotFound, which the override clears, so the same request is also
  counted as routing capacity limited.

Return the fallback untouched when it is ModelNotFound. The 429 upgrade is
preserved for every other fallback, which is the case it was written for:
accounts that can serve the model exist but are all cooling down, where the
fallback is 503 and retrying is meaningful.

Claude-Session: https://claude.ai/code/session_01LwvyJVV2sdqiu41Z9JDNUq
2026-09-01 15:18:30 +08:00
HuntercodeT e93e6368fc fix(scheduler): keep OpenAI passthrough flag in the scheduler snapshot projection
filterSchedulerExtra is an allowlist and never carried openai_passthrough /
openai_oauth_passthrough, while credentials.model_mapping is preserved in full.
Candidate filtering (ListSchedulableAccounts) reads that trimmed projection
(sched:meta:<id>), so there a passthrough account looks exactly like a
whitelist-mode one: Account.IsModelSupported falls back to the model_mapping
whitelist and the account is dropped as model_not_supported. Forwarding, which
hydrates the full account from sched:acc:<id>, still honors passthrough - hence
"testing the account directly succeeds, but the gateway reports no available
accounts supporting model X".

The practical impact is that a stale leftover whitelist silently shrinks the
candidate pool of accounts that were explicitly configured to let the upstream
decide. When no account in the group lists the requested model, the group is
fully dark for it even though every account can serve it.

This is not a regression of #4936: that fix corrected the predicate
(Account.IsModelSupported short-circuits passthrough before the mapping check),
this one restores the input the predicate is fed. The projection allowlist
predates #4936 by three and a half months and has never carried the flag, so
the fix simply never took effect on the snapshot path.

Add both keys to the allowlist, plus a regression test that round-trips the
projection through JSON (the real sched:meta write/read path) and asserts the
model gate still lets a non-whitelisted model through for a passthrough account.

Claude-Session: https://claude.ai/code/session_01LwvyJVV2sdqiu41Z9JDNUq
2026-09-01 15:13:11 +08:00
alfadb 200b1406d7 fix(gateway): strip Anthropic fallbacks unless server-side-fallback beta is present
Clients now send body.fallbacks; upstream rejects it without
anthropic-beta: server-side-fallback-2026-07-01. Mirror the
context_management sanitize so OAuth mimic / default API-key
beta paths no longer 400 with "fallbacks: Extra inputs are not permitted".
2026-09-01 14:23:33 +08:00
WizardPro 421a83282a fix(openai): accept scheduled automation bootstrap without call id 2026-09-01 11:36:26 +08:00
刘佳林 aa7a811e66 feat(groups): add deny-or-downgrade control for reasoning effort ceiling
OpenAI/Composite groups can now reject requests that exceed the max
reasoning effort instead of always rewriting them down to the ceiling.
Existing groups keep the previous auto-downgrade behavior.
2026-09-01 10:15:45 +08:00