Skip to content

multi2api v1.9.13

Choose a tag to compare

@github-actions github-actions released this 20 Oct 11:35
· 125 commits to master since this release

v1.9.13

本次两条主线:全项目 review 找到并修掉 6 个真 bug,以及看板 token 口径统一为「总 Token」。


一、全项目 Review(9 轮,6 个真 bug)

按 /review 的五轴(正确性 / 可读性 / 架构 / 安全 / 性能)分维度过了一遍,覆盖 session / server / wire / pool / upstream / admin / account / logbuf / 三个协议转换包 / 前端内联脚本。

⚠ 一个安全问题(建议所有部署升级)

internal/account 的落盘错误被静默吞掉 7 处(a0fcaf0)

saveLocked() 真的会失败(磁盘满、权限不足、Windows 下文件被占用时 os.WriteFile / os.Rename 都会返回错误),而 7 处调用一律 _ = s.saveLocked()。

其中最严重的一处是 Revoke:

用户点了「退出登录」,看到成功提示 —— 而那个 session 在进程重启后会从磁盘恢复、重新可用。

其余后果按调用点不同:AdvanceAuthVersion 没更新、Cleanup 没落盘、TouchLogin 没记。

修法:加 saveOrLog(scene),失败时打一条明确日志(含"内存已生效但磁盘未写入,重启会丢失这次修改"),Revoke 那条额外提示"重启后该 session 会复活,务必排查磁盘/权限"。场景名按调用函数命名,日志里一看就知道是哪条路径。

其余 5 个

提交 位置 症状 级别
4223728 logbuf/sink.go bufio.Scanner 未检查 sc.Err() —— 单行超 1 MiB 时循环正常退出,日志静默截断,HTTP 层拿到一份看起来成功的不完整数据 Major
8e98b9a loomy/adminpoints.go 面板快照的序列化 / 写临时文件 / 改名三处失败全静默,"临时文件写成功了但目标还是旧的",从外面看什么都没发生 Minor
8e98b9a clientlogin 归档凭证写入失败被吞 —— 用户以为"这次同步把 N 个号都归档了",实际磁盘上少了几个 Minor
147d96a anthroconv tool_result 合并用的是裸断言 .([]any),靠上一行 hasToolResult 隐式保证类型 —— 逻辑没错,但跨行隐式契约是 panic 的温床(网关 panic = 所有客户端同时断连) Nit

共同点:失败时一个字都不留,用户只会觉得"数据莫名其妙不对"。

判定「不是问题」的(有证据,留档防重复怀疑)

wire 的 SSE 解析          用 bufio.Reader 不是 Scanner ⇒ 无 64KB 单行上限
cache_hit_tokens 异常值     frameUsage 只取最后一帧;矛盾的是旧 tokens 不是 hit
pool.provState 无 delete   key 是 provider 名,天然有界
pool 锁内 IO              误报(SetStore 只是赋值)
upstream Body Close       规范;cancelReadCloser 正确
admin cacheRowTrusted     阈值合理:对新数据零误判
wire 字段白名单            24 字段;stream_options 是设计权衡
前端 XSS                  119 处 innerHTML,2 处可疑都是 SVG 坐标
前端 setInterval           顶层注册,无重复
前端 panelPolls           四条退出条件齐全(本项目唯一完整的轮询生命周期管理)

完整报告见仓库 tasks/review-full-2026-10-20.md。


二、看板 token 口径统一为「总 Token」

原来面板上并存三个数(总处理量 / 输出 token / 推理 token),彼此定义不同,看谁都像"总数"却互相矛盾。

现在合并为一张「总 Token」,等于 prompt_tokens + completion_tokens,并在所有展示位置统一:

调用统计的三张卡        → 一张「总 Token」
消耗分布副标题          → 总 Token
两个环图标签与取值      → 总 Token
堆叠柱取值 / 分组       → 总 Token
热力图着色 / tooltip    → 总 Token
图表纵轴峰值 / 档位阈值  → 总 Token
近 7 天表格 Token 列    → 总 Token
格子详情 / 鼠标读数      → 总 Token
对话测试的用量行        → 总 Token

⚠ tok/s 保留输出 token(改了就错)

速度的物理定义是「生成 N 个字用了 T 秒」。用总 Token 会得出"每秒 40 万 token"这种读数,因为输入里含几万到几十万的缓存上下文。所以性能健康条的吞吐、日志表格的 tok/s 列仍然是输出量。


三、缓存命中率口径统一

原来两处并列,看起来自相矛盾:

近 7 天表格  10/20 → 89%     (按**天**累加)
近 24h 趋势  18时  → 43%     (按**小时**独立)

算法完全相同,差的只是桶粒度。 但站在两个数前面的读者不会知道这件事。

改法:24h 趋势改为当日累计到该整点,于是最后一个点 == 表格今天的值 == 卡片值。

2026-10-20 04时   当日累计 94.6%   (本小时只有 60.2%)
2026-10-20 13时   当日累计 89.5%   (本小时只有 45.3%)
2026-10-20 19时   当日累计 89.3%   ← 与表格的 89% 一致

鼠标悬停时两个数都给:19:00 累计 89.3% · 本小时 52.9% · 请求 28 · 总 Token 9.8M

⚠ 代价:曲线变单调平滑(累计量只增不减),"某一小时质量差"被摊平 —— 但那个信息用柱子看 Token 量更合适。


四、新增前端语义门禁

修 hover 读数时发现一个坑:语法门禁拦不住语义错。

rd.setAttribute('x', ...);
+ '  累计 ' + (r.has ? ...)      ← 悬空的一元加表达式,求值后丢弃
rd.style.display = 'block';     ← 元素显示了,但内容从没被写

+ '...' + x; 是完全合法的 JS,语法检查永远不报。但用户看到的是"鼠标悬浮时没有东西显示"。

新增 tests/frontend/verify_tooltip_assign.js(4 项):

① 读数段落里必须有 `rd.textContent =` 赋值
② 全局扫描:头部行以 ; 结束却还有 + 续行 ⇒ 悬空
③ 读数字段齐全(累计 / 本小时 / 请求 / 总 Token)
④ hover 读数从 host._ctData 取当次数据(不是闭包快照)

反向验证过:人为删掉赋值行,门禁立刻报两项 FAIL。

⚠ 这条守卫我写错了两次判据(第一版误报 44 处、第二版误报 18 处),最终用「把续行往上追到头部行,若头部以 ; 结尾则悬空」这个精确判据才成立。


五、验证状态

go build ./...    exit=0
go test ./...     exit=0   ok=39  FAIL=0
go vet ./...      exit=0   (copylocks / loopclosure 无告警)
门禁① 语法         6/6
门禁② 语义         4/4
CI                  verify + 5 平台构建 + 发布资产 全绿

⚠ 没做到的(如实说明)

  1. 没跑 -race —— 本机无 gcc(CGO_ENABLED=0,go test -race 报 C compiler "gcc" not found)。所有并发结论来自 go vet 与代码阅读,没有竞态检测器的证据。
  2. hover 读数没有真浏览器验收 —— 逻辑修复 + 静态门禁都过了,但隔离实例验证卡在登录态(未登录时看板不渲染 #cacheTrend)。已把 CDP 脚本留在 tests/frontend/cdp_verify_tooltip.js,补上登录即可跑。
  3. 没有逐文件通读 359 个生产文件 —— 用的是模式扫描 + 定点取证;anthroconv / openaicompat / qoderwasm 只做了 panic 风险扫描。

六、已知未修(需要方向决策)

csg 上游的 total_tokens 是累积值。

实测 590 条里 573 次递增,第一条 199 → 最后条 493,134,真实总量只有 0.49M,而按行累加得到 228.6M —— 虚高 463 倍。

这不是我们算错,而是上游字段本身的口径问题。要修需要一个累积值检测(记住同上游上一次的值取差值 / 加绝对上限 / 或干脆不信该字段),三种方案各有代价,等明确方向后再做。


升级方式

五个平台产物任选,SHA256SUMS.txt 校验:

wb2api-server-darwin-amd64.tar.gz
wb2api-server-darwin-arm64.tar.gz
wb2api-server-linux-amd64.tar.gz
wb2api-server-linux-arm64.tar.gz
wb2api-server-windows-amd64.zip

⚠ 前端内嵌在二进制里,升级后请 Ctrl+F5 硬刷新浏览器,避免旧 HTML 缓存。