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

@haisto/opencode-model-output-metrics

v1.0.0

Published

Per-request time-to-first-token (TTFT) latency in the OpenCode TUI prompt footer, with P99 and latency color coding

Readme

GitHub License English NPM Version


兼容性:仅支持 OpenCode v2.2.5 及以上(@opencode/plugin 2.x),经 cli.json 加载。

包名:@haisto/opencode-model-output-metrics(与 GitHub 仓库同名)。功能已收窄为只看首字延迟:不再有侧边栏面板、模型排行或 token 统计。


1. 它显示什么

首字延迟(TTFT,time to first token)= 从宿主发出这次模型请求,到模型吐出第一个 token 的时间。

每一次请求都会单独计时,包括同一轮里工具调用之后的再次请求,也包括一次对话里你后来又发的每一条消息。读数挂在输入框状态栏右侧(prompt.footer.status 之后),不占侧边栏高度,不抢正文空间:

刚提交,还在等首字     ttft 0.12s
出字瞬间固定下来       ttft 4.84s
工具跑完,下一次请求    ttft 0.08s
这一次出字             ttft 4.51s
  • 一行只有一个读数:每次请求直接替换上一次,不做并排累积 —— 状态栏回答的是「这一次等了多久」。
  • 等待阶段读数每 200ms 刷新,从提交(或上一次工具结束)那一刻就开始涨;等待与出字是同一种形状,不加省略号之类的标记。
  • 出字瞬间该次读数固定下来(精确到毫秒),下一次请求再从零开始。
  • 没有任何可显示的内容时,插件渲染一个零宽空盒子 —— 不会留下占位的空白或残留数字。

2. 颜色

颜色只做一件事:让你不用读数字就知道这次快不快。

| 区间 | 颜色 | |------|------| | 快阈值以下 | 主题的 success(绿) | | 两阈值之间 | 主题的 warning(黄) | | 慢阈值以上 | 主题的 error(红) | | 这一轮已结束(读数停留窗口) | 主题的 muted(灰) |

着色看当前显示的那个读数:等待时它随已等时长上涨,于是颜色会随着跨过阈值自己从绿走到黄再到红 —— 这是本插件最有用的一处反馈,你不需要盯着数字看它涨。

默认阈值:1.5s 内算快,5s 以上算慢。可用环境变量覆盖,两个都要给且 FAST < SLOW,否则整组回退默认值:

TTFT_FAST_MS=800
TTFT_SLOW_MS=3000

不想染色就 /ttft-color 关掉,读数用正常前景色显示。

3. 安装

3.1 方式一:OpenCode 命令安装(推荐)

按 Ctrl + P 打开命令面板,搜索 install plugin,输入:

@haisto/opencode-model-output-metrics@latest

3.2 方式二:手动安装

# 1. 安装
npm install -g @haisto/opencode-model-output-metrics@latest

# 2. 写入 TUI 插件配置(推荐,自动改 ~/.config/opencode/cli.json)
npx @haisto/opencode-model-output-metrics
// 或手动编辑 ~/.config/opencode/cli.json(全局配置)
{
  "$schema": "https://opencode.ai/v2/cli.json",
  "plugins": ["@haisto/opencode-model-output-metrics@latest"]
}

3.3 重启 OpenCode

进入任意 session,输入框右侧即可看到读数。

4. 命令

| 命令 | 功能 | |------|------| | /ttft | 显示 / 隐藏读数 | | /ttft-color | 打开 / 关闭按延迟染色 | | /ttft-stat | 切换统计口径:最近一次 / P99 |

两条命令同时出现在命令面板(Ctrl + P)里,分组为 TTFT。

5. 口径

| 环节 | 取什么 | |------|--------| | 起点 | session.step.started 的 data.started(宿主记录的请求真正发出时刻;事件本身懒发布,晚于请求,不能直接用事件时间) | | 等待期起点 | 提交(session.execution.started)、模型要工具的步骤结束(step.ended 且 finish = tool-calls)、工具结束(tool.success / .failed)。只用来让读数在等待时先动起来,拿到精确起点后被替换;工具执行期间(tool.called / tool.progress)停表 | | 终点 | 该次请求的第一个输出:session.step.streamed,兜底为 session.text.delta / session.reasoning.delta / session.tool.input.delta / session.tool.input.ended | | 范围 | 每一次请求各算一次。同一轮里工具调用之后的再次请求、一次对话里后发的每条消息,都单独记一笔 | | 结束 | session.execution.succeeded / .failed / .interrupted,或兜底的 session.idle;只影响「是否落定」与灰色,不影响已记录的读数 | | 隔离 | 按会话独立计时。子代理会话有自己的请求序列,不与父会话混算 | | 子代理 | 后代会话在跑时,父会话并没有在等模型:这一行改为代显子代理的读数(前缀 ttft> 区分),切进子代理窗口也能看到自己那一次;后代停下后从那一刻重新起算,精确值也扣掉这段重叠 | | 并发子代理 | 多个后代同时在跑时显示最慢的那一个(有在等的优先),与派出顺序无关,不会在几个时钟之间来回跳 | | 统计口径 | 默认「最近一次」;切到 P99 后显示最近 200 个样本的 99 分位(最近秩法,取到的永远是真实观测值)。P99 没有等待态,数字在两次请求之间保持稳定 |

几个刻意的选择:

  • 推理与工具参数也算首字。只调工具、不写正文的请求,首字就发生在工具参数生成时;把它们排除会让这类请求永远等不到读数。
  • 起点用步骤级事件而不是轮次级。轮次级的 session.execution.started 一轮只发一次,只能测到每轮第一次请求;步骤级的 session.step.started 才是每次请求一对一的。
  • 读数显示单值、逐次替换。并排多值是「历史」,而状态栏要回答的是「这一次」;一行一个数才读得快,宽度也不会随历史增长。
  • 等待期另立「预备起点」。step.started 被宿主懒发布在首个输出时刻,只靠它计时,等待阶段根本看不见 —— 读数会凭空出现。于是从提交、模型要工具的步骤结束、或工具结束那一刻起就先起算,拿到精确值再替换。
  • 工具执行期间停表。长工具(跑测试、装依赖)不是「等首字」,不停表会让读数先涨到十几秒、再在首字到达时突然缩回一秒多。
  • 首字先于起点到达时按消息 ID 配对。两个事件谁先到不确定;用 assistantMessageID 把先到的首字挂回起点,否则这次请求会被算成「一直在等」。
  • 不显示 0ms 伪读数。请求结束却始终没有输出(中断、上游报错),说明这次没有首字可言,直接丢弃,而不是报一个假的 0ms。
  • 事件时刻优先用宿主的 created,只在它缺失或与本地时钟相差超过一天时才回退本地时钟,避免坏数据算出天文数字。
  • 一轮结束后读数降为灰色并保留 8 秒,够你在回答落定后回看一眼,又不会让过期数字长期占据状态栏。

6. 边界情况

| 情况 | 行为 | |------|------| | 插件在某次请求中途才挂载 | 这次请求没有起点,不记(不猜);下一次请求起正常 | | 请求结束但从未出字 | 丢弃该次,不显示 | | 同一会话里并发两次请求 | 按 assistantMessageID 区分,各自计时 | | 子会话切换、多会话并行 | 各自独立,状态栏只显示当前可见会话 | | 宿主在 home 路由等无会话场景 | 不显示 |

7. 设置存储

| 键 | 内容 | |----|------| | ttft_meter.mode | compact / hidden | | ttft_meter.color | true / false |

读取端逐项收敛:任一项损坏只回退该项,不影响另一项,也不会让插件启动失败。

8. 日志与排障

默认不写日志:插件只往状态栏写一行读数,没有需要默认留痕的行为。排障时显式打开:

OPENCODE_MODEL_METRICS_LOG_LEVEL=debug

打开后写入:

~/.opencode-model-output-metrics/model-output-metrics.log

| 变量 | 作用 | |------|------| | OPENCODE_MODEL_METRICS_LOG_FILE | 指定日志文件路径(不设则用上面的默认路径) | | OPENCODE_MODEL_METRICS_LOG_LEVEL | trace / debug / info / warn / error;留空或非法值都等于关闭 |

读数不出现时,先确认你正在一个 session 里(home 路由没有会话 ID),再确认这一轮确实已经触发过 session.execution.started。

9. 更新

由于 OpenCode 已知问题 #6774,插件缓存会锁死在首次安装的版本。

# 1. 清缓存(Windows)
Remove-Item -Recurse -Force "$env:USERPROFILE\.cache\opencode\npm\@haisto\opencode-model-output-metrics@latest"
# 1. 清缓存(macOS / Linux)
rm -rf ~/.cache/opencode/npm/@haisto/opencode-model-output-metrics@latest
  1. Ctrl + P → install plugin → @haisto/opencode-model-output-metrics@latest
  2. 重启 OpenCode

10. 开发

npm install
npm test               # vitest
npm run typecheck      # tsc --noEmit
npm run typecheck:test # 含测试与脚本
npm run build          # 清空 dist 后重建(含 tui.js)
src/
  index.tsx     插件入口:订阅事件、挂状态栏插槽、注册命令
  host.ts       唯一感知 OpenCode v2 API 的适配层(事件 / 主题 / 存储 / 提示)
  commands.tsx  /ttft、/ttft-color 与 /ttft-stat
  settings.ts   显示模式、染色开关、阈值(环境变量)
  meter/
    types.ts    事件与视图类型
    meter.ts    首字状态机(纯函数,注入时钟)
    format.ts   定宽时长格式化(5 列不抖动)
  ui/
    color.ts    主题归一化 + 阈值色带
    meter.tsx   一行读数组件
tests/          状态机、格式化、色带、设置、命令、适配层、安装器

设计取舍见 docs/superpowers/specs/2026-10-31-model-output-metrics-design.md。

11. 兼容性

代码完全模型无关:只要宿主按 OpenCode 事件协议上报 session.execution.started 与输出片段,任何模型都能量出首字延迟。

12. License

MIT