Skip to content

Latest commit

 

History

History
561 lines (353 loc) · 41.3 KB

File metadata and controls

561 lines (353 loc) · 41.3 KB

相对最新 New API 上游的目标差异

基线、定位与迁移原则

  • 目标上游基线upstream/maina63364d15(2026-07-14,fix: infer MiniMax vendor for MiniMax models (#6164))。
  • 本文性质:这是将二开版重建在最新 New API 基线上的目标规格,不是对当前分支已实现状态的宣称;每一项均需在迁移分支中重新实现、测试和验收。
  • 安全回退点:迁移前状态已保存在 backup/pre-upstream-migration-v100.0.16,对应当前稳定版本 v100.0.16
  • 同步规则:上游仅同步提交,不拉取上游 Git tag(upstream 配置为 --no-tags)。迁移采用“以最新上游为新基线、按行为重新移植”的方式,不直接合并或大范围 cherry-pick 历史分叉。
  • 前端原则:凡是本文要求提供页面、设置、管理入口或前端展示的保留功能,必须同时在最新上游 web/defaultweb/classic 两套前端实现,并保证权限边界与核心行为一致。两套实现均须融入各自当前的页面结构和接口约定,不能回退成旧前端。
  • 范围原则:本文只列出最终要保留或新增的差异。已选择直接采用上游的能力,以及明确不迁移的旧功能,不另设清单。

1. 采用上游 perf_metrics,并增加全局频道双 RPM 保护

目标与上游关系

模型表现的基础能力采用上游 pkg/perf_metrics,不迁移旧版自定义 model_health_slice_5m 表、公共“模型健康度”页面和其质量判定逻辑。上游的 perf_metrics 已提供按模型和分组聚合的成功率、延迟、TTFT、TPS,以及分钟、5 分钟、小时桶和定期落库能力,应成为唯一的模型表现统计基础。

在此基础上新增频道维度的过载保护,目的不是重定义模型健康,而是避免某一个频道被短时间流量打满后继续被路由选择。

行为契约

  • 限制范围是全局的频道 ID:同一频道在所有用户、令牌、节点和请求入口上的统计合并计算。
  • 每个频道同时维护两种滑动窗口 RPM:
    • 总请求 RPM:进入该频道实际发起的一切请求;
    • 成功请求 RPM:获得成功结果的请求。
  • 路由挑选频道前检查双阈值;任一阈值饱和时,该频道在当前选择中被跳过,继续选择其他可用频道。
  • 因频道 RPM 已饱和而被本地跳过的尝试,不得向上游 perf_metrics 写入成功样本或失败样本;它既不代表模型调用成功,也不代表模型调用失败。
  • 用户或管理员的用户级 RPM 豁免仅影响用户请求限流,不能绕过频道级保护。
  • 成功与失败样本仍遵从上游 perf_metrics 的调用语义。特别是音频/Whisper 请求只要上游调用成功,即使 completion token 为 0,也应按成功样本记录;不得再引入以 token 或 response_bytes 作为 Whisper 成败的特殊规则。

配置 / API / UI

  • 频道配置应提供总请求 RPM 和成功请求 RPM 两个可独立启用的阈值;未配置或非正值表示该维度不限制。
  • 管理端应能看见频道当前限流配置与可解释的跳过原因,便于排障;web/defaultweb/classic 均应遵循各自最新上游的设计体系展示。
  • 需要提供与现有频道选择、重试和日志机制一致的错误/跳过标识,避免被错误归类为模型失败或用户限流。
  • /pricing 模型卡片右下角的三段健康趋势固定表示 24h / 1h / 5m,按从左到右排列;每段均以对应窗口内成功请求数除以总请求数计算,窗口无样本时显示灰色。当前生产配置使用 5min 桶,因此 5m 为当前对齐桶,1h 聚合当前及前 11 个桶,24h 聚合当前及前 287 个桶;不得再显示为最近三个非空桶。三段健康趋势的颜色阈值仅在该卡片内下调 10 个百分点:>=90% 深绿、>=80% 浅绿、>=60% 黄、<60% 红;不得改变详情页、图表或其他成功率展示的标准阈值。

数据与兼容性

  • 使用上游 perf_metrics 数据模型和迁移,不新增旧健康度切片表,也不对历史健康度数据做结构迁移。
  • 频道 RPM 计数可以是进程内或适合多实例部署的共享实现,但必须保证“全局频道 ID”语义;部署拓扑变化时不得退化为仅单实例保护。
  • 旧版健康度历史数据不作为新统计口径的输入。

迁移约束与验收

  • 验证成功/失败、超时、频道跳过、重试成功等路径的 perf_metrics 样本数和成功率均符合定义。
  • 并发压测下,达到任一频道阈值后新请求会稳定绕开该频道,而不会把跳过行为污染为模型失败。
  • 验证 Whisper 成功响应(包括 completion token 为 0 的情况)记录为成功。
  • 验证用户 RPM 白名单/管理员豁免仍无法突破频道双 RPM 阈值。

2. 保留用户级 RPM 豁免

目标与上游关系

保留二开版中管理员或配置指定用户 ID 对上游“用户请求 RPM”限制的豁免能力。它是面向可信内部用户或运维账号的用户级策略扩展,不改变上游普通用户的限流语义。

行为契约

  • 被配置豁免的用户不受用户请求 RPM 限制;未豁免用户继续遵从上游限流规则。
  • 豁免只作用于用户级请求 RPM,不作用于并发、额度、频道状态、频道双 RPM 保护或任何安全拦截。
  • 配置变更应及时生效,并在失效配置、用户不存在或权限不足时采用安全的非豁免行为。

配置 / API / UI

  • 管理端提供清晰的用户 ID 配置、查看和移除能力,并说明作用范围。
  • 权限校验必须沿用最新上游管理员权限模型;不得让普通用户自行声明豁免。

数据与兼容性

  • 延续现有配置语义,迁移时做必要的数据兼容;不改变用户表主结构或既有用户额度数据。

迁移约束与验收

  • 覆盖普通用户被限流、豁免用户不受用户 RPM 限制、豁免用户仍受频道 RPM 限制三类测试。

3. 完整保留 Recent Calls 调试能力

目标与上游关系

保留二开版最近 100 次调用的运维调试能力,并适配最新上游的 relay、日志、鉴权和默认前端。它是短周期诊断缓存,不替代上游正式消费日志或审计日志。

行为契约

  • 在内存维护最近 100 条调用的环形索引;容量满后按时间淘汰最旧项。
  • 每条记录可关联完整请求体、非流式响应体、流式原始 chunk、汇总文本、错误信息、时序与必要的路由上下文。
  • 敏感请求头必须经过脱敏后才能展示或持久化;不得因调试功能泄露授权凭据、Cookie、令牌或其他隐私字段。
  • 大体积内容写入临时文件,内存索引仅保存必要元数据和引用;请求结束、过期淘汰或服务清理时应回收对应临时文件。
  • 流式内容既要保留原始 chunk,也要支持用于排障的聚合文本;异常中断时仍应留下可用的错误上下文。

配置 / API / UI

  • 提供仅管理员可访问的 Debug API,以及 web/defaultweb/classic 的调试页面,支持查看列表、单次详情以及关联内容。
  • 两套页面和接口应延续最新上游的鉴权、分页/加载、错误状态和各自的视觉规范。
  • 两套 Recent Calls 页面必须在固定内容布局中提供纵向滚动,列表表格提供横向滚动,脱敏 JSON 详情提供独立双向滚动,避免长路径、响应体或大量记录被容器裁切。
  • 可配置或明确约束缓存容量、文件保留期和最大记录体积,避免调试功能无界占用磁盘。

数据与兼容性

  • Recent Calls 是瞬态诊断数据,不进入正式业务数据迁移,也不依赖变更用户、日志或消费记录表结构。
  • 临时文件目录、权限与清理策略必须适用于容器部署和多实例场景;多实例时页面应明确其可见范围或使用一致的共享方案。

迁移约束与验收

  • 分别验证普通请求、流式请求、上游错误、客户端中断和第 101 条记录淘汰场景。
  • 验证敏感头脱敏、临时文件回收、管理员鉴权与非管理员拒绝访问。

4. 在上游兑换码基础上保留随机额度扩展

目标与上游关系

兑换码功能以最新上游的兑换基础和新 UI 为准,在其上保留随机化批量发码能力,而不是移植旧页面或旧数据流。

行为契约

  • 支持兑换码前缀。
  • 每个兑换码可在配置的最小/最大额度之间独立随机生成额度。
  • 随机额度只固化在每条记录的 quota 字段中,不能编码进兑换码文本。随机额度兑换码与普通兑换码保持相同的 32 字符总长度,格式为“可选前缀 + 密码学安全随机后缀”,避免短码、额度泄露和可枚举猜测。
  • 支持大批量创建,并在创建完成后向管理员返回本次生成的完整兑换码列表。
  • 管理员可下载本次生成结果的 TXT 文件;下载内容、编码和权限必须可预测且不暴露其他批次数据。
  • 随机范围、数量、前缀和有效期等参数必须经过边界校验,防止负额度、反向区间或不可控的大任务。

配置 / API / UI

  • 在两套上游兑换码管理入口中增加一致的扩展字段和批量结果操作,不另起旧式管理页面。
  • 创建 API 返回批次标识、生成数量、失败明细(若有)及生成密钥;下载 API 仅允许创建者授权范围内的管理员使用。

数据与兼容性

  • 沿用上游兑换码及额度的数据模型,随机结果应固化为每条码的实际额度,避免兑换时重新随机。
  • 保证事务边界:成功返回的码与数据库中可兑换的码一一对应。

迁移约束与验收

  • 验证固定额度、随机额度、单条和大批量创建,以及 TXT 下载权限和内容一致性。

5. 消费日志与错误日志强制记录客户端 IP

目标与上游关系

保留“消费日志和错误日志始终记录 ClientIP”的运维取证能力。该差异只收紧这两类日志的 IP 记录策略,不应回退或丢失最新上游的请求 ID、审计字段和其他日志改进。

行为契约

  • 生成消费日志和错误日志时,无论用户是否开启个人记录 IP 选项,均写入可信代理链解析后的 ClientIP
  • 其他日志类别仍遵从最新上游的隐私与记录策略,除非其自身另有明确要求。
  • IP 获取必须依赖最新上游已验证的反向代理/可信代理配置,禁止直接信任任意客户端伪造的转发头。

配置 / API / UI

  • 用户设置中如保留“记录 IP”开关,应明确说明其对消费/错误日志不适用,避免误导。
  • 管理端日志查询需继续支持上游既有字段、筛选条件和权限控制。

数据与兼容性

  • 不改变现有日志表的核心结构;仅保证写入路径的 IP 字段稳定填充。
  • 迁移历史日志不强制回填未知 IP。

迁移约束与验收

  • 覆盖用户关闭记录 IP 后的成功消费和失败请求,均应保存正确 ClientIP;同时确认其他日志隐私策略未被意外扩大。

6. LinuxDO 多站点 OAuth 回调

目标与上游关系

保留多站点部署下 LinuxDO OAuth 登录后返回原始站点的能力,但必须用最新上游 OAuth 的 state、会话和前端机制重新设计,不能直接复制旧版静态 HTML 跳转逻辑。

行为契约

  • 登录发起时将目标来源站点与必要上下文绑定到 OAuth state。
  • state 必须签名、防篡改、短时过期、一次性或具备等价的重放保护,并与发起会话相关联。
  • 回调完成身份验证后,只能跳转到来源白名单内的 HTTPS/受控站点;任何未授权 origin、协议、路径构造或 state 校验失败都必须安全拒绝或回落到默认站点。
  • 不得形成开放重定向,也不得把访问令牌、敏感 state 或用户信息透传给不受信任站点。
  • LinuxDO 控制台的唯一 callback 由运营方自行设置到固定站点的现有前端回调页,例如 https://elysia.h-e.top/oauth/linuxdo;应用不再维护 LINUXDO_OAUTH_CALLBACK_URLLINUXDO_OAUTH_ALLOWED_ORIGINS 作为第二配置源。授权请求和 token exchange 均不发送或推导 redirect_uri,避免与 LinuxDO 实际 callback 漂移。
  • 登录发起时 state 绑定来源 HTTPS origin、随机 nonce 与短时过期时间并签名,同时将 nonce 和 origin 写入发起站点自己的浏览器会话;不依赖入口代理提供的 Host/协议头来判定最终登录站点。
  • LinuxDO 回调到运营方选定站点的 /oauth/linuxdo 前端页后,前端按上游原生流程调用 /api/oauth/linuxdo。API 验签 state 后若发现当前 Cookie 不是发起会话,只返回来源站点 /oauth/linuxdo 的跳转信息且不消费 state、不兑换 token;前端携带原始 codestate 和 OAuth 错误参数跳到来源站点。来源站点前端再次调用同一 API,只有与 nonce、origin 匹配的发起会话才能消费 state、兑换 token并获得原生 user JSON。
  • 跨站和同站分支都不得将 access token 或用户资料置入 URL。

配置 / API / UI

  • 两套前端的登录页和回调错误页均向用户给出不泄露安全细节的失败提示。

数据与兼容性

  • 允许以安全迁移方式承接现有多站点配置,但不能默认信任旧配置中的任意 URL。
  • state 密钥、过期时长和会话绑定应通过安全配置管理,不写入前端或日志。

迁移约束与验收

  • 验证运营方选择 elysia/oauth/linuxdo 为 callback 时的 elysia 本站登录与 elysiver → elysia 前端 → elysiver 完整闭环、过期 state、篡改 state、跨会话重放、入口 Host 改写,以及非发起会话只能获得前端跳转信息而不能消费 code/state。

7. FingerprintJS 用户关联与管理员检索

目标与上游关系

保留 FingerprintJS visitor_id 与客户端 IP 的用户关联能力,作为管理员的风险关联和排障辅助,并在两套最新上游前端重新实现。

行为契约

  • 对同一用户,以 (visitor_id, IP) 二元组去重;同一对重复上报不得产生重复记录。
  • 每个用户最多保留最近 5 个关联,超过上限时按既定时间顺序淘汰较旧项。
  • 管理员可按用户、visitor ID 或关联信息查询,并在结果中看见必要的时间和关联上下文。
  • 该信息只用于受控的管理/风控目的,不得被普通用户枚举、跨用户查询或作为唯一身份认证因素。

配置 / API / UI

  • 前端 FingerprintJS 集成应具备加载失败降级:核心登录、调用与管理功能不能因指纹脚本不可用而崩溃。
  • 后端写入和查询接口均要求管理员/当前用户的严格权限边界,并对输入做长度与格式限制。
  • 两套管理员指纹页面必须提供页面纵向滚动和表格横向滚动,长 visitor ID、IP、关联用户及大量历史记录不得因固定内容布局而不可见。

数据与兼容性

  • 关联记录可独立建模并设置必要索引;必须避免对高频请求造成无界写入。
  • 保留和展示周期应与隐私政策、部署地域要求相匹配,并支持后续清理。

迁移约束与验收

  • 验证重复去重、第五条上限淘汰、管理员查询和越权拒绝,以及前端 SDK 不可用时的降级。

8. 活跃任务与历史观测

目标与上游关系

完整保留旧版普通聊天请求临时活跃槽、实时排名与异常历史观测。该功能只描述聊天请求活跃度,不参与任何其他业务的并发、调度或生命周期控制。定时采样复用最新上游的系统任务调度机制。

行为契约

  • 默认全局临时活跃槽上限为 1000、单用户上限为 50,且均应可通过受控配置调整。
  • 仅 Chat Completions、Completions、Responses、Messages 和 Gemini generateContent 请求刷新活跃槽;其他请求类型不读取、不占用也不释放这些槽。
  • 普通聊天 activity 保持旧版算法与含义:直接对原始请求体执行 strings.Fields 分词、token 等权、带进程启动随机盐的 64-bit SimHash,取不到正文时才退化为模型名;汉明距离不超过 5 时归并,并以 LRU 和全局/单用户上限维护。成功消费日志与错误日志路径刷新活跃度;关闭消费日志时沿用旧版行为,不额外记录该消费路径。
  • 管理端实时查询默认使用最近 30 秒窗口,但 GET /api/active-task/stats?window=<seconds>&limit=<n> 必须保留旧版可选窗口能力:窗口最大 3600 秒,排名默认 50 条、最大 200 条;两套前端都提供常用窗口选择。每 10 分钟执行一次异常值采样,该采样固定查询最近 600 秒窗口,并仅持久化活跃 profile 数不少于 5 的非管理员用户。部署 Redis 时使用 active_task_activity:* 在实例间共享临时状态。
  • 定时持久化高活跃状态,管理员能够查看历史活跃任务;只保存指纹、用户与聚合计数,不保存请求正文、凭据或 prompt 内容。
  • 临时槽匹配、LRU 淘汰和时间窗口查询必须保持旧版统计含义,服务重启后允许从空的临时状态重新开始。

配置 / API / UI

  • 管理端展示全局/用户槽位使用、活跃任务历史和必要的筛选维度;不新增普通用户模型用量页面。
  • 两套管理端页面必须支持纵向滚动和表格横向滚动;日期格式化应先把 zhCNzhTW 等 i18n 内部语言标识规范化为合法 BCP 47 locale,不能因 toLocaleString 的非法 language tag 导致整页崩溃。
  • 所有数据查询必须落实上游权限模型、分页限制和时间范围限制。
  • 槽位上限由 ACTIVE_TASK_SLOT_GLOBAL_LIMIT(默认 1000)和 ACTIVE_TASK_SLOT_USER_LIMIT(默认 50)控制,所有值在服务端限定安全范围。
  • API:GET /api/active-task/statsGET /api/active-task/history(管理员,历史接口分页且可按 user_id 筛选)。

数据与兼容性

  • 热路径状态保存在内存或 Redis,历史观测使用独立的低频持久化模型。部署已启用 Redis 时,临时活跃槽跨实例共享;未配置 Redis 时允许单实例内存回退。
  • high_active_task_records 为独立的低频历史快照表,启动时通过 migrateDBmigrateDBFast 自动 AutoMigrate;不修改既有消费、任务或健康度历史表。
  • 不改写既有消费记录的含义,任务统计与 token 用量应可追溯至已有日志/任务来源。

迁移约束与验收

  • 并发测试验证 1000/50 两级临时槽阈值、竞争下的原子性、LRU 淘汰和时间窗口清理。
  • 验证 SimHash 相似任务处理、LRU 上界、定期持久化和权限隔离。

9. 定价供应商兼容归并

目标与行为契约

  • /api/pricing 必须兼容升级前遗留的同名、不同 ID provider/vendor 记录,避免 /pricing 侧栏在默认前端和经典前端重复显示同一个供应商。
  • 构建定价缓存时按 strings.ToLower(strings.TrimSpace(name)) 规范化名称并归并;同名记录稳定选择最小 ID 作为 canonical provider,重复记录中更完整的描述和图标可补齐 canonical 响应。
  • 所有模型引用的历史 vendor ID 都必须在响应中重映射到 canonical ID,使旧模型元数据仍能正确归类;供应商列表按 canonical ID 稳定排序。
  • 该兼容只发生在 pricing API 缓存/响应层,不删除历史记录、不修改表结构、不批量改写模型元数据,也不要求全库备份。

两套前端与验收

  • 默认前端与经典前端都消费同一份去重后的 vendors 和模型 vendor ID;不得仅在某一套前端用临时 Set 掩盖 API 重复。
  • 验证大小写和首尾空格归一、同名 ID 重映射、输出顺序稳定,以及线上 /api/pricing 返回的供应商重名计数为 0。

10. 模型性能趋势的真实桶时间轴

行为契约

  • 模型广场模型详情中的延时与成功率折线图必须按后端 perf_metrics.bucket_ts 的真实时间桶展示。桶宽由性能指标设置决定,前端必须根据相邻返回点自动推断显示精度:小于 1 分钟显示秒,分钟/5 分钟/小时桶显示到分钟,日及以上桶显示日期;不得硬编码为小时或 5 分钟。当运行时桶宽为 5 分钟时,例如 14:0514:10 必须是不同类别,不能统一格式化为 14:00 后被图表库合并。
  • 从排行榜点击模型进入的详情复用同一性能详情与折线图实现,因此必须得到完全一致的 5 分钟时间轴行为;不得为模型广场与排行榜维护两套不同的聚合逻辑。
  • 跨日期的 24 小时窗口标签应同时包含月/日和时/分,避免前后两天同一时刻发生类别碰撞。成功率折线图的 ISO 时间标签同样必须保留分钟。

前端范围与验收

  • 当前最新版上游的性能详情和排行榜仅存在于 default 前端,classic 前端没有对应延时/成功率折线图入口;不得为了“对称”凭空新增另一套功能。若 classic 后续引入该入口,必须复用相同的真实桶时间格式规则。
  • 分别使用 minute5minhour(以及未来可能的小于 1 分钟或日级)桶验证显示精度自适应;连续桶的横轴类别不得合并,延时和成功率两张图必须一致,并验证跨午夜时标签仍唯一。

11. 保留 CPU 优先的 Token 估算与采样校准算法

目标与上游关系

在适配最新上游 token counter 接口的前提下,保留原有“真实计数采样 + 校准 + 字符倍率估算”的 CPU 优先算法。目标是控制高频文本 token 计算成本,同时维持对实际用量足够稳定的估算。

行为契约

  • 对 OpenAI 文本请求,按采样策略执行真实 token 计数,得到可用于校准的样本。
  • 非采样或可快速估算路径依据经校准的字符倍率估计 token,而不是对每次长文本都做完整重计数。
  • 校准数据应按必要维度隔离,防止不同模型、语言或请求形态相互污染;样本不足时使用保守默认值。
  • 计费、限额和日志使用的 token 口径必须在误差、回退与异常情况下保持可解释,不能因优化出现负数、溢出或明显少计。

配置 / API / UI

  • 提供受控配置用于调整采样率、默认倍率、校准边界和是否启用;不向普通用户暴露影响全局计费的开关。
  • 需要有可观测指标,至少可比较真实采样值、估算值、误差分布和 CPU 成本。

数据与兼容性

  • 校准状态是可丢失/可重建的派生数据,不能依赖修改历史消费表结构。
  • 最新上游 token 计数器的模型适配、缓存和异常处理必须保留,二开算法仅替换/扩展其调用策略。

迁移约束与验收

  • 对中英文、短文本、长文本和不同模型进行准确性与吞吐基准;验证采样校准收敛、采样失败回退与计费边界。

12. 应用层 Gin gzip 可控开关

目标与上游关系

保留应用层 Gin gzip 默认关闭、按环境变量显式开启的部署策略,以避免与反向代理/CDN 的压缩配置重复或不兼容。最新上游已有的路由功能应完整保留。

行为契约

  • 默认不启用应用层 gzip。
  • 仅当 ENABLE_GIN_GZIP 明确启用时,对适用响应启用 gzip,压缩级别使用 gzip.BestSpeed
  • 行为应一致覆盖最新上游相关 API、管理面板与 Web 路由,不得只在某个入口生效。
  • 已压缩、流式 SSE、WebSocket、文件下载或不适合压缩的响应必须遵从正确的协议排除规则,不能造成双重压缩或破坏流式传输。

配置 / API / UI

  • 在部署说明中说明该开关与 Nginx、Caddy、CDN 等上游压缩层的关系。
  • 环境变量解析应接受明确布尔值,对非法值采用安全默认并给出可诊断日志。

数据与兼容性

  • 这是运行时行为,不引入数据库结构变化。

迁移约束与验收

  • 分别验证默认关闭、显式开启、Content-Encoding/Vary 头、SSE 未被缓冲或压缩破坏,以及各主要路由一致性。

13. Claude/OpenAI 协议转换热路径性能复刻

目标与上游关系

在最新上游的转换架构上,尽可能复刻旧版 Claude/OpenAI 转换热路径性能改进;这是明确的性能目标,而非仅在未来 profiling 发现问题后才选择性优化。

行为契约

  • 重点覆盖 Claude 与 OpenAI 请求/响应的高频转换、字段映射、流式事件转换和常见工具调用路径。
  • 通过减少不必要的对象分配、重复序列化、全量 JSON 往返和临时字符串构造降低 CPU 与 GC 压力;当前 Claude→OpenAI 请求路径对工具/消息按输入容量预分配,system 内容块使用 strings.Builder 聚合。
  • 所有优化必须保持协议语义:工具调用、思维/内容块、停止原因、用量、错误和流式顺序不可改变。
  • 对最新上游新增模型、供应商和转换分支,优先复用其正确性实现,再在不改变外部行为的范围内做同等优化。

配置 / API / UI

  • 不需要面向用户的新开关;调试或基准开关只能用于开发/运维环境,不能改变生产协议输出。

数据与兼容性

  • 不修改业务数据结构;优化不得依赖请求对象在异步链路中不安全地复用。

迁移约束与验收

  • 为非流式、SSE、工具调用、错误响应和多模态/扩展字段建立协议回归用例。
  • 对典型负载提供迁移前后基准,比较吞吐、分配、GC 和 P95 延迟,并确认输出兼容。

14. SSE 刷新节流与 Recent Calls chunk 批量落盘

目标与上游关系

保留流式输出的刷新节流以及 Recent Calls 原始 chunk 批量临时文件落盘。在最新上游已有的流读取、上下文清理和 panic 处理之上叠加,不能替换或削弱其连接安全处理。

行为契约

  • **前 3 个 SSE 事件永不节流,必须立即 flush。**这保证客户端尽快收到首包、角色事件和早期内容。
  • 自第 4 个事件起允许按受控策略合并/节流 flush,以降低高频小 chunk 造成的系统调用和代理压力。
  • 事件字节内容、顺序、终止标记和错误事件必须原样可见;节流只调整刷新时机,不得丢弃、重排或合并协议事件。
  • Recent Calls 对流式原始 chunk 使用批量写入临时文件,避免每个 chunk 单独触发磁盘写入;请求结束、失败或淘汰时必须 flush 并安全关闭。

配置 / API / UI

  • 节流间隔、批量大小和最大缓冲量应有安全默认值和受控配置;配置不能延迟前三个事件。
  • Debug 页面应继续能够查看保留的原始 chunk 与聚合文本,明确其仅用于排障。

数据与兼容性

  • 临时 chunk 文件沿用 Recent Calls 的生命周期和清理机制,不进入正式日志表。
  • 客户端断开时不得因缓冲而无限等待;应与请求上下文取消联动。

迁移约束与验收

  • 自动化测试精确断言前三个事件立即刷新,后续事件可按阈值批量刷新,且完整字节序列与未节流基线一致。
  • 压测验证高 chunk 频率下 CPU/系统调用下降,并验证中断、错误和文件回收。

15. 用户级平衡泄漏防护

目标与上游关系

保留基于 gitleaks 的平衡扫描能力,并把它作为用户级可配置的出站内容安全防护,与最新上游 SSRF 等安全能力共存而非互相替代。

行为契约

  • 在请求转发至上游之前执行扫描;命中规则时阻断转发。
  • 覆盖 OpenAI Chat、OpenAI Responses、Anthropic、Gemini 请求,以及无法结构化解析时的后备文本提取路径。
  • 对带完整会话历史的聊天请求,只扫描从倒数第二条 user 消息开始到请求末尾的消息后缀,包含其后的 assistanttool 和最新 user 内容;只有一条 user 时从该条开始,无 user 或非聊天请求时保留原扫描范围。不得因缩小重复历史扫描而跳过当前轮工具输入、工具结果或独立 prompt。
  • 静态 system prompt、tools schema 和第三近及更早的历史消息不在每轮重复扫描;这只减少客户端反复回传完整历史造成的重复工作,不改变转发给上游的请求内容。
  • 除 gitleaks 规则外,保留 sk-... 形式的兜底规则以覆盖常见 API 密钥泄露。
  • 扫描命中后必须记录一条错误用量/错误日志,供管理员审计,但日志内容自身必须脱敏,不能再次泄露命中的秘密。
  • 用户级设置和默认策略应明确:管理员可定义默认,用户只能在授权范围内调整自己的策略;不得让用户降低全局强制安全要求。

配置 / API / UI

  • web/defaultweb/classic 均提供用户设置和管理员策略入口,说明扫描范围、阻断效果及隐私处理。管理员策略 LeakProtectionBalancedForceEnabled 走既有 options 自动持久化;启用时覆盖用户的关闭偏好,个人页面明确显示为不可关闭。
  • 规则加载、规则版本和扫描失败策略应可观测;规则引擎异常时采用明确的安全策略,不能静默放行或造成无诊断的全站拒绝。

数据与兼容性

  • 规则文件/缓存和用户设置应与业务表分离或以可兼容方式扩展;不修改历史请求正文。
  • 与上游 SSRF、防滥用、审计和内容处理链路保持顺序可解释。

迁移约束与验收

  • 对各协议的密钥命中、非命中、结构化字段、后备文本、误报处理和扫描器故障进行测试。
  • 验证命中请求未到达上游、产生脱敏错误日志,且用户权限和强制策略不可绕过。
  • 使用生产流量 pprof 验证扫描窗口优化:30 秒样本中进程累计 CPU 从 116.88s 降至 16.82s,平均 CPU 从 389.07% 降至 56.07%CheckRequestLeakProtection 累计 CPU 从 103.50s 降至 1.57s,保留 Go heap 从 573.60 MiB 降至 93.58 MiB。该数据用于记录目标实例上的实测收益,不替代协议和安全回归测试。

16. 客户端断开后停止外层重试

目标与上游关系

保留请求断开后的重试终止策略,补充最新上游已有的流连接断开清理。重点是文本 relay 和任务 relay 的外层选频道/重试循环,避免客户端已经离开后仍消耗频道资源。

行为契约

  • 每次准备选择下一个频道或开始下一次重试前检查请求 context。
  • 出现上游错误后,如客户端 context 已取消、deadline 已到或连接已断开,立即停止,不再选频道、不再发起重试。
  • 正常仍连接的客户端继续遵从上游重试、退避、错误分类和频道选择语义。
  • 已启动的流读取与资源关闭保持上游的清理行为,新增逻辑不吞掉原始取消信号。

配置 / API / UI

  • 无需用户可见配置;日志/指标应能区分“因客户端断开终止”与“重试耗尽/上游失败”。

数据与兼容性

  • 不引入表结构变化。对已实际产生的用量仍遵从上游计费/日志规则,不能把未发起的重试记为调用。

迁移约束与验收

  • 为文本 relay 和任务 relay 模拟首尝试失败后客户端取消,断言没有第二次频道选择或出站请求。
  • 验证未取消请求仍可按策略重试成功,流式断开资源得到释放。

17. EPay 主动对账

目标与上游关系

保留 EPay 待支付订单的主动对账能力,覆盖充值和订阅两类订单。实现应复用最新上游安全的订单完成路径,使支付回调和主动对账的最终状态一致。

行为契约

  • 保持旧版自动查单频率和窗口:系统任务每 1 分钟运行一次,每次只查询创建时间位于最近 10 分钟内且仍为 pending 的 EPay 充值订单和订阅订单;默认启用,可用 EPAY_ORDER_RECONCILE_ENABLED=false 显式关闭。EPAY_ORDER_RECONCILE_AUTO_WINDOW_SECONDS 默认 600EPAY_ORDER_RECONCILE_BATCH_SIZE 默认 100
  • 提供管理员触发的全量扫描与 dry-run;dry-run 只报告拟处理结果,绝不修改订单、额度或订阅状态。
  • 对账时严格校验支付服务商返回值、商户号、订单号、金额、币种/必要业务字段和订单状态,不能仅凭“支付成功”标志完成订单。
  • 完成订单必须有事务性幂等保障:回调与对账并发、重复扫描或重复回调都只能产生一次额度/订阅权益变更。
  • 查询失败、签名异常、金额不匹配或状态矛盾应记录可诊断错误,保留待处理状态或进入明确的人工处理路径,绝不自动发放。

配置 / API / UI

  • 管理端在两套前端的充值账单中提供对账入口:默认执行最多 100 条本地待支付订单的 dry-run,并展示扫描、完成、跳过、失败汇总;实际执行须经过二次确认。手动执行写入汇总管理审计,定时任务保留完整执行报告;页面不展示支付密钥或服务商原始响应。
  • 全量扫描属于高风险运维操作,需管理员权限、明确确认和审计记录;dry-run 应作为默认安全选项。

数据与兼容性

  • 沿用上游订单、订阅和充值数据模型;必要的对账执行记录可以独立扩展,但不得破坏现有支付回调。
  • 定时任务通过系统任务 DB lease 避免多实例重复执行,并继续依赖订单完成路径的事务幂等处理回调竞争。

迁移约束与验收

  • 覆盖每分钟调度、最近 600 秒筛选、充值/订阅的支付成功、未支付、金额不符、服务商错误、重复回调、回调与扫描并发、全量扫描和 dry-run。

18. Relay 热路径优化

目标与上游关系

保留 relay 的通用热路径性能优化,并与第 11 节的协议转换优化互补:本节关注请求处理、泄漏检测和字段过滤等通用链路,必须保持最新上游 relay 的全部行为。

行为契约

  • 复用或池化泄漏检测器等可安全复用的热路径对象,严格在每次使用前重置、在异常路径归还,并避免跨请求数据泄露。
  • 对“移除禁用字段”等简单 JSON 变换提供快速路径:在能够安全判定时使用 sjson.DeleteBytes 在原始字节上删除固定字段路径,避免整段 JSON 解码再编码。
  • 快速路径必须保持透传语义、字段删除语义、JSON 合法性、字符编码和请求体可读性;遇到复杂 JSON、无法安全判断或任何异常时可靠回退至上游标准实现。
  • 不能破坏最新上游的 provider 适配、重试、流式转发、用量统计、日志、审计、安全检查和错误处理顺序。

配置 / API / UI

  • 不向终端用户暴露会改变语义的性能开关。可在开发/压测环境提供指标或调试开关,用于确认快速路径命中率和回退原因。

数据与兼容性

  • 不引入或修改业务表结构;对象池不得保留请求正文、授权信息或用户数据。
  • 所有优化均应对多实例和高并发安全。

迁移约束与验收

  • 以标准实现为 oracle,对空体、嵌套对象、数组、转义字符串、Unicode、超大 body、无效 JSON 和流式请求做字节/语义回归。
  • 通过 race 检测、并发测试、基准测试验证无跨请求污染、无泄漏,且在目标负载下有可量化收益。

19. 首页 iframe 按需 JWT 自动登录

目标与行为契约

  • 通过 IFRAME_JWT_SECRET 启用首页嵌入页面的 JWT 登录能力;未配置时不签发 JWT,首页 iframe 的其他能力保持不变。
  • iframe 需要认证时向父页面发送 IFRAME_AUTH_REQUEST。父页面只校验消息是否来自当前首页 iframe 的 contentWindow,不依赖 sandbox 下可能为 nullevent.origin,也不按 iframe 域名做额外校验。
  • 只有通过窗口对象校验的请求才调用后端签发接口;iframe 不请求时不产生签名开销。父页面防止并发重复签发,并在异步请求完成后再次确认 iframe 窗口未被替换。
  • 后端使用现有 dashboard session 验证用户,采用 HS256 签发 5 分钟有效的无状态 JWT。载荷仅包含与用户表一致的 idusernamedisplay_name,以及 JWT 标准 iatexp 字段。
  • 父页面通过 IFRAME_AUTH_RESPONSE 将 JWT 和原始 requestId 返回同一 iframe 窗口。JWT 不写入 URL、localStorage、数据库或日志,不提供消费和撤销接口。

配置 / API / UI

  • 环境变量:IFRAME_JWT_SECRET,建议使用至少 32 字节的随机值,并与 iframe 服务端验签配置保持一致。
  • API:POST /api/iframe-jwt,要求现有用户 session,通过关键接口限流保护;返回 token 和固定的 expires_in: 300
  • web/defaultweb/classic 首页均实现相同消息协议;iframe 后端必须固定使用 HS256 和共享 secret 验签,并检查 exp

迁移约束与验收

  • 验证未配置 secret、未登录、非首页 iframe 窗口请求、正常签发、并发重复请求、iframe 被替换和 5 分钟过期路径。
  • 解码结果不得出现邮箱、角色、权限、额度、session、API token 或其他用户敏感字段。

20. SSE 延迟触发保活

目标与上游关系

将从请求开始就固定周期发送 Ping 改为“延迟触发 + 触发后固定间隔”。请求开始后的触发窗口内不写入 Ping,以保留渠道切换和透明重试能力;达到触发时间后进入不可逆的保活阶段,持续向下游客户端写入 SSE 注释,直到流结束。该 Ping 的方向是 relay 到下游客户端,不是向上游供应商发送请求。

行为契约

  • ping_trigger_seconds 表示流请求建立多久后触发 Ping,默认 75 秒。计时从请求开始,不因真实上游流数据到达而重置或延后。
  • ping_interval_seconds 表示触发后的发送间隔,默认 5 秒;每次写入的协议内容仍为 : PING\n\n,标准 SSE 客户端将其作为注释忽略。
  • 第一次 Ping 写出后,本请求永久进入保活阶段。即使真实流数据恢复,也继续按固定间隔发送 Ping,直到客户端断开、请求结束或发生终止错误。
  • 客户端断开、请求结束、写入失败、上下文取消或最大保护时长到达时必须停止计时器和 goroutine。
  • Ping 与正常 SSE 数据继续使用同一写锁和写 deadline,不能交错破坏事件字节;协议明确不接受自定义 Ping 的适配器继续通过 DisablePing 禁用。
  • 首次 Ping 前应保留足够长的上游正常响应和透明重试窗口,因为一旦向下游写出任何保活字节,后续上游失败通常无法再切换为尚未开始的 HTTP 响应。触发后停止或降低 Ping 频率不能恢复这一能力,因此不应再根据真实数据重置保活阶段。
  • 仅向下游写过 : PING 注释、尚未写出任何模型文本、reasoning、tool call、usage、协议错误或终止事件时,上游 attempt 失败仍允许进入现有串行渠道重试循环。Ping 不得被视为语义响应。
  • 一旦写出任意有业务含义的流事件,禁止透明切换渠道,避免重复文本、冲突的工具调用、重复 usage 和跨模型状态不一致。
  • Ping 的触发状态与时间基准属于整个客户端请求,不能因切换渠道重新等待触发时间;每个上游 attempt 的 scanner、response body、context 和 Ping goroutine 必须完全退出后才能启动下一 attempt,禁止并行竞速或双 writer。
  • 只写过 Ping 且所有渠道最终失败时,HTTP 状态已无法更改,必须按客户端原始协议写入流内错误:OpenAI Chat 使用 data: {"error":...} 并结束流,Claude 使用 event: error,Responses 使用 event: response.failed。不得在 SSE 字节后混入普通 JSON HTTP 错误。
  • 每个 attempt 独立重置 ReceivedResponseCountStreamStatus 和首响应计时;请求级 Ping/语义输出状态、使用渠道链和客户端连接跨 attempt 保留。失败 attempt 不得执行成功用量结算,最终成功 attempt 正常结算,全部失败沿用外层统一退款与错误处理。
  • Ping 后重试继续执行渠道错误日志、Recent Calls 错误记录、自动禁用判断、错误用量日志和 重试:A->B 链路日志,并额外记录不含请求内容的 keepalive-only retry 原因。

配置 / API / UI

  • general_setting.ping_interval_enabled:总开关。
  • general_setting.ping_trigger_seconds:Ping 触发时间,必须大于等于 1 秒。
  • general_setting.ping_interval_seconds:触发后的 Ping 间隔,必须大于等于 1 秒。
  • web/defaultweb/classic 均显示两个独立输入项,并明确说明触发后持续发送直到流结束。部署时将旧 ping_idle_threshold_seconds 迁移为新的触发时间配置并删除旧 key,避免两套语义并存。

数据与兼容性

  • 不修改业务表结构,配置沿用全局 options/config 持久化机制。
  • 保持原 SSE Ping 字节兼容;只改变发送时机。非流式请求和 DisablePing=true 的协议不受影响。

迁移约束与验收

  • 测试持续有数据的流不会推迟固定触发时间;触发后会按固定间隔连续发送;真实数据恢复后仍继续发送直到流结束。
  • 验证客户端断开、上游 EOF、[DONE]、写入失败和禁用 Ping 时无 goroutine、ticker/timer 或响应体泄漏。
  • 覆盖“仅 Ping 后首渠道失败、第二渠道成功”“仅 Ping 后所有渠道失败”“首个语义事件后失败不得切换”“失败 attempt 自动尾部 usage/DONE 被抑制”“重试不重置 75 秒触发基准”“两次 attempt 不存在重叠 Ping goroutine”等回归路径。