Commit Graph
462 Commits
Author SHA1 Message Date
cyh f49a393566 fix(openai): retain default models in mixed account catalogs 2026-09-06 02:02:49 +08: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
Wesley Liddick 7b693ae429 Merge pull request #6188 from JialinLiu-codedance/feat/usage-requested-reasoning-effort
feat(usage): 使用记录展示映射前的推理强度
2026-08-28 16:18:50 +08:00
Long LiandCursor 77b6c5bb0c merge: sync contrib/routed-codex-model-catalog with upstream v0.1.182
Keep catalog membership on schedulable accounts and intersect advertised
capabilities across all active group members that map an alias. Preserve
upstream plugin, service-tier, and models-list body-limit changes.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-25 21:28:57 +09:00
刘佳林 11ada80d58 feat(usage): 使用记录展示映射前的推理强度
用量行新增 requested_reasoning_effort,记录分组策略与模型族改写前的客户端请求值;推理强度列与模型列一致,主行显示请求值,有映射时用 ↳ 展示实际转发值。
2026-08-25 15:10:52 +08:00
feeeei 73aabc861c build: 取消 gosec G703/G704 全局排除,生产代码逐点 nolint、测试文件按路径豁免;DEV_GUIDE 同步 golangci-lint v2.13 2026-08-24 12:21:06 +08:00
feeeei 75faedda95 计费:fast/priority 按上游响应实际档位只降不升计费
此前 OpenAI service_tier 与 Anthropic speed 的计费档位只看请求侧,上游
因容量把 priority/fast 静默降为 default/standard 时仍按 2x 收费。现在:

- upstreamResponseModelObserver 同时观察响应侧档位:OpenAI 只信终止事件
  与无类型 body 里的 service_tier(response.created 为请求回显,忽略),
  Anthropic 读 usage.speed;查找仅在带 model 的帧上触发
- ResolveBillingServiceTier 只降不升:响应档位比请求便宜才采纳,更贵、
  未知或缺失一律沿用请求侧,与图片尺寸归并、response_model 计费的
  "不得更贵"不变式一致
- usage_logs.service_tier 记录实际计费档位,降级写
  billing.service_tier_downgraded 审计日志;HTTP、WS 及 ws_v2 relay 全覆盖
2026-08-23 16:53:28 +08:00
Long Li 3e98a5a1ad fix(composite): route exact account model aliases
Composite model catalogs expose exact account model_mapping aliases, but request routing only considered explicit routes or model-name detection. Unprefixed aliases were selectable but unroutable.

Resolve unique exact account ownership before the detector, keep explicit routes authoritative, and fail closed when an alias is claimed across platforms.
2026-08-22 14:43:40 +09:00
IanShaw027 7c53a842e4 合并上游 main,保留 5925 的 Grok 重试上限与 5888 的协议兼容修复
同账号重试采用次数上限加 deadline;畸形 tools 在出站前删除;compaction 422 与结构化错误扫描一并保留。
2026-08-21 08:49:44 +08:00
IanShaw 2ab24a1e77 修正 Grok 429 边界与 stream idle 重试上限 2026-08-20 06:43:31 -07:00
IanShaw 953028718d 修复 Grok 错误分类与容量重试 2026-08-20 01:02:40 -07:00
IanShaw d1c6456d08 合并上游最新主分支兼容修复 2026-08-19 23:15:50 -07:00
IanShaw acce29af27 补齐 OpenAI 与 Grok 协议兼容处理 2026-08-19 23:13:29 -07:00
IanShaw 7dae055f2a 计费:识别并记录 Anthropic Fast 请求 2026-08-19 06:35:11 -07:00
Wesley Liddick e943f817b1 Merge pull request #5729 from lbyxiaolizi/fix/responses-chat-reasoning-content-passback
fix(openai-compat): Responses→Chat 桥接按 reasoning item id 缓存回注 reasoning_content (#5520)
2026-08-19 14:03:03 +08:00
lbyxiaolizi 612436a5a7 fix(openai-compat): Responses→Chat 桥接按 reasoning item id 缓存回注 reasoning_content
修复 #5520:Codex 经 force_chat_completions 桥接到 DeepSeek thinking 上游时,
历史中的 encrypted-only reasoning item(summary 为空 + 不透明 encrypted_content,
远程 compaction / 跨会话恢复后常见)取不出明文,后续 assistant 消息缺
reasoning_content,DeepSeek 400 "The reasoning_content in the thinking mode
must be passed back to the API",客户端仅看到通用 502。

reasoning item 的 id 一定会被客户端回传,以其为 key 做服务端缓存:

- GatewayCache 新增 Set/GetReasoningContent(Redis,默认 TTL 7 天)
- 响应侧:流式扫 response.output_item.done、非流式扫 output,把 reasoning
  全文按 item id 写缓存;客户端断连后 drain 期间用 detached ctx 仍会写完
- 请求侧:apicompat 新增 ResponsesToChatCompletionsRequestWithOptions 与
  ReasoningContentByID 钩子,encrypted-only item 查缓存补回 pendingReasoning;
  缓存 miss/出错一律 fail-open 维持原行为
- 自愈:历史里带明文 summary 的 reasoning item 顺手刷新缓存,覆盖 Redis
  flush / 跨实例漂移

测试:apicompat(命中恢复/miss 保持原样/明文优先)、service 端到端(流式
写缓存、请求侧回注+自愈)、repository miniredis 存取。
2026-08-17 16:48:36 +08:00
wucm667 a288bab73a fix(gateway): align passthrough model discovery 2026-08-13 12:40:06 +08:00
IanShaw027 1f58e25ab3 Merge upstream/main into feat/grok-complete-integration
冲突集中在 chat completions / messages 两条 Responses 转发路径:
upstream 给 OpenAIForwardResult 增加了 UpstreamResponseModel 与
UpstreamResponseModelConflict(配套 beginUpstreamResponseModelObservation
观测器),本分支在同样位置把返回值改成了具名变量以便挂 Grok 原生搜索计数。
两侧不互斥,合并结果同时保留上游的响应模型观测字段与 Grok SearchCount 逻辑。

frontend/pnpm-lock.yaml 取 upstream 版本:package.json 与 upstream 完全一致,
本地差异只是 pnpm install 的重解析噪音。
2026-08-08 11:12:56 +08:00
IanShaw027 165b072908 fix(grok): gateway media/voice routing, models, and status UI polish
Align gateway Grok media/voice paths and model lists, harden upstream failure
and quota handling, clear non-Grok video generation config migration, and polish
temp-unsched/status indicators with model whitelist updates.
2026-08-08 01:07:27 +08:00
Brisbanehuang db0bff82c7 feat(usage): audit upstream response models
(cherry picked from commit 839036224f795c8ee5dc6718a2a14372a45eea44)
2026-08-07 09:40:11 -04:00
IanShaw027 d7c9e7167b fix(grok): 三轮评审 — 流式 Search 去重与调度/计费加固
- P0: 直播 SSE SearchCount 跨事件 call_id 去重,避免 ~2× 附加费
- SearchCount/Audio/WebSearchCalls 走 mandatory usage task
- web_search:uuid 等 forced request_id 优先于 client/local
- Sanitize 始终剥离 cookie;ApplyOAuth 清 grok_needs_reauth_at
- free 判定:paid 证据压过陈旧 free 凭据
- Gateway 列表应用 free soft-gate;token/body-read 可 failover
- web_search 重试支持 WaitPlan 获取;周 PeriodEnd 不再回填月 end
2026-08-07 17:58:23 +08:00
IanShaw027 245d069602 fix(grok): 二轮评审残留 — Search 叠加计费与 fail-closed 安全
- SearchCost 叠加 token(openai/gateway),未定价 warn
- Token URL 校验失败回落 DefaultTokenURL,禁止 Effective 旁路
- free 判定收窄 paidSignal(仅 plan/月额度),usage% 不否决 free
- web_search: mandatory 计费、uuid request_id、上游 failover 重选账号
- 调度阈值:7d/30d 不跨期 until + 48h stale 可选跳过
- SanitizeStoredCredentials 接入 create/update/bulk/SSO/ApplyOAuth
- ApplyOAuth 成功清除 grok_needs_reauth;SSO 允许 header_override_enabled
- VideoModelPrices 视为媒体定价完整;realtime 正常关闭仍计费
2026-08-07 17:40:21 +08:00
IanShaw027 ccf7ba3ba7 fix(grok): P2 分层清理、主对话 SearchCount 与调度 7d/30d
- DoGrokNativeResponsesJSON 去 gin.Context,UA 固定 CLI 身份
- GrokOAuthService 去掉 redis 直依赖,session store 由 wire 注入
- 主对话/流式响应统计 web_search_call/x_search/tool_search 写入 SearchCount
- Grok 调度阈值候选并入官方 weekly/monthly billing %
2026-08-07 17:21:01 +08:00
IanShaw027 7a81468282 fix(grok): 按 review 优先级修复计费漏扣、auth 投影与 free 门禁
P0: content 与 status 共用 claim 计费;稳定 grok-video request_id;
auth 热路径投影 video_model_prices。
P1: claim 失败释放可重试;视频绑定 TTL≥24h;SSO 凭据白名单与脱敏;
Token URL 校验;free soft-gate 默认 1M 并统一 free 判定;
Voice 预检余额;STT 抗低报;Realtime 失败不计费;search 未定价告警。
2026-08-07 17:15:43 +08:00
IanShaw027 b59a3df5b5 fix(grok): 视频在 status 返回 video.url 后再按上游参数计费
创建/generations 仅绑定账号并缓存 create-time 定价快照;轮询到
video.url / download_url 等成功 URL 时一次性扣费。时长/分辨率/模型
优先取上游 status 字段,缺失则回退创建快照,再套默认 8s/480p。
Redis SetNX + request_id 去重,避免重复轮询双扣。
2026-08-07 16:29:57 +08:00
IanShaw027 79df1647d4 feat(grok): 账单绝对金额、调度阈值 UI、搜索/Voice 计费与 /v1/web_search
- BillingSummary 输出 prepaid/monthly_used/on_demand 绝对金额,并映射官方 7d/30d 进度条
- 账号编辑页支持 credentials.account_scheduling_threshold 覆盖;Settings 文案细化
- groups.search_price_per_1k + 管理端/缓存全链路;SearchCount/AudioUsage 请求级计费
- Voice TTS/STT/Realtime 成功路径 RecordUsage;独立 /v1/web_search 原生 Grok 搜索
2026-08-07 15:52:55 +08:00
zhiyu 2eb24814fe fix(codex): 强制统一出站身份并让客户端版本号跟随官方发布
上游 /backend-api/codex 在容量紧张时按客户端身份分优先级降载,被降载的请求
HTTP 200 后立刻推流内 server_is_overloaded。此前网关对配不出官方身份的客户端
整体回退到硬编码的 codex_cli_rs/0.144.1(落后官方 4 个发布),这些请求稳定
落在被优先丢弃的一侧。

- 强制统一出口:所有 OAuth 出站的 User-Agent / originator / version 一律改写
  为网关规范身份,客户端自报身份不参与构造;HTTP / 透传 / WS / alpha-search /
  探针全覆盖。compat 桥接故意删除 originator 的路径保持 no-op。
- 版本号收敛为单一来源,运行时优先级为面板覆写 → 自动同步值 → 内置常量;
  UA 与 version 头同源派生,不再各自硬编码。
- 新增 3 小时自动同步官方客户端最新稳定版,面板可关闭,无需为跟版本而发版。
- 流内 server_is_overloaded / slow_down 改为先在同账号有界重试再切号,并标记为
  请求级瞬时故障,不再据此临时封禁账号。
- 移除被取代的降载身份黑名单、浏览器 UA 兜底及其辅助函数。
2026-08-03 20:14:58 +08:00
Brisbanehuang fad2f215e8 fix(profit-control): decouple gate from image intent, propagate gate via selection, restore eager sticky fallback
Review fixes for the profit-control feature commit:

- Image intent no longer disables the profit gate. The shared /v1/responses
  handler previously skipped the pricing context (and therefore the gate)
  whenever the platform-wide image intent predicate matched, which includes
  Codex's passive image_gen namespace declaration: any client could disable
  admission control for anthropic/gemini/antigravity groups by declaring a
  namespace tool in the request body. Both /v1/responses paths now always
  install the token pricing context; image intent only drives capability
  routing and image billing. Mixed token+image requests stay token-gated;
  only dedicated media endpoints remain out of scope.
- Out-of-scope paths are now explicitly suppressed instead of implicitly
  ungated: Grok media (billed by media multipliers; also prevents in-flight
  video lookups from turning into spurious 404s), OpenAI-group count_tokens
  (unbilled), and Live calls (duration billed) carry a suppress marker that
  every install point honors, including the defensive scheduler-entry
  install.
- The gate resolved during selection now travels back to handlers on the
  AccountSelectionResult. The shared gateway installed the gate only on a
  scheduler-local context, so handler-side post-slot terminal rechecks and
  post-admission sticky binding were no-ops for anthropic/gemini/
  antigravity/shared-grok requests (and for composite-routed member groups
  on the OpenAI path). Handlers re-apply the carried gate via
  ContextWithSelectionProfitGate before the terminal recheck and binding;
  the WS acquired-selection branch gained the previously missing recheck.
- Sticky binding semantics restored for ungated traffic:
  BindStickySessionAfterProfitAdmission falls back to the official eager
  bind when no gate is installed (wait paths lost their only binding point
  otherwise), reads the pre-existing binding at bind time only when gated
  (removes the unconditional per-request Redis read the feature added to
  the shared handlers), and the legacy engine's three selection-time
  binding writes are skipped under a gate so a terminally vetoed account
  can no longer become the new sticky target.
- Responses WS connections re-freeze pricingAt and re-resolve the gate at
  every turn (BeforeTurn) and bill each turn with its own instant, closing
  the connect-at-valley/bill-at-valley window; a turn that fails the
  recheck closes the connection so the client reselects on reconnect.
- Terminal recheck no longer swaps a DB-fresh selected account for a
  staler snapshot object (UpdatedAt guard), and observer counters are
  documented as per-evaluation.
- Migrations renumbered 191/192 -> 192/193 after the passkey migration
  landed upstream as 191.

New regressions: selection-carried gate propagation (control group proves
the pre-fix no-op), image intent not disabling the shared gate, eager
binding fallback without a gate (both services), gated
read-failure/sentinel-miss binding semantics, legacy-engine deferred
binding under a gate with eager behavior preserved ungated, turn-level
pricing refresh (config re-resolution, scheduled-group precedence,
suppress, mid-connection disable), and suppress-marker coverage for the
request pricing context.
2026-08-01 22:39:32 +08:00
Brisbanehuang 20ad5ec506 feat(scheduler): per-group profit control for token account admission
Group pricing (rate multiplier, peak windows, per-user overrides) and
account cost (accounts.rate_multiplier) already live side by side, but
nothing stops the scheduler from handing a request to an account whose
cost multiplier exceeds what the group's pricing can profitably serve.
Add an opt-in per-group profit gate that filters scheduling candidates
by a margin rule, while ordering, scoring, stickiness and breakers keep
working unchanged among qualified accounts.

Admission rule: an account qualifies iff U <= D * (1 - min_margin -
safety_buffer) within a small relative epsilon, where U is
accounts.rate_multiplier (0 is legal; missing/negative/NaN/Inf are
conservatively rejected as invalid) and D is the requester's effective
downstream multiplier (user-group override ?? group default, times the
group peak factor) frozen at the request's pricing instant.

- groups gain profit_control_enabled / profit_min_margin /
  profit_safety_buffer (migration 191); the durable auth-cache
  invalidation trigger additionally watches the profit and pricing
  columns (migration 192) so out-of-band group edits cannot leave
  stale auth snapshots; GetByKeyForAuth explicitly projects the new
  columns and the API-key auth snapshot version is bumped to force a
  refresh of pre-existing snapshots
- request-level pricing instant: token entry points install pricingAt
  into ctx; the profit threshold D and the RecordUsage peak factor
  read the same instant, so one request never changes price mid-flight
  across waits/retries/failover (media and unwired paths keep the
  existing record-time semantics)
- the gate covers token requests on openai, anthropic, gemini, grok
  and antigravity groups: OpenAI-family handlers via
  WithOpenAIRequestPricingContext (responses incl. WS bridge, chat
  completions, messages, embeddings, alpha search), the shared gateway
  via WithGatewayTokenRequestPricing (messages, chat completions,
  responses, gemini model actions); composite groups cannot enable it
  directly; image/video/models/usage/count_tokens stay ungated and an
  explicit image-generation intent suppresses the gate end to end
- post-slot recheck: after a slot is acquired the account is re-read
  via SchedulerSnapshotService.GetAccount (scheduler cache, then DB;
  only when both fail the check fails open with WARN + metric); a
  vetoed account releases its slot and joins the request's exclusion
  set for reselection; sticky bindings are written only after the
  final check passes, and an over-threshold sticky account is skipped,
  not deleted, so it comes back once its rate recovers
- sticky-session cache contract: GatewayCache.GetSessionAccountID now
  returns ErrStickySessionNotFound on a miss (mapped from redis.Nil in
  the repository implementation, mirroring ErrRefreshTokenNotFound) so
  the profit sticky path can distinguish "no binding yet" from a real
  read failure without importing the cache driver in service code
- cross-group re-entry (composite parent -> member group) resolves the
  gate against the member group and clears a stale parent gate instead
  of letting a foreign threshold veto accounts
- per-platform/group activity counters (installs, threshold vetoes,
  invalid-rate vetoes, refresh failures) for observability
- admin UI: profit-control section on the five platforms' group forms
  with percent input, validation and platform-switch reset; group
  create/update/duplicate normalize and validate the config at a
  single choke point
- cmd/profit-preview: offline what-if tool that replays the production
  admission semantics over an exported config/account/override/model
  dump, reports per-model admitted-account counts under the default
  and the worst-case (lowest user override) D, and surfaces probe-sync
  staleness as warnings without affecting admission

Tests: service unit coverage for gate resolution/veto/threshold
epsilon/pricing instant/suppress marker/scheduler filtering and
post-slot recheck (incl. -race on the profit surface), unit-tagged
handler slot-recheck and capability-mapping regressions, sqlmock and
real-PostgreSQL integration regressions for the GetByKeyForAuth
projection and the migration-192 trigger watch list, API contract
update, and frontend specs for the five-platform form helpers.
2026-08-01 22:39:31 +08:00
Heatherm Huang ee332cee64 Fix composite model defaults for linked platforms 2026-07-23 09:20:52 +08:00
Heatherm Huang a008b63c16 Add composite group route registry 2026-07-23 09:20:18 +08:00
jjaw f7979ae8ff 优化 Responses SSE 事件类型解析 2026-07-19 18:50:33 +08:00
王鹏 fc4089f292 fix(openai): bound native responses first output wait
Add an opt-in first semantic output budget for native HTTP Responses, including response-header wait. Keep preamble and keepalive bytes non-semantic so a stalled account can fail over once without replaying its response IDs. Defaults remain disabled.

Related to #4201, #4185, and #4248. Complements the HTTP/2 dead-connection fix in #4207.
2026-07-15 13:01:16 +08:00
Heatherm Huang 343390057d fix(grok): fail over OAuth credential errors safely 2026-07-14 14:55:30 +08:00
shaw 084d26cbd2 refactor(service): 纯移动拆分 gateway_service.go(7294→1289行) 2026-07-08 08:49:20 +08:00
shaw d754be0d8e refactor(gateway): 抽取 CC forwarder 公共管线并拆分两大 service 文件
PR #3802 遗留项:三个 CC forwarder(raw 直转 / responses 回退 / messages
回退)间约 85% 重复的 HTTP 管线与 SSE 循环骨架收敛到新文件
openai_gateway_cc_pipeline.go,messages / chat_completions 两条主路径中
逐字相同的错误处理块一并接入。各路径有意保留的行为差异(GLM effort
归一化、fast policy 适用范围、ClientDisconnect 语义、Grok 分支等)留在
调用方,未强行统一。

同时对两个最臃肿的网关文件做纯移动拆分(零语义变化,逐字节校验):

- openai_gateway_service.go 7821→4872 行:
  调度 → openai_gateway_scheduling.go
  passthrough → openai_gateway_passthrough.go
  用量/计费/codex 快照 → openai_gateway_usage.go
- gateway_service.go 10912→7294 行:
  调度 → gateway_scheduling.go
  Anthropic APIKey 直通 → gateway_anthropic_passthrough.go
  Bedrock → gateway_bedrock.go

回归保障:全部搬迁块与 HEAD 逐字节 diff 一致;留存文件经"HEAD 减去搬迁
范围"重构比对,差异仅为 goimports 移除的孤儿 import;定向单测
(fallback/raw/cyber/grok/transport/调度/用量/广域 Forward-Handle 扫描)
全绿;另经独立对抗审计确认零行为差异。
2026-07-07 22:14:46 +08:00
li 867616fca8 fix(websearch): 剥离历史消息中上游不接受的 web search 块
Web Search 模拟合成的 server_tool_use / web_search_tool_result 块
(srvtoolu_ws_ 前缀)被客户端回传到后续 /v1/messages 历史后,
上游一律 400(GLM 等第三方兼容上游只接受
text/thinking/image/tool_use/tool_result),会话从此卡死。

新增 FilterWebSearchHistoryBlocks 预过滤:
- 模拟伪造块对所有上游无条件剥离;
- 真实 web search 块仅对 passback-required 协议族剥离
  (复用 ResolveThinkingProtocol),不误伤官方 Anthropic 会话;
- 接入 Anthropic 通用转发与 API Key 透传两条路径的既有 pre-filter 区。

Fixes #3677
2026-07-07 13:41:40 +08:00
shaw 31b6e0d94a fix: 请求头覆写审计问题修复(禁止名单缺口/beta 对称性/批量清空防护)
后端:
- 禁止名单补充 content-type(multipart boundary 为每请求随机值,静态覆写必坏
  images 路径)、x-claude-code-session-id / x-client-request-id(会击穿每请求
  会话同步,与已禁的 session_id 等同类)、cookie / x-goog-api-key(与透传路径
  的入站鉴权残留清洗保持一致)
- anthropic-beta 覆写与 body 净化对称:四个 Anthropic 构建器在
  sanitizeAnthropicBodyForBetaTokens 前以覆写值为有效 finalBeta,避免覆写
  丢 token 后 header/body 不对称被上游 400
- 保存/应用两路径共用 normalizeHeaderOverrideEntry,消除双份校验规则漂移
- GetHeaderOverrides 按 modelMappingCache 先例增加热路径缓存(WS 每消息
  重建头场景收益最大);ApplyHeaderOverrides 移除无观测效果的排序

前端:
- 禁止名单镜像同步新增项;值长度改按 UTF-8 字节校验(与后端 len() 对齐)
- Create 弹窗切换平台时重置覆写配置,避免上一平台模板行串台
- BulkEdit:开启覆写但无有效行时拦截保存(防止整键替换静默清空所选账号
  既有配置);开启分支显示整体替换警告;混合平台选择时隐藏模板按钮
- 删除未使用的 isBlockedHeaderOverrideName 导出
2026-07-06 20:05:50 +08:00
shaw ec7b20649e feat: apikey 账号支持请求头覆写(Anthropic/OpenAI)
- 账号 credentials 新增 header_override_enabled / header_overrides,
  仅对 anthropic/openai 平台的 api_key 账号生效
- 转发前对同名请求头做大小写不敏感覆盖(EqualFold 全量删除后按
  wire casing 写入单值),值为空的条目视为占位不参与覆盖
- 覆盖全部出站路径:/v1/messages(标准+透传)、count_tokens、
  /v1/responses、chat completions、embeddings、images、全部 WS 模式、
  账号测试/探测、上游模型列表
- 创建/编辑/批量更新统一校验:RFC 7230 名称格式、去重、长度/条目上限、
  24 个禁止覆写头(认证/连接控制/accept-encoding/sec-websocket-*/
  会话隔离头),应用时二次防御过滤
- 前端三个账号弹窗新增开关+键值行编辑器+模板按钮(Claude Code CLI /
  Codex CLI 标准头,值为空),本地校验与后端规则对齐,i18n 中英文
2026-07-06 19:11:08 +08:00
shaw a1b2b32e08 fix: prevent silent usage_logs drops under queue overflow (#3656)
高并发下扣费成功但 usage_logs 被双层异步队列静默丢弃(单用户实测丢失 49%),
导致消费排行与对账缺口。根因是 6a685727 为避免溢出时单行 INSERT 踩踏数据库,
把三处"队列满"改成了立即终态丢弃。

改为有界阻塞背压,兼顾数据不丢与批量写入模式不变:

- CreateBestEffort/createBatched 入队移除 default 立即丢弃分支,
  队列满时阻塞等待(受 detached 15s ctx 期限约束)
- writeUsageLogBestEffort 收到 dropped 不再直接放弃,统一走
  repo.Create 同步兜底(仍经批处理器;ctx 耗尽时换新 detached 窗口),
  重复写入由 ON CONFLICT DO NOTHING 幂等防护
- worker 池默认溢出策略 sample→sync(viper 默认 + 池常量),
  溢出时提交方内联执行(提交点在响应写出后,不阻塞客户端),
  显式配置 sample/drop 的部署不受影响
- 附带修复 ensure*Batcher 的存量数据竞争:channel nil 检查移入 sync.Once

正常负载零行为变化;突发时短暂等待替代永久丢失;数据库只见批量 INSERT。
2026-07-03 21:02:32 +08:00
Wesley Liddick 6bc6998b0e Merge pull request #3627 from alfadb/fix/ollama-anthropic-bearer-auth
fix(anthropic): 支持 API Key Bearer 认证方式
2026-07-02 17:37:30 +08:00
Wesley Liddick 4e23c77ece Merge pull request #3626 from DanisJiang/fix/claude-code-stream-keepalive
fix: prevent Claude Code stream keepalive stalls
2026-07-02 17:35:48 +08:00
shaw 11a3da65c0 fix(group): harden peak-rate config handling and label peak windows with server timezone
Follow-up fixes to #3569 based on code audit:

- Expose server_timezone / server_utc_offset in public settings (and the
  __APP_CONFIG__ injection payload) and label every peak-window display
  with the server UTC offset, so users don't misread the billing window
  as browser-local time
- Unify CreateGroup/UpdateGroup peak-config sanitization via a single
  NormalizePeakRateConfig chokepoint: non-subscription groups always get
  peak fields cleared; unparseable window strings and negative
  multipliers are scrubbed when peak is disabled
- Replace hot-path time.Parse in PeakMultiplierAt with a manual HH:MM
  parser (accept set verified byte-for-byte identical to
  time.Parse("15:04") by exhaustive fuzzing) and reuse it in validation
- Revert the zero-behavior CalculateCost indirection churn in
  billing_service/gateway_service introduced by #3569
- Remove dead GetGroupPlatformMap and the duplicate deref helper in the
  admin handler package
- Share frontend peak formatting via utils/peak-rate.ts, unify the ×N
  label format, and move hardcoded Chinese tooltips to i18n keys
2026-07-02 16:11:07 +08:00
alfadb 7869b7fe38 fix(anthropic): 支持 API Key Bearer 认证方式 2026-07-02 10:43:08 +08:00
Yuhao Jiang a5781fe31d fix Claude Code stream keepalive stalls 2026-07-01 21:01:11 -05:00
xueshiji 1034f576d7 fix: 高峰倍率全链路透传、计费术语修正与边界处理
- 高峰倍率信息透传至可用渠道、支付计划、结算信息等 API,前端
  GroupBadge / GroupOptionItem / SubscriptionPlanCard / PaymentView /
  SubscriptionsView 统一展示高峰时段与倍率标签
- 修正计费术语:"文本倍率" → "token 倍率",明确高峰倍率同时作用于
  token 计费的图片 token,图片按次计费不受高峰影响
- 允许高峰倍率 multiplier=0,支持高峰时段免费策略
- 切换分组类型为 standard 时自动清除高峰倍率配置
- 长上下文计费与标准计费路径改用内部实现,移除冗余中间调用
- 前端高峰倍率相关控件文案改为 i18n
- 新增多组高峰倍率相关单元测试
2026-07-01 17:46:12 +08:00
xueshiji 5e9529bb88 Merge branch 'Wei-Shaw:main' into main 2026-07-01 14:00:51 +08:00
shaw 59e9356c51 feat: 抹除 Anthropic OAuth 请求中客户端 dateline 隐写指纹
对 /v1/messages 转发到 Anthropic OAuth/setup-token 账号的请求做 dateline
归一化,将 system prompt 与 <system-reminder> 块中 "Today's date is …"
语句里的 4 种撇号变体与 "/" 日期分隔符还原为 ASCII 撇号 + "-",抹除某些
客户端在检测到非官方 base URL 时注入的 3 bit 隐写指纹。API Key 账号不受
影响。新增系统设置开关 enable_client_dateline_normalization,默认开启。
2026-07-01 10:54:18 +08:00
xueshiji 915c60b150 feat(group): 订阅分组新增可选的高峰时段倍率,以支持智谱等coding plan的高峰时段 2026-06-30 14:17:05 +08:00
wucm667 9f5b57fc96 fix(billing): 防止余额计费持续透支 2026-06-22 10:30:27 +08:00