Repository navigation
Releases
v0.13.1
v0.13.1:统一版本号(/healthz、OpenAPI、镜像 tag 从此一致),功能与 v0.13.0 相同。SCHEMA_VERSION 仍为 32,无数据迁移。
版本同步
pyproject.toml(唯一真源)→0.13.1。- 同步
src/version.py回落值、uv.lock、web/package.json、CITATION.cff、README 镜像示例、故障模板样例、TECHNICAL.md示例。 - 升级后
GET /healthz的version与/openapi.json不再报旧的0.11.0。
功能
- 与 v0.13.0 相同:凭证导入支持整段 Cookie / DevTools Copy as cURL 与渠道自动识别;灵犀同账号导入合并、换登录后旧号复探禁用;探测遇 401 统一硬禁用。
- 已运行 v0.13.0 的部署,仅需更新镜像并重启后端即可看到正确版本号。
v0.13.0
v0.13.0:凭证导入支持整段 Cookie / DevTools Copy as cURL,并修复灵犀「登录新账号、旧号失效」后的残留问题。SCHEMA_VERSION 不变(32,无新列新表),升级需要重启后端。
凭证导入(管理台 → 添加凭证 → JSON 导入)
- 输入框现在接受四种内容:渠道 JSON、整段 Cookie、裸 token、浏览器 DevTools 的 Copy as cURL (bash)。
- cURL 里的
Cookie:/Authorization:头会被提取,HttpOnly Cookie 也能导入。 - 按明确特征自动识别渠道:
wps_sid→ 灵犀、xhx_token/ JWTiss=xiaohuanxiong→ 小浣熊、accessToken/refreshToken→ TRAE、AK/SK → CodeArts、model_app_key→ OfficeAce;CodeBuddy / Qoder 的 token 有歧义,沿用下拉选择。 - 成功导入后记住渠道;改输入会重新按内容识别,手选渠道优先。
- 带
provider的单条 JSON(导出/脚本同形)自动拆包;多条数组提示走「批量导入」。
灵犀会话
- 实测确认:同一浏览器配置登录另一个 WPS 账号,会让上一次的
wps_sid立刻 401。多账号需用不同浏览器配置 / 无痕窗口分别登录后各取一次 Cookie。 - 手工导入按
uid就地合并:同一账号重登更新既有行并解除禁用,不再新增重复凭证。 - 导入新灵犀凭证后自动复探其它灵犀凭证:确认 401 的旧号硬禁用,管理台显示「需重新登录」;仍有效的多账号不受影响。
- 额度探测、手动探测、登录/导入后的即时探测统一处理 DEAD:不再把 401 留成「未知」继续被调度。
- 导入页在已有灵犀凭证时显示上述限制提示。
升级提示
SCHEMA_VERSION仍为 32,无数据迁移。- 更新镜像(
:v0.13.0或:latest)后必须重启后端:本次改动包含探测逻辑与导入合并。 - 升级后启动探测会把已 401 的旧灵犀凭证自动标记为「需重新登录」。
v0.12.0
v0.12.0:新增第九个上游 灵犀(WPS 灵犀),外加凭证合并、Codex 托管工具兼容、调度归因等修复;Playground 模型选择条重做。SCHEMA_VERSION 不变(32,无新列新表),升级需重启后端。
新增渠道:灵犀(lingxi)
- 桌面端 agent-runtime 通路:一次对话三条请求(建 cowork 会话 → 领 credits run → 私有 SSE),上游按 run 计费,会话 + run 按「账号 × 模型」缓存 1 小时。
- 鉴权为 WPS 账号 cookie
wps_sid,没有登录按钮;上游无 refresh 接口,以 401 判定过期,重贴 Cookie 即可。 - 额度=智点:
total_balance进余额,赠送批次进 15 天到期阶梯;签到按领前/领后余额差判真,不信上游 200 假成功。 - 工具 schema 自动压平
anyOf/oneOf/$ref;ToS 灰区(伪装桌面客户端头),详见 README。
凭证与调度
- 同账号重新登录就地合并既有凭证,按身份交集合并,避免不同账号数据互相覆盖。
- 本机网络 / DNS 失败独立归因,不再连带冷却凭证。
- 目录 x0 档按实测扣费校准,免费档不再压过到期积分。
- 错误文本脱敏、扣费快照即时失效。
兼容与稳定性
- Codex 托管工具不再 400,业务码兼容字符串形态。
- CodeArts 签到活动按
type挑选,campaignId取当轮响应。 - 小浣熊页面访问保活;Zen 429 降噪。
- 补渠道死亡 / 签到失败告警、活跃上报日志类名;签到失败改指数退避。
Playground 选择条
- 三栏:当前选择|渠道|强制,窄屏纵向堆叠;「强制」栏只在实际指定渠道时渲染。
- 渠道徽章统一「渠道|倍率」分体两段,尺寸收敛到一档(10px 字号 / 18px 高)。
- 选中态画在徽章自身边框与底色上,去掉
ring + ring-offset的外圈空缝。 - 移除徽章内部嵌套的倍率小块。
升级提示
SCHEMA_VERSION仍为 32,无数据迁移;更新镜像(:v0.12.0或:latest)后重启后端。- 使用灵犀需在「登录渠道」页重新导入 WPS Cookie(
wps_sid)。
v0.11.0
v0.11.0:新增第九个渠道 小浣熊(raccoon),签到 / 活跃上报 / 积分明细全链路,外加 Codex 会话头识别与 CodeArts 倍率修正。SCHEMA_VERSION 不变(32,无新列新表),升级必须重启后端。
新增渠道
- 小浣熊(
raccoon,商汤):标准 OpenAI 兼容网关,鉴权是网页会话 JWT(cookiexhx_token,约 30 天)。没有登录轨道,只能 JSON 导入(也接受裸 JWT / 整段 cookie)。 - 未知模型两层拦截:上游对写错的模型名静默回落默认模型并回 200。发请求前按目录校验,流首帧再比对
model回显,不一致判协议违规。 - 额度=积分:每笔请求扣 1 分(与 token 量无关),每日 00:00 发 300 分(7 天有效);
billing_effective_multiplier直接当倍率,0 = 限免。 - 额度明细:四池
quota_packages+ 流水推导的expiry_ladder(快过期先用)。 - 签到=懒发放核对:摸一次余额触发补发 + 读流水确认
daily_grant,只认流水不看 HTTP 200;三态可区分。 - 活跃上报=设备心跳:
x-client-*头族齐即 200、幂等,device_id 由 uid 稳定派生。 - 并发:实测 5 并发放行、第 6 个 429 →
RACCOON_MAX_CONCURRENCY默认 2 + 独立 pacer。
修复
- Codex 会话头识别(openai/codex#22193):补
thread-id/session-id,此前 Codex 会话粘性全部落空。 - CodeArts 倍率只取常规档:错峰折扣价不再被当成模型价(错峰 x0 会让整池流量打到该渠道)。
- 小浣熊模型名收敛:
sn-deepseek-v4-1-flash-1017的1017是快照日期,并入deepseek-v4.1-flash。
升级:docker compose pull && docker compose up -d(或重建),必须重启后端。
v0.10.2
v0.10.2:签到审计日志与单条凭证导出、公网部署(Docker + Caddy)、安全与统计落库的一批收口。SCHEMA_VERSION 30 → 32(新增 checkin_events 表 + credentials.cooling_kind 列,幂等),升级必须重启后端。
v0.10.1 之后的 30 个提交,含新端点与新能力,按维护期口径发 patch。
新增能力
- 签到审计日志(
checkin_events,SCHEMA_VERSION30 → 31):每轮签到落一行(时间 / 结果 / 已签 / 领取积分 / 上游业务码 / 文案 / 触发方式),凭证页「更多操作 → 签到记录」直接回看。解决「显示签到成功却没加积分」无从取证的问题;保留期同请求明细。端点GET /api/credentials/{id}/checkin-events。 - 单条凭证导出(
GET /api/credentials/{id}/export,issue #31):与批量导入条目同形的 JSON(provider/credential/nickname/tags),可原样再导入;仅 admin/operator,落credential.export审计。文件含明文令牌,响应no-store。 - 公网部署两种形态(PR #26 / #27):
docker-compose.public.yml+deploy/caddy/Caddyfile,Caddy 自动 HTTPS、HTTP→HTTPS、SSE 逐帧不缓冲、XFF 覆盖式写入、/metrics公网摘除;TLS_MODE=internal支持只有公网 IP、没有域名的部署(自签 CA,ACME_EMAIL可留空)。 - 冷却来源(
credentials.cooling_kind,SCHEMA_VERSION31 → 32):revive不再顺手解掉上游点名的冷却(限流 / 余额 / 权益 / 风控);要清走force=true,管理台分「恢复」/「强制恢复」。老库补列为 NULL = 来源未知,按旧行为清除。
修复
- 整库重建后旧 Cookie 免登录(issue #37):服务器重置后新库
session_epoch从 0 重来,APP_SECRET未换时旧 Cookie 的ep=0重新匹配新账号 → 刷新页面直接进后台。现校验会话iat不早于账号created_at(留 60s 时钟余量);老 Cookie 无iat跳过,升级不踢人。principal_from_request与/authorize共用session_valid()。 - CodeArts 参数无效被误判权益耗尽:
001001005子串含1005,被「1005=权益耗尽」判据命中 → 账号级 12h 冷却、整渠道空转。改为INVALID(换号无用,跳过该渠道),真业务码1005仍判PLAN。 - CodeArts 签到 confirm 失败不再报成功:
claim成功后丢弃confirm返回值、仍报成功 → 当日封账不再补确认,用户看到成功而上游积分未到账。改为 confirm 失败即判失败,下一轮走「只补 confirm」收尾。 - Zen 探活不再因限流清空渠道:匿名探测遇 429 / 403 被判不可用 → 整条免费池「无模型」。改为只剔除确定不可用(400/401/403/404),429 / 5xx / 超时保留。
- 额度耗尽的 401 不再硬禁用凭证:按现场复探额度收口为
CREDIT(积分是分钟级烧完的,周期探额 60 分钟一轮,401 到达时库里的正数已过期);真的 AppKey 失效仍判DEAD。 - 模型能力拒绝跳过整条渠道:
001001020 not multimodal由REQUEST改判INVALID——同渠道换任何号都回同一条,判换号会让凭证吃满MAX_ROTATE、带图请求永远轮不到支持图片的渠道。 - 失败归因收尾行取最后一次错误的真实类型,
revive不再解上游冷却(见上)。 - 签到与余额冷却的日界固定北京时间,不再跟随进程本地时区(UTC 容器里日界漂 8 小时)。
- 周期 rollup 改增量窗口(最近 2 小时,整点对齐):原来每 5 分钟全表 GROUP BY,持写锁期间
record()撞 busy_timeout 被吞 → 这段用量静默丢失。 - 安全:网关
model_base_url白名单(HTTPS + 非内网/保留段)、/v1用户配额与 Key 配额双层判定、APP_SECRET拒已知占位值 +scripts/rotate_app_secret.py、uvicorn 不代管 XFF、登录不存在用户跑 dummy PBKDF2 消时序枚举、CI 加 pip-audit。 - CodeArts 请求体白名单改黑名单:
top_k/seed/parallel_tool_calls/max_completion_tokens/stream_options等不再静默丢弃。 - 前端:四页取数失败不再伪装成空数据(新增
QueryErrorState);401 清缓存回登录页(免登录端点的 401 不踢人);30s 超时归一为ApiError(0, "timeout")。 - 其他:成长中心 / 活跃上报跳过软暂停凭证;记错误时凭证已被并发删除不再 503;签到封账用例按北京时间修 CI(UTC)假失败;web 依赖瘦身(去 shadcn CLI,本地化样式层)。
升级注意
- 必须重启后端(含 migration:
checkin_events建表 +credentials.cooling_kind补列)。 - 本次换过服务器 / 重建过库的部署,更新后旧会话 Cookie 会自动失效,需重新登录一次。
v0.10.1
v0.10.1:备份/恢复与批量导入、月度账单与飞书/Slack 告警、用户级策略收口 Playground,外加单实例锁与出口粘性。SCHEMA_VERSION 29 → 30(users 补 allowed_models / quota,幂等补列),升级必须重启后端。
v0.10.0 之后的 46 个提交。按维护期口径发 patch,但含新端点与新能力。
新增能力
- 备份 / 恢复管理(端点 + 管理台
/backups页):下载只认backups/内的普通*.sqlite3,拒绝路径分隔符与符号链接;运维脚本写库前自动备份,只留最近 5 份。 - 批量凭证导入(
POST /api/credentials/batch-import):一次 ≤100 条,逐条校验并隔离失败,返回带index的 created / skipped / failed;同渠道解密后内容完全一致去重。前端支持 JSON 数组 / JSONL / 选文件,失败行回填 JSONL 重试。 - 月度账单(
GET /api/billing,operator):按人读小时汇总、按 Key 读明细并补归属用户与 Key 名;json/csv(带 BOM)/jsonl(逐条明细),两种导出都写ACTION_DATA_EXPORT审计。 - 三类新告警:预算分层(
ALERT_BUDGET_CNY/ALERT_BUDGET_LEVELS,按本地自然月可定价成本打 50/80/95/100% 档,只报当前最高档,升级时旧档由迟滞状态机写成已恢复,100% 为 critical)、单 Key 用量暴涨、非工作时段请求(后两者默认阈值 0 = 关闭)。 - 告警出口扩到飞书 / Slack:飞书按
msg_type=text、Slack 按{text}组包,多渠道并发,单个失败不影响其它渠道。 - 用户级策略(
SCHEMA_VERSION30):users新增allowed_models/quota,空串 = 不限制。会话 Cookie 只回答「你是谁」,不回答「你能用哪些模型、一天用多少」——viewer 用 Playground 直调任意模型可整体绕过 Key 的模型白名单与配额,现 Playground 与/v1共用同一套校验,配额按 username 聚合(Playground 的api_key_id为 NULL 也计入)。用户管理页可直接编辑,写操作落ACTION_USER_POLICY审计。 - 全局 kill switch(
gateway_paused,热更):所有 Key 鉴权的/v1立即 503,管理面保留;开与关两个方向都落审计,原因只进日志不回显。 - 凭证出口池(
CREDENTIAL_PROXY_POOL):按sha256(credential_id)取模分配,重启不漂移;LazyClients按(流/短,代理)分池,因为 httpx 的代理是连接池级参数。优先级:凭证池 →PROVIDER_PROXIES[渠道]→ 直连;未配池时行为逐字节不变。 - 冷却到期半开探测:到期先进
HALF_OPEN,只放行 1 个探测请求,成功才全量回池。 - Zen 门禁配置化 + 渠道 ToS 风险登记:
ZEN_USER_AGENT/ZEN_API_ROOT/ZEN_GATE_TOOLS全部可配,默认值保持实测通过的形态;ToS 按 green / gray / red 登记理由,导入凭证响应与列表透出、审计写等级,新增渠道漏登记启动即失败。
引擎与兼容层
- DSML 回读:DeepSeek V3.2/V4 偶发把工具调用写成 DSML 文本,解析单/双竖线两种方言、V4「自闭合 invoke 带 string」与缺外层包装的裸 invoke 块;流式增量过滤,普通正文零缓冲、跨 chunk 分片不断、未闭合标记收尾原样放行。回读出调用时
finish_reason由stop改写为tool_calls(否则客户端不执行),tool_calls缺id按 index 补并记忆。 - 坏参数 fail-closed:非流式下坏
arguments判InvalidToolArguments(kind=INVALID)→ 换上游重试、最终 400,不冷却凭证、不把坏 JSON 递给客户端。 - 用量对账:成功请求正文体量与上报
output_tokens明显不符时 WARNING(上游吞 usage / 静默替换模型的间接信号),只告警不改响应。 - 截断流留痕:
error_type=truncated_stream,凭证照常save_success(上游没报错);档位降级与 tool 输出压缩(默认关,不破坏前缀缓存);上游model观测,Qoder 回auto视为占位名忽略。 - 会话粘性接受
session_id请求体键。 - TRAE 从元数据帧补回真实模型名;CodeBuddy 国际站指纹对齐官方 CLI、
11158归model_not_found、探活结果落盘。
信道判死语义修正
11140账号受限不再永久禁用:新增ErrKind.RESTRICTED,先于 DEAD 判定,落 2h 冷却(采用上游Retry-After,夹 24h),当前请求跳该渠道其余凭证继续试其它渠道;窗口内(10 分钟)第二次命中升级为渠道级 24h 暂停,只冷却不禁用。统计新增account_restricted(前端「账号被上游限制」)。保留 DEAD +disabled_reason:真 session 失效仍按原语义硬禁用。- 硬禁用写入可操作原因:
ErrorOutcome新增disabled_reason,CodeBuddy11140写受控文案「账号存在异常行为,已被限制使用」,不再一律session dead——重登无效,只能申诉或换号。 - 探活判死即隐藏、复探恢复:判死隐藏不再要求「同时仍挂 blocked 负缓存」,人工「立即探活」判死时也会隐藏;
failed_rows()每轮把判死行一起复探,用上游原名;恢复入口收敛为「复探成功」。 - Qoder 流内信封与 HTTP 错误同一判据:
classify_error_code增加 401/403 分支,有会话失效标记 → DEAD,否则 → QUEUE/RESTRICTED,403 不再一律判死。
运维
- 单实例锁:SQLite 允许多进程并发写(WAL + 忙等),起两个实例不报错只会安静互相覆盖——同一凭证被探两次、同一小时汇总写两遍。
src/instance_lock.py对DATA_DIR取 flock,run()在build_app之前调用(后者会建库/迁移/应用待恢复备份)。锁文件常驻不删,进程被 SIGKILL 时内核自动释放。取锁失败以退出码 0 返回并打印持有者 PID:launchdKeepAlive.SuccessfulExit=false、systemdRestart=on-failure、composerestart都只在非零退出时重拉,非零会变回崩溃循环。launchd 改条件KeepAlive,systemdExecStart从 uvicorn CLI 改为src.main(否则绕过锁),TimeoutStopSec与 launchdExitTimeOut补到 95s。 - 日志轮转改截断式 +
scripts/rotate-logs.sh(拷贝 → 压缩成功 → 截断,任一步失败都不动原文件);去掉 BSD 专用stat,Linux CI 才能真跑;tool输出压缩日志降 DEBUG。
前端
全局错误边界(顶层兜住登录/路由/chunk 失败,Layout 内再兜一层保留侧栏)+ 首启向导;任务与配置两级 tab、配置分组细化;明细表排序改列头下拉,渠道/凭证合并一列;额度包明细只展示剩余 > 0 的包;模型趋势 Y 轴上限取全部在画线的公共最大值;告警规则数量与文案对齐七类。
文档
新增四张图(请求主链路 / 账号流 / 数据模型 / 权限矩阵);33 篇 md 逐篇对照 src/、web/src/、scripts/、compose 校正:schema 30、表清单 13 张(补 model_probes)、可热更配置 77 项、管理台 15 页、告警七类。
升级
SCHEMA_VERSION 29 → 30:users 补 allowed_models / quota(默认 '' = 不限制,老库幂等补列,补列后 Playground 行为与补列前一致)。升级必须重启后端。
v0.10.0
v0.10.0 修复版:出口排队重开与非流式保活、统计页重排与失败归因「已救回」,外加一批「静默出错」的收口。SCHEMA_VERSION 不变(29,无新列新表),升级必须重启后端。
v0.9.1 之后的 45 个提交以修复与收敛为主,但含新端点与新能力,故按 minor 发。
- 新端点
GET /api/stats/percentile-timeline(operator+):延迟分位时序,P50 中位数 + P95 上界两条线,给出「典型值 + 长尾」两层。只认latency/ttfb两个指标,其余显式 400。 - 排队重开(Qoder / TRAE 的
isQueued/RESOURCE_LIMIT):不冷却凭证、不换号,等retryAfterSeconds后在原号重开。轮次与单轮等待都有上限;已出帧的流不重开。排队不消耗换号预算(此前三次排队就把max_rotate吃满、直接 503)。 - 非流式保活(
NONSTREAM_KEEPALIVE_SECONDS,默认 0 = 关):stream=false聚合完成前一个字节都不发 → CDN / 反代空闲超时直接切连接。宽限期内完成仍是逐字节相同的 JSONResponse,超时才转 chunked 先吐空白心跳再补正文。 - 统计页重排:一张图一个系列;延迟另加分位图;时间轴补齐小时桶(稠密时间轴);口径说明折叠。
- 失败归因区分「已救回」:失败数是尝试口径(换号重试每次上游拒绝各落一行),只看它会把「已救回的尝试」读成「用户受损」。新增
rescued(同用户同request_id另有成功行),前端展示「未救回 = failures − rescued」。救援关联限定同用户——request_id取自入站X-Request-ID,客户端可伪造,只按 ID 关联会跨用户误判(已复现)。 - 图指标改语义化聚合:图表指标不再混算,口径写进 README / TECHNICAL。
一批「静默出错」的收口(每条都配了回归测试):
- Qoder 保活帧打断整条流:上游在流中发
{"headers":…,"body":"null"}保活,此前判成协议违规 → 抛异常 → 整条流失败,而模型其实已在正常输出,客户端只看到回复被截断。判据改用结构(json.loads后是None/ 空 dict / 空 list),保活帧直接跳过。 - 排队报文被漏判 → 好凭证被硬禁用:上游把「排队中」包成 403,排队标记藏在再序列化过的字符串里,
parse_queue_hint只认 dict/list → 漏判 → 403 落 DEAD → 可自愈的凭证被打进冷宫。改为下钻字符串 + 排队判据进classify_error_code(任何入口都不漏判)。 - 一条坏凭证连累整批(
凭证解密失败:APP_SECRET 是否被更换过?→ 500):解密失败早先直达顶层 → 整个聊天请求 500、整轮后台任务挂掉、整页模型列表 500。改为按路径分口径:批处理路径(选号、模型列表、后台任务、即时探测)捕获后跳过该凭证并记日志;单条探测端点照旧如实报 500——管理员该去处理这个号。 - 探活判死只影响展示,别名登记保留:上游目录撒谎(挂着某个模型但推理端点 404)时,判死把该 (渠道, 模型) 从模型列表摘掉;旧实现连别名登记一起摘,执行侧因「没有渠道登记该模型」回退成全部渠道扇出 → 请求被发到一堆根本没有该模型的上游,各回「无此模型」,负缓存写在从没有该模型的渠道上。现补登记回别名表:仍收窄到真正持有它的渠道,快速 503「该模型暂时不可用」。
- TLS 中断(
ssl.SSLError)不再逃成 internal_error:它不是httpx.TransportError,落不进任何分类分支 → 异常一路逃到出口,表现为一帧internal_error:不换号、不冷却、统计里零痕迹,失败归因面板完全看不到。归 SOFT(仍只认具体类,不放开到OSError整体——磁盘满也是 OSError 子类,误判会去冷却一个完全健康的凭证)。 - 国际站白名单混入国内站主机时一律拒绝:只校验「端点在白名单里」不够——把国内站主机加进国际站白名单后端点校验会通过,国际站账号的 Token 照样发往国内站网关(跨站发送即凭证泄露)。
- JSON 溢出成
inf的合法数字(1e999)→ 400 而非 500:字面量闸拦得住NaN/Infinity,拦不住合法 JSON 数字溢出;在解析层加parse_float守卫,三个出口共用一个入口。 - 指定凭证(pin)的类型与存在性校验:非字符串
credential_id→ 400(放任进 sqlite3 绑定 → 500);不存在的 id → 400 且不清掉别人的 pin(此前端点回 200,管理员以为已指定,实则把指定清空了)。 - 统计
metric取值域显式 400:未知取值此前静默回落到「请求次数」,客户端拼错参数会拿到一份「看起来正常」的图,错误被完全吞掉。 - 未匹配的
/api、/v1对任何 HTTP 方法都回统一 JSON 404:只注册 GET 时,未匹配的 POST 会落到 Starlette 的 405{"detail":…},与统一错误体形状不符。 ADMIN_USERNAMES只在引导期提权:库里已有活跃 admin 时不再改写任何人的角色。此前每次启动都无条件提权,管理台刚降级的用户重启后又变回 admin,「降级立即生效」被重启撤销。- 加载失败 ≠ 空数据:告警页此前在请求失败时显示「暂无告警记录…一切正常时保持空白」,用户会把故障读成「系统健康」。改为显式错误态;统计页同款问题一并修。
- 外链 scheme 白名单(
safeHttpUrl):管理台外链不再直接透传上游给的 URL。 - 冷却对齐上游正文点名的重置时刻:CodeBuddy 国际站 429+
6004只在 msg 文案里给恢复时刻、不给Retry-After头,原实现取不到 → 回落 600s 基数 → 到期重试再撞 429(实测一个号一天被爆 42 次)。 - 冷却日志区分账号级与模型级作用域:此前账号级冷却也打「模型 <名>」,读起来像只锁了这一个模型。
- 留存任务回收过期账号级冷却:过期
cooling_until永留库里,管理台列表按原始值下发 → 排序失真 + 幽灵记录。 - CodeArts 非 JSON 响应对外不带上游 URL:异常文本带 URL 只进日志。
- 用户名单渠道式交互收口:TRAE 回调打不开时改用粘贴链接完成登录(回调由浏览器打开,NAS / 远程部署里常直接「网络错误」);回调基址落在回环 / 通配主机时改用当前请求 origin;Cookie 不可用时按
loginTraceID反查发起人,绑定在那一次 pending 登录上。
其他:
- 探活判死不误伤扇出:
BLOCKED独立成model_not_found(原先并进invalid_request,读起来像客户端参数写错,实际是上游把模型下架了)。 - 签到失败不再是 500:上游拒绝(账号被禁用 / 登录过期)是这条凭证的真实答复,收成
ok=false+ 可读 message。 - 单渠道模型也下发
by_provider:此前只给多渠道,单渠道模型的原代号对外完全不可见(排障时看不到实际请求的 key)。 - provider 层去重(净减约 210 行):四家 OpenAI 兼容渠道的 SSE 解析骨架合一(zen / kilo / officeace / codebuddy,净减 177 行)、
AuthStateStore泛型化(4 份 owner/consume/cancel/cleanup 合一)、华为云系凭证字段合一、懒建连接池统一到LazyClients、pacer 的wait_turn+release配对收进paced()上下文管理器。 - 任务调度 / 仓储层的机械重复收敛:runner 的 12 分支周期 if-chain 改字典派发、repo 的 27 处
int(now if now is not None else time.time())收进_now()、executor 的 4 份排队重开逻辑收成_plan_queue_restart()。 - 依赖安全更新:body-parser 2.4.0(GHSA-27r3-jxmh-p828)、fast-uri 3.1.9、source-map-js 1.2.2(GHSA-68fv-2mgg-jv7q)、certifi 2026.10.19。
- CI 抗抖动:npm registry 的两处单点加显式重试;
pnpm audit与 Dockerfile 取 pnpm 本体两处都加重试。 - 前端依赖瘦身:
.npmrc加auto-install-peers=false,lock 从 1023 个包降到 601 个,pnpm audit --prod0 漏洞;DockerfileCOPY web/.npmrc(否则容器内 pnpm 退回默认autoInstallPeers=true,与 lockfile 不匹配直接ERR_PNPM_LOCKFILE_CONFIG_MISMATCH)。
文档(README 中英 / TECHNICAL / PROPOSAL)全量对齐当前代码:环境变量表覆盖 config.py 全部字段、schema 版本与表数、统计口径与图表指标语义、出口保活与排队重开的行为契约。
v0.9.1
v0.9.1 修复版:CodeArts 福利链路下线、模型列表按探活结论收敛、输出速度拆双口径,外加前端依赖瘦身与 CI 抗抖动。SCHEMA_VERSION 28 → 29(删除三列积分账),升级必须重启后端。
上游每日 1000 万免费 token 池已下线(活动落地页与官方文档均 404,客户端模型列表里已无福利模型),故全链路移除。积分账成为 CodeArts 唯一的额度来源,直接落在通用列(quota_remaining 等)上。
-
对外形状变化:
GET /v1/models不再下发benefit字段;凭证页不再把 CodeArts 拆成「内置 / 福利」两组,一个真实渠道一个组,组头写「积分合计」;额度探测响应不再带credits_remaining/credits_total。 -
DB:
schema.sql删掉credentials上的quota_credits_remaining/quota_credits_total/quota_credits_expiry_ladder;migrate.py新增_MIGRATION_COLUMN_DROPS(ALTER TABLE DROP COLUMN,列不存在时忽略,要求 SQLite ≥ 3.35),SCHEMA_VERSION升到 29。老库升级自动完成,无需手工迁移。 -
credit_events.ledger与?ledger=保留:账本由凭证渠道决定(codearts→credits,其余 →pool),每轮探测只记一条流水。 -
白名单去掉福利网关三主机;删掉
units.py/backfill.py与两个一次性折算脚本。 -
模型列表摘掉探活判死的 (渠道, 模型):判死且仍被
blocked负缓存锁着的条目才逐渠道抽掉(两个条件缺一不可——只看到期会让模型永久消失,只看判死不会自动恢复)。探活面板命中黑名单的行整条不返回;点名直连不受影响。 -
成本口径修正:OfficeAce 的快照日代号
deepseek-v4.1-flash-0930与 CodeBuddy 国际站的区域代号deepseek-v4.1-flash-sg在价表里查不到,cost_usd/cost_cny恒为 NULL(实测两条渠道共 3600+ 条)。改为逐 id 登记「代号 → 基础模型」,不做通用剥尾(OpenRouter 的-1017是另一个价位的快照,通用剥尾会把两套价格混成一个)。历史明细需scripts/backfill_cost.py --apply补齐。 -
模型名归一:CodeBuddy 的
Xiaomi MiMo-V2.6-Pro归到mimo-v2.6-pro,与 TRAE 的同名模型合并,不再多出一个xiaomi-mimo-v2.6-pro。 -
OfficeAce 登录:新增「换账号」(浏览器里留着上个华为云会话时会直接放行,导致「新增凭证」变成「同一账号再登一遍」);凭证昵称改为取
principal_urn末尾的用户名(此前退化成一串 32 位账号 ID);轮询失败透出上游可行动的原因(如「这个账号还没开通 OfficeAce 服务」),不再统一吞成「请重试」。 -
渠道展示名统一为 OfficeAce / CodeBuddy 国际 / Kilo。
-
输出速度拆「正文 / 总产出」双口径:
tps_visible= (output_tokens−reasoning_tokens) ÷ 生成时长,tps=output_tokens÷ 生成时长,两列同分母。思考 token 成批快吐,与正文混算会远高于屏幕刷新速度(实测占比 50–67% 时约 190 与 63 tok/s)。下沉到逐请求明细与实时流;主口径无样本时回落估算口径并前置≈。明细表顺带合并列:时间拆两行(时刻在上、日期在下),渠道 / 凭证合成一列。 -
凭证页渠道组头显示模型数与倍率:折叠态即可读「数量 + 最低倍率」,hover 展开逐条「模型名 + 该渠道倍率」(按渠道取,不拿合并值冒充)。最低倍率只统计收费模型,整组免费时写「全免」;未给倍率显示
—而非「免费」。浮层拆成两档表面:短提示走反色气泡,多行清单走 panel(描边 + 阴影、不出箭头)。 -
前端依赖瘦身:
@lobehub/icons把@lobehub/ui/antd声明为 peer,自动安装会拖进 400+ 个用不到的包。.npmrc加auto-install-peers=false,lock 从 1023 个包降到 601 个;真正需要的 peer(@testing-library/dom)显式声明。副产品:pnpm audit --prod从 1 条 low 变成 0 漏洞。纯删除,无版本漂移。 -
Dockerfile
COPY web/.npmrc:否则容器内 pnpm 退回默认autoInstallPeers=true,与 lockfile 的settings.autoInstallPeers: false不匹配,直接ERR_PNPM_LOCKFILE_CONFIG_MISMATCH。 -
CI 抗 npm registry 抖动:
pnpm audit与 Dockerfile 里 corepack 取 pnpm 本体两处都加了显式重试(审计端点偶发ERR_SOCKET_TIMEOUT,corepack 取 pnpm 偶发 503,且 corepack 自身不重试)。
README / README.en / TECHNICAL / PROPOSAL / AGENTS / SECURITY / design-system MASTER 全量对齐当前代码:schema 版本与表数、技术栈版本、热更设置数(60)、任务数(12)、环境变量表(覆盖 config.py 全部 96 个字段)、.npmrc 与 lockfile/Dockerfile 的耦合约束。
v0.9.0
v0.9.0 新增第八个渠道 officeAce(华为云果办 / AgentArts),覆盖对话、模型、额度、签到、刷新与登录全链路。SCHEMA_VERSION 不变(28),升级必须重启后端。
- 新渠道 officeAce(
officeace):点「登录 officeAce」走华为云授权,回调由云端承接(/v1/claw/auth/callback),本服务不监听端口、用户不粘链接,后端按 2.5s 轮询/v1/claw/auth/code取码(与官方桌面端同一机制)。前端补齐渠道标签、缩写、配色、图标、登录入口与额度口径;OFFICEACE_*三个环境变量已透传到 compose。 - 两套凭据、两个面、两种签名(本渠道唯一的复杂度来源):聊天用永不过期的
Basic model_app_key:model_app_secret;控制面(积分 / 签到 / 费率)用约 2h 的临时 AK/SK +security_token+project_id,由预刷新任务续期。控制面/v1/claw/*走华为云SDK-HMAC-SHA256;模型网关/v1/subscription*、/v1/model-token-rates只认V11-HMAC-SHA256(多一层 region 作用域 + 密钥派生)。混用的回包是APIG.0303/APIG.0624/ 恒 401verify ak sk signature fail,不告诉你哪一步错。refresh_token与client_id=pdp5_for_agentarts+ DPoP 私钥三者绑定、一次性轮换,且与 CodeArts 的票不通用。 - 错误分类:限流走伪 200(HTTP 200 +
data:{"error":…}),并有「首帧正常、第二帧才是错误」的第二种形态,故不能在首帧成功后停止检查。81004(该号没这个模型)/81009(名字不认)归BLOCKED并登记模型级负缓存(成功一次即撤销,同渠道同模型的其它凭证一并跳过);81111/81112/81114归限流,只冷却「该凭据 × 该模型」;14018归欠费。 - 不做主动模型探活:目录 34 个里实测只有 11 个真能用,但探活要真发推理、消耗积分,而额度是稀缺资源。模型级拒绝改由真实请求的
BLOCKED负缓存处理,比开机烧 34 次推理省得多。同理不做 credit 估算:上游给了cost_coefficient与tokens_per_point,但二者的叠乘关系官方未说明,凭空折算会与真实扣点对不上、并让跨渠道额度排序整体失真。 max_tokens改写:可用模型里 9/11 是推理模型,给 64 会被思考预算吃光、正文为空。≥16一律追加 1024 余量再夹到模型硬上限(glm-5*131072 /qwen3*65536 /deepseek-v4.1-flash393216);<16原样透传(探活的精确请求);未给不补。max_completion_tokens归一成max_tokens。- 积分即主账:单位
officeace_points(新用户一次性 4000 + 每日签到 1000),没有并行的 token 池,remaining/total直接是积分。每笔奖励独立到期,故额度口径写「逐笔到期」,调度仍按到期阶梯「快过期的先用」。 - 每日签到:
POST {gateway}/v1/subscription/bonus/claim是写操作(调用即真发积分),body 必须给 {} + JSONContent-Type——官方调用点是空 body,但实测那样回 500OfficeAce.11020001。上游对当日重复领取幂等,故不需要本地记「今天领过」。收获按bonus_skus的剩余量增量算(points是活动累计发放量,由多个渠道共同贡献,拿它当本次收获会虚高)。封账到下个北京时间 0 点,次日新活动一出现自动补签。 - 登录状态按 username 分键:
AuthStateStore做 reservation(begin/owner/session_entry/touch/consume/cancel/cleanup,带 TTL 与 pending 上限)。单槽全局态会让两个管理员的并发登录互相覆盖、把 A 的凭证记到 B 名下;并发轮询同一 state 时consume只有一路成功,另一路抛错——否则同一账号会出现两条重复凭证。 - 网关地址归一:
client-permission-validate下发的api_base带/v2,models.json里存的不带;统一去掉尾部/v2再拼端点,否则聊天拼出/v2/v2/chat/completions。网关主机每账号一个,故不进OFFICEACE_ALLOWED_ENDPOINTS(白名单只管控制面,带临时 AK/SK 签名的请求只发往白名单主机)。 - 单账号单用户:官方《套餐管理》明确「个人版面向个人用户,限制单账号单用户使用」,故本渠道不做多人共用,每个使用者各自添加自己的凭证。
其他改动(v0.8.0 之后):
- CodeBuddy 国际站模型列表按套餐探活过滤:
11158/11102判定为「该套餐不含此模型」并从列表剔除,限流错误保留(限流是临时的,不该让模型消失)。 - 模型列表连续失败退避:连续失败时逐步拉长重试间隔,避免往一个已经坏掉的上游持续打请求刷日志。
- 读体断开回 499:客户端在读请求体时断开,此前落到通用异常处理回 500 并记
error级日志;现按 499 记录,不再污染错误率。 - 传输层异常归类 + 上游错误码补全:把连接重置 / TLS 失败这类传输层故障与「上游明确拒绝」分开归类,探针与日志相应降噪。
- 签到 / 预刷新跳过用户软暂停的凭证:
enabled=0的凭证不再参与签到与预刷新轮次,口径与调度器is_selectable一致。 - benchmarks 浮动别名取同族最新快照的分:OpenRouter 把浮动别名钉在旧快照、并把该条换成 Non-reasoning 变体,导致智力分「掉」15 分;现按
canonical_slug的-YYYYMMDD归族取最新快照,显式带日期戳的 id 仍用自己的分。 - 默认模型改
deepseek-v4.1-flash,默认黑名单与线上生效值对齐。 - 前端:套餐明细弹层按可用高度封顶,不再被顶出视口。
已知限制:officeAce 上游账号尚未开通模型网关(client-permission-validate 返回 subscription: null),故聊天、模型列表、积分与签到的端到端验证是按协议文档 + mock 完成的;开通后用真实账号复核。cache_control 透传在该渠道同样未验证。
v0.8.0
v0.8.0 覆盖 v0.5.0 之后的全部改动(A/B/C/D/E 五批,SCHEMA_VERSION 25 → 28)。升级必须重启后端。
新增
模型面(批次 C)
- 主动探活 + 人工接管:真实请求失败只能证明「这次没打通」,分不清「上游真没这个模型」与「额度 / 限流 / 网络抖动」。新表
model_probes+ 后台任务(MODEL_PROBE_MINUTES,默认0= 关闭,可热更)只探仍被blocked负缓存锁住的 (渠道, 模型)——遍历全量模型表要真发几百次对话,耗的是用户额度,而负缓存才是唯一会静默避让一个模型 6–24h 的状态。- 三态判定:成功 → 清该渠道的
blocked负缓存(TTL 走完之外的唯一提前恢复入口);BLOCKED/INVALID→failed,保留负缓存并累加连续失败;额度 / 限流 / 网络 / 超时 / 空流 / 任何异常 →inconclusive,绝不禁用,且把连续失败归零(一串无关故障不该凑成「连续失败」判死好模型)。 - 与生产完全隔离:直连
provider.stream_chat,不经 executor,不写usage_events、不动冷却(除成功时清)、不改健康度、不参与选号。结论落独立的model_probes表——探测失败 ≠ 上游拒绝。 - 端点(operator + CSRF + 审计):
GET /api/models/probes、POST /api/models/probes/probe(立即探活,不受接管限制)、POST /api/models/probes/manual(on/off)。人工接管后自动探活整行跳过,接管不改负缓存。 - 「模型列表」页新增探活面板:三态结论、负缓存号数与最晚到期、最近探活 / 错误摘要、立即探活、接管 / 交还。
- 三态判定:成功 → 清该渠道的
- 模型可用性视图 + 一键黑名单:
GET /api/models/availability(operator)聚合最近 N 天(默认 7、可调 1–30)error_type为upstream_blocked/invalid_request的失败,按(渠道, 模型)给出失败次数、最近失败时刻与「是否已在黑名单」;「模型列表」页可切「疑似下线」筛选,行内「加入黑名单」把该模型追加进model_blocklist(读现值再写,不覆盖既有项)。 - 自定义模型别名(
MODEL_ALIASES,可热更):对外名=模型@渠道;对外名2=模型。列表与请求解析用同一份别名表(不会「列表有、请求 404」),条目带custom_alias: true;与真实 id / 自动别名撞车、目标不存在、渠道不持有该模型时该段记 ERROR 并跳过。 /v1/models补cost与reasoning_efforts:刊例价(输入 / 输出 / 缓存读,USD / 百万 token,缺价时整个字段缺失而不是 0)与生效中的推理档位。GET /v1/models/{id}单模型查询(OpenAI 同形):认原代号 / 归一键 / 展示名 / 自定义别名,带命名空间的原代号也能查;白名单外与不存在同为 404(无法用单查探测池内模型)。
配额与自助用量(批次 B)
usage_events.api_key_id(schema 26):把每条请求归属到发起它的 API Key,作为计量地基;老库补列后历史行为NULL= 未归属。- Key 级配额(schema 27):一个 JSON 字段六项独立,
0/ 留空 = 不限制——daily_requests/daily_tokens/daily_cost_usd/rpm/tpm/max_concurrency。当日类按北京时间自然日(与凭证护栏同口径),速率与并发是进程内滑动窗口(重启清零)。超限 429 +Retry-After指向可重试时刻。判定本身出异常时一律放行并记 warning——计量故障不能把正常请求打死。 GET /v1/usage?days=1|7|30自助用量:持 Key 的调用方查自己的用量、剩余额度与当前速率占用(只读本地明细,不触发上游,不含凭证昵称或池级信息)。
统计(批次 D)
- 失败归因:
GET /api/stats/errors(operator+)按(渠道, 模型, 错误类型)聚合ok=0明细;统计页「失败归因」面板点行即钻取到下方明细并自动加「只看失败」(GET /api/stats/events?ok=0),键盘 Enter / Space 同样触发。 - P50 / P90 / P99 分位:与 P95 同一套 SQL(一次子查询算完四档,nearest-rank,n 为有值样本数);分组表延迟列的表头有四档切换,不铺开四列。只统计成功请求。
- 请求明细实时流(SSE):明细面板「实时」开关打开后订阅
GET /api/stats/events/stream,展示当前筛选范围内刚落库的请求(与历史明细分开展示)。事件在StatsCollector.record事务提交后由进程内 hub 广播(不轮询数据库)。
引擎与出口(批次 A)
- 流式超时(默认关闭,可热更):
STREAM_FIRST_EVENT_TIMEOUT_SECONDS在出帧前超时则换号重试(此时切换安全);STREAM_IDLE_TIMEOUT_SECONDS在已出帧后超时则只发一帧错误收尾——半截输出不可回滚,换号会把两个账号的内容拼给客户端。首事件超时默认必须保持关闭:实测慢上下文首字可达 40–71s,默认值会误杀正常请求(推荐60;空闲推荐300)。 Retry-After解析:上游Retry-After(含Try again in 17h 59m这类文本式)→ 「何时恢复」的 epoch 秒,冷却按真实恢复时刻算,不再一律套固定 TTL。- 短冷却「等待续上」(
SHORT_COOLDOWN_WAIT_SECONDS,默认0):全池都在短冷却、最短剩余不超过该值时等待后重取一次,而不是直接 503;等待期间不占在途租约、不计入轮换预算。 n > 1显式 400:本网关的响应结构只能产出一条候选,此前静默只回一条,客户端以为拿到了 n 条。- 选号加入在途负载键:并发不再全压同一凭证。
cache_control原样透传(Anthropic):落点与 Anthropic 一致,只做结构校验、取值不解释;不带标记时映射结果与旧版逐字节一致。response_format语义收口(chat):text/json_object直通,json_schema展开。
会话与任务(批次 E)
- 会话粘性接受请求头标识:优先级 请求体显式键(
conversation_id/prompt_cache_key)> 请求头(x-session-id/x-opencode-session/x-session-affinity/conversation-id)> 消息前缀指纹兜底。头与指纹值都掺username再哈希(两用户同值不串号),头必须过字符白名单,非法则丢弃退回兜底。 - 签到定时(
CHECKIN_TIMES/CHECKIN_JITTER_MINUTES,可热更):本地时间HH:MM多时点,抖动在时点之前提前0..N分钟把多部署摊开;按「日期 + 时点」去重,抖动只改执行时刻不会跑两次。配了时点后循环改为每 60 秒醒一次(否则抖动 = 0 时窗只有一分钟,必然整天漏签)。留空 = 全天每 10 分钟检查(旧行为)。 - 服务端日志页(
/logs,仅管理员):GET /api/logs取最近若干行、GET /api/logs/streamSSE 先回放缓冲再推新行;级别筛选、关键字过滤、跟随最新、暂停与清空。没有任何写入或执行入口。每行发出前过脱敏白名单,命中token/secret/authorization/cookie/password/api_key或sk-…形状的整行丢弃(LOG_BUFFER_LINES,0= 关闭该页)。 - 请求级结构化日志(
REQUEST_LOG_ENABLED,logfmt):每个业务请求一行event=request request_id=… method=… route=… status=… duration_ms=…;路由用模板不用实际 ID,不写 query string / 请求体 / 用户名,靠request_id关联usage_events。静态资源与探针不记。
管理台
- CodeArts 内置组健康度改按积分账派生:凭证页把 CodeArts 拆成「内置 / 福利」两组、每组只讲一本账,但健康度一直读主账(每日 token 池),导致内置组出现「额度列显示积分还剩 99%、健康度却 0%」的自相矛盾。新增派生的
quota_credits_health(不参与调度,选号仍只读主账health),前端按展示组取对应账本的健康度。
变更
SCHEMA_VERSION25 → 28:新增model_probes表;usage_events.api_key_id、api_keys.quota两个新列(走幂等补列);新增索引idx_usage_key。老库启动时自动迁移,无需手工操作。- 新增可热更项:
MODEL_ALIASES、STREAM_FIRST_EVENT_TIMEOUT_SECONDS、STREAM_IDLE_TIMEOUT_SECONDS、SHORT_COOLDOWN_WAIT_SECONDS、MODEL_PROBE_MINUTES、CHECKIN_TIMES、CHECKIN_JITTER_MINUTES、LOG_BUFFER_LINES、REQUEST_LOG_ENABLED。 - TRAE 上下文压缩预算按实测抬到 1M:
_CHANNEL_WINDOW_FLOOR = {"trae": 1_048_576}。实测 1,450,000 字符(1,035,544 token)答对、1,470,000 字符报4022/4026,此前按更小的窗口提前压掉了本可用的上下文。 - 后台任务由 11 类增至 12 类(新增模型主动探活)。
修复
- CodeArts 积分账与主账健康度串台(见上「管理台」):同一行出现「额度 99% + 额度耗尽」的矛盾状态。
- 签到配了时点后整天漏签:轮询周期仍是 10 分钟、而抖动 = 0 时时窗只有一分钟。
升级
docker compose pull && docker compose up -d镜像:ghcr.io/robbsluo/coding2api:v0.8.0(同时更新 :latest)。
升级注意:
- 数据库 schema 从 v25 升至 v28,启动时自动幂等迁移(建
model_probes表 + 补两列 + 建索引)。升级后必须重启后端,否则会出现「新前端 + 旧后端」错配(新端点 404,页面弹出与真实原因无关的兜底文案)。 - 新增的后台探活默认关闭(
MODEL_PROBE_MINUTES=0)——探活会真发上游请求、消耗额度。要开请在「任务与配置」页填周期(热更,不必重启),实际建议 ≥60 分钟(负缓存本身 6h 起步)。 - 三个流式 / 冷却相关开关默认全关,升级后行为与旧版一致;要启用请按部署实际填(首事件超时推荐
60,空闲超时推荐300)。