[English](./README.md) | 日本語

# pi-metrics

実行が終わるたびに、メトリクス行を出力する Pi Coding Agents 拡張機能。

リポジトリ: [yuru7/my-pi-extensions](https://github.com/yuru7/my-pi-extensions)

```text
Worked for 1m 19.1s · TPS 227.8 tok/s · TTFT 2.0s · in 12.2K · out 1.7K · cost $1.234567
```

メトリクスは `agent_start` から `agent_settled` までを対象に収集するため、複数のターンやツール呼び出しを含む実行でも1行のみ表示されます。リトライ、Compaction、実行が settle する前にキューへ入ったフォローアップもすべて含まれます。

実行中は、エディタ上部にある Pi の working インジケータに `Working (5s)` のように経過時間がライブ表示されます。

## インストール

```bash
pi install npm:@yuru7/pi-metrics
```

## 設定

Pi のエージェントディレクトリ（既定では `~/.pi/agent/pi-metrics.json`。`PI_CODING_AGENT_DIR` やカスタマイズした設定ディレクトリも考慮されます）に `pi-metrics.json` を作成します。ファイルは省略可能で、なければ下記の既定フォーマットが使われます。

```json
{
  "format": "Worked for {elapsed} · TPS {tps} tok/s · TTFT {ttft} · in {input} · out {output}"
}
```

`format` 文字列では次のプレースホルダーが使えます:

| プレースホルダー | 意味 | 例 |
| --- | --- | --- |
| `{elapsed}` | プロンプト送信（`input`）から `agent_settled` まで | `1m 19.1s` |
| `{tps}` | 生成の出力トークン毎秒 | `227.8` |
| `{ttft}` | 計測可能な最初のターンでの初回トークンまでの時間 | `2.0s` |
| `{input}` | 全ターンの入力トークン合計 | `12.2K` |
| `{output}` | 全ターンの出力トークン合計 | `1.7K` |

プレースホルダーは複数回登場してもかまいません。`{foo}` のような未知のプレースホルダーはそのまま残るため、打ち間違いは目に見えたままになり、将来のプレースホルダーもそのまま動きます。`tps` と `ttft` は計測できないとき `n/a` と表示され、推定値の `tps` には先頭に `≈` が付きます（例: `≈66.7`）。フォーマット自体の形が変わることはありません。

末尾に付く **cost** セグメントはプレースホルダーではなく、`format` の一部でもありません。実行で正のコストが報告された場合、描画後の行に ` · cost $1.234567` が追加されます。コストを報告しないプロバイダーではゼロのままで、区切り文字も含めてセグメントごと省略されます。値は小数点以下6桁で表示されるため、1回あたりの小さなコストも確認できます。

```json
{ "format": "{elapsed} | {input} → {output} | {tps} tok/s" }
```

```text
23.7s | 8.1K → 1.3K | 181.4 tok/s
```

ファイルが存在しないのは正常で、何も表示されません。JSON が不正、または `format` が文字列でない場合は既定フォーマットにフォールバックし、警告を1回だけ表示します。どちらの場合も Pi は動作を続け、未知のフィールドは無視されます。

## working インジケータのライブ表示

実行中、エディタ上部の Pi のストリーミングインジケータには、メッセージを送信してからの経過時間が表示されます: `Working (5s)`、`Working (1m19s)`、`Working (1h2m3s)`。小数点や空白を伴わない整数秒で、経過した単位だけを使います。

タイマーはプロンプトの `input` イベントを起点とするため、認証待ちやプロンプト前のコンパクションも含まれ、実行が settle するまで毎秒更新されます。settle すると Pi の既定の working メッセージに戻ります。この値はメトリクス行と同様、TUI モードでのみ表示されます。

ライブ値と確定行の `{elapsed}` は同じ起点（プロンプトの `input` イベント）を共有します。ライブ値は毎秒更新して整数秒を表示し、確定値は `agent_settled` 時点の値を小数点以下1桁で表示します。

## 数値の意味

- **elapsed** はプロンプトを送信した時点（`input`）から始まるため、認証待ちやプロンプト前のコンパクションも含まれ、`agent_settled` で終わります。
- **cost** は、その実行で確定したアシスタントメッセージの `usage.cost.total` を合計した金額（米ドル）です。プロバイダーがそれらのメッセージについて報告するコストであり、別途の請求明細ではありません。コストを報告しないプロバイダーはゼロとして扱われ、実行の合計が正でない限り cost セグメントは表示されません。
- **input** / **output** は、その実行で確定したすべてのアシスタントメッセージの `usage` を合計したものです。ストリーム途中の部分的な更新がトークンとして加算されることはありません。
- **TTFT** はトークンの差分が観測された最初のターンを対象に、そのターンの開始から最初の差分までの時間を計測します。実行開始から最初のトークンまでの待ち時間ではありません。
- **TPS** は出力トークンのレートです。計測値は最初と最後のトークン差分の間（ストールを除く）を使うため、初回トークンまでの時間、ツール実行、リトライは含まれません。トークンの差分だけを数えるので、空のブロック開始・終了のストリームイベントで区間が広がることもありません。ストリーム内で 500 ms を超える間隔はストールとみなして除外します。1ターン全体が2つ未満の差分で流れた場合や、差分がその前の沈黙よりはるかに短いバーストで届いた場合（バッファリングまたは合流されたストリーム）は、その区間が生成時間を反映しないため、代わりにそのターンの応答時間（レイテンシを含む）を使い、値には先頭に `≈` を付けた推定値として表示します。出力トークンがない、または計測可能な時間がないターンは `n/a` になります。ターンをまたぐと、TPS は採用した出力トークンの合計を採用した生成時間の合計で割った値になります。短い区間はプロバイダーのストリーム分割の仕方に影響されることがあります。推定値が1つでも含まれる場合、集計値にも `≈` が付きます。

この行は標準テーマの `dim` 色を使い、背景はありません。セッションには何も書き込まれないため、セッションを再開しても以前のメトリクスが再生されることはありません。メトリクスと working インジケータのライブ表示は、非対話モード（`rpc`、`json`、`print`）では描画されません。状態はリセットされるため、次の実行はきれいに始まります。

## 謝辞

この拡張は [maplezzk/pi-extensions](https://github.com/maplezzk/pi-extensions) の [`pi-metrics`](https://github.com/maplezzk/pi-extensions/tree/main/packages/pi-metrics) にインスパイアされて作成しました。

## 開発

```bash
pnpm install
pnpm run check        # 型チェック + テスト
pnpm run package:check
```
