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

@thd3178/opencode-poorguy-ratelimit

v1.8.1

Published

OpenCode plugin for multi-key rotation with sliding-window rate limiting

Readme

@thd3178/opencode-poorguy-ratelimit

English

OpenCode 插件:给 AI 请求做滑动窗口限流——按每分钟请求数精确控速,避免触发 RPM 上限导致的 429;多 key 场景自动轮询分流,抬升整体吞吐,缩短首 token 时间。

功能

  • 滑动窗口限流:按 rpm 精确限制每把 key 每分钟请求数,超出等着
  • 多 key 分流:round-robin / least-used / random 三种策略,多把 key 并发吞吐叠加
  • 主动限流 + 429 兜底:先本地控速,真撞 429 时单 key 指数冷却 + 自动切换
  • 透明接入:不改模型行为,只给 Authorization 做轮换和流量整形
  • 零外部依赖:纯 TypeScript,Bun 直跑

安装

npm 包名写进 ~/.config/opencode/opencode.jsonc(或 .json)的 plugin 数组:

{
  "plugin": [
    "@thd3178/opencode-poorguy-ratelimit"
  ]
}

opencode 重启后自动从 npm 装 @latest 并加载。

配置文件

配置文件路径(Bun 自动创建文件的父目录,不存在会自动建):

~/.config/opencode/opencode-poorguy-ratelimit.jsonc

最小可用(key 从 opencode.json 的 provider.<name>.options.apiKey 自动读取,插件里不写 key):

{
  "providers": {
    "nim": {
      "rpm": 40
    }
  }
}

多 key 轮换(写你自己的其他 key,覆盖自动读取的):

{
  "providers": {
    "nim": {
      "rpm": 40,
      "keys": [
        "nvapi-xxx...",
        "nvapi-yyy...",
        "nvapi-zzz..."
      ]
    }
  }
}

进一步调优:

{
  "enabled": true,                    // 插件总开关
  "strategy": "round-robin",          // round-robin | least-used | random
  
  // 只对这里配置的 provider 生效
  "providers": {
    "nim": {
      "rpm": 40,                       // 单 key 每分钟上限
      "keys": ["..."],                 // 可选:多 key,不写则用 opencode.json 里的 apiKey
    },
    "google": { "rpm": 60 }
  },
  
  // 429 后该 key 进入冷却(冷却期间跳过);无 Retry-After 头时从 baseDelayMs 指数翻倍
  "backoff": {
    "baseDelayMs": 5000,
    "maxDelayMs": 120000
  },
  
  // 日志文件开关(~/.config/opencode/opencode-poorguy-ratelimit.log)
  "logging": {
    "enabled": true
  }
}

字段速查

| 字段 | 类型 | 默认 | 说明 | |---|---|---|---| | enabled | boolean | true | 插件总开关;false 则插件完全不加载 | | strategy | string | round-robin | 轮询策略:round-robin 严格轮换 / least-used 优先用本分钟用得最少的 / random 随机 | | providers | object | {} | 只有列在这里的 provider 才会被限流/轮换;未配置的 provider 完全插件不管 | | providers.<name>.rpm | number | 40 | 每把 key 每分钟允许请求数;总吞吐 = rpm × key 数量 | | providers.<name>.keys | string[] | 无 | 可选。配置了就在这几把之间轮换;未配置则自动读取 opencode.json 中该 provider 的 options.apiKey(不需要在插件配置里重复写 key) | | providers.<name>.maxConcurrent | number | 2 | 同一时刻允许最多几个并发请求。超出的请求排队等前面的请求(包括流式接收完成)再发,防止 NIM 因并发触发 429 | | backoff.baseDelayMs | number | 5000 | 429 后首次冷却毫秒数(无 Retry-After 头时);连续 429 指数翻倍 | | backoff.maxDelayMs | number | 120000 | 本地指数退避的冷却上限;服务器返回的 Retry-After 不受此值截断,原样生效 | | logging.enabled | boolean | true | 是否写插件日志文件 |

provider 名字必须是 opencode.json 里 provider 对象的键名(key),不是显示名。

工作原理

每来一条请求
  ↓
插件拦截该 provider 的 fetch(所有真实请求都经过这里:主对话 / 工具链子请求 / 副标题 / subagent)
  ↓
查该 provider 的可用 keys(跳过 429 冷却中的)
  ↓
按 round-robin / least-used / random 选一把 key
  ↓
该 key 的滑动窗口还有余量?→ 放行并计一次
  ↓
窗口已满(rpm 满了)?→ 等待最早的那条过期后重试,期间其他 key 的窗口照常可用
  • 每把 key 独立计时,互不挤占——多 key 就是多管道
  • 窗口是滑动 60 秒,不是自然分钟的整桶重置
  • 429 响应由插件直接捕获(response.status == 429),触发指数冷却
  • subagent / 工具子请求 / 标题生成,只要用该 provider 就计入,不分是不是当前 session 主流程

运行时可观测

  • Toast:每次请求右上角弹 🔑 [provider] key…xxxx · 窗口 N/40 · key x/y;限流时弹等待提示;429 时弹冷却通知
  • 日志文件:~/.config/opencode/opencode-poorguy-ratelimit.log(UTF-8,GBK 控制台读会乱码属正常现象),每行记录 key 尾号和本次等待时间

常见问题

Q: 为什么配置了多把 key,体感没变化? 首次使用或修改配置后,需要重启 opencode。如果刚加完 key 没多久,least-used/round-robin 策略仍在给某一把 key 的窗口做初始化。如果 rpm 值远低于你真实单 key 的 NIM 限制,等于自己把自己限死了——确认你的 key 对应的 NIM 实际 RPM 额度。

Q: 为什么只在发消息的时候看到限流提示,工具调用没有? 那是旧版本的 bug(chat.params hook 只拦截每个 stream 的第一个请求)。1.6.0+ 已改成在 fetch 层拦截,所有请求一视同仁。

Q: 需要把 apiKey 写进插件配置吗? 不需要。插件会自动读取 opencode.json 里 provider.<name>.options.apiKey。只有当你想加一把额外的 key 做轮换时才需要把新 key 写进 keys 数组。

安全提醒

  • npm publish 时 npm 可能要求 2FA / granular token;token 建议用完即 revoke
  • 任何在 opencode.json 里以明文形式存在的 apiKey,写到配置文件或贴给 AI 时注意脱敏

许可证

MIT