npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@arcaneorion/pi-provider-manager

v0.4.3

Published

Visual config panel for models.json + roundrobin failover engine + provider health stats for pi coding agent

Downloads

258

Readme

pi-provider-manager

给 pi 编程助手 加上可视化配置面板、智能轮询故障转移引擎与渠道健康统计——一个扩展读懂、调优你所有大模型渠道。

一个 pi 扩展,三项核心能力:

  • 可视化配置面板:/providers 打开本地网页面板,图形化编辑 models.json 与轮询配置,拖拽排序、在线发现上游模型、一键保存即热重载。
  • 轮询故障转移引擎:把多个真实渠道/模型注册成一个虚拟 roundrobin 模型,请求时自动故障转移——首个候选超时或出错就切下一个,全程你无感。可选开启测速排序,让最快的渠道永远排在前面。
  • 渠道健康统计:7 天滚动成功率、首字延迟(TTFT)、总延迟,被动采集、跨 CLI 共享,面板一目了然。

特性一览

🎛 可视化配置面板

/providers 启动本地网页服务器(仅 127.0.0.1),浏览器打开两个标签页:

  • 模型配置:逐字段编辑 ~/.pi/agent/models.json——provider、模型、API Key、请求头、compat 字段、thinking level map 全覆盖。支持从上游端点拉取可用模型、与已配置模型 diff、一键增删。
  • 轮询配置:编辑 ~/.pi/agent/roundrobin/config.json,配置虚拟模型元数据、候选池、超时/冷却、测速排序策略,保存即热重载。支持预设组(多套候选组合一键切换)。

面板走固定端口 17890 + 持久化 token:多终端共用同一实例——第二个 CLI 跑 /providers 检测到端口已占用,直接打开已有面板而非重启。面板闲置 5 分钟自动关闭;前台打开时每 3 秒轮询健康数据顺带保活,活跃面板永不超时;由本进程启动的 server 在 pi 会话退出时一并关闭。

🔄 轮询故障转移引擎

把多个真实渠道注册成一个虚拟模型 roundrobin/<组名>,用 /model 选中它,之后所有请求走故障转移引擎:

  1. 请求按顺序试候选,首个成功的就粘住(sticky 策略)。
  2. 当前候选在首响应阶段超时或出错 → 自动切下一个候选。
  3. 单候选原地重试 maxRetriesPerCandidate 次(指数退避)后才换渠道;耗尽则进冷却。
  4. 整轮全炸:测速关闭时清冷却重试 + 等最早冷却结束;测速开启时重测排序再战。
  5. 流中途出错(内容已吐出)直接返回不重放——避免内容/工具调用乱序;该候选仍记一次失败 + 进冷却(让 7 天统计与 smart 排序能看到“常吐一半断”的渠道)。

真正发生故障转移时,TUI 弹一次 toast:↔ 轮询故障转移到 XXX——纯提示不进对话历史,不污染 LLM 上下文。

请求隔离:请求 glm 组绝不会测速/影响到 deepseek 组——按组名严格隔离。

⚡ 测速排序(可选,强烈推荐)

开启后,引擎不再只会被动重试——它会主动测量每个候选的真实速度并重新排队。

怎么测:给每个候选发一个极简真实对话(普通候选 maxTokens=16,reasoning 候选抬到 2048——Anthropic 类 API 开 thinking 时要求 max_tokens > thinking budget 最小 1024,16 会被 400 拒绝导致误杀),默认 prompt 欧拉函数的意义?,复用面板模型测试的同一套 streamSimple 内核——看起来就是正常聊天流量,不会被当成探活封号。测量首字延迟(TTFT)与总延迟。

TTFT 兑底(v0.4.2):TTFT 优先认首个 text_delta(真正首字);reasoning 模型小 maxTokens 可能全花在 thinking 上、永不产 text_delta,此时退而认首个 thinking_delta(模型开始产出的信号)作为 TTFT 兑底,避免该候选被当“拿不到首字”直接垫底。

测速失败自动重试:单次测速失败且非 abort/鉴权问题 → 退避 1.5s 重试,最多共 3 次尝试(首试 + 重试 2 次)。三连败才判 ✗ + recordFailure 进冷却。这是为了不把“基本可用但暂时抖动”的渠道一次判死——一个 86% 成功率的渠道,三连败概率仅 ≈0.3%,抖动几乎必能救回;真挂的渠道三次都败判死正确,多花的只是注定失败的请求(不耗 token)。另:只要测速最终 ok=true,该候选就排在所有失败候选之前(“能用”本身就是兑底);ttft/latency 缺失时用组内中位数参与排序(v0.4.2 起)。真实请求路径的 maxRetriesPerCandidate 原地重试哲学同样适用于测速路径。

四种排序键:

| 排序键 | 算法 | 适用场景 | |--------|------|----------| | ttft | 首字延迟(默认) | 追求交互体感,首字快=响应快 | | latency | 总延迟 | 追求整轮最快 | | hybrid | 0.7×ttft + 0.3×latency 加权和 | 兼顾首字与总延迟 | | smart | 0.5×ttft_norm + 0.3×(1−reliability) + 0.2×latency_norm 三维加权 | 又快又稳,可靠性差的候选被压下去 |

smart 的可靠性兜底:reliability 用贝叶斯平滑从 7 天历史算:(success + 2.5) / (total + 5)(先验 = 5 次 50% 成功率)。

  • 全新候选(0 次)→ 0.5 中性,给机会但不越过高可靠老候选
  • 单次成功(1/0)→ 0.583,往中性拉回,不被单次结果带偏
  • 10 次全成 → 0.833,高但留余地;10 次全败 → 0.167 沉底,新候选能排它前面

速度维度做组内 min-max 归一化(0=最快),加权后分数越小越好,升序排序。测速失败的候选进冷却排末尾。

中位数兑底(v0.4.2):ttft/latency 缺失(null)的可用候选,用组内实测中位数参与排序(语义:该维度未知 → 假设中等水平),而非直接垫底 Infinity。不污染持久化字段——health.jsonl 仍存真实 null,面板仍显示“无首字”,仅排序时用中位数代理。全组都没测出该维度才退回 Infinity。

测速后:成功候选清冷却上前、失败候选进冷却沉底、currentIndex 强制归 0(放弃旧 sticky 位置——实测速度是更强的实时信号)。排序结果保持到下次测速。

何时触发:

  1. 首次请求某组(新增,懒触发)——本会话内首次请求一个开启了测速的轮询组时,同步跑一次测速排序:等排完再发首请求(首次就享受排序,代价是首请求多等几秒到几十秒)。不同组独立判定“首次”(lastSpeedTestAt===0),面板保存配置不再重置这个状态。v0.4.1 改动:取代了原先“session_start / 面板保存就狂测所有组”的行为——现在启动后不测,用到哪个组才测哪个。
  2. 请求整轮全炸——所有候选试过 + 重试耗尽 + 全冷却,触发重测重排。受 minIntervalMs(默认 60s)节流,避免持续故障时疯狂烧 token。
  3. 手动——/rr-speedtest [组名] 命令,或面板 ⚡ 按钮,绕过节流立即重测。/rr-speedtest 还会在终端上方打印详细结果表(逐候选 TTFT/延迟/排序/失败原因,见下文「手动测速」),30s 后自动消失。

⏱ 动态请求超时(测速开启时自动启用)

测速关闭时,单候选首响应超时 = 静态 timeoutMs(默认 30s)。测速开启后,超时变动态:

动态超时 = max(timeoutMs, min(120000, round(实测 ttft × 2.0)))     // 下限 = 你配的 timeoutMs(面板可调); 上限 120s 防病态样本(曾测出 ttft 327s)把超时抬到很大。中转站波动大就调大 timeoutMs 兼容临时劣化

这个动态值同时守护两处:

  1. 首响应——首个流事件到达前的等待上限(替代静态 timeoutMs)。
  2. 流中空闲——start 之后任意两个 chunk 之间的停顿上限。v0.4.0 新增:以前流一旦 start 就再无超时保护,候选首字几秒到达后慢慢吐几十秒,pi 一直干等——这就是"卡死"的根因。现在流中卡顿超过动态超时立即 abort。

没测出首字的候选(ttft=null,测速时就没拿到首字)回退静态 timeoutMs(默认 30s),null 回退双保险,不会被动态超时误杀。

超时后:当作普通流前失败处理——消耗一次 maxRetriesPerCandidate(不是立即换渠道),退避后重试同一候选,耗尽才进冷却换渠道。想"超时即换"就把 maxRetriesPerCandidate 设 0。内容已转发后的流中空闲超时属 terminal(不能重放,直接终止);该候选仍记一次失败 + 进冷却(与前述流中途出错一致,让 health/smart 看到质量问题)。

面板轮询 tab 每个候选显示计算出的动态超时值("超时 26s"),一眼看清每个候选当前的有效超时。

📊 渠道健康统计

每个渠道的 7 天滚动统计显示在面板(历史均值,非仅当前进程):

  • 成功/失败次数与成功率
  • 平均首字延迟(TTFT)
  • 平均总延迟

每次请求追加一行到 ~/.pi/agent/roundrobin/health.jsonl,多 CLI 共享(无文件锁,best-effort)。启动时自动剪除 7 天外旧事件。TTFT 取首个 text_delta 到达时刻,Latency = message.timestamp 到 message_end——不依赖队列配对,中断/取消不污染延迟统计。

轮询候选不再双计数(第四轮审计修复):早期版本里轮询获胜候选会在 health.jsonl 被双写(引擎 recordSuccess/Failure 一次 + 全局 message_end 被动采集又一次),总数×2。现已修复:成功转发的 done 事件改写为虚拟模型 provider,message_end 钩子不再重复记录,每候选只落一条。

🔧 手动测速

  • 在 pi 里:/rr-speedtest(所有开启测速的组)或 /rr-speedtest <组名>(指定组,支持 Tab 补全组名)。测完在终端编辑器上方打印逐候选详细表:每个候选一行,按排序顺序显示 #排名 TTFT 延迟(可用)或 ✗ 失败原因(不可用),30s 后自动消失,同时弹 toast 摘要。
  • 在面板里:轮询 tab "测速排序" 行的 ⚡ 按钮,调用 POST /api/rr/manual-speedtest。

两者都绕过 minIntervalMs 节流,但尊重 speedTestRunning 锁(不会对正在测速的组重复测)。


安装

pi install npm:@arcaneorion/pi-provider-manager

使用

  1. /providers 启动面板,在轮询 tab 添加候选(从已配置模型里选)、保存配置。
  2. /model 选择 roundrobin/<组名>。
  3. 之后所有请求走故障转移引擎。

保存即热重载——轮询引擎立即 pickup 新候选,无需重启。

跨 CLI 局限:热重载只对当前面板所属的 CLI 进程即时生效。若你有多个 pi 终端在跑,其他终端的轮询组不会自动重载(显示的候选/配置仍是旧的),需重启该终端或在其内重新触发加载。


配置

models.json

标准 pi models.json,面板支持完整 schema 编辑。

roundrobin/config.json

{
  "virtualModel": {
    "id": "roundrobin",
    "name": "Model Round Robin",
    "reasoning": true,
    "input": ["text", "image"],
    "contextWindow": 200000
  },
  "candidates": [
    { "provider": "my-openai", "model": "gpt-4o" },
    { "provider": "my-anthropic", "model": "claude-sonnet-4-20250514" }
  ],
  "log": true,
  "timeoutMs": 30000,
  "cooldownMs": 60000,
  "strategy": "sticky",
  "maxRetriesPerCandidate": 2,
  "speedTest": {
    "enabled": false,
    "sortKey": "smart",
    "prompt": "欧拉函数的意义?",
    "timeoutMs": 60000,
    "concurrency": 5,
    "minIntervalMs": 60000
  }
}

字段说明

| 字段 | 类型 | 默认 | 说明 | |------|------|------|------| | virtualModel | object | — | 虚拟模型元数据(id/name/reasoning/input/contextWindow/maxTokens/thinkingLevelMap/compat)。id 在加载时强制为组名;maxTokens 未设默认 16384,contextWindow 未设默认 200000。 | | candidates | array | [] | [{ "provider": "...", "model": "..." }, ...]。未知组合会被跳过;解析后为空则禁用该组。 | | log | boolean | true | 追加到 ~/.pi/agent/roundrobin/roundrobin.log。 | | timeoutMs | number | 30000 | 单候选首响应超时;测速开启且拿到 ttft 时作为动态超时下限 max(timeoutMs, min(120000, ttft×2.0)),没有 ttft 时回退它本身。中转站波动大就调大(如 45000=45s),给临时劣化更多恢复时间。 | | cooldownMs | number | 60000 | 候选失败(重试耗尽)后的冷却窗口。 | | strategy | "sticky" | "sticky" | 成功后候选推进策略:sticky=黏住当前候选直到失败,冷却后回首选;round-robin=成功后指向下一个候选(均分流量);primary=恒回首选(0),仅首选冷却/失败时用备选。 | | maxRetriesPerCandidate | number | 2 | 单候选原地重试次数(不含首试),指数退避。设 0 = 超时/失败即换渠道。 | | speedTest.enabled | boolean | false | 开启测速排序(见上方测速章节)。 | | speedTest.sortKey | ttft/latency/hybrid/smart | ttft | 排序键。hybrid = 0.7×ttft+0.3×latency;smart = 三维加权含贝叶斯平滑的 7 日成功率。 | | speedTest.prompt | string | "欧拉函数的意义?" | 测速 prompt,空则用默认。复用面板模型测试同一真实对话内核。 | | speedTest.timeoutMs | number | 60000 | 单次测速尝试的超时(≥1000)。失败后退避 1.5s 重试,最多 3 次尝试(retries 默认 2)。太短会误判慢但可用的候选。 | | speedTest.concurrency | integer | 5 | 并行测速 worker 数(≥1)。设为候选数 = 全组并行测。 | | speedTest.minIntervalMs | number | 60000 | 自动测速节流间隔(≥0)。手动 /rr-speedtest 与 ⚡ 按钮绕过此节流。 | | speedTest.retries | integer | 2 | 单次测速失败后重试次数(不含首试),退避 1.5s。设 0 = 失败即判 ✗(快但无抖动容错)。面板可编辑。 |


安全

  • 仅监听 127.0.0.1:本机回环,不暴露到网络。
  • 192-bit token 鉴权:首次启动生成(randomBytes(24))。为支持多 CLI 无缝复用同一面板,token 持久化到 ~/.pi/agent/roundrobin/.panel-token,跨会话/跨终端复用,非每次随机;所有 /api/* 路由必须带 X-Config-Token 头。
  • 同机威胁模型:本地 127.0.0.1 only,同机其他进程理论上能读到 token 文件或访问端口——这是用「固定 token 换多 CLI 复用」的明确取舍。介意可删 ~/.pi/agent/roundrobin/.panel-token 强制重置。
  • API Key 服务端解析:支持 $ENV_VAR(如 $OPENAI_API_KEY,运行时从环境变量取值),浏览器永远拿不到解析后的明文。
  • 保存自动备份:每次写 models.json / config.json 前先复制一份带时间戳的 .bak,误改可回退。

开发

仓库含 9 个 vitest 测试套件(tests/),覆盖配置解析、健康存储、轮询故障转移、前后端联动等。

许可证

MIT