@orbit-labs-ai/pi-metrics0.0.7
Observed latency, TTFT-excluded decode-speed estimates, live stream rates, and full numerical session measurements for Pi.
Untested on omp
This is a pi package. No one has reported whether it works on omp yet. If you try it, add your result below.
Reports 0
No reports yet.
Readme
Pi Metrics
English | 简体中文
为 Pi 提供可观测的活动延迟、包含推理用量但排除 TTFT 的解码速度估计,以及快速、实时变化的近似已观测流速率。显式接入的 bridge 可为子 SDK 会话及其他 agent 提供独立归属的请求 TTFT/TPS;兼容的 subagent 工具 details 在没有计时时提供仅用量报告。数值测量保存在 Pi 会话中。存在兼容的逐次尝试收据时,/metrics 和详情还会独立显示 Translator reported usage(Translator 已报告用量);原生总量仍保持原生口径。扩展不修改提示词、不包装 provider、不额外调用模型,也不尝试恢复模型的私有推理内容。
版本: 0.0.7;父会话测量 schema 为 2,agent 事件契约为 1。已为以 @orbit-labs-ai/pi-metrics 名称进行首次公开 npm 分发及 GitHub 发布做好准备。尚未确认发布完成;下文 npm 命令仅在维护者验证 0.0.7 已可获取后适用。参见更新日志。
环境要求
- Pi 1.1.0+、Node.js 22.19+。
- Pi 以宿主 peer 依赖提供
@earendil-works/pi-coding-agent、@earendil-works/pi-ai和@earendil-works/pi-tui,不将其打包。无需单独执行npm install或构建。发布候选版使用未经修改的官方 Pi 1.4.0 SDK 检查。 - 页脚需要交互式终端 UI。print、JSON、RPC 和 SDK 会话仍可记录数据。
/metrics通知需要支持 UI 的宿主,例如 TUI 或支持相应协议的 RPC 客户端。
快速开始
确认 npm 发布后,加载固定版本的包,仅本次使用,不注册持久安装:
pi -e npm:@orbit-labs-ai/pi-metrics@0.0.7
如需只记录、不替换页脚:
pi -e npm:@orbit-labs-ai/pi-metrics@0.0.7 --metrics-no-footer
发布前,或进行源码开发时,改在仓库根目录加载:
pi -e ./Pi/pi-metrics/index.ts
源码命令也接受 --metrics-no-footer。不要与现有安装同时加载第二份。加载本身不发送模型请求;解析 npm 来源会从注册表下载包代码。日常提示词及 Pi 自身的后台工作仍遵循你的 provider 配置和计费规则。
持久 npm 安装
维护者确认 npm 可获取后:
pi install npm:@orbit-labs-ai/pi-metrics@0.0.7
Pi 将包安装到其托管 npm 目录,并注册到个人 Pi 设置。新建会话,或在空闲时执行 /reload。精确版本已固定:pi update --extensions 会核对安装状态,但不会将其提升到新版本。更新需要明确指定新版本;不要替换活跃会话正在使用的文件,也不要加载两份副本。
从 0.0.6 升级
已有本地副本不会自动更新。先检查 pi list,等待空闲后,使用旧 Metrics 注册项的精确来源/路径移除注册,再安装固定 npm 版本(确认发布之后),或注册一个全新的稳定副本。保留旧本地目录,直到没有会话使用它。不要移除无关包或改写会话历史。父会话 schema 2 和 agent 契约 1 不变。Translator 用量报告需要单独升级到支持收据的 Translator;无法回填早期缺失收据,也不会自动转发子会话收据。
包含文件
公开 tarball 使用明确的文件白名单:六个运行时 TypeScript 模块(index.ts、metrics.ts、footer.ts、agents.ts、agent-bridge.ts、translator.ts)、package.json、中英文 README、中英文更新日志,以及从仓库复制的本地 MIT LICENSE。不包含测试、开发脚本、日志、个人运行数据、生成的宿主类型声明或打包依赖。translator.ts 仅读取兼容的用量收据,不是内置的 Translator 工具副本。Pi 仍是运行宿主,而不是与 Metrics 一同安装的包依赖。
持久本地安装
不要从经常切换工具分支的 checkout 安装。 Pi 会直接加载本地包路径。请将工具复制到 Git 仓库外全新的稳定目录:
mkdir -p "$HOME/.local/share/orbit-tools" &&
mkdir "$HOME/.local/share/orbit-tools/pi-metrics" &&
cp -R ./Pi/pi-metrics/. "$HOME/.local/share/orbit-tools/pi-metrics/" &&
pi install "$HOME/.local/share/orbit-tools/pi-metrics"
从仓库根目录运行。目标已存在时会故意失败。最后一步将本地目录注册到个人 Pi 设置,并非 npm 安装。新建会话,或在空闲时执行 /reload。不要再用 -e 同时加载第二份。
更新: 先检查现有安装,将新源码复制到另一个全新目录,再把旧包注册项替换为新路径。不要覆盖运行中会话使用的副本,也不要同时注册两版。旧副本在没有会话使用前应保留。更新是手动操作,不会自动从本分支拉取。若采用其他路径,移除时也应使用实际注册的路径。
命令与默认值
| 命令或参数 | 行为 |
|---|---|
/metrics |
当前分支的活动/请求延迟均值、有效吞吐量、请求覆盖率、原生全会话用量、独立的 Translator 已报告用量,以及其他 agent 的观测/明细。 |
/metrics details |
在 Pi 原生编辑器中查看/复制所有当前页脚指标和扩展状态,包括隐藏的空闲状态。即使提交,编辑也会被丢弃,不发送任何内容;RPC 改用通知。 |
/metrics footer on |
在 TUI 中启用/重新取得 Metrics 页脚。 |
/metrics footer off |
关闭页脚,继续记录。 |
/metrics footer collapse / expand |
选择单行或三行页脚,不停止记录。 |
/metrics footer toggle / Alt+M |
Metrics 拥有页脚时切换折叠/展开。 |
--metrics-no-footer |
启动时不安装自定义页脚,默认 false;仍记录数据。 |
右侧的 [▾ Collapse]/[▸ Expand] 按钮可在支持鼠标的 Pi fullscreen 模式点击(已在 1.3.0 验证);窄窗口显示 [▾]/[▸]。regular 模式保留终端原生鼠标选择,请用 Alt+M 或命令。如果终端不将 Alt 作为 Meta 发送,请使用命令。点击不会从提示词编辑器夺取键盘焦点。折叠/展开不会抢回已被其他扩展替换的页脚;需要时明确执行 footer on。
开关仅保存在内存中。新会话和重载默认展开,是否启用按 CLI 参数初始化;同一 runtime 内 off/on 保留所选布局。没有重置/删除记录命令,也没有单独的偏好设置文件。
页脚兼容性
Pi 只有一个自定义页脚槽位。启用 Metrics 可以替换其他扩展的页脚。关闭 Metrics 时,仅在它仍拥有该槽位的情况下恢复 Pi 内置页脚,不恢复先前其他扩展的布局。如果已被其他扩展替换,关闭 Metrics 不会清除那个替代页脚。
其他扩展通过 ctx.ui.setStatus() 发布的文字会去除控制序列,不会从那些扩展中删除。默认只隐藏精确匹配的已知空闲键值对:lsp → LSP: idle,以及 pi-background-task → 0 bg tasks。活跃及未知状态仍可显示,警告/错误文字优先;空闲状态仍可在 /metrics details 查看。Metrics 未拥有页脚时,详情会明确标明 Git 分支/扩展状态不可用。
无法组合多个自定义页脚布局。其他扩展应占用页脚时使用 --metrics-no-footer。不要为处理页脚冲突重置无关扩展。
阅读页脚
展开页脚最多三行,折叠后为一行。保留已有符号。当前目录采用加粗的主题强调色,作为视觉锚点;指标数值加粗,标签及模型/分支等信息较淡,组间间距统一。活跃扩展状态与末行共用空间,不额外增加行。宽终端展开示意:
~/project · ⎇ feature/demo provider/model · high [▾ Collapse]
◷ TTFT 840ms obs ↯ EFF TPS 38.4 tok/s · last ⋯ OBS TPS ≈42.6 tok/s ↗ IN 12k ↙ OUT 2.1k
◫ CTX ━╌╌╌╌╌ 24.0% · 48k/200k ◇ COST $0.012 ◈ CACHE R 8.2k · W 0 · HIT 40%
无活跃扩展状态时的折叠示意:
TTFT 840ms TPS 38.4 · last ◫ CTX 24% provider/model · high … +1 [▸ Expand]
较窄终端以 … +N 标明省略的次要指标组数量,长值用省略号截断,可选标题信息直接隐藏,不换行。长目录的末尾仍保持可见。/metrics details 保留所有数值及完整状态文字;显示不全时可用它查看。折叠状态优先显示 TTFT 和 TPS,与展开视图使用相同的父活动数值及 last/pending/so far 标记。简写 TPS 指以 token/秒为单位的 EFF TPS,不是实时 OBS TPS。高上下文占用(70% 警告/90% 错误色)、警告/活跃状态、父活动状态及带归属的 agent 性能优先于普通上下文、模型信息和目录;可选组按宽度显示,长路径/模型不再挤掉性能指标。极窄终端仍不可避免地截断文字。全部布局按 Unicode 终端列宽计算。
无需 Nerd Font。字体家族和字号由终端而非 Pi 控制;可选带有真实粗体字重的等宽字体,如 JetBrains Mono 或 SF Mono。本扩展只调整主题颜色、字重和间距,不修改终端配置,也不安装字体。
| 字段 | 含义 |
|---|---|
| 标题行 | 突出的当前目录、按空间显示的 Git 分支、会话名、选定 provider/model 和 thinking level,以及折叠按钮。最近实际响应模型不同时,完整详情始终另行标出。 |
TTFT … obs |
本次活动首次观测到内容的时间。跨工具轮次和自动续接循环保持不变,不按每次 API 请求重新开始。 |
EFF TPS |
已报告输出量/从首次观测到 token 至响应完成的时间,按活动内的请求汇总。用于估计解码速度;包含已报告推理 token,但排除每个请求的 TTFT、工具执行及请求间空隙。 |
last/pending/so far |
EFF TPS 保留上次估计,直到新的有效估计产生。当前活动覆盖不足时,保留值标为 last;只有尚无估计时才使用 pending。活动仍在进行时,已完成且符合条件的请求汇总标为 so far,而非最终值。 |
OBS TPS ≈ |
仅在实时滚动速率为正时显示:已暴露文本、思考及工具参数字符的近似到达速率。不可用、归零或响应结束时隐藏,不是隐藏推理速率。 |
AGENT …/Agents: … active requests |
显式接入的子/其他 agent 请求观测,独立展示。空间允许时显示最近开始的已记录 agent 请求的 TTFT/EFF TPS/状态。活跃数量指已观测请求,不是全部进程或任务数。/metrics details 保留每个 source/agent/request、模型、覆盖率及仅用量工具报告。不会用 agent 的速率替代父会话速率。 |
IN/OUT |
整个会话文件的 Pi 原生输入/输出用量,包括废弃分支及通过顶层工具用量报告的子 agent 用量。输出已包含标准化后的推理用量。不加入 Translator 自定义收据。 |
CTX |
Pi 当前估计的上下文 token/容量及百分比;未知时用 ?,包括压缩之后。 |
CACHE |
整个会话的读/写缓存 token。HIT 为最近已定稿请求的 cacheRead / (input + cacheRead + cacheWrite)。 |
COST |
按 Pi 保存的用量及配置价格累计的美元费用,不是账单或订阅余额。缺少费用不代表免费。 |
原生总量是动态更新的已存储计量,不是实时流估算。 Pi 记录定稿的 assistant/工具用量及辅助用量后,总量随之更新。当前 Workbench 为子 agent 返回顶层工具用量,因此这些工作已包含在原生 IN/OUT/CACHE/COST 中。Agent/details 记录只提供观测或明细;Metrics 不会再次相加。若工具只提供 details 而未提供原生用量,原生总量可能不完整。CTX 和缓存 HIT 跟随当前分支,不采用全会话计量口径。/metrics details 将这些口径与当前活动性能分开标注。
不可用的延迟/速率显示 —,不伪造零值;但 EFF TPS 有历史估计时会保留并标为 last,不活跃的 OBS TPS 则隐藏。原生用量/费用保留 Pi 的标准化数值,包括其零值默认值。失败、中止及 deferred 响应会标注。恢复的部分活动标为 incomplete,不显示最终有效速率。保存失败时显示警告/状态,并在事件处理边界通过 Pi 扩展错误机制报告,不向 JSON/RPC stdout 写入破坏协议的文字。
测量定义
可观测 TTFT 与活动边界
一次活动从首个 agent_start 开始,到 agent_settled 结束,包含其中的工具轮次、自动重试、压缩后的续接及队列工作。排队的 steering/follow-up 消息可能属于同一次活动,因此不一定对应一条键入提示词或 Pi 底层的一个 turn。
首个非空标准化 thinking_delta、text_delta 或 toolcall_delta 固定活动 TTFT。空 delta、开始标记及传输元数据不计入。首个思考、首个文本及首个工具参数时间分别记录。工具执行后,或同一未结束活动内再次触发 agent_start 续接时,不重置该时钟。
每个对话请求也保留自身的时间信息:从本扩展 context_with_system 处理器运行,到首次观测 delta 及 message_end。响应计时在工具执行前冻结;最终用量在 turn_end 获取,此时消息变换及工具结果已持久化。活动和请求使用同一单调时钟,保存的时长保留完整数值精度。ISO 墙钟起止时间另行保存,不能替代单调时钟计算时长。
这些是客户端观测边界,不是按下 Enter 后的延迟、首个 HTTP 字节时间或服务端首次生成 token 的时间。对应 hook 之前的工作不计入;区间内的认证、后续准备、传输、缓冲、provider 重试及观测开销均计入。若在活动/请求已开始后才接入,缺失的时间保持未知。
隐藏思考:能测量什么,不能测量什么
- 仅被 Pi 界面隐藏的思考仍产生标准化事件,可正常观测。
- provider 可能只暴露推理摘要,而非完整内部推理;这些摘要仅属于已观测流。
- 从未流式暴露的推理无法实时计数,也无法由本扩展确定其精确首 token/生成时间。
- Pi 标准化后的
usage.output包含usage.reasoning。推理属于子集,不能再次相加。即使没有暴露 delta,也保存最终输出总量,但 EFF TPS 要求观测到首个 token。 - 保留 Pi 提供的标准化推理计数,但缺失或零值不能证明没有思考:部分 Pi adapter 会把未提供的细分默认置零。
reasoningBreakdown明确区分positive和zero-or-unreported。 - 无法将其中专属的隐藏推理 token 数和时长从已暴露摘要、prefill、排队等延迟中分离。因此
hiddenReasoningTokens和hiddenReasoningMs明确为null,不进行猜测。
计量依赖 provider 报告及 Pi/其他扩展的标准化结果。客户端指标无法恢复未报告的用量、未公开的推理文本或精确服务端解码时间。
有效 TPS
streamMs = response completion time - first observed token time
request effective TPS = normalized output tokens / (streamMs / 1000)
activity effective TPS = sum(output tokens) / (sum(request streamMs) / 1000)
分母从首次观测到非空思考、文本或工具参数 delta 开始,到工具执行前的 message_end 结束。每个请求的全部初始等待(TTFT)都被排除,而非每次活动只排除一次。工具执行、请求间空隙及独立辅助模型工作也不计入。完整 responseMs 仍保存用于延迟分析,但不再作为 EFF TPS 的分母。
例如,初始等待 20 秒,随后在 5 秒流式阶段内报告 200 个输出 token,则为 200 / 5 = 40 tok/s,不是 200 / 25 = 8。只延长初始等待,不会改变 EFF TPS。
这是基于客户端观测的解码速度估计,不是精确的服务端解码速度或单 token 延迟。已报告输出仍包含推理;若 provider 在首次观测到 token 之前生成了隐藏推理,这些 token 仍在分子中,但对应初始时间被排除。例如,在上述 5 秒已观测区间内,用 1,200 个已报告 token(含 1,000 个隐藏推理 token)计算会得到 240 tok/s。因此隐藏推理、缓冲和完成阶段开销可能影响估算;不会推测或补回无法观测的隐藏时长。
请求只有在以 stop、length 或 toolUse 结束、输出用量为有效正数、首 token 至完成的时长已知且为正时才符合条件。推理/输出计数矛盾、缺少用量、错误、中止和 deferred 响应均不符合条件。若单个缓冲块到达后才完成响应,即使没有有意义的滚动 OBS 速率,也可计算 EFF TPS。只有用量而没有已观测 delta 的响应,或零长度流,无法提供新的 EFF TPS,不退回使用完整请求时间。零输出保守地视为不可用,因为 Pi 也用零表示缺失用量。
任何已开始请求尚未完成或不符合条件时,记录中的活动 EFF TPS 都不可用;覆盖计算不会悄悄排除失败请求。页脚会跨工具调用、新内容、续接请求及新活动保留上次有效估计,直到新的有效估计将其替换。当前覆盖不足时,last 表示这只是显示层保留值,并非当前活动的最终速率。若从未有过估计,EFF TPS 仍显示 —。
重载和分支导航仅从当前分支已定稿的请求记录重建保留值,包括后来变为不完整的活动中较早已完成且符合条件的请求前缀。不会将临时响应 checkpoint 当作已定稿历史;新会话不携带保留值。临时 so far 估计仍可能因最终消息变换而修正或撤回;只有有效的权威定稿估计才会替换保留值。该显示行为不改变保存的测量、有效性判定或计算公式。
/metrics 另外报告被覆盖请求的总输出量/已观测流总时长(即按流时长加权的速率),并明确给出已覆盖/已记录数量,不使用各请求速率的简单平均。历史或辅助原生用量不混入分子。Provider durationMs 另行保存,不替代该公式的分母。
近似已观测流 TPS
已观测文本、思考及工具参数的 Unicode code point 数/4(包括空白),除以最长为滚动 2 秒的已观测时间。窗口在请求开始处截断,而非在首个到达块处截断,避免夸大启动阶段速率。计数放入 100 ms 桶,边界桶会产生近似误差。至少需要两个非空 delta,且距离首个 delta 已过 250 ms。即使模型仍在隐藏推理,没有已暴露输出到达时,该速率也可能降为零。在获得正值估计前,页脚隐藏 OBS TPS;速率归零或响应结束时再次隐藏;已暴露输出恢复且滚动速率为正时重新显示。静默期间仍继续采样,零值样本仍保存在会话记录中。
采样/重绘间隔为 250 ms,受事件循环延迟影响。这不是真实 tokenization;语言、代码、emoji、JSON、摘要及批量到达都可能显著影响估算。≈ 和独立的 OBS 标签是有意保留的,不据此外推隐藏 token 速率。
子 agent 与其他 agent
可用接入与边界
Pi 父会话的 message_update 事件描述的是父会话,不是所有子进程。兼容的 subagent single/parallel/chain details 结构提供 details.results[].usage,不提供子请求开始或非空 delta 时间戳;Pi 1.3.0 随包附带的 examples/extensions/subagent/index.ts 也使用此结构。这些聚合用量不足以计算 TTFT 或解码 TPS。工具墙钟时长包含启动、排队、工具及多个请求,绝不作为子请求流时长。
Metrics 在 turn_end 自动从该 subagent details 结构复制白名单中的最终用量:agent/model、结果位置、模式、退出码、轮次、输入/输出/缓存 token 及费用。不读取任务/消息/stderr 文本,不猜测计时,也不回填旧工具结果。只有顶层已定稿工具结果具有此仅用量适配所需的持久 entry ID;缺少该边界的原始嵌套 details 不导入。这些是仅供拆分查看的明细报告,不是原生总量排除的金额。当前 Workbench 通过顶层 result.usage 返回子 agent 用量;Pi 已经计入,Metrics 不会再加一次 details。旧版/仅 details 工具可能没有报告原生用量,修改版或第三方工具的结构也可能不同。Metrics 不从 details 推算缺失的原生总量。
要获得真正的已观测计时,需要显式为子 agent 或调度器接入:
- Pi SDK 子会话: 将导出的
createAgentMetricsBridge内联扩展加入子会话的DefaultResourceLoader.extensionFactories。它观测子会话的context_with_system、首个非空标准化 thinking/text/tool-argument delta、message_end及权威turn_end用量。响应计时在子工具执行前冻结,每个子请求使用独立 ID。发布开始、首次观测、最终快照;已知请求缺少完成边界时发布中断快照。不启动 agent、不包装 provider、不改提示词、不创建定时器,也不调用模型。 - 其他 agent/子进程: 使用生产者自身的单调请求/delta/完成边界实现下文数值契约,并显式转发至父会话事件总线或工具 details。总线只在同一进程内有效。不会安装 IPC 服务、stdout 解析器、文件监视器、私有配置发现机制或自动子进程注入。
- 计时属于观测者本地。通过 IPC 测量到达事件的调度器须标注
external_request_start,其传输/缓冲开销包含在内。不可将不同进程的时钟相减。两种接入都不测量服务端生成时间或未暴露的推理。
在现有可信调度器内部,为每次子调用在首个提示词前创建新的 bridge(调度器已提供 pi、ctx、toolCallId;非工具 agent 使用 null):
import { randomUUID } from "node:crypto";
import { createAgentMetricsBridge } from "/stable/pi-metrics/agent-bridge.ts";
import { AGENT_EVENT } from "/stable/pi-metrics/agents.ts";
const anchorId = ctx.sessionManager.getLeafId();
if (!anchorId) throw new Error("A parent session entry is required for attribution");
const childMetrics = createAgentMetricsBridge({
source: "my-dispatcher",
agentId: randomUUID(),
sessionId: ctx.sessionManager.getSessionId(),
anchorId,
toolCallId,
emit: observation => pi.events.emit(AGENT_EVENT, observation),
});
// Add childMetrics to the CHILD resource loader's extensionFactories.
// The existing dispatcher still owns child creation, cancellation and disposal.
使用已审查的稳定源码路径,而非照抄占位路径。此片段本身既不启动进程,也不为已有进程自动接入。不要用 pi -e 加载 agent-bridge.ts:它是需要显式归属/传输参数的 factory,不是独立扩展。子会话单独加载 Metrics 时只记录自身会话,不会自动转发到父会话。取消时先等待子会话 abort,再 dispose。崩溃/直接 dispose 可能留下 running 快照;重载/导航后显示为 interrupted,不伪造最终用量。父会话被替换或导航时应停止转发,新工作须创建新的子调用 ID/anchor。没有实时子 agent 字符速率采样器:运行中可提供 TTFT,EFF TPS 要等用量定稿。
数值型外部 agent 契约 v1
在父 runtime 中执行 pi.events.emit("orbit.pi-metrics.agent.v1", snapshot),或把相同快照组成的数组放在工具结果/更新的 details.piMetrics。Metrics 观测 tool_execution_update、tool_execution_end 和 turn_end 中已定稿的工具结果;details 快照的 toolCallId 必须等于该事件的工具 ID,包括嵌套 ID。总线不会推断工具归属:须传正确 ID 或 null。这是可信接入协议,不是带认证的遥测端点。
以下字段全部必需,不可用时明确用 null。仅数值测量指不含提示词、回答、推理文本、参数、结果、任意标签或原始事件;仅允许这些白名单标识符/枚举及数值。出现未知字段会拒绝整条快照,而非将其保存。
| 字段 | 契约 |
|---|---|
version |
固定为 1。 |
source、agentId、requestId |
生产者命名空间、唯一子调用、唯一请求/尝试。跨传输和重放使用同一三元组。不要用可复用角色名充当调用 ID,也不要通过此通道再次报告父会话自己的请求。 |
sessionId、anchorId、toolCallId |
目标父会话 ID、调度时捕获的现存父分支 entry;toolCallId 是已知父工具 ID 或 null。会话不匹配或 anchor 不在当前分支时拒绝。helper 固定这些 ID,不会重新指向新目标。 |
provider、model |
实际响应模型的标识符或 null;helper 从权威 assistant 消息定稿。 |
| 标识符格式 | 1–256 个 Unicode 字母/数字或 ._:/@+-,首字符必须是字母/数字。不含空格或控制序列。只有 toolCallId、provider、model 可以为 null。 |
sequence |
非负安全整数,同一请求的新快照严格递增。允许仅发送完成快照;流式发送快照时从 0 开始。 |
state |
running、complete 或 interrupted。complete 包括具有最终用量的失败/中止响应,不仅是成功响应。终态快照不可修改。 |
timingBasis |
context_with_system(子 Pi hook)、external_request_start(生产者注明的开始边界)或 unavailable。 |
elapsedMs、ttftMs、responseMs |
从同一个本地单调请求开始时刻计算的有限非负毫秒数,不超过 Number.MAX_SAFE_INTEGER;也可为 null。ttftMs 是首次非空已观测 delta,已知后不可改变。responseMs 在响应完成时冻结,排除工具。已知时间满足 ttftMs ≤ responseMs ≤ elapsedMs,允许 TTFT 缺失。unavailable 要求三项均为 null;其他口径要求 elapsedMs。有计时的 complete 要求 responseMs。 |
outputTokens、reasoningTokens |
非负安全整数或 null。输出已包含推理,推理是子集,不得超过输出。不重复相加,也不推测隐藏 token。零输出不能建立可用吞吐量。 |
stopReason |
stop、length、toolUse、error、aborted、deferred 或 null。complete 必须非空;running 要求 responseMs、两个 token 数和 stopReason 全为 null。 |
例如,完成快照的测量字段为 elapsedMs: 30000、ttftMs: 20000、responseMs: 25000、outputTokens: 200、reasoningTokens: 150、stopReason: "stop",则 EFF TPS 为 200 / 5 = 40,不是 200 / 30,也不是 350 / 5。不接收速率字段,由 Metrics 计算。错误、中止、deferred/不完整请求、未知/零流时长和未知/零输出均不产生 EFF TPS。逐 agent 汇总按被覆盖输出/被覆盖流秒数计算,并列出已覆盖/已记录请求数,不累加并行速率或取简单均值。覆盖率不能说明生产者从未报告的请求。
保存前先校验并复制标量字段。完全重复及较低序号的过期重放被忽略;冲突修订、归属/计时口径/首次观测改变、耗时倒退及终态后改变被拒绝。保存的 (source, agentId, requestId) 标识在整个父会话文件(含废弃分支)内,对总线/details/重载投递去重。显示和汇总只使用当前分支 entry。未完成历史恢复为 interrupted,恢复后的迟到回调不能继续推进它。即使 anchor 是共享祖先,之前出现过的子调用在恢复后也不能再附加新的请求 ID;新工作需要新的调用 ID。重放不会把定稿记录复制到缺少它的分支。生产者须协调身份:不同 ID 声称代表同一物理请求时,无法自动识别。
只有成功保存的快照推进去重状态,保存失败可重试。有 UI 时通过警告/状态报告保存失败,不回显载荷。总线发送后不等待结果,不提供投递确认或自动重试;传输可靠性由调度器负责。拒绝次数显示在页脚/详情中,会话恢复/导航时清零;被拒绝的载荷不保存。runtime 结束时取消总线订阅。Metrics 不会把外部记录加到父活动/请求覆盖或原生 IN/OUT/CACHE/COST;底层的子 agent 工作可能已通过工具原生用量计入。Details 聚合用量也绝不加入已接入请求的吞吐量分子。
Translator 已报告用量
/metrics 和 /metrics details 提供独立的 Translator reported usage 区域。只读取 customType 为 orbit.translator.usage.v1 的 custom entry,范围是包含废弃分支的整个会话文件。显示已记录尝试数、已报告/未报告数量、仅已报告部分的小计,以及按 provider/model、方向和状态拆分的明细。失败或重试的尝试只要有数值用量仍计入;输出已包含推理子集。
这不是合并的会话账单。页脚原生 IN/OUT/CACHE/COST 保持不变。子会话的 Translator 收据可能只留在子会话中,永远不到达父会话,因此父会话收据小计或“原生用量加 Translator 用量”都不能代表完整花费。不显示合并总量。收据没有请求时钟,不影响 TTFT、EFF TPS、OBS TPS、提示词处理或性能覆盖率。
- 没有有效收据: 显示
Unavailable;报告从升级到支持收据的 Translator 后开始,不回填早期用量。这不能证明未使用翻译或翻译免费。 usage: null: 已记录的尝试,但用量未报告,不是零费用尝试。混合报告只显示已知小计,未报告尝试单独计数。Pi 标准化后的数值零费用按报告显示,不证明请求免费,也不是账单。- 去重: 每个
requestId在整个会话中只对应一份不可变收据,不按分支、provider 或方向另行计数。完全重复的收据忽略。同一 ID 的有效收据发生冲突时,整个 ID 从金额/尝试计数中排除,不任意选择某个金额;显示格式错误、重复收据数量及冲突 ID 数量。格式错误收据不占用 ID。旧 Translator outcome 聚合记录即使包含 usage,也绝不累加。 - 只读: 每次命令读取当前会话 entry;不复制收据、不迁移会话、不扫描其他会话历史或个人配置、不调用模型或发送网络请求。恢复/导航不改写记录。不安装收据传输或子会话自动转发机制。
收据契约 v1
这些自定义 entry 由兼容的 Translator 生产者负责;Metrics 仅读取。data 必须且只能包含以下字段(不含提示词、回答、错误文本或 provider 原始载荷):
| 字段 | 契约 |
|---|---|
version |
固定为 1。 |
requestId |
一次实际尝试的唯一字符串,重放/转发时保持不变;不同重试使用不同 ID。 |
provider、model |
Provider 和模型标识字符串。 |
usage |
完整 Pi Usage,未报告时明确为 null。input、output、cacheRead、cacheWrite、totalTokens 为必需的非负安全整数;可选 reasoning 和 cacheWrite1h 分别是 output 和 cacheWrite 的子集。cost 必须包含有限非负数 input、output、cacheRead、cacheWrite、total,每项不超过 Number.MAX_SAFE_INTEGER。不转换字符串,不把缺失字段默认填零。 |
status |
非空状态字符串;无论结果如何,数值用量均计入。 |
direction |
input、output 或 subagent。 |
nativeAccounting |
固定为 false;这些收据不是原生 usage entry。 |
字符串字段不得为空白,最多 256 个 UTF-16 code unit,不得包含控制/格式字符或行/段落分隔符。未知字段、无效版本/类型/数值及矛盾的子集会导致拒绝该收据,不回显载荷。小计超出安全数值范围时显示不可用,不显示不安全的数值。标准化后的 provider 数据也无法补出 provider 从未报告的用量。若不同 ID 声称代表同一次物理尝试,无法自动识别。
会话记录与隐私
所有数据以 Pi custom entry 保存,不进入模型上下文。默认位置为 ~/.pi/agent/sessions/ 或你配置的会话目录;/session 可定位当前文件。不创建额外数据库、指标文件、网络端点或遥测上传。使用 --no-session 或内存 SDK 会话时,数据仅留在内存中。
父会话 schema 2 保持原有三种 entry,独立的 agent 契约 1 entry 不改写它们:
customType |
保存内容 |
|---|---|
orbit.pi-metrics.v2 |
权威定稿的请求,以 messageEntryId、requestId、runId 和请求序号关联。 |
orbit.pi-metrics.run.v2 |
活动开始/进度/最终汇总,state 区分 running、settled、interrupted。 |
orbit.pi-metrics.trace.v2 |
request_start、first_observation、samples、临时 response_checkpoint、request_interrupted 及 tool_start/tool_end 事件。 |
orbit.pi-metrics.agent.v1 |
经校验的其他 agent 请求标量快照,含 source/agent/request 和父分支/工具归属。每个标识使用最近接收的快照,不可把连续快照相加。 |
orbit.pi-metrics.delegation.v1 |
最终 subagent details 用量,关联持久 toolResultEntryId,保留并行/chain 的独立结果位置;仅供明细查看,不伪造计时,不额外计入原生总量。 |
orbit.translator.usage.v1 |
由兼容的 Translator 写入,Metrics 只读:逐次尝试的数值用量或 null、请求 ID、provider/model、方向/状态及 nativeAccounting: false。不保存重复副本。 |
新的请求/活动记录用 throughputBasis: "reported_output_tokens/observed_stream_elapsed_seconds" 标识 EFF TPS 公式。活动的 coveredStreamMs 是作为分母的流时长之和。measuredRequestMs 和 coveredRequestMs 仍保留完整请求时长用于分析,不用于吞吐量计算。采用完整请求时间口径的旧 schema 2 记录仍可读取。
记录保留所有可获得的数值测量:
- 活动/请求的可观测 TTFT、首文本/思考/工具参数、完整响应时间、已观测流区间、ISO 时间戳、计时/吞吐量定义及速率不可用原因。
- 请求/实际响应模型、provider/API、停止原因、provider 时长、开始时的 thinking level、白名单中的最终 Pi 用量/费用、标准化推理细分及明确未知的隐藏思考字段。
- 分类已观测字符数、delta 数、估算值及采样的时间戳/速率/累计字符数,包括静默等待期间的采样。
- 请求起止上下文快照、整个会话用量/费用、请求覆盖率及加权活动吞吐量。
- 每个工具的标识、名称、父 ID、请求 ID、起止偏移、时长及错误/完成状态。并行/嵌套工具的墙钟时长按区间并集计算,而非直接相加。Provider 重复使用的工具 ID 按请求区分。
不降采样、不设历史上限: 保留每一个实际产生的采样点。通常按 128 个一批(约 32 秒)追加,响应结束或有序中断时再刷新。chunkIndex、firstSampleIndex、sampleCount、sampleChunks 和 samplesComplete 使重建可以核验。按 requestId 查找匹配的 samples trace entry,以 chunkIndex 排序后连接,再追加定稿请求中的内联 samples 后备数据;不要重复连接临时 checkpoint 中的副本。实际采样时间戳会暴露定时器延迟/错过的触发机会;这不是逐 token 或每个 delta 的轨迹。
首次观测 checkpoint 在响应完成前保存精确的已观测延迟。响应 checkpoint 在工具完成前保留冻结的计时,但属于临时结果:turn_end 变换后的最终请求 entry 替代其用量。活动进度采用紧凑快照;listsComplete: false 表示省略完整 ID/工具列表,并非实际没有这些项。计数仍然可用,各工具的 trace entry 单独保存,活动结束/中断汇总则包含完整列表,避免反复复制不断增长的列表。
完整保留会使会话文件及 Pi 的内存历史逐渐增长。崩溃可能丢失当前未刷新的采样批次、最终用量或最终活动汇总;已有 checkpoint 仍可用,但不能补造缺失数据。中断或缺失边界会标为部分记录。存储失败会停止周期采样,避免未保存的缓冲无限增长,并标记捕获不完整。SDK runtime 被释放后无法再保存最终汇总;SDK 调用者应先 await session.abort(),再 session.dispose()。Pi 1.1 自身也可能在直接中途释放时报告缺少 assistant 条目的边界错误。
指标中不重复保存提示词、回复/思考文本、工具参数/结果、凭据或 provider 原始载荷。Pi 原生转录仍保留正常的对话数据。模型 ID、工具名、时间戳、token 和费用可能敏感,分享前应检查会话。“完整”指全部已捕获测量和 provider 提供的标准化用量,不包括无法获得的模型内部推理。
历史、分支与 schema 1
最近性能及汇总跟随当前分支;token/费用按 Pi 的整个会话口径累计,包括废弃分支、压缩、分支摘要、缓存保温及已报告的嵌套工具用量。这些辅助工作不获得父会话对话请求计时,也不混入父会话 EFF TPS。显式接入的 agent 使用独立的分支内记录和覆盖率;仅用量的 subagent details 是可能与原生用量重叠的信息明细,不重复相加。Translator 收据小计采用整个会话范围,与原生总量分开,只覆盖该会话内实际存在的收据。
现有 schema 1 和 schema 2 entry 原样保留。读取时使用保存的 streamMs 重算请求速率,不使用旧的缓存 outputTps 或完整 responseMs。旧 schema 2 活动速率从关联请求的流时长重建;若请求缺失或不符合条件,活动速率保持不可用,不沿用完整请求时间口径的旧速率。无法重建 schema 1 活动分组/TTFT,或被旧版降采样丢弃的点。不为安装前的请求回填时间。恢复/导航不会重复创建请求记录;选到 assistant 条目但不包含其后的指标 entry 时,不包含该定稿测量。
开发与测试
在仓库根目录运行:
npm --prefix Pi/pi-metrics test
npm --prefix Pi/pi-metrics run typecheck
npm --prefix Pi/pi-metrics run test:cli
npm --prefix Pi/pi-metrics run test:package
(cd Pi/pi-metrics && npm pack --dry-run --ignore-scripts)
请在仓库检出目录中运行:npm tarball 有意不包含测试和开发脚本。test:package 验证 dry-run 及实际 tarball 的精确文件清单、公开发布元数据、仅由宿主提供的 peer 依赖及复制的 MIT 许可证,然后在检出目录之外解包,使用真实 SDK 和干净的临时配置加载包清单。通过合成 Translator 收据验证 /metrics 和 /metrics details,不执行安装,不调用 provider 或网络。打包使用离线 npm 和空的临时 npm 配置,不读取注册表凭据或真实 provider 设置。解包需要 tar(受支持的 macOS/Linux 测试宿主已提供)。
测试使用合成时钟、隔离的本地 HTTP provider、真实 Pi SDK 及已安装包声明的 bin.pi 可执行文件。完整测试包含仅在临时配置目录中的安装/移除。如果禁止执行安装命令,可运行不安装的子集;Node 参数必须位于文件列表之前(通过 npm test -- 追加参数不会过滤此脚本):
cd Pi/pi-metrics
node --test --test-skip-pattern='^real (CLI|fullscreen terminal|regular terminal)' test/*.test.mjs
这会排除四项原有的依赖安装操作的 CLI 测试,不代表完整套件通过。两项 one-run fullscreen/regular 测试仍通过 -e 验证加载、折叠 TTFT/TPS、鼠标/命令开关、缩放及正常退出,不执行安装。两种测试路径都不会安装依赖。
终端检查使用独立的 tmux server,不加载用户配置,并使用临时 HOME/agent/workspace/session 目录、环境变量白名单及离线模式。不使用付费模型、个人提示词或个人配置。配置隔离不是操作系统沙箱。
新增 agent 回归覆盖严格数值校验、并行/串行归属、未知/失败计时、推理子集、重复/冲突/过期快照、保存失败重试、按分支恢复/中断、没有计时的 details 用量、不重复计算原生用量及载荷隐私。真实 SDK 测试使用隔离的合成父会话/并行子会话和 localhost provider,包含子工具延迟、最终用量变换、数值总线投递及磁盘恢复。不运行随包附带的子进程 subagent 工具,也不对真实 provider 进行 benchmark。折叠页脚回归验证 TTFT/TPS 优先级、保留速率标记及按宽度显示上下文/模型/状态。Translator 回归使用合成收据,覆盖严格校验、三个方向、失败/重试、null 与零值、格式错误/重复/冲突数据、不安全求和、忽略旧聚合记录、全会话跨分支去重、只读命令、RPC/无页脚显示,以及原生用量/性能不变。真实 SDK fixture 在完全不调用 provider 的情况下检查磁盘恢复和命令显示,并将原生子 agent 总量与 Pi 自身会话统计比较。CLI fixture 用更高的视口检查加长的计量报告,再恢复原有 32 行页脚/缩放检查。
既有覆盖还包括跨工具/内容/新活动保留 EFF TPS、新估计替换、按分支恢复且不改变覆盖判定、静默窗口内 OBS TPS 隐藏及恢复、不受 TTFT 影响的 EFF TPS、缺失/零长度流、历史完整请求速率转换、隐藏推理计算、UI 隐藏思考、跨工具/续接保持活动 TTFT、并行/嵌套工具、无损分块、临时/最终用量、旧版历史、错误、中止、SDK 释放和会话恢复。页脚回归还覆盖三行/单行高度上限、Unicode 与长路径、已知空闲过滤且不改变其他扩展状态、警告优先级、高上下文提示、折叠的所有权/释放及详情隔离。真实 CLI 检查覆盖稳定副本安装/移除、JSON、fullscreen/dark 与 regular/light 终端、鼠标折叠/展开、regular 模式键盘切换、原生详情取消/提交且不发送编辑内容、缩放、页脚共存/开关、禁用参数、嵌套源码重载及正常退出。
Pi 不可用时,宿主测试明确跳过。终端测试还需要 macOS/Linux 上的 tmux。应报告跳过,不能宣称完整通过。SDK 查找支持托管安装、npm 嵌套依赖及 pnpm 风格的 SDK/依赖符号链接。这些布局使用合成文件系统 fixture 验证,不代表分别测试了不同安装发行形式。
类型检查需要宿主 SDK 和现有 TypeScript 编译器;缺少时会失败,不会静默跳过。可选路径覆盖:
PI_SDK_PATH="/absolute/path/to/@earendil-works/pi-coding-agent/dist/index.js" \
PI_TSC_PATH="/absolute/path/to/typescript/lib/tsc.js" \
npm --prefix Pi/pi-metrics run typecheck
PI_SDK_PATH 指向文件而非目录;PI_TSC_PATH 指向编译器 JavaScript 入口,而非 shell wrapper。运行时不需要这两个设置。类型检查不生成构建产物。
0.0.7 发布候选版验证(2026-10-28): 在 macOS、未经修改的官方 Pi 1.4.0 SDK、Node.js 26.12.0 和 tmux 3.8 下,最终不加过滤的 npm test 通过 95 项测试,零失败/跳过,包括全部四项安装/移除 CLI 测试,以及两项无需安装的单次加载终端测试。使用现有 TypeScript 5.9.3 编译器,通过严格 SDK 类型检查。较早的委派检查通过 91 项选定测试,把四项安装测试留给维护者;父任务的完整运行取代该部分结果。打包检查 2/2 通过,包含 npm pack --dry-run、无打包依赖的实际 12 文件 tarball,以及无 provider/网络调用的隔离清单加载和两个命令。npm/GitHub 发布和活跃安装启用是独立操作。本套件的 Translator 收据是合成 fixture,不是真实 provider 或生产者到父会话转发测试。
本机发布验证: 0.0.6 通过完整测试集 84 项测试,无跳过,包括在临时配置中执行的四项依赖安装操作的 CLI 测试、真实 SDK 子会话接入/工具 details 计量,以及隔离 tmux 的 fullscreen/regular 检查;环境为 macOS、Pi 1.4.0 SDK、Node.js 26.12.0、tmux 3.8。使用已存在的 TypeScript 5.9.3 编译器及文档中的 PI_TSC_PATH 覆盖参数,通过严格 SDK 类型检查。较早一次完整运行在本机同时运行其他测试集时,fullscreen 终端测试等待折叠页脚超时;随后的完整重跑及另外两次 test:cli 运行均通过。0.0.5 在 macOS、Pi 1.3.0、Node.js 26.12.0 下通过 69 项测试,无跳过,严格 TypeScript 检查通过,包含隔离 tmux 的鼠标/键盘/详情检查。0.0.4 在 Pi 1.2.0、Node.js 26.8.2、tmux 3.7c 下通过 62 项测试,无跳过,严格 TypeScript 检查通过。0.0.3 及以前的版本在 Pi 1.1.0 上验证。
验证边界: 本机检查不是 CI 或真实 provider benchmark。桌面字体/配色、任意第三方页脚组合及其他宿主版本/平台仍需单独验收。Fixture 验证的是计算和生命周期行为,不是 provider 报告的准确性或服务端解码速度。
卸载
对于固定 npm 版本示例:
pi remove npm:@orbit-labs-ai/pi-metrics@0.0.7
对于本地副本示例:
pi remove "$HOME/.local/share/orbit-tools/pi-metrics"
若来源/路径不同,使用实际注册项,再在空闲时重载或重启 Pi。单次加载则退出后不再传入 -e。npm 移除会取消注册并删除托管包;本地移除只取消路径注册,不删除工具副本目录。确认没有会话使用后才能删除该目录。会话历史及指标保留:卸载不改写会话、不清除用量/费用。删除会话是需要单独明确执行的操作。
许可证
MIT © 2026 Orbit Labs contributors。npm tarball 和稳定副本安装示例均在工具目录内包含仓库的 MIT 许可证。