Skip to content

merge: integrate official Sub2API v0.2.18 - #299

Merged
github-actions[bot] merged 49 commits into
mainfrom
sync/upstream-0.2.18
Oct 24, 2026
Merged

github-actions[bot] merged 49 commits into
mainfrom
sync/upstream-0.2.18

Conversation

@HTExplicit

@HTExplicit HTExplicit commented Oct 24, 2026 •

Copy link
Copy Markdown
Owner

整合官方 v0.2.18(ac1b8f1271be0d674fa6f074403c1410f30a3a81),采用官方流式计费、错误处理、工具流转换、调度缓存算法与键名、Vertex 配额及生图 gate。14 个文本冲突按 .downstream/upstream-review-v0.2.18.md 中的明确决议处理,保留固定随机设备、独立 buffered 补参和其他不冲突的内置能力。

平台/状态筛选恢复官方单选和 localStorage,Vertex 对应区域采用官方布局;倍率计时器按最终决定保留官方 immediate: false 原句,实际不启动。依赖约束采用官方,保留 xlsx 0.20.3。新增官方迁移 246 与已有同编号文件共存,不改历史 SQL/checksum。

验证:冻结锁安装、前端类型检查、UI detector、变更对应的前后端定向用例及静态差异检查通过。前端新增测试的 debounce 隔离问题已修正并仅复验失败及直接影响。正常必需 PR 检查保留;已修复正常 CI 发现的官方 Gemini image gate 测试夹具缺少兼容服务依赖问题,六个直接相关子场景定向复验通过;生产代码未因此添加保护。未调用真实模型或支付接口,未部署。

zenggang and others added 30 commits October 16, 2026 11:17
- 原生 /v1beta、Claude 兼容 /v1/messages、Chat 兼容三条流式路径:读取中断由客户端
  断开引起(context.Canceled 或请求 context 已取消),且已观测到 token 用量或已回吐
  图片时,以已收集结果收尾并返回成功,handler 照常提交 usage;ForwardResult 标记
  ClientDisconnect
- 读到结束分片后断开按完整用量计费;中途断开按断开前最后一份 usageMetadata 计费,
  断开前无任何用量时仍不计费
- 图片数:收到 finishReason 前断开只计已送达的图片(原生按已观测 inlineData,Claude/
  Chat 兼容流不转发图片计 0);收到 finishReason 后断开与正常完成同口径
- Chat 兼容流写失败断开的既有返回同样带上断开标记与结束状态
- 原生路径读错误时不处理未以换行结尾的残行,避免截断的 usageMetadata 覆盖已收集用量
- 上游 context 不解绑:Gemini 兼容链路没有流空闲超时,解绑后上游挂起会让读取无限期阻塞
- 客户端未断开时的上游读错误、非流式客户端断开、Antigravity 非流式转换的读错误
  行为不变
grok-4.7-fast 没有专属价格,落入「未知 Grok 文本模型」兜底按 grok-4.6
价卡计费,与普通 grok-4.7 同价($2 / $0.50 / $6),少收一半。

- 内置兜底价新增 grok-4.7-fast:<200k 为 $4 输入 / $1 缓存读取 / $12 输出
  (每百万 token,标准价 2 倍);>=200k 整次请求为 $6 / $1.50 / $18,
  即 Fast 基础价 1.5 倍,阈值口径与其它 xAI 价卡一致(达到即进高档)
- grok-4.7-build-fast(Grok Build 侧的 Fast ID,见 Wei-Shaw#7502)同样命中该价卡
- 本地价格快照新增 grok-4.7-fast 条目,带 xai/ 前缀的写法也能识别

价格来源:https://docs.x.ai/developers/pricing(Grok 4.7 Fast,仅 Cursor
与 Grok Build 提供)。

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Vertex 拦截 prompt(promptFeedback.blockReason,如 PROHIBITED_CONTENT)时 usageMetadata
  可能只带 cachedContentTokenCount、缺 promptTokenCount,promptTokenCount − cachedContentTokenCount
  得到负的 input_tokens 与负费用
- extractGeminiUsage 与 antigravity 非流式 / 流式(累计用量、message_start)四处换算改为差值下限 0,
  缓存命中仍照上游所报计入 cache_read_input_tokens
- 补充表测试、原生流式拦截用例与 antigravity 两条路径用例
handleCCStreamingFromAnthropic 与 handleResponsesStreamingResponse 写客户端失败时
立即返回,message_delta 的 output_tokens 未合并,usage 记为 0。改为断开后停止向
下游写入、继续读完上游合并 usage,与原生 /v1/messages 路径一致;ForwardResult
标记 ClientDisconnect。

两条路径改用 anthropicNativeLinePump 读上游,按 gateway.stream_data_interval_timeout
限制逐行读间隔,上游挂住时关闭 body 并按已累计 usage 收尾。
异步生图任务把上游返回的图片 url 下载后转存对象存储,此前沿用默认 http.Client,
与同步 Images 端点 url 回填(c227863d)的下载策略不一致。

- 下载目的地为 localhost、回环、私网、链路本地、CGNAT 等非公网地址时不下载,
  重定向的每一跳同样校验;直连时在 socket 层按实际连接 IP 判断
- 继续支持环境变量代理:经代理时先校验目标主机的解析结果
- 响应声明类型与字节嗅探都不是图片时任务失败,不再按 png 落盘
- 下载客户端进程内共享,复用连接池;超时与大小上限不变
…ad-policy

fix(images): 异步生图转存的图片下载与同步回填策略对齐
…-disconnect-billing

fix(gemini): 流式中途客户端断开时按已收集用量计费
…t-tokens

fix(gemini): prompt 被拦截只报缓存命中时 input_tokens 不再为负
- 新增 AccountTypeSupportsQuota / SupportsAccountQuota(apikey/bedrock/service_account)
  作为配额账号类型的唯一判定,累加、超限停调度、额度通知、接口输出与计费仓储统一改用;
  池模式等其它特性仍只看 IsAPIKeyOrBedrock
- service_account 配额语义与 apikey 一致:配置了任一上限才累加,用满停调度,跨阈值发额度通知
- 计费事务中账号已删除时跳过账号配额计数,与 API Key 对称,用户扣费不再整笔回滚
- 账号额度通知只对配置了上限的账号做检查
- 前端创建/编辑弹窗对 Vertex 开放配额设置,设上限后用量列显示配额进度条;
  账号类型判断收敛到 supportsAccountQuota
- 门禁作用于展示该开关的 Gemini 系分组(gemini/antigravity/composite);无分组 Key、
  anthropic 等其他平台分组不受影响,OpenAI/Grok 仍走各自门禁
- 生图判定:复用图片接口的 gemini-*-image 判定(isGeminiCompatibleImageModel),或请求体
  generationConfig.responseModalities 含 IMAGE(camelCase/snake_case、字符串/整数枚举、
  重复键逐个检查);countTokens 不拦
- 两道检查:/v1/messages、/v1/chat/completions、/v1/responses、/v1beta/models 入口在
  选号前按客户端请求名与渠道映射名预检;Gemini 兼容与 Antigravity 的 6 个转发函数在
  账号模型映射后再按上游模型名检查,覆盖账号级别名
- 拒绝统一返回 400(Gemini 原生 INVALID_ARGUMENT,其余 invalid_request_error),
  不触发换号与账号处罚,下游 sub2api 把本实例当上游时不会因此停用账号;
  请求标记为本地功能门禁
- 升级兼容:迁移 246 把存量 gemini/antigravity 分组的生图开关置为开启;新建分组与简易
  模式默认分组对 gemini/antigravity 默认开启(与 grok 同为 OR 语义,创建时传 false 不生效),
  前端创建表单同步默认值;composite 不回填(同一开关还控制 OpenAI 生图),未开启的
  composite 分组升级后 Gemini 生图返回 400
- 未覆盖:最终模型名不符合 gemini-*-image 形态(如上游中转自定义的 nano-banana 别名)
  且请求体未声明 IMAGE 模态;门禁按 Key 所属分组判断,Claude Code 专用分组降级到
  其他分组时仍按原分组
客户端在线时,Anthropic 账号的 chat/responses 兼容流在上游未发出结束事件
(message_stop、带 stop_reason 的 message_delta 或 [DONE])就中断,不再补发
finish_reason / [DONE] / response.completed 报成功:
- 读间隔超时、读错误、message_start 之后直接 EOF:service 写出协议内终止
  事件(CC 为 error 帧,Responses 为沿用流内 id 与 sequence_number 的
  response.failed),返回错误并带上已观测到的 usage;读间隔超时调用
  HandleStreamTimeout。
- 流内 event: error:立即结束读取,与 /v1/messages 共用
  sseStreamErrorEventFailover 转为 UpstreamFailoverError,未输出前换号重试,
  客户端在线时不计费。
- 两个 handler 的错误分支对带结果的错误照常入账。

客户端断开后的读取:stream_data_interval_timeout 未启用时按 180s 启用读间隔
上限;请求 ctx 取消也视为断开。复用 anthropicNativeStreamInterval。行泵在
计时器到期且已有缓冲行时优先交付数据,下游写入慢不再被判为上游空闲。
GetModelMapping 命中缓存前要计算 model_mapping 的签名:收集全部 key、
排序、再逐个写入 fnv64a。签名只存放在 Account 的未导出字段里,不持久化、
不序列化、不跨进程比较,排序的唯一作用是消除 Go map 的随机遍历顺序。

改为逐条目计算 FNV-1a(key、分隔字节、value 或非字符串标记)并经 fmix64
收尾后求和:内容相同签名相同,单条改值、改键、两个 key 互换 value 都能检出,
且不分配内存。30 条映射单次计算约 2.3µs/1 次分配降到约 0.8µs/0 次分配;
选号时每个候选账号每次调用 3 次。
setAccountModelRateLimitSnapshot 原地写 account.Extra 以及其中的
model_rate_limits map。选号阶段的 Anthropic Fable 阈值过滤
(filterAccountsBySchedulingThreshold → ApplyAccountSchedulingThreshold)
会对候选账号调用它;候选账号的 Extra 一旦与其它请求共享,原地写就会造成
并发 map 读写并污染共享对象。

改为复制旧的 limits 与 Extra 顶层、写入新条目后替换 account.Extra,
不修改任何已有 map,本请求后续读取到的结果不变。其余调用方
(ratelimit_cline、cline_usage、openai_images_balance)都通过同一个
*Account 读取结果,不依赖原地修改可见。
三个入口(Gemini 原生、/v1/messages 的 Gemini 分支与 Claude 路径)
每个请求都会查询分组的 antigravity:forced 快照桶。纯 Gemini/Anthropic
分组的该桶恒为空,空快照按未命中处理,于是每个请求都回源 PG 并重新
发布快照,而判定结果只被 antigravity 账号的 503 原地重试与模型限流
预检查读取。

入口改为挂载延迟判定:antigravity 账号首次需要时才查询快照,同一请求
内只查一次;显式设置的 SingleAccountRetry 标记仍优先。其他平台的请求
不再触发该查询。
GetSnapshot 每次选号都 ZRANGE 全组账号 ID、分块 MGET 全组 sched:meta
并逐条 JSON 解码,Redis 出口、CPU 与 GC 都随分组号数线性增长。

- 每个 bucket 的解码结果常驻进程内存。每次读取用一次 MGET 取 ready、
  active、全局修订号与 bucket 平台修订号(mixed 另加 antigravity),全部与
  条目一致且条目未超过 10 秒才复用,否则重新加载。last_used 仍每次读取,
  套用在本次调用独占的副本上,LRU 语义不变。
- 修订号是随机串:精简记录改写后写入所涉平台的修订号,与最后一批写入同一
  pipeline 发出;删除记录、账号平台变化(SET ... GET 取回旧值比对)、
  写入出错时写入全局修订号,出错路径在后台合并写入。
- 母本发布前预解析模型映射;副本在指针、长度与运行时版本都匹配时直接复用,
  不计算签名。
- 未命中、空快照、记录缺失、解码失败、Redis 出错的返回与原实现一致;选中后
  的整账号读取仍每次重新解码,转发路径对账号的原地修改不触及常驻对象。

真实 Redis 端到端(SelectAccountWithLoadAwareness,本机):
  100 号:3.83ms → 0.73ms,Redis 出口 251KB → 7.0KB,分配 2.03MB → 0.23MB
  1000 号:27.8ms → 1.92ms,Redis 出口 2.47MB → 25KB,分配 20.2MB → 1.97MB
  每次都未命中:100 号 3.83ms → 3.10ms,1000 号 27.6ms → 21.9ms
常驻内存约为每号 7KB × 活跃 bucket 数;闲置超过 100 秒的条目在写入新
条目时清除。
…utput-metadata-20261016

fix(openai): preserve overload retries after queued and metadata events
…stence

fix(accounts): persist platform and status filters
…-scan

fix(security): clear dependency security scan failures
feat(account): Vertex 账号支持配额上限
…eneration-gate

fix(gemini): 分组「允许当前分组生图」开关对 Gemini 系入口生效
…eam-disconnect-billing

fix(gateway): Anthropic 账号 chat/responses 兼容流式客户端断开后读完上游再计费
…vity-probe

perf(gateway): 单 antigravity 账号分组判定改为按需求值,纯 Gemini/Anthropic 分组不再每请求回源数据库
Wei-Shaw and others added 16 commits October 21, 2026 17:44
…ture

perf(account): 模型映射签名改为与遍历顺序无关、零分配
…memo

perf(scheduler): 调度快照解码结果常驻进程内存,选号不再每次全量读取并解码分组
计费事务中账号配额累加位于余额(或订阅用量)扣减之后、提交之前,与同一用户
共享的余额行锁处在同一持有区间。累加语句约 9.4KB,无论账号设置了哪些上限,
都要解析、规划并求值总额度、日、周三个维度的全部分支;lib/pq 每次执行都重新
规划,同一用户的并发计费在这段上串行。

- 预先拼好 4 条语句:只累加总额度、加日维度、加周维度、日周都累加(与原语句
  去空白后逐字相同)。按账号设置的日/周上限选择,只设总额度的账号使用约 1KB
  的语句。
- 选择依据来自构造计费命令时的账号配置:UsageBillingCommand 新增
  AccountQuotaHasDailyLimit / AccountQuotaHasWeeklyLimit,不参与幂等指纹。
  省略维度的语句在 WHERE 中要求 extra 为对象、被省略维度未设上限(键不存在、
  JSON null 或不大于 0 的数字);库中配置不符时命中 0 行,再执行日周都累加的
  语句,结果与原语句一致。条件只用 ->、jsonb_typeof 与 jsonb 比较,对任何行
  求值都不报错。
- 事务内语句顺序、加锁顺序、越线入队 scheduler_outbox 与返回的额度状态不变。
  集成测试对 15 种 extra 配置 × 4 种选择组合,与原语句比对 extra、updated_at、
  返回值与报错码,并断言各语句是否命中。

单条语句耗时(PostgreSQL 18,BEGIN/ROLLBACK 内,扣除空事务):
  只设总额度 0.77ms → 0.24ms
  设日上限   0.79ms → 0.50ms
  设周上限   0.79ms → 0.57ms
  日周都设   不变

压测(PostgreSQL 18、NVMe、lib/pq;1 个用户经 41 个只设总额度的账号计费,
同时运行 usage_logs 批量写入;TPS):
  并发 32:488 → 1209
  并发 64:424 → 874
  并发 128:357 → 575
…ment-variants

perf(billing): 账号配额累加按已设置的周期维度选择语句
…-image-size

fix(deepseek): avoid duplicate image payloads
…ema-constants

fix(tools): normalize parameter constants and annotations
…re-json-403

fix(ratelimit): avoid penalizing Cloudflare 1010 blocks
…27.3

fix(build): 对齐 Docker 默认 Go 镜像至 1.27.3
…fast-pricing

fix(billing): Grok 4.7 Fast 按官方 Fast 价卡计费 (Wei-Shaw#8119)
…oad-recovery-20261023

fix(openai): 缓解 Codex overload 并恢复空流事件后的安全重试
…v2-3pqr-j5jr

DOMPurify <=3.4.16 is affected by two advisories published 2026-10-22:
- GHSA-cg2g-xh64-v4q7 (critical): SAFE_FOR_TEMPLATES bypass via split
  template expressions in attached shadow root content on DOM output modes
- GHSA-mrv2-3pqr-j5jr (high): IN_PLACE hook removing a parent/sibling leaves
  detached subtree event handlers armed

Bump the direct dependency and the pnpm override floor to 3.4.17 so the
mermaid-transitive copy dedupes to the same patched version. Lockfile-only
regen via pnpm 9 (matches CI); no other package changes.
@HTExplicit HTExplicit added upstream-review-required Manual upstream review is required upstream-reviewed Human upstream overlap review completed and removed upstream-review-required Manual upstream review is required labels Oct 24, 2026
@HTExplicit
HTExplicit marked this pull request as ready for review October 24, 2026 03:17
@github-actions
github-actions Bot merged commit 6ac28b7 into main Oct 24, 2026
21 checks passed
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.

8 participants