Files
ModelRouter/internal
JianFeeeee 92c240cc25 feat(channels): 源改为串行 + 按模型节流(照参考 model-gate.ts)
照参考的实测依据:这些平台的**并发配额按模型**分别计量,与"还剩多少 token"
无关(那边实测撞 429 时 token 还剩 299.4 万)。同账号下:

    GLM-5.3-Flash  605 次 200    0 次限流   ← 从未撞过
    GLM-5.3         74 次 200   21 次重试  6 次最终 429

两条措施缺一不可:
① **串行**(max_concurrent=1):并发发起必然浪费一个配额;
② **最小间隔**(rpm):串行只保证"不重叠"、不保证"有间隔" —— 连续放行的两次
   调用可能只隔几十毫秒,仍会撞并发窗口。

此前插件注册源一律 `max_concurrent = 2` 且**从不设 rpm** ⇒ 既会重叠又无间隔。
现全部改为 `max_concurrent = 1` + `rpm = 20`(默认 3 秒一个,可按平台覆盖)。

⚠️ 参考的另一条教训也记进注释了:闸门**只包 fetch,不包 captcha**。
那边第一版把 mint 与 fetch 一起放进闸门,mint 耗时从 200-500ms 暴涨到
2500-3100ms(因为 mint 被串行后变成"排在 N 个人后面再 mint")。
我们的 rpm 节流在内核层、只作用于上游 dispatch,天然不涉及这一点。

顺带:`list_sources` 补暴露 `rpm` —— 运维与判据都要能看到它。只看
max_concurrent 无法区分"串行但有间隔"与"串行且无间隔"(后者仍会撞窗口)。

判据 `TestChannelSourcesAreSerialWithThrottle`:源必须 max_concurrent=1 且 rpm>0。
回归:9 个包全绿。
2026-10-14 16:00:34 +08:00
..