# 機能設計書 26-使用量追跡

## 概要

本ドキュメントは、OpenCodeにおける使用量追跡機能の設計を定義する。使用量追跡は、AIプロバイダーへのリクエストで消費されたトークン数とコストを計算・記録する機能である。

### 本機能の処理概要

使用量追跡機能は、AIモデルからのレスポンスに含まれる使用量情報を解析し、入力・出力・キャッシュ・推論トークン数を抽出してコストを計算する機能である。

**業務上の目的・背景**：AIサービスの利用コストを可視化し、ユーザーが使用状況を把握できるようにする。予算管理やコスト最適化の判断材料を提供する。

**機能の利用シーン**：AIが応答を生成するたびに使用量が計算され、セッション画面やダッシュボードに表示される。請求情報の算出にも使用される。

**主要な処理内容**：
1. AIプロバイダーからの使用量メタデータの取得
2. トークン数の分類（入力/出力/キャッシュ読み取り/キャッシュ書き込み/推論）
3. モデル固有の料金情報に基づくコスト計算
4. プロバイダー間の使用量カウント方法の差異吸収
5. 200Kトークン超過時の特別料金適用

**関連システム・外部連携**：各AIプロバイダーのSDK（Anthropic、OpenAI、Bedrock等）。models.devからの料金情報取得。

**権限による制御**：特別な権限制御はない。使用量情報はセッション情報の一部として管理。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 2 | セッション画面 | 補助機能 | セッション内のトークン使用量の追跡 |
| 14 | ワークスペースダッシュボード | 主機能 | Zen使用状況のグラフ表示、使用量セクション |
| 17 | 請求管理 | 主機能 | 使用量に基づく請求情報の表示 |

## 機能種別

計算処理 / データ集計

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| model | Provider.Model | Yes | 使用されたAIモデル情報 | Provider.Modelスキーマ |
| usage | LanguageModelV2Usage | Yes | AI SDKからの使用量情報 | AI SDKの型 |
| metadata | ProviderMetadata | No | プロバイダー固有のメタデータ | 任意のオブジェクト |

### 入力データソース

- AI SDKのレスポンス（usage、metadata）
- models.devのモデル料金情報（cost）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| cost | number | 計算されたコスト（USD） |
| tokens.total | number | 総トークン数 |
| tokens.input | number | 入力トークン数 |
| tokens.output | number | 出力トークン数 |
| tokens.reasoning | number | 推論トークン数 |
| tokens.cache.read | number | キャッシュ読み取りトークン数 |
| tokens.cache.write | number | キャッシュ書き込みトークン数 |

### 出力先

- アシスタントメッセージのcostフィールド
- アシスタントメッセージのtokensフィールド
- StepFinishPartのcost/tokensフィールド

## 処理フロー

### 処理シーケンス

```
1. 使用量情報の抽出
   ├─ usage.inputTokens
   ├─ usage.outputTokens
   ├─ usage.reasoningTokens
   ├─ usage.cachedInputTokens
   └─ metadata.anthropic.cacheCreationInputTokens等

2. プロバイダー別の調整
   └─ Anthropic/Bedrockはキャッシュトークンを除外済みのinputTokens
   └─ OpenRouter等はキャッシュトークンを含むinputTokens

3. 総トークン数の計算
   └─ Anthropic/Bedrockは手動計算、その他はtotalTokensを使用

4. コストの計算
   ├─ 200K超過時は特別料金を適用
   ├─ 入力トークン × 入力料金
   ├─ 出力トークン × 出力料金
   ├─ キャッシュ読み取り × キャッシュ読み取り料金
   ├─ キャッシュ書き込み × キャッシュ書き込み料金
   └─ 推論トークン × 出力料金（暫定）

5. 結果の返却
```

### フローチャート

```mermaid
flowchart TD
    A[開始] --> B[使用量情報取得]
    B --> C{プロバイダー判定}
    C -->|Anthropic/Bedrock| D[キャッシュトークン別カウント]
    C -->|その他| E[キャッシュトークン含むinputTokens調整]
    D --> F[総トークン数計算]
    E --> F
    F --> G{200K超過?}
    G -->|Yes| H[特別料金を適用]
    G -->|No| I[通常料金を適用]
    H --> J[コスト計算]
    I --> J
    J --> K[Decimal.jsで精密計算]
    K --> L[結果返却]
    L --> M[終了]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-26-01 | キャッシュトークン除外 | Anthropic/Bedrockはキャッシュトークンを除外してinputTokensを調整 | 該当プロバイダー使用時 |
| BR-26-02 | 200K超過料金 | 入力+キャッシュ読み取りが200Kを超える場合、experimentalOver200K料金を適用 | 大規模コンテキスト使用時 |
| BR-26-03 | 推論トークン料金 | 推論トークンは出力料金と同じレートで課金（暫定） | 推論モデル使用時 |
| BR-26-04 | 無効値の安全処理 | NaN/Infinity等は0として扱う | 常時 |
| BR-26-05 | 料金単位 | 料金は100万トークンあたりのUSD（models.dev形式） | 常時 |

### 計算ロジック

コスト計算の詳細:
```typescript
// 料金選択（200K超過チェック）
const costInfo =
  model.cost?.experimentalOver200K &&
  tokens.input + tokens.cache.read > 200_000
    ? model.cost.experimentalOver200K
    : model.cost

// コスト計算（Decimal.jsで精度保証）
cost = new Decimal(0)
  .add(new Decimal(tokens.input).mul(costInfo?.input ?? 0).div(1_000_000))
  .add(new Decimal(tokens.output).mul(costInfo?.output ?? 0).div(1_000_000))
  .add(new Decimal(tokens.cache.read).mul(costInfo?.cache?.read ?? 0).div(1_000_000))
  .add(new Decimal(tokens.cache.write).mul(costInfo?.cache?.write ?? 0).div(1_000_000))
  .add(new Decimal(tokens.reasoning).mul(costInfo?.output ?? 0).div(1_000_000))
  .toNumber()
```

## データベース操作仕様

### 操作別データベース影響一覧

使用量追跡機能自体はDBを直接操作しない。計算結果はメッセージ情報の一部として保存される。

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| （間接的） | message | UPDATE | AssistantMessage.cost、AssistantMessage.tokensに保存 |
| （間接的） | part | UPDATE | StepFinishPart.cost、StepFinishPart.tokensに保存 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | なし | 無効な数値（NaN/Infinity） | safe関数で0に変換 |

### リトライ仕様

リトライ機構は不要（同期的な計算処理のため）。

## トランザクション仕様

トランザクションは不要（純粋な計算処理）。

## パフォーマンス要件

- 使用量計算は1ms以内に完了すること
- Decimal.jsによる精度保証

## セキュリティ考慮事項

- コスト情報はユーザーに表示されるのみ
- 料金情報はmodels.devから取得（信頼できるソース）

## 備考

- 推論トークンの料金計算は暫定実装（TODOコメントあり）
- Venice等のカスタムプロバイダーのキャッシュトークンにも対応

---

## コードリーディングガイド

本機能を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: データ構造を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | message-v2.ts | `packages/opencode/src/session/message-v2.ts` | AssistantMessage.cost、AssistantMessage.tokens定義（421-431行目） |
| 1-2 | message-v2.ts | `packages/opencode/src/session/message-v2.ts` | StepFinishPart.cost、StepFinishPart.tokens定義（241-258行目） |
| 1-3 | provider.ts | `packages/opencode/src/provider/provider.ts` | Provider.Model.cost定義（630-647行目） |

**読解のコツ**: トークン情報とコスト情報の構造を理解し、モデル料金定義との対応を把握する。

#### Step 2: メイン処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | index.ts | `packages/opencode/src/session/index.ts` | Session.getUsage関数（771-848行目） |

**主要処理フロー**:
1. **778-781行目**: safe関数 - 無効値を0に変換
2. **782-784行目**: 基本トークン数の抽出
3. **786-794行目**: キャッシュトークンの取得（プロバイダー固有メタデータ対応）
4. **800-803行目**: Anthropic/Bedrockのキャッシュトークン除外判定
5. **805-816行目**: 総トークン数の計算
6. **829-846行目**: コスト計算（Decimal.jsによる精密計算）

#### Step 3: 料金情報を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | models.ts | `packages/opencode/src/provider/models.ts` | ModelsDev.Model.cost定義（37-52行目） |
| 3-2 | provider.ts | `packages/opencode/src/provider/provider.ts` | fromModelsDevModel関数（679-744行目） |

### プログラム呼び出し階層図

```
Session.getUsage (public API)
    │
    ├─ 入力検証
    │      ├─ model: Provider.Model
    │      ├─ usage: LanguageModelV2Usage
    │      └─ metadata: ProviderMetadata
    │
    ├─ safe() ヘルパー
    │      └─ NaN/Infinityを0に変換
    │
    ├─ トークン抽出
    │      ├─ inputTokens
    │      ├─ outputTokens
    │      ├─ reasoningTokens
    │      ├─ cachedInputTokens
    │      └─ cacheCreationInputTokens (メタデータから)
    │
    ├─ プロバイダー判定
    │      └─ excludesCachedTokens判定
    │
    ├─ 調整済みinputTokens計算
    │      └─ excludesCachedTokens ? inputTokens : inputTokens - cache
    │
    ├─ 総トークン計算
    │      └─ Anthropic/Bedrockは手動計算
    │
    └─ コスト計算
           ├─ 200K超過チェック
           ├─ Decimal.js使用
           └─ 各トークン種別 × 料金 / 1,000,000
```

### データフロー図

```
[入力]                    [処理]                         [出力]

model.cost ─────┐                                    ┌─> cost (USD)
                │                                    │
usage ──────────┼──▶ Session.getUsage() ─────────────┼─> tokens.total
                │           │                        ├─> tokens.input
metadata ───────┘           │                        ├─> tokens.output
                            │                        ├─> tokens.reasoning
                            ├─ トークン分類           ├─> tokens.cache.read
                            │                        └─> tokens.cache.write
                            ├─ プロバイダー調整
                            │
                            └─ Decimal.js計算
                                   │
                                   └─ 200K超過判定
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| index.ts | `packages/opencode/src/session/index.ts` | ソース | Session.getUsage関数の実装 |
| message-v2.ts | `packages/opencode/src/session/message-v2.ts` | ソース | AssistantMessage、StepFinishPartの型定義 |
| provider.ts | `packages/opencode/src/provider/provider.ts` | ソース | Provider.Modelの定義、料金情報 |
| models.ts | `packages/opencode/src/provider/models.ts` | ソース | models.devからの料金情報取得 |
