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