@modular-prompt/extract 0.3.0 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. package/README.md +84 -39
  2. package/dist/cache-lifecycle.d.ts +3 -1
  3. package/dist/cache-lifecycle.d.ts.map +1 -1
  4. package/dist/cache-lifecycle.js +21 -0
  5. package/dist/cache-lifecycle.js.map +1 -1
  6. package/dist/cli/add-command.d.ts.map +1 -1
  7. package/dist/cli/add-command.js +2 -1
  8. package/dist/cli/add-command.js.map +1 -1
  9. package/dist/cli/args.d.ts +3 -1
  10. package/dist/cli/args.d.ts.map +1 -1
  11. package/dist/cli/args.js +15 -1
  12. package/dist/cli/args.js.map +1 -1
  13. package/dist/cli/create-command.d.ts +2 -0
  14. package/dist/cli/create-command.d.ts.map +1 -1
  15. package/dist/cli/create-command.js +12 -5
  16. package/dist/cli/create-command.js.map +1 -1
  17. package/dist/cli/extract-command.d.ts.map +1 -1
  18. package/dist/cli/extract-command.js +9 -3
  19. package/dist/cli/extract-command.js.map +1 -1
  20. package/dist/cli/list-command.d.ts +1 -0
  21. package/dist/cli/list-command.d.ts.map +1 -1
  22. package/dist/cli/list-command.js +3 -1
  23. package/dist/cli/list-command.js.map +1 -1
  24. package/dist/cli/manifest.d.ts +11 -0
  25. package/dist/cli/manifest.d.ts.map +1 -1
  26. package/dist/cli/manifest.js +16 -0
  27. package/dist/cli/manifest.js.map +1 -1
  28. package/dist/cli/store.js +1 -1
  29. package/dist/cli/store.js.map +1 -1
  30. package/dist/cli.js +18 -14
  31. package/dist/cli.js.map +1 -1
  32. package/dist/create-extract-runtime.d.ts +22 -0
  33. package/dist/create-extract-runtime.d.ts.map +1 -0
  34. package/dist/create-extract-runtime.js +27 -0
  35. package/dist/create-extract-runtime.js.map +1 -0
  36. package/dist/create-extract-session.d.ts.map +1 -1
  37. package/dist/create-extract-session.js +4 -1
  38. package/dist/create-extract-session.js.map +1 -1
  39. package/dist/create-mlx-extract-runtime.d.ts +18 -7
  40. package/dist/create-mlx-extract-runtime.d.ts.map +1 -1
  41. package/dist/create-mlx-extract-runtime.js +11 -1
  42. package/dist/create-mlx-extract-runtime.js.map +1 -1
  43. package/dist/create-pytorch-extract-runtime.d.ts +13 -0
  44. package/dist/create-pytorch-extract-runtime.d.ts.map +1 -0
  45. package/dist/create-pytorch-extract-runtime.js +52 -0
  46. package/dist/create-pytorch-extract-runtime.js.map +1 -0
  47. package/dist/default-models.d.ts +3 -8
  48. package/dist/default-models.d.ts.map +1 -1
  49. package/dist/default-models.js +4 -16
  50. package/dist/default-models.js.map +1 -1
  51. package/dist/extract-runtime-types.d.ts +16 -0
  52. package/dist/extract-runtime-types.d.ts.map +1 -0
  53. package/dist/extract-runtime-types.js +2 -0
  54. package/dist/extract-runtime-types.js.map +1 -0
  55. package/dist/extract-store.d.ts +21 -2
  56. package/dist/extract-store.d.ts.map +1 -1
  57. package/dist/extract-store.js +49 -10
  58. package/dist/extract-store.js.map +1 -1
  59. package/dist/index.d.ts +5 -0
  60. package/dist/index.d.ts.map +1 -1
  61. package/dist/index.js +2 -0
  62. package/dist/index.js.map +1 -1
  63. package/dist/model-resolution.d.ts +15 -5
  64. package/dist/model-resolution.d.ts.map +1 -1
  65. package/dist/model-resolution.js +87 -27
  66. package/dist/model-resolution.js.map +1 -1
  67. package/dist/types.d.ts +6 -0
  68. package/dist/types.d.ts.map +1 -1
  69. package/docs/API.md +316 -0
  70. package/docs/CACHE_DESIGN.md +475 -0
  71. package/docs/LOCAL_MODEL_SETUP.md +765 -0
  72. package/docs/PROMPT_MODULE_SPEC.md +482 -0
  73. package/package.json +8 -6
  74. /package/bin/{modular-extract.js → modular-prompt-extract.js} +0 -0
@@ -0,0 +1,475 @@
1
+ # プロンプトキャッシュ設計
2
+
3
+ プロンプトキャッシュシステムの設計思想とキャッシュライフサイクル管理の仕様。
4
+
5
+ ## 目次
6
+
7
+ - [概要](#概要)
8
+ - [対象読者](#対象読者)
9
+ - [PromptCacheControllerインターフェース](#promptcachecontrollerインターフェース)
10
+ - [CacheHandle](#cachehandle)
11
+ - [retain / release ヒント機構](#retain--release-ヒント機構)
12
+ - [実装](#実装)
13
+ - [MlxCacheController](#mlxcachecontroller)
14
+ - [PyTorchCacheController](#pytorchcachecontroller)
15
+ - [GoogleGenAICacheController](#googlegenaicachecontroller)
16
+ - [ファイルロック機構](#ファイルロック機構)
17
+ - [Incremental Prefillとsupersedes](#incremental-prefillとsupersedes)
18
+ - [QueryResult.usage との関係](#queryresultusage-との関係)
19
+ - [関連ドキュメント](#関連ドキュメント)
20
+
21
+ ## 概要
22
+
23
+ PromptCacheControllerは、プロンプトキャッシュのライフサイクルを管理するインターフェースです。キャッシュの準備・再利用・削除を統一的に扱い、各AIサービスの特性に応じた実装を提供します。
24
+
25
+ 対応実装:
26
+ - **MlxCacheController** - KVキャッシュファイルを管理(Apple Silicon最適化)
27
+ - **PyTorchCacheController** - cpu-minimal の KV キャッシュファイルと CUDA の process-local KV cache を管理
28
+ - **GoogleGenAICacheController** - GoogleGenAI APIのキャッシュ機能を管理
29
+
30
+ ## 対象読者
31
+
32
+ - **フレームワーク利用者** - PromptCacheControllerを使ってキャッシュを活用する開発者
33
+ - **フレームワーク貢献者** - キャッシュコントローラーを実装する開発者
34
+
35
+ ## PromptCacheControllerインターフェース
36
+
37
+ ```typescript
38
+ export interface PromptCacheController {
39
+ recordQuery?(): void;
40
+ prepare(params: CachePrepareParams): Promise<CacheHandle>;
41
+ release(ref: string): void;
42
+ close(): Promise<void>;
43
+ }
44
+ ```
45
+
46
+ ### prepare(params)
47
+
48
+ キャッシュを準備し、`CacheHandle`を返します。同一パラメータの場合は既存キャッシュを再利用します。
49
+
50
+ ```typescript
51
+ export interface CachePrepareParams {
52
+ model: string;
53
+ instructions?: Element[];
54
+ data?: Element[];
55
+ tools?: ToolDefinition[];
56
+ reasoningEffort?: 'low' | 'medium' | 'high';
57
+ /** VLM image sources belonging to the cacheable prefix. */
58
+ images?: string[];
59
+ /** Maximum image edge used for VLM preprocessing. */
60
+ maxImageSize?: number;
61
+ }
62
+ ```
63
+
64
+ **動作**:
65
+ - 同じパラメータで呼ばれた場合、メモリまたはディスクから既存キャッシュを返す
66
+ - 新規の場合、キャッシュを作成してCacheHandleを返す
67
+ - incremental prefillが可能な場合、既存キャッシュをベースに差分のみをprefill
68
+
69
+ **戻り値**: `Promise<CacheHandle>`
70
+
71
+ ### release(ref)
72
+
73
+ 「もう要らない」というヒントを送ります。即座の削除を保証しません。
74
+
75
+ **動作**:
76
+ - メモリキャッシュから即座に除外(再利用候補から外れる)
77
+ - ファイルやAPIリソースの削除タイミングは実装依存
78
+ - MlxCacheController / PyTorchCacheController: `close()`時にrelease済みエントリを削除
79
+ - GoogleGenAICacheController: 即座にAPIサーバー側リソースを削除
80
+
81
+ **パラメータ**:
82
+ - `ref` - CacheHandle.refの値
83
+
84
+ ### close()
85
+
86
+ リソースのクリーンアップを行います。
87
+
88
+ **動作**:
89
+ - インフライトリクエストの完了を待つ
90
+ - 管理対象キャッシュの削除
91
+ - release済みエントリの実際の削除(MlxCacheController / PyTorchCacheController)
92
+
93
+ **戻り値**: `Promise<void>`
94
+
95
+ ### recordQuery() (オプション)
96
+
97
+ クエリ統計の記録。ドライバーがquery実行時に呼び出します。
98
+
99
+ ## CacheHandle
100
+
101
+ キャッシュの参照と、キャッシュに含まれる内容を示すメタデータ。
102
+
103
+ ```typescript
104
+ export interface CacheHandle {
105
+ ref: string;
106
+ trimTokens?: number;
107
+ includes: {
108
+ instructions: boolean;
109
+ dataElementCount: number;
110
+ tools: boolean;
111
+ };
112
+ supersedes?: string;
113
+ }
114
+ ```
115
+
116
+ ### フィールド
117
+
118
+ **ref**
119
+
120
+ キャッシュの一意な参照。
121
+ - MlxCacheController (LM): ファイルパス(例: `/tmp/mlx-prompt-cache-abc123/def456.safetensors.zip`)
122
+ - MlxCacheController (VLM): `mlx-vlm` exact snapshot のファイルパス(text-only の例: `/tmp/mlx-prompt-cache-abc123/def456.vlm.safetensors/exact_<hash>.safetensors`、画像ありは `.vlm-vision.safetensors` namespace)
123
+ - PyTorchCacheController (cpu-minimal LM): backend 固有のファイルパス(例: `/tmp/pytorch-prompt-cache-abc123/def456.pytorch-cache`)
124
+ - PyTorchCacheController (CUDA LM): Python process-local の `memory://` ref
125
+ - GoogleGenAICacheController: API名(例: `cachedContents/xyz789`)
126
+
127
+ #### mlx-vlm 0.7.0(Phase 2–3)
128
+
129
+ VLM の text-only ref はディスク上の `exact_cache_v1` snapshot です。作成元 Python process の終了・再起動後も、固定 `cacheDir` の `cache-index.json` に記録された実体(`cacheDir` 相対 path)からロードできます。相対 path にすることで、extract の staging directory を rename しても index を再利用できます。`mlx-vlm-memory://` は Phase 1 互換の明示 ref に限った process-local fallback であり、MlxCacheController や extract の主経路では使用しません。
130
+
131
+ - `make_prompt_cache(model.language_model, max_kv_size=None)` で空の prompt cache を生成
132
+ - `stream_generate(..., prompt_cache=cache)` で prefill / cached suffix generation に cache を渡す
133
+ - `apc_adapters.clone_cache_entry(entry, *, min_capacity_tokens, eval_targets)` で generation 前に cache entry を複製
134
+
135
+ ディスク保存には 0.7.0 の `DiskBlockStore` を使います。論理 cache path を専用 namespace として `DiskBlockStore(root, namespace, num_workers=1)` を開き、prefill 後に次を呼びます。
136
+
137
+ - `save_exact_cache(cache_hash, token_ids, extra_hash, prompt_cache)` — cache 全体を非同期 snapshot として保存
138
+ - `close()` — writer queue を drain して保存完了を確定
139
+ - `load_exact_cache(cache_hash)` — `exact_<hash>.safetensors` を復元
140
+
141
+ `APCManager.store_exact_cache()` / `lookup_exact_cache()` も調査しましたが、これは APC のメモリ LRU・prefix lookup と連動する API です。Phase 2–3 は TypeScript controller が完全一致キーを管理し、VLM incremental prefill を行わないため、backend では直接 `DiskBlockStore.save_exact_cache` / `load_exact_cache` を採用しています。保存形式の metadata は `layout: exact_cache_v1`、`cache_hash`、`extra_hash`、`token_ids`、cache entry 数などです。
142
+
143
+ VLM の text-only 論理 path が `/cache/<key>.vlm.safetensors` の場合、実体は `/cache/<key>.vlm.safetensors/exact_<hash>.safetensors`、sidecar は実体 path に `.meta.json` を付けた `/cache/<key>.vlm.safetensors/exact_<hash>.safetensors.meta.json` です。sidecar には LM と同じ `token_count`、`prefix_offsets`、`prefix_hashes` を保存し、さらに backend 固有の `layout: exact_cache_v1` / `cache_hash` を持ちます。load 時は sidecar の `cache_hash` から導出した exact snapshot path と実際の ref を照合し、mismatched sidecar、snapshot 不在、破損 snapshot は cache load failure として cold path に落とします。`.vlm.safetensors` は APC namespace directory の名前であり、実体の拡張子は `.safetensors` です。
144
+
145
+ LM の `.safetensors.zip`(zip 内 `prompt_cache.safetensors`)と VLM の snapshot は別形式で、相互に読み込みません。
146
+
147
+ ##### 画像あり VLM(Phase 3)
148
+
149
+ 画像を含む cacheable prefix は、text-only VLM とは別の `/cache/<key>.vlm-vision.safetensors/` namespace に保存します。実体の snapshot codec は mlx-vlm 0.7.0 の `DiskBlockStore.save_exact_cache()` ですが、modular-prompt の `vision_cache_v1` sidecar と namespace を含む保存契約は text-only の `exact_cache_v1` と非互換です。LM の `.safetensors.zip` とも非互換です。text-only ref を画像付き query に、画像付き ref を text-only query に渡した場合は load を拒否して cold path に戻します。
150
+
151
+ 画像付き snapshot の sidecar には次を保存します(token IDs 本体は DiskBlockStore の exact snapshot metadata に保存します)。
152
+
153
+ - `layout: vision_cache_v1`、`backend: mlx-vlm`、`cache_hash`、`token_count`
154
+ - APC exact snapshot に渡した `extra_hash` と表示用の `image_hash`
155
+ - `image_count`、`image_refs`、`max_image_size`
156
+ - `vision_feature_cache_version: mlx-vlm-0.7.0`
157
+
158
+ `MlxVlmBackend` は mlx-vlm 0.7.0 の `VisionFeatureCache` を process-local に保持し、`stream_generate(..., vision_cache=...)` を通じて upstream が `cached_image_features` をモデルへ渡す経路を使います。0.7.0 の upstream PIL key は `tobytes()` のみなので、backend は wrapper を挟み、mode・寸法・bytes と画像列 index を含む digest string を upstream cache に渡します。これにより同じ bytes 長でも mode / 寸法が異なる画像の feature が誤共有されません。永続化するのは prompt/KV exact snapshot と sidecar の同一性情報であり、opaque な MLX の projected feature tensor 自体は保存しません。プロセス再起動後は画像を再処理して feature cache を再構築します。
159
+
160
+ 画像同一性は、`load_and_resize_images()` 後の正規化済み PIL payload(mode、幅・高さ、bytes)を hash して `extra_hash` にします。mlx-vlm dispatch 前の pixel tensor は backend から直接取得できないため、prefill と load が同じ modular-prompt 側の正規化入力を検証できる設計にしています。これにより、同一 token 列でも画像が異なる場合は別の `extra_hash` / controller key になり、resize 条件が異なる場合も cache miss になります。load 時は sidecar の layout、hash、画像数、resize 条件、feature cache version、exact snapshot の token 数と `extra_hash` を検証し、さらに保存済み token IDs が現行 prompt の prefix と一致することを確認します。失敗時は `cache_loaded: false` の cold path です。
161
+
162
+ 画像付き VLM は新規 prompt の fresh prefill と exact load に限定します。`base_cache_path`、`trim_to_tokens`、VLM incremental prefill / prefix reuse は本 Phase でも対象外です。
163
+
164
+ 依存関係では 0.7.0 が `mlx>=0.32.2`、`mlx-audio>=0.4.8`、`jinja2>=3.1.0` を要求するため、lock file は `mlx-audio==0.5.3` として解決しています。`mlx`、`mlx-lm`、`transformers` の既存 pin / override は維持しています。
165
+
166
+ **trimTokens**
167
+
168
+ KVキャッシュを指定トークン数にトリムして読み込む(incremental prefill用)。
169
+
170
+ - 指定時、キャッシュファイルの先頭N個のトークンのみを使用
171
+ - incremental prefillでベースキャッシュとの共通プレフィックス長を指定
172
+
173
+ **includes**
174
+
175
+ キャッシュに含まれる内容のフラグ。ドライバーが重複コンテンツ送信を避けるために使用。
176
+
177
+ - `instructions` - システムプロンプト等が含まれるか
178
+ - `dataElementCount` - データ要素の個数
179
+ - `tools` - ツール定義が含まれるか
180
+
181
+ **supersedes**
182
+
183
+ incremental prefillで置き換えられた元キャッシュのref。
184
+
185
+ - 新しいキャッシュ作成時にベースとして使われた古いキャッシュを示す
186
+ - このフィールドが設定されると、元キャッシュは自動的に`release()`される
187
+
188
+ ## retain / release ヒント機構
189
+
190
+ キャッシュの保守に「ヒント(意図表明)モデル」を採用しています。
191
+
192
+ ### 設計の背景
193
+
194
+ キャッシュは「あってもなくてもよい」性質を持ちます。この特性から:
195
+
196
+ - **作成は自動的** - 必要に応じてフレームワークが自動生成
197
+ - **削除を利用側に明示的に設計させるのは非対称で負担が大きい**
198
+ - **利用側は「もう要らない」という意図を伝えるだけでよい**
199
+ - **実際の削除タイミングはコントローラーの責務**
200
+
201
+ ### 2つの状態
202
+
203
+ **retain(デフォルト)**
204
+
205
+ キャッシュを保持する状態。`prepare()`の再利用候補となります。
206
+
207
+ **release**
208
+
209
+ 「もう要らない」というヒント。以下の効果があります:
210
+
211
+ - メモリキャッシュから即座に除外される
212
+ - `prepare()`の再利用候補から外れる
213
+ - ファイルやAPIリソースの削除タイミングは実装依存
214
+
215
+ ### release()を呼んでも
216
+
217
+ **MlxCacheController / PyTorchCacheController**:
218
+ - ファイルは即座に削除されない
219
+ - `cache-index.json`のエントリに`hint: 'release'`が記録される
220
+ - `close()`時にrelease済みエントリのファイルが削除される
221
+ - 外部プロセス(`sprite-claude cache clean`等)もrelease済みエントリを削除対象にできる
222
+
223
+ **GoogleGenAICacheController**:
224
+ - APIサーバー側リソースが即座に削除される(課金対象の可能性があるため)
225
+
226
+ ## 実装
227
+
228
+ ### MlxCacheController
229
+
230
+ Apple Siliconに最適化されたMLXモデル用のKVキャッシュ管理。
231
+
232
+ **特徴**:
233
+ - LM は `.safetensors.zip`形式でKVキャッシュをファイル保存(zip内エントリは`prompt_cache.safetensors`)
234
+ - LM の保存時はsafetensorsの出力ストリームをzipエントリへ直接渡し、非圧縮ファイルを作成しない
235
+ - LM は既存の非圧縮`.safetensors`キャッシュを読み込まない
236
+ - incremental prefillサポート(既存キャッシュをベースに差分のみprefill)
237
+ - トークンレベルのプレフィックス照合(prefix_hashes)
238
+ - 固定キャッシュディレクトリモードとmanaged一時ディレクトリモード
239
+ - VLM は text-only の `exact_cache_v1` と画像付きの `vision_cache_v1` を専用 namespace へ保存
240
+ - VLM の画像 feature tensor は process-local、incremental prefill と LM cache との相互利用は対象外
241
+
242
+ **キャッシュディレクトリモード**:
243
+
244
+ | モード | `managedDir` | `cacheDir` | 説明 |
245
+ |--------|-------------|-----------|------|
246
+ | 一時ディレクトリ | `true` | 未指定 | プロセス終了時に自動削除 |
247
+ | 固定ディレクトリ | `false` | 指定あり | `close()`でrelease済みのみ削除。`cache-index.json`で状態管理 |
248
+
249
+ **cache-index.json**:
250
+
251
+ 固定ディレクトリモード時、以下の情報を記録:
252
+
253
+ ```typescript
254
+ interface CacheIndexEntry {
255
+ key: string;
256
+ model: string;
257
+ formatterOptionsHash: string;
258
+ elementHashes: string[];
259
+ toolsHash?: string;
260
+ reasoningEffort?: string;
261
+ createdAt: string;
262
+ hint?: 'retain' | 'release';
263
+ /** キャッシュ形式の backend(省略時は旧 LM エントリ) */
264
+ backend?: 'lm' | 'vlm' | 'pytorch';
265
+ /** backend cache の cacheDir 相対 path(必要な backend のみ) */
266
+ path?: string;
267
+ }
268
+ ```
269
+
270
+ PyTorch の index の `backend: 'pytorch'` は cache 形式だけを示し、runtime variant / device は区別しません。そのため固定 `cacheDir` は一つの `cpu-minimal` runtime または CUDA runtime 専用とし、CPU と CUDA で共有することは禁止します。runtime ごとに別の固定ディレクトリを指定してください。CUDA はそもそも cache ファイルを作らず process-local registry を使用するため、プロセス再起動後の disk hit はありません。
271
+
272
+ **incremental prefillフロー**:
273
+
274
+ 1. 新しい`prepare()`呼び出し
275
+ 2. `findBestBase()` - 要素ハッシュの前方一致でベースキャッシュを選定
276
+ 3. トークンレベルのプレフィックス照合(prefix_hashes)で共通トークン数を確認
277
+ 4. ベースキャッシュのKV値を再利用し、差分のみprefill
278
+ 5. 新キャッシュの`supersedes`にベースキャッシュのrefを記録
279
+ 6. ベースキャッシュを自動的に`release()`
280
+
281
+ ### TransformersLmBackend(PyTorch)
282
+
283
+ PyTorch の Transformers LM は、MLX とは互換でない backend 固有の
284
+ `pytorch_kv_v1` 形式を使用します。保存形態は runtime variant により異なります。
285
+
286
+ - `cpu-minimal` は cache 本体を `torch.save` の payload として指定された cache path に保存
287
+ - `cpu-minimal` の payload には KV state と、load 時の prefix 検証に使う token IDs を保存
288
+ - `cpu-minimal` の `<cache path>.meta.json` には `layout`、`token_count`、`prefix_offsets`、
289
+ `prefix_hashes`、`model_id`、`dtype`、`device` を保存
290
+ - `cpu-minimal` の load 時に layout、token 数、prompt prefix、model ID、dtype、device を検証し、
291
+ 不一致・破損・欠損は cache miss として cold path に戻す
292
+ - `cpu-minimal` で `base_cache_path` と `trim_to_tokens` を指定した場合は、base cache を clone・trim
293
+ して suffix だけを prefill し、新しい cache と meta を保存
294
+ - CUDA は `memory://` 相当の process-local registry に KV state を保持し、ファイルを作成しない。
295
+ incremental prefill / prefix metadata を拒否するため controller は plain prefill にフォールバックし、
296
+ プロセス終了・restart 後は cache miss になる
297
+
298
+ PyTorch の cache payload は Transformers の legacy tuple と `Cache` の KV layer を
299
+ 扱います。`Cache` の trim は clone に対して論理 token 数を更新し、static cache の容量は
300
+ 維持します。元の cache ref は変更されません。
301
+ PyTorch の cache は MLX / provider 間で共有しません。
302
+
303
+ #### PyTorchCacheController
304
+
305
+ `PyTorchCacheController` は上記の `cache_prefill` / `generate` 契約を
306
+ `PromptCacheController` に適合させ、要素・tools・formatter・reasoning の組み合わせから
307
+ cache key を作ります。`PyTorchDriver` の `cacheController` に渡すと、
308
+ `LocalInferenceDriver` の `onCapabilitiesLoaded` 後に backend process へ bind されます。
309
+
310
+ - `cpu-minimal` の cache 本体は `<key>.pytorch-cache`、sidecar は `.meta.json` とし、index には
311
+ `backend: 'pytorch'` を記録する。CUDA はファイルを作らず process-local ref を使う
312
+ - 固定 `cacheDir` では index の相対 path とファイルロックを使い、release 済みの
313
+ `cpu-minimal` entry だけを `close()` 時に削除する。固定 `cacheDir` を CPU/CUDA 間で共有してはならない
314
+ - managed directory(`cacheDir` 未指定)は process 終了時に一時ディレクトリを削除する
315
+ - CPU/minimal backend の prefix metadata が利用できる場合は、要素と token prefix を照合して
316
+ incremental prefill を行う。CUDA backend はこの metadata と incremental prefill を拒否するため plain
317
+ prefill にフォールバックする。CUDA の cache は restart 後に再利用しない
318
+ - VLM / 画像入力では controller を bind せず、cache を無効にする
319
+
320
+ ### GoogleGenAICacheController
321
+
322
+ GoogleGenAI APIのキャッシュ機能を管理。
323
+
324
+ **特徴**:
325
+ - APIサーバー側でキャッシュを管理
326
+ - TTL(有効期限)ベースの自動削除
327
+ - release時に即座にサーバー側リソースを削除
328
+
329
+ **設定**:
330
+
331
+ ```typescript
332
+ interface GoogleGenAICacheControllerConfig {
333
+ ttl?: string; // デフォルト: '3600s'
334
+ displayName?: string;
335
+ }
336
+ ```
337
+
338
+ **TTL管理**:
339
+ - キャッシュ作成時にTTLを指定
340
+ - ローカルで期限切れキャッシュを掃除(`sweepExpired()`)
341
+ - サーバー側でも自動削除される
342
+
343
+ ## ファイルロック機構
344
+
345
+ MlxCacheControllerは、固定キャッシュディレクトリモード(`managedDir: false`)時に`cache-index.json`の読み書きに対してファイルロックを使用します。
346
+
347
+ ### 目的
348
+
349
+ - 同一マシン上で複数プロセスが同じキャッシュディレクトリを共有する場合の安全性確保
350
+ - 外部プロセス(`sprite-claude cache clean`等)からの安全なキャッシュ操作
351
+
352
+ ### 実装
353
+
354
+ `proper-lockfile`ライブラリによるアドバイザリロックを使用:
355
+
356
+ ```typescript
357
+ import { lock as lockFile } from 'proper-lockfile';
358
+
359
+ // 読み込み時
360
+ const release = await lockFile(this.indexPath, { realpath: false });
361
+ try {
362
+ const raw = await readFile(this.indexPath, 'utf-8');
363
+ // ... parse and use
364
+ } finally {
365
+ await release();
366
+ }
367
+
368
+ // 書き込み時
369
+ const release = await lockFile(this.indexPath, { realpath: false });
370
+ try {
371
+ await writeFile(this.indexPath, JSON.stringify(this.cacheIndex, null, 2));
372
+ } finally {
373
+ await release();
374
+ }
375
+ ```
376
+
377
+ ### 対象
378
+
379
+ - **固定ディレクトリモード**(`managedDir: false`)のみ
380
+ - **一時ディレクトリモード**(`managedDir: true`)はプロセス間共有がないためロック不要
381
+
382
+ ## Incremental Prefillとsupersedes
383
+
384
+ MlxCacheController と PyTorchCacheController(LM)は、既存キャッシュをベースに差分のみをprefillする「incremental prefill」をサポートします。PyTorch の CUDA runtime のように backend が incremental metadata を受け付けない場合は plain prefill にフォールバックします。
385
+
386
+ MLX VLM の text-only (`exact_cache_v1`) と画像付き (`vision_cache_v1`) は完全一致の disk hit / fresh prefill に限定し、`findBestBase()`、`base_cache_path`、`trim_to_tokens`、prefix reuse は no-op とします。PyTorch VLM / 画像入力の cache は現行 backend で無効です。
387
+
388
+ ### フロー
389
+
390
+ 1. **新しいprepare()呼び出し**
391
+ - 新しいプロンプトに対してキャッシュを準備
392
+
393
+ 2. **ベース選定(findBestBase)**
394
+ - 要素ハッシュ(elementHashes)の前方一致でベースキャッシュ候補を抽出
395
+ - トークンレベルのプレフィックス照合(prefix_hashes)で最長一致を確認
396
+ - 最も多くのトークンを再利用できるキャッシュを選定
397
+
398
+ 3. **incremental prefill実行**
399
+ - ベースキャッシュのKV値をロード
400
+ - `trimTokens`で共通プレフィックス長を指定
401
+ - 差分のみをprefillして新キャッシュを作成
402
+
403
+ 4. **supersedes記録**
404
+ - 新キャッシュの`supersedes`フィールドにベースキャッシュのrefを記録
405
+
406
+ 5. **自動release**
407
+ - ベースキャッシュを自動的に`release()`
408
+ - メモリキャッシュから除外され、`cache-index.json`に`hint: 'release'`が記録される
409
+
410
+ ### prefix_hashes
411
+
412
+ 各キャッシュファイルには、トークンプレフィックスのハッシュ情報が`.meta.json`として保存されます:
413
+
414
+ ```typescript
415
+ interface PrefixMeta {
416
+ token_count: number;
417
+ prefix_offsets: number[]; // [100, 200, 500] など
418
+ prefix_hashes: string[]; // 各offsetまでのトークン列のSHA-256ハッシュ
419
+ }
420
+ ```
421
+
422
+ これにより、要素ハッシュが部分一致する場合でも、実際のトークン列での共通プレフィックス長を正確に検証できます。
423
+
424
+ ### 利点
425
+
426
+ - **プロンプトが段階的に拡張される場合に効率的**
427
+ - 例: instructions固定、dataが増加
428
+ - **prefillコストの削減**
429
+ - 共通部分のprefillを省略し、差分のみ処理
430
+ - **自動クリーンアップ**
431
+ - 古いキャッシュが自動的にreleaseされる
432
+
433
+ ## QueryResult.usage との関係
434
+
435
+ プロンプトキャッシュの利用状況は、ドライバーが `QueryResult.usage` の任意フィールドとして報告します。
436
+
437
+ ```typescript
438
+ usage?: {
439
+ promptTokens: number;
440
+ completionTokens: number;
441
+ totalTokens: number;
442
+ cacheReadTokens?: number; // 今回リクエストでキャッシュから読んだトークン数
443
+ cacheWriteTokens?: number; // 今回リクエストでキャッシュに新規書き込みしたトークン数
444
+ };
445
+ ```
446
+
447
+ ### MlxCacheController + MlxDriver
448
+
449
+ | フィールド | ソース |
450
+ |---|---|
451
+ | `promptTokens` | Python ストリーム終端 meta の `prompt_tokens` |
452
+ | `completionTokens` | Python ストリーム終端 meta の `generation_tokens` |
453
+ | `cacheReadTokens` | クエリで使用した KV キャッシュのトークン数(LM/VLM とも `.meta.json`。VLM の exact snapshot は `token_count` を使用) |
454
+ | `cacheWriteTokens` | 同一 `streamQuery` 内の `prepare()` で新規作成した prefill トークン数(`getStats().cacheGrowthTokens` の差分) |
455
+
456
+ `promptTokens` はキャッシュ分を差し引いた値ではありません。キャッシュヒット分は `cacheReadTokens` で別途報告します。
457
+
458
+ VLM の cache load が失敗した場合、Python stream meta の `cache_loaded: false` を受けて、そのリクエストの `cacheReadTokens` は 0(フィールド省略)になります。prefill 自体が完了していれば `cacheWriteTokens` は実際に作成した prefill 分を示します。これは process restart 後の disk load 失敗にも適用されます。
459
+
460
+ ### AbortSignal とキャッシュ
461
+
462
+ MLX ドライバーで `QueryOptions.signal` により推論をキャンセルする場合:
463
+
464
+ 1. TS が `ProcessCommunication.cancelActiveStream()` で Node `Readable` を destroy
465
+ 2. stdin に `{"method":"cancel"}\n` を送信
466
+ 3. Python の `_stream_to_stdout` が `poll_cancel()` でループを抜け、`\0` でレスポンス終端
467
+ 4. TS が stdout をドレインし、キューを解放(次リクエストを受け付け可能に)
468
+
469
+ キャンセル後も `MlxCacheController` が作成済みのキャッシュファイルは保持されます。`release()` / `close()` のライフサイクルは通常どおりです。
470
+
471
+ ## 関連ドキュメント
472
+
473
+ - [Driver APIリファレンス](./DRIVER_API.md) - AIDriverインターフェースとドライバー一覧
474
+ - [ローカルモデルセットアップガイド](./LOCAL_MODEL_SETUP.md) - MLX、PyTorch、Ollamaのセットアップ
475
+ - [packages/driver/README.md](../packages/driver/README.md) - ドライバーパッケージの詳細