@tryhamster/gerbil 1.0.0-rc.8 → 1.0.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 (179) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +247 -84
  3. package/dist/architectures-C1I5V3Dt.mjs +6070 -0
  4. package/dist/architectures-C1I5V3Dt.mjs.map +1 -0
  5. package/dist/browser/index.d.ts +264 -588
  6. package/dist/browser/index.d.ts.map +1 -1
  7. package/dist/browser/index.js +585 -2334
  8. package/dist/browser/index.js.map +1 -1
  9. package/dist/cli.mjs +625 -1098
  10. package/dist/cli.mjs.map +1 -1
  11. package/dist/defaults-9komdrbY.mjs +24 -0
  12. package/dist/defaults-9komdrbY.mjs.map +1 -0
  13. package/dist/frameworks/express.d.mts +1 -3
  14. package/dist/frameworks/express.d.mts.map +1 -1
  15. package/dist/frameworks/express.mjs +7 -7
  16. package/dist/frameworks/express.mjs.map +1 -1
  17. package/dist/frameworks/fastify.d.mts +1 -1
  18. package/dist/frameworks/fastify.d.mts.map +1 -1
  19. package/dist/frameworks/fastify.mjs +3 -3
  20. package/dist/frameworks/fastify.mjs.map +1 -1
  21. package/dist/frameworks/hono.d.mts +1 -1
  22. package/dist/frameworks/hono.d.mts.map +1 -1
  23. package/dist/frameworks/hono.mjs +4 -4
  24. package/dist/frameworks/hono.mjs.map +1 -1
  25. package/dist/frameworks/next.d.mts +3 -2
  26. package/dist/frameworks/next.d.mts.map +1 -1
  27. package/dist/frameworks/next.mjs +4 -4
  28. package/dist/frameworks/next.mjs.map +1 -1
  29. package/dist/frameworks/react.d.mts +1 -1
  30. package/dist/frameworks/trpc.d.mts +1 -1
  31. package/dist/frameworks/trpc.d.mts.map +1 -1
  32. package/dist/frameworks/trpc.mjs +4 -4
  33. package/dist/frameworks/trpc.mjs.map +1 -1
  34. package/dist/gerbil-BHrJJIa4.mjs +1656 -0
  35. package/dist/gerbil-BHrJJIa4.mjs.map +1 -0
  36. package/dist/gerbil-BT9fCydo.d.mts +488 -0
  37. package/dist/gerbil-BT9fCydo.d.mts.map +1 -0
  38. package/dist/gerbil-DomNfIr1.mjs +4 -0
  39. package/dist/gpu/hooks.d.mts +520 -0
  40. package/dist/gpu/hooks.d.mts.map +1 -0
  41. package/dist/gpu/hooks.mjs +1188 -0
  42. package/dist/gpu/hooks.mjs.map +1 -0
  43. package/dist/gpu/index.d.mts +2 -0
  44. package/dist/gpu/index.mjs +6 -0
  45. package/dist/gpu-33qCAtHW.mjs +3615 -0
  46. package/dist/gpu-33qCAtHW.mjs.map +1 -0
  47. package/dist/index-Dgmb2kE3.d.mts +245 -0
  48. package/dist/index-Dgmb2kE3.d.mts.map +1 -0
  49. package/dist/index-jEAL2s-A.d.mts +2022 -0
  50. package/dist/index-jEAL2s-A.d.mts.map +1 -0
  51. package/dist/index.d.mts +22 -487
  52. package/dist/index.d.mts.map +1 -1
  53. package/dist/index.mjs +13 -8
  54. package/dist/index.mjs.map +1 -1
  55. package/dist/indexeddb-store-BWIMtxxH.mjs +103 -0
  56. package/dist/indexeddb-store-BWIMtxxH.mjs.map +1 -0
  57. package/dist/indexeddb-store-ClH12Xnl.mjs +4 -0
  58. package/dist/integrations/ai-sdk.d.mts +75 -6
  59. package/dist/integrations/ai-sdk.d.mts.map +1 -1
  60. package/dist/integrations/ai-sdk.mjs +131 -15
  61. package/dist/integrations/ai-sdk.mjs.map +1 -1
  62. package/dist/integrations/langchain.d.mts +1 -1
  63. package/dist/integrations/langchain.d.mts.map +1 -1
  64. package/dist/integrations/langchain.mjs +5 -5
  65. package/dist/integrations/langchain.mjs.map +1 -1
  66. package/dist/integrations/llamaindex.d.mts +1 -1
  67. package/dist/integrations/llamaindex.d.mts.map +1 -1
  68. package/dist/integrations/llamaindex.mjs +5 -5
  69. package/dist/integrations/llamaindex.mjs.map +1 -1
  70. package/dist/integrations/mcp-client.mjs +3 -3
  71. package/dist/integrations/mcp-client.mjs.map +1 -1
  72. package/dist/integrations/mcp.d.mts +3 -2
  73. package/dist/integrations/mcp.d.mts.map +1 -1
  74. package/dist/integrations/mcp.mjs +5 -5
  75. package/dist/{mcp-BvbriaBy.mjs → mcp-1DaMsaBc.mjs} +4 -4
  76. package/dist/mcp-1DaMsaBc.mjs.map +1 -0
  77. package/dist/memory/index.d.mts +3 -0
  78. package/dist/memory/index.mjs +6 -0
  79. package/dist/memory-D1P7Tmda.mjs +4 -0
  80. package/dist/memory-DVN0MnIG.mjs +132 -0
  81. package/dist/memory-DVN0MnIG.mjs.map +1 -0
  82. package/dist/memory-Dj0J1v88.mjs +294 -0
  83. package/dist/memory-Dj0J1v88.mjs.map +1 -0
  84. package/dist/moonshine-stt-BLyVoRpB.mjs +4 -0
  85. package/dist/moonshine-stt-v_P_Ci_m.mjs +11936 -0
  86. package/dist/moonshine-stt-v_P_Ci_m.mjs.map +1 -0
  87. package/dist/{one-liner-s-lD8rCC.mjs → one-liner-DnQn7HJK.mjs} +14 -16
  88. package/dist/one-liner-DnQn7HJK.mjs.map +1 -0
  89. package/dist/repl-jV5gcJFA.mjs +9 -0
  90. package/dist/skills/index.d.mts +270 -320
  91. package/dist/skills/index.d.mts.map +1 -1
  92. package/dist/skills/index.mjs +5 -5
  93. package/dist/{skills-CD3Orlex.mjs → skills-DX8D59UH.mjs} +187 -32
  94. package/dist/skills-DX8D59UH.mjs.map +1 -0
  95. package/dist/{tools-Bi1P7Xoy.mjs → tools-DQ1mPUw5.mjs} +34 -22
  96. package/dist/tools-DQ1mPUw5.mjs.map +1 -0
  97. package/dist/{types-CiTc7ez3.d.mts → types-D6FiR_oh.d.mts} +106 -12
  98. package/dist/types-D6FiR_oh.d.mts.map +1 -0
  99. package/dist/types-DQBe2lFo.d.mts +165 -0
  100. package/dist/types-DQBe2lFo.d.mts.map +1 -0
  101. package/dist/{utils-CZBZ8dgR.mjs → utils-DKO55ZmZ.mjs} +1 -1
  102. package/dist/{utils-CZBZ8dgR.mjs.map → utils-DKO55ZmZ.mjs.map} +1 -1
  103. package/dist/vector-B0panuy6.mjs +95 -0
  104. package/dist/vector-B0panuy6.mjs.map +1 -0
  105. package/docs/PROJECT-STATE.md +321 -0
  106. package/docs/adding-a-model-family.md +280 -0
  107. package/docs/ai-sdk.md +70 -61
  108. package/docs/architecture/overview.md +17 -7
  109. package/docs/browser.md +203 -8
  110. package/docs/embeddings.md +156 -0
  111. package/docs/gerbil-site-native-migration.md +217 -0
  112. package/docs/gpu-engine/architectures.md +398 -0
  113. package/docs/gpu-engine/ir.md +372 -0
  114. package/docs/gpu-engine/kernels.md +718 -0
  115. package/docs/gpu-engine/paper.html +1759 -0
  116. package/docs/gpu-engine/paper.md +2109 -0
  117. package/docs/gpu-engine/safetensors.md +312 -0
  118. package/docs/gpu-engine/tokenizer.md +302 -0
  119. package/docs/memory-rag.md +91 -0
  120. package/docs/metal-safari-intel.md +190 -0
  121. package/docs/mobile-failure-diagnosis.md +124 -0
  122. package/docs/mobile.md +99 -0
  123. package/docs/observability.md +230 -0
  124. package/docs/onnx-removal-plan.md +339 -0
  125. package/docs/research/autoresearch-portable.md +904 -0
  126. package/docs/research/dispatch-reduction-hivemind.md +84 -0
  127. package/docs/research/ios-safari-model-caching.md +117 -0
  128. package/docs/research/mobile-webgpu-speed-fusion.md +135 -0
  129. package/docs/research/native-stt-model-selection.md +49 -0
  130. package/docs/research/native-tts-model-selection.md +90 -0
  131. package/docs/research/native-vs-chromium-decision.md +152 -0
  132. package/docs/research/nemotron-mamba2-inference.md +910 -0
  133. package/docs/research/qwen35-multimodal.md +293 -0
  134. package/docs/research/qwen36-gemma4-targets.md +337 -0
  135. package/docs/research/sota-embedding-models.md +179 -0
  136. package/docs/research/sota-mobile-models-2026.md +263 -0
  137. package/docs/research/sota-modality-models.md +202 -0
  138. package/docs/research/tps-baselines.md +71 -0
  139. package/docs/research/webgpu-m4-reference.md +104 -0
  140. package/docs/site-update-plan.md +155 -0
  141. package/docs/structured-output.md +123 -0
  142. package/docs/stt.md +63 -446
  143. package/docs/tts.md +77 -499
  144. package/docs/vision.md +100 -338
  145. package/package.json +22 -7
  146. package/dist/chrome-backend-CORwaIyC.mjs +0 -1212
  147. package/dist/chrome-backend-CORwaIyC.mjs.map +0 -1
  148. package/dist/chrome-backend-DIKYoWj-.mjs +0 -3
  149. package/dist/gerbil-CJ3ifloF.mjs +0 -4
  150. package/dist/gerbil-Dw4Qj77e.mjs +0 -1631
  151. package/dist/gerbil-Dw4Qj77e.mjs.map +0 -1
  152. package/dist/gerbil-qOTe1nl2.d.mts +0 -431
  153. package/dist/gerbil-qOTe1nl2.d.mts.map +0 -1
  154. package/dist/kokoro-BNTb6egA.mjs +0 -20210
  155. package/dist/kokoro-BNTb6egA.mjs.map +0 -1
  156. package/dist/kokoro-DFRQ1OeM.js +0 -20212
  157. package/dist/kokoro-DFRQ1OeM.js.map +0 -1
  158. package/dist/mcp-BvbriaBy.mjs.map +0 -1
  159. package/dist/one-liner-s-lD8rCC.mjs.map +0 -1
  160. package/dist/repl-DveXw36T.mjs +0 -9
  161. package/dist/skills-CD3Orlex.mjs.map +0 -1
  162. package/dist/stt-CpLYbGFd.mjs +0 -433
  163. package/dist/stt-CpLYbGFd.mjs.map +0 -1
  164. package/dist/stt-DRPLEEHB.mjs +0 -3
  165. package/dist/stt-Te8Qz-Ay.js +0 -433
  166. package/dist/stt-Te8Qz-Ay.js.map +0 -1
  167. package/dist/tools-Bi1P7Xoy.mjs.map +0 -1
  168. package/dist/transformers.web-DokyH3rP.js +0 -3
  169. package/dist/transformers.web-M6mCnEYJ.js +0 -30382
  170. package/dist/transformers.web-M6mCnEYJ.js.map +0 -1
  171. package/dist/tts-C0xx3CtE.js +0 -724
  172. package/dist/tts-C0xx3CtE.js.map +0 -1
  173. package/dist/tts-DXgsKGCe.mjs +0 -3
  174. package/dist/tts-DeGANMNV.mjs +0 -730
  175. package/dist/tts-DeGANMNV.mjs.map +0 -1
  176. package/dist/types-CiTc7ez3.d.mts.map +0 -1
  177. /package/dist/{auto-update-S9s5-g0C.mjs → auto-update-BVaLXcDE.mjs} +0 -0
  178. /package/dist/{chunk-CkXuGtQK.mjs → chunk-B9cbKln6.mjs} +0 -0
  179. /package/dist/{microphone-DaMZFRuR.mjs → microphone-Bqmoz9_K.mjs} +0 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gpu-33qCAtHW.mjs","names":["tensors: Record<string, TensorDesc>","nodes: OpNode[]","executionOrder: string[]","tensors: Record<string, TensorDesc>","nodes: OpNode[]","executionOrder: string[]","prompt: number[]","seq: number[]","audioTokens: number[]","_heapIndices: Uint32Array | null","_heapValues: Float32Array | null","penaltySet: Set<number> | null","cumulative","data: ArrayBufferView","sizes: Array<[number, number, number]>","rows","enc","p","resolved: Record<string, number[]>","entries: Array<{ buffer: GPUBuffer }>","GEMMA4_IMAGE_PROCESSOR: ImageProcessorConfig","QWEN3_5_IMAGE_PROCESSOR: ImageProcessorConfig","DEFAULT_PROBE: WebkitGroupProbe","next: WebkitGroupProbe","kvMode: KvMode","visionBundle: {\n executor: VisionExecutor;\n config: Record<string, unknown>;\n posEmbedTable: Float32Array;\n } | null","VisionExecutorImpl","maxSeqLen: number","webkitGroupSize: number | undefined","host","inputIds: number[]","messages: ChatMessage[]","generatedIds: number[]","finishReason: GenerateResult[\"finishReason\"]","nextToken: number","value: unknown","g4patches: Float32Array","g4gridHW: [number, number]","pre","gThw: [number, number, number]","numPatches","vision","imageTokenId","imageRun: number[]","imageRun","inputIds","patches: Float32Array","gridTHW: [number, number, number]","input","ids","cap","tokenQueue: string[]","resolve: (() => void) | null","checks: IntegrityCheckEntry[]","argmax","entry: IntegrityCheckEntry","e: any","reference: Record<string, { sum: number; first4: number[] }>"],"sources":["../src/gpu/architectures/gemma4_vision.ts","../src/gpu/architectures/qwen3_5_vision.ts","../src/gpu/kani-tts.ts","../src/gpu/sampler.ts","../src/gpu/vision-executor.ts","../src/gpu/vision-preprocess.ts","../src/browser/device-guards.ts","../src/gpu/index.ts"],"sourcesContent":["/**\n * Gemma 4 vision tower (`gemma4_vision`) graph generator.\n *\n * Builds the 16-layer SigLIP-style ViT that turns image patches into pooled,\n * projected image-embedding tokens of dim `text_hidden` (1536 for E2B), ready to\n * splice into the Gemma 4 text stream at `image_token_id`. This is the VISION\n * ENCODER ONLY — it does not splice tokens into the text stream (that is the LM\n * side, src/gpu/index.ts).\n *\n * FAMILY-GENERAL: every dimension is read from `vision_config` + `text_config`,\n * so the same generator serves Gemma 4 E2B / E4B / any sibling that ships a\n * `gemma4_vision` tower — only the dims differ.\n *\n * Architecture (verified against the live HF config.json `vision_config`, the\n * safetensors header of google/gemma-4-E2B-it + mlx-community/gemma-4-e2b-it-4bit,\n * and transformers `modeling_gemma4`):\n *\n * patch_embedder:\n * input_proj : Linear(3·patch² → hidden, NO bias) [hidden, 3·patch²]=[768,768]\n * applied to pre-flattened 16×16×3 patches → [N, 768]\n * + position embeddings: TWO learned tables [pos_size, hidden]; per patch\n * look up table[0][x] + table[1][y] (axial 2D), host-gathered → [N, 768]\n * per encoder layer (Gemma sandwich norms, BIDIRECTIONAL attention):\n * input_layernorm (RMSNorm) → q/k/v_proj (NO bias)\n * → per-head q_norm / k_norm (RMSNorm over head_dim, NO v_norm)\n * → 2D axial RoPE (θ=100) on Q,K (host cos/sin, rotate_half)\n * → Attention (NON-causal, 12 heads, head_dim 64)\n * → o_proj (NO bias) → post_attention_layernorm (RMSNorm) → residual Add\n * pre_feedforward_layernorm (RMSNorm)\n * → GeGLU: down( gelu_tanh(gate) · up ) (gate/up/down, NO bias)\n * → post_feedforward_layernorm (RMSNorm) → residual Add\n * (no extra final norm — last layer's post_feedforward_layernorm is the tail)\n * pooling:\n * spatial k×k average-pool (k = pooling_kernel_size = 3) over the patch grid,\n * expressed as a host-built sparse [Npool, N] pooling matrix · hidden → [Npool,768]\n * multimodal projector (embed_vision.embedding_projection):\n * Linear(hidden → text_hidden) → [Npool, 1536] spliced at image_token_id\n *\n * Gemma 4 RMSNorm stores the FULL gain directly (NO +1 bake — see model-loader),\n * matching the text tower. The 2D axial RoPE cos/sin and the axial position-embed\n * gather are functions of the patch grid only (not weights/pixels), so they are\n * computed on the host (vision-preprocess: buildGemma4VisionPositionTensors) and\n * fed as input activations — same discipline as the Qwen ViT.\n *\n * Mobile-safety: the only ops used are MatMul / MatMulBias / RMSNorm /\n * ApplyRotaryEmb / Attention(causal:false) / GELU / Mul / Add — all already\n * mobile-validated (≤16KB workgroup, no select(), clamped exp, no `enable f16`).\n * The pooling reuses MatMul (host pooling matrix) so NO new kernel is required.\n */\n\nimport type { ModelArchConfig, ModelCapabilities, ModelGraph, OpNode, TensorDesc } from \"../ir.js\";\nimport { GEMMA4_VIS_KEYS } from \"../ir.js\";\n\nexport interface Gemma4VisionGraphInfo {\n hiddenSize: number;\n numHeads: number;\n headDim: number;\n depth: number;\n intermediateSize: number;\n textHidden: number;\n patchSize: number;\n patchDim: number;\n poolingKernelSize: number;\n ropeTheta: number;\n rmsNormEps: number;\n}\n\n/**\n * Resolve the Gemma 4 vision dims from a raw HF config. Accepts either the\n * top-level config (reads `.vision_config` + `.text_config.hidden_size`) or a\n * bare vision_config (then `textHidden` falls back to the projector row count if\n * present, else hidden). Family-general — no E2B constants.\n */\nexport function resolveGemma4VisionInfo(rawConfig: Record<string, unknown>): Gemma4VisionGraphInfo {\n const vcfg = (rawConfig.vision_config as Record<string, unknown>) ?? rawConfig;\n const tcfg = (rawConfig.text_config as Record<string, unknown>) ?? {};\n\n const hidden_size = vcfg.hidden_size as number; // 768\n const num_heads = vcfg.num_attention_heads as number; // 12\n const head_dim = (vcfg.head_dim as number) ?? Math.floor(hidden_size / num_heads); // 64\n const depth = vcfg.num_hidden_layers as number; // 16\n const intermediate_size = vcfg.intermediate_size as number; // 3072\n const patch_size = vcfg.patch_size as number; // 16\n const in_channels = (vcfg.num_channels as number) ?? 3;\n const patch_dim = in_channels * patch_size * patch_size; // 768\n const pooling_kernel_size = (vcfg.pooling_kernel_size as number) ?? 1;\n const rope = (vcfg.rope_parameters as Record<string, unknown>) ?? {};\n const rope_theta = (rope.rope_theta as number) ?? 100.0;\n const rms_norm_eps = (vcfg.rms_norm_eps as number) ?? 1e-6;\n const text_hidden = (tcfg.hidden_size as number) ?? hidden_size;\n\n return {\n hiddenSize: hidden_size,\n numHeads: num_heads,\n headDim: head_dim,\n depth,\n intermediateSize: intermediate_size,\n textHidden: text_hidden,\n patchSize: patch_size,\n patchDim: patch_dim,\n poolingKernelSize: pooling_kernel_size,\n ropeTheta: rope_theta,\n rmsNormEps: rms_norm_eps,\n };\n}\n\n/**\n * Dequantize an MLX affine-int4 weight to a plain f32 [rows, cols] matrix.\n * MLX packs 8 int4 values per u32 (low-nibble first); each group of `groupSize`\n * columns shares one scale + bias: w[r,c] = scale[r, c/gs] * q + bias[r, c/gs].\n * Used for the Gemma 4 multimodal projector (`embed_vision.embedding_projection`)\n * in MLX-4bit checkpoints, where (unlike the BF16 ViT body) the projector is int4.\n */\nexport function dequantizeMLXProjection(\n packed: Uint32Array,\n scales: Float32Array,\n biases: Float32Array,\n rows: number,\n cols: number,\n groupSize: number,\n): Float32Array {\n const out = new Float32Array(rows * cols);\n const numGroups = cols / groupSize;\n const u32PerRow = cols / 8;\n for (let r = 0; r < rows; r++) {\n const rowPacked = r * u32PerRow;\n const rowScale = r * numGroups;\n for (let c = 0; c < cols; c++) {\n const word = packed[rowPacked + (c >> 3)];\n const q = (word >>> ((c & 7) * 4)) & 0xf;\n const g = Math.floor(c / groupSize);\n out[r * cols + c] = scales[rowScale + g] * q + biases[rowScale + g];\n }\n }\n return out;\n}\n\n/**\n * If the Gemma 4 multimodal projector arrived as an MLX affine-int4 triplet\n * (`embed_vision.embedding_projection.{weight(U32), scales, biases}`), dequantize\n * it in-place to a plain f32 `embed_vision.embedding_projection.weight` and drop\n * the scales/biases, so the vision graph's plain MatMul on the projector works for\n * MLX-4bit checkpoints too. No-op for BF16 (HF) checkpoints (weight already f32).\n */\nexport function dequantizeGemma4VisionProjection(\n weights: Map<string, { data: ArrayBufferView; shape: number[] }>,\n groupSize: number,\n rows: number,\n cols: number,\n): void {\n const wKey = GEMMA4_VIS_KEYS.projW;\n const w = weights.get(wKey);\n const sc = weights.get(\"embed_vision.embedding_projection.scales\");\n const bi = weights.get(\"embed_vision.embedding_projection.biases\");\n // Already f32 (BF16 source) or missing quant sidecars → nothing to do.\n if (!w || !(w.data instanceof Uint32Array) || !sc || !bi) return;\n const scales =\n sc.data instanceof Float32Array\n ? sc.data\n : new Float32Array(sc.data.buffer, sc.data.byteOffset, sc.data.byteLength / 4);\n const biases =\n bi.data instanceof Float32Array\n ? bi.data\n : new Float32Array(bi.data.buffer, bi.data.byteOffset, bi.data.byteLength / 4);\n const f32 = dequantizeMLXProjection(w.data, scales, biases, rows, cols, groupSize);\n weights.set(wKey, { data: f32, shape: [rows, cols] });\n weights.delete(\"embed_vision.embedding_projection.scales\");\n weights.delete(\"embed_vision.embedding_projection.biases\");\n}\n\n/**\n * Ensure the Gemma3n multimodal-embedder norm weights exist in the weights map.\n *\n * `embed_vision.embedding_post_projection_norm` is a no-scale RMSNorm in HF\n * (Gemma3nRMSNorm(..., with_scale=False)), so the checkpoint ships NO weight for\n * it — we synthesize a ones gain (matching the kernel's `(x/rms)*weight`).\n *\n * `embed_vision.soft_embedding_norm` IS a learned RMSNorm; the full BF16 repo ships\n * its gain. Lean exports (e.g. some MLX-4bit conversions) omit it — we fall back to\n * a ones gain so the model still loads and produces coherent (if not bit-exact)\n * output. Call BEFORE VisionExecutor.uploadWeights().\n *\n * Gemma 4 vision RMSNorm stores the FULL gain (no +1 bake), so a ones gain is a\n * true identity scale.\n */\nexport function ensureGemma4VisionEmbedderNorms(\n weights: Map<string, { data: ArrayBufferView; shape: number[] }>,\n visionHidden: number,\n textHidden: number,\n): void {\n const ones = (n: number) => ({ data: new Float32Array(n).fill(1), shape: [n] });\n if (!weights.has(GEMMA4_VIS_KEYS.postProjNormW)) {\n weights.set(GEMMA4_VIS_KEYS.postProjNormW, ones(textHidden));\n }\n if (!weights.has(GEMMA4_VIS_KEYS.softEmbNormW)) {\n weights.set(GEMMA4_VIS_KEYS.softEmbNormW, ones(visionHidden));\n }\n}\n\n/**\n * Patch the ClippedMatMul nodes of a Gemma 4 vision graph with the calibrated clip\n * scalars from the checkpoint (Gemma4ClippableLinear's per-tensor input/output\n * min/max buffers), then drop those scalar tensors from the weights map so the\n * vision executor doesn't try to upload them as GPU buffers. Call BEFORE\n * VisionExecutor.uploadWeights(). Missing scalars default to ±inf (clip = identity),\n * so a checkpoint without calibration still loads.\n */\nexport function patchGemma4VisionClips(\n graph: ModelGraph,\n weights: Map<string, { data: ArrayBufferView; shape: number[] }>,\n): void {\n const readScalar = (key: string | undefined): number | undefined => {\n if (!key) return undefined;\n const e = weights.get(key);\n if (!e) return undefined;\n const d =\n e.data instanceof Float32Array\n ? e.data\n : new Float32Array(e.data.buffer, e.data.byteOffset, e.data.byteLength / 4);\n return d.length > 0 ? d[0] : undefined;\n };\n for (const node of graph.nodes) {\n if (node.opType !== \"ClippedMatMul\") continue;\n const a = node.attributes;\n const imin = readScalar(a.clip_input_min_key as string | undefined);\n const imax = readScalar(a.clip_input_max_key as string | undefined);\n const omin = readScalar(a.clip_output_min_key as string | undefined);\n const omax = readScalar(a.clip_output_max_key as string | undefined);\n if (imin !== undefined) a.imin = imin;\n if (imax !== undefined) a.imax = imax;\n if (omin !== undefined) a.omin = omin;\n if (omax !== undefined) a.omax = omax;\n for (const k of [\n a.clip_input_min_key,\n a.clip_input_max_key,\n a.clip_output_min_key,\n a.clip_output_max_key,\n ] as Array<string | undefined>) {\n if (k) weights.delete(k);\n }\n }\n}\n\n/**\n * Build the Gemma 4 ViT graph. Shaped by symbolic \"N\" (number of patches, runtime)\n * and \"Np\" (number of pooled tokens = ceil(grid_h/k)·ceil(grid_w/k), runtime),\n * resolved from input tensor dims — like the Qwen ViT's \"N\"/\"Nm\".\n */\nexport function generateGemma4VisionGraph(rawConfig: Record<string, unknown>): ModelGraph {\n const info = resolveGemma4VisionInfo(rawConfig);\n const {\n hiddenSize: hidden_size,\n numHeads: num_heads,\n headDim: head_dim,\n depth,\n intermediateSize: intermediate_size,\n textHidden: text_hidden,\n patchDim: patch_dim,\n rmsNormEps: eps,\n } = info;\n\n const qkv_dim = num_heads * head_dim; // 768 (== hidden for E2B)\n\n const tensors: Record<string, TensorDesc> = {};\n const nodes: OpNode[] = [];\n const executionOrder: string[] = [];\n\n function addTensor(desc: TensorDesc): void {\n tensors[desc.name] = desc;\n }\n function addNode(node: OpNode): void {\n nodes.push(node);\n executionOrder.push(node.id);\n }\n function constant(name: string, shape: (number | string)[], safetensorsKey = name): void {\n addTensor({ name, shape, dtype: \"f32\", storage: \"constant\", safetensorsKey });\n }\n function activation(name: string, shape: (number | string)[]): void {\n addTensor({ name, shape, dtype: \"f32\", storage: \"activation\" });\n }\n\n /** MatMul (no bias): out[M,N] = in[M,K] @ W[N,K]^T. M from `mTensor`. */\n function matmul(\n id: string,\n input: string,\n weight: string,\n output: string,\n K: number,\n N: number,\n mTensor: string,\n ): void {\n addNode({\n id,\n opType: \"MatMul\",\n inputs: [input, weight],\n outputs: [output],\n attributes: { M_tensor: mTensor, K, N },\n });\n }\n /**\n * Gemma4ClippableLinear: out = clamp(clamp(x,imin,imax) @ W^T, omin, omax).\n * The four clip scalars live in the checkpoint as per-tensor tensors under\n * `<projBase>.{input_min,input_max,output_min,output_max}`; the loader reads\n * them and patches the op's imin/imax/omin/omax attributes (and drops the\n * scalar tensors), like Gemma4's layer_scalar. `projBase` is the canonical\n * key prefix of the projection (e.g. `vision_tower.encoder.layers.0.self_attn.q_proj`).\n */\n function clippedMatmul(\n id: string,\n input: string,\n weight: string,\n output: string,\n K: number,\n N: number,\n mTensor: string,\n projBase: string,\n ): void {\n addNode({\n id,\n opType: \"ClippedMatMul\",\n inputs: [input, weight],\n outputs: [output],\n attributes: {\n M_tensor: mTensor,\n K,\n N,\n clip_input_min_key: `${projBase}.input_min`,\n clip_input_max_key: `${projBase}.input_max`,\n clip_output_min_key: `${projBase}.output_min`,\n clip_output_max_key: `${projBase}.output_max`,\n },\n });\n }\n /** RMSNorm over `hidden` (per row), Gemma full-gain (no +1; baked elsewhere=none). */\n function rmsnorm(\n id: string,\n input: string,\n w: string,\n output: string,\n hidden: number,\n seqTensor: string,\n ): void {\n addNode({\n id,\n opType: \"RMSNorm\",\n inputs: [input, w],\n outputs: [output],\n attributes: { hidden_size: hidden, eps, seq_len_tensor: seqTensor },\n });\n }\n function residualAdd(id: string, a: string, b: string, output: string): void {\n addNode({\n id,\n opType: \"Add\",\n inputs: [a, b],\n outputs: [output],\n attributes: { count_tensor: a, hidden_size },\n });\n }\n\n // ── Inputs (host-written activations) ──\n activation(\"g4v_patches\", [\"N\", patch_dim]); // [N, 768] pre-flattened patches\n activation(\"g4v_pos_embeds\", [\"N\", hidden_size]); // [N, 768] axial pos-embed (host gather)\n activation(\"g4v_cos\", [\"N\", head_dim]); // [N, 64] 2D-axial rotary cos\n activation(\"g4v_sin\", [\"N\", head_dim]); // [N, 64] 2D-axial rotary sin\n // Pooling matrix [Np, N], host-built (avg-pool weights). Multiplied as MatMul:\n // pooled[Np, hidden] = poolW[Np, N] @ hidden[N, hidden] (K=N, output N=hidden)\n // N is symbolic, so the pooling matrix is declared [Np, N] and resolved at run.\n activation(\"g4v_pool_w\", [\"Np\", \"N\"]);\n\n // ── Patch embed: input_proj (no bias) + axial position embeddings ──\n // input_proj.weight is [hidden, patch_dim]=[768,768]. Linear, no bias.\n constant(GEMMA4_VIS_KEYS.patchEmbedProjW, [hidden_size, patch_dim]);\n activation(\"g4v_patch_mm\", [\"N\", hidden_size]);\n activation(\"g4v_h0\", [\"N\", hidden_size]);\n\n matmul(\n \"g4v_patch_proj\",\n \"g4v_patches\",\n GEMMA4_VIS_KEYS.patchEmbedProjW,\n \"g4v_patch_mm\",\n patch_dim,\n hidden_size,\n \"g4v_patches\",\n );\n addNode({\n id: \"g4v_add_pos\",\n opType: \"Add\",\n inputs: [\"g4v_patch_mm\", \"g4v_pos_embeds\"],\n outputs: [\"g4v_h0\"],\n attributes: { count_tensor: \"g4v_patch_mm\", hidden_size },\n });\n\n let prev = \"g4v_h0\";\n\n for (let i = 0; i < depth; i++) {\n const p = `g4v_b${i}`;\n\n // ── input_layernorm (RMSNorm) ──\n constant(GEMMA4_VIS_KEYS.inputNorm(i), [hidden_size]);\n activation(`${p}_n1`, [\"N\", hidden_size]);\n rmsnorm(`${p}_input_norm`, prev, GEMMA4_VIS_KEYS.inputNorm(i), `${p}_n1`, hidden_size, prev);\n\n // ── q/k/v projections (NO bias) ──\n constant(GEMMA4_VIS_KEYS.qProjW(i), [qkv_dim, hidden_size]);\n constant(GEMMA4_VIS_KEYS.kProjW(i), [qkv_dim, hidden_size]);\n constant(GEMMA4_VIS_KEYS.vProjW(i), [qkv_dim, hidden_size]);\n activation(`${p}_q`, [\"N\", qkv_dim]);\n activation(`${p}_k`, [\"N\", qkv_dim]);\n activation(`${p}_v`, [\"N\", qkv_dim]);\n const saBase = `vision_tower.encoder.layers.${i}.self_attn`;\n clippedMatmul(\n `${p}_q_proj`,\n `${p}_n1`,\n GEMMA4_VIS_KEYS.qProjW(i),\n `${p}_q`,\n hidden_size,\n qkv_dim,\n `${p}_n1`,\n `${saBase}.q_proj`,\n );\n clippedMatmul(\n `${p}_k_proj`,\n `${p}_n1`,\n GEMMA4_VIS_KEYS.kProjW(i),\n `${p}_k`,\n hidden_size,\n qkv_dim,\n `${p}_n1`,\n `${saBase}.k_proj`,\n );\n clippedMatmul(\n `${p}_v_proj`,\n `${p}_n1`,\n GEMMA4_VIS_KEYS.vProjW(i),\n `${p}_v`,\n hidden_size,\n qkv_dim,\n `${p}_n1`,\n `${saBase}.v_proj`,\n );\n\n // ── per-head q_norm / k_norm (RMSNorm over head_dim) ──\n constant(GEMMA4_VIS_KEYS.qNormW(i), [head_dim]);\n constant(GEMMA4_VIS_KEYS.kNormW(i), [head_dim]);\n activation(`${p}_qn`, [\"N\", qkv_dim]);\n activation(`${p}_kn`, [\"N\", qkv_dim]);\n rmsnorm(`${p}_q_norm`, `${p}_q`, GEMMA4_VIS_KEYS.qNormW(i), `${p}_qn`, head_dim, `${p}_q`);\n rmsnorm(`${p}_k_norm`, `${p}_k`, GEMMA4_VIS_KEYS.kNormW(i), `${p}_kn`, head_dim, `${p}_k`);\n\n // ── 2D axial RoPE on Q, K (host cos/sin, rotate_half) ──\n activation(`${p}_qr`, [\"N\", qkv_dim]);\n activation(`${p}_kr`, [\"N\", qkv_dim]);\n addNode({\n id: `${p}_rope_q`,\n opType: \"ApplyRotaryEmb\",\n inputs: [`${p}_qn`, \"g4v_cos\", \"g4v_sin\"],\n outputs: [`${p}_qr`],\n attributes: { num_heads, head_dim },\n });\n addNode({\n id: `${p}_rope_k`,\n opType: \"ApplyRotaryEmb\",\n inputs: [`${p}_kn`, \"g4v_cos\", \"g4v_sin\"],\n outputs: [`${p}_kr`],\n attributes: { num_heads, head_dim },\n });\n\n // ── bidirectional attention ──\n activation(`${p}_attn`, [\"N\", qkv_dim]);\n addNode({\n id: `${p}_attention`,\n opType: \"Attention\",\n inputs: [`${p}_qr`, `${p}_kr`, `${p}_v`],\n outputs: [`${p}_attn`],\n attributes: { num_q_heads: num_heads, num_kv_heads: num_heads, head_dim, causal: false },\n });\n\n // ── o_proj (NO bias) ──\n constant(GEMMA4_VIS_KEYS.oProjW(i), [hidden_size, qkv_dim]);\n activation(`${p}_o`, [\"N\", hidden_size]);\n clippedMatmul(\n `${p}_o_proj`,\n `${p}_attn`,\n GEMMA4_VIS_KEYS.oProjW(i),\n `${p}_o`,\n qkv_dim,\n hidden_size,\n `${p}_attn`,\n `${saBase}.o_proj`,\n );\n\n // ── post_attention_layernorm (RMSNorm, applied to the attn-block output BEFORE\n // the residual add — Gemma \"post-norm\" sandwich) ──\n constant(GEMMA4_VIS_KEYS.postAttnNorm(i), [hidden_size]);\n activation(`${p}_post_attn`, [\"N\", hidden_size]);\n rmsnorm(\n `${p}_post_attn_norm`,\n `${p}_o`,\n GEMMA4_VIS_KEYS.postAttnNorm(i),\n `${p}_post_attn`,\n hidden_size,\n `${p}_o`,\n );\n\n // residual 1\n activation(`${p}_res1`, [\"N\", hidden_size]);\n residualAdd(`${p}_residual1`, prev, `${p}_post_attn`, `${p}_res1`);\n\n // ── pre_feedforward_layernorm (RMSNorm) ──\n constant(GEMMA4_VIS_KEYS.preFfNorm(i), [hidden_size]);\n activation(`${p}_n2`, [\"N\", hidden_size]);\n rmsnorm(\n `${p}_pre_ff_norm`,\n `${p}_res1`,\n GEMMA4_VIS_KEYS.preFfNorm(i),\n `${p}_n2`,\n hidden_size,\n `${p}_res1`,\n );\n\n // ── GeGLU MLP: down( gelu_tanh(gate) · up ) (gate/up/down, NO bias) ──\n constant(GEMMA4_VIS_KEYS.gateProjW(i), [intermediate_size, hidden_size]);\n constant(GEMMA4_VIS_KEYS.upProjW(i), [intermediate_size, hidden_size]);\n constant(GEMMA4_VIS_KEYS.downProjW(i), [hidden_size, intermediate_size]);\n activation(`${p}_gate`, [\"N\", intermediate_size]);\n activation(`${p}_up`, [\"N\", intermediate_size]);\n activation(`${p}_gelu`, [\"N\", intermediate_size]);\n activation(`${p}_geglu`, [\"N\", intermediate_size]);\n activation(`${p}_mlp`, [\"N\", hidden_size]);\n\n const mlpBase = `vision_tower.encoder.layers.${i}.mlp`;\n clippedMatmul(\n `${p}_gate_proj`,\n `${p}_n2`,\n GEMMA4_VIS_KEYS.gateProjW(i),\n `${p}_gate`,\n hidden_size,\n intermediate_size,\n `${p}_n2`,\n `${mlpBase}.gate_proj`,\n );\n clippedMatmul(\n `${p}_up_proj`,\n `${p}_n2`,\n GEMMA4_VIS_KEYS.upProjW(i),\n `${p}_up`,\n hidden_size,\n intermediate_size,\n `${p}_n2`,\n `${mlpBase}.up_proj`,\n );\n addNode({\n id: `${p}_gelu_op`,\n opType: \"GELU\", // gelu_pytorch_tanh\n inputs: [`${p}_gate`],\n outputs: [`${p}_gelu`],\n attributes: { count_tensor: `${p}_gate` },\n });\n addNode({\n id: `${p}_geglu_mul`,\n opType: \"Mul\",\n inputs: [`${p}_gelu`, `${p}_up`],\n outputs: [`${p}_geglu`],\n attributes: { count_tensor: `${p}_gelu`, hidden_size: intermediate_size },\n });\n clippedMatmul(\n `${p}_down_proj`,\n `${p}_geglu`,\n GEMMA4_VIS_KEYS.downProjW(i),\n `${p}_mlp`,\n intermediate_size,\n hidden_size,\n `${p}_geglu`,\n `${mlpBase}.down_proj`,\n );\n\n // ── post_feedforward_layernorm (RMSNorm, before residual add) ──\n constant(GEMMA4_VIS_KEYS.postFfNorm(i), [hidden_size]);\n activation(`${p}_post_ff`, [\"N\", hidden_size]);\n rmsnorm(\n `${p}_post_ff_norm`,\n `${p}_mlp`,\n GEMMA4_VIS_KEYS.postFfNorm(i),\n `${p}_post_ff`,\n hidden_size,\n `${p}_res1`,\n );\n\n // residual 2\n activation(`${p}_res2`, [\"N\", hidden_size]);\n residualAdd(`${p}_residual2`, `${p}_res1`, `${p}_post_ff`, `${p}_res2`);\n\n prev = `${p}_res2`;\n }\n\n // ── Spatial average pooling → pooled tokens [Np, hidden] ──\n // pooled = poolW[Np, N] @ hidden[N, hidden]. Reuse MatMul: input=poolW (the\n // \"activation\" matrix), weight=encoder output viewed as [hidden, N]? No — MatMul\n // is out[M,K]@W[Nout,K]^T. We instead treat poolW as the LHS [Np, N] and the\n // encoder hidden as the RHS, but MatMul transposes the 2nd operand. To compute\n // poolW @ hidden directly we use a dedicated AvgPoolMatMul that does\n // out[r, c] = sum_k poolW[r, k] * hidden[k, c] (no transpose on hidden).\n activation(\"g4v_pooled\", [\"Np\", hidden_size]);\n addNode({\n id: \"g4v_pool\",\n opType: \"PoolMatMul\",\n inputs: [\"g4v_pool_w\", prev],\n outputs: [\"g4v_pooled\"],\n attributes: { K_tensor: prev, hidden_size, np_tensor: \"g4v_pool_w\" },\n });\n\n // ── Multimodal embedder (Gemma3nMultimodalEmbedder), soft/image path ──\n // HF forward for image features:\n // x = pooled * sqrt(vision_hidden) # scale-invariant before RMSNorm → omitted\n // x = soft_embedding_norm(x) # RMSNorm over vision hidden (WITH gain)\n // x = embedding_projection(x) # Linear(vision_hidden → text_hidden), no bias\n // x = embedding_post_projection_norm(x) # RMSNorm over text_hidden, with_scale=False (gain=1)\n // The post-projection RMSNorm is what brings the image-soft tokens to the same\n // ~unit-RMS scale as the ×sqrt(text_hidden) text embeddings they are spliced into;\n // without it the projector output is ~sqrt(text_hidden)× too large and the LM\n // ignores the image. The post-proj norm has no learned gain, so the loader\n // synthesizes a ones weight; soft_embedding_norm's gain is a real weight (loader\n // falls back to ones if a lean checkpoint omits it).\n constant(GEMMA4_VIS_KEYS.softEmbNormW, [hidden_size]);\n activation(\"g4v_soft_normed\", [\"Np\", hidden_size]);\n rmsnorm(\n \"g4v_soft_emb_norm\",\n \"g4v_pooled\",\n GEMMA4_VIS_KEYS.softEmbNormW,\n \"g4v_soft_normed\",\n hidden_size,\n \"g4v_pooled\",\n );\n\n // embed_vision.embedding_projection.weight is [text_hidden, hidden]=[1536,768].\n // No bias in the released checkpoint (weight/scales/biases are the int4 quant\n // triplet for the WEIGHT, dequantized by dequantizeGemma4VisionProjection).\n constant(GEMMA4_VIS_KEYS.projW, [text_hidden, hidden_size]);\n activation(\"g4v_proj_out\", [\"Np\", text_hidden]);\n matmul(\n \"g4v_proj\",\n \"g4v_soft_normed\",\n GEMMA4_VIS_KEYS.projW,\n \"g4v_proj_out\",\n hidden_size,\n text_hidden,\n \"g4v_proj_out\",\n );\n\n constant(GEMMA4_VIS_KEYS.postProjNormW, [text_hidden]);\n activation(\"g4v_image_embeds\", [\"Np\", text_hidden]);\n rmsnorm(\n \"g4v_post_proj_norm\",\n \"g4v_proj_out\",\n GEMMA4_VIS_KEYS.postProjNormW,\n \"g4v_image_embeds\",\n text_hidden,\n \"g4v_proj_out\",\n );\n\n const config: ModelArchConfig = {\n hidden_size,\n num_layers: depth,\n num_heads,\n num_kv_heads: num_heads,\n head_dim,\n intermediate_size,\n vocab_size: 0,\n context_length:\n ((rawConfig.vision_config as Record<string, unknown> | undefined)\n ?.max_position_embeddings as number) ?? 0,\n rms_norm_eps: eps,\n norm_type: \"rmsnorm\",\n rope_base: info.ropeTheta,\n rope_dim: head_dim,\n kv_layout: \"LHSd\",\n is_moe: false,\n has_vision_tower: true,\n vision_architecture: \"gemma4_vision\",\n vision_patch_size: info.patchSize,\n vision_embed_dim: hidden_size,\n };\n\n const capabilities: ModelCapabilities = { text: true, vision: true, moe: false };\n\n return {\n architecture: \"Gemma4VisionModel\",\n config,\n capabilities,\n tensors,\n nodes,\n executionOrder,\n inputs: [\"g4v_patches\", \"g4v_pos_embeds\", \"g4v_cos\", \"g4v_sin\", \"g4v_pool_w\"],\n outputs: [\"g4v_image_embeds\"],\n };\n}\n","/**\n * Qwen3.5 vision tower (ViT) graph generator.\n *\n * Builds the 12-layer ViT that turns image patches into merged image-embedding\n * tokens of dim `out_hidden_size` (1024, matching the LM hidden size). This is\n * the VISION ENCODER ONLY — it does not splice tokens into the text stream.\n *\n * Architecture (verified against transformers 5.12 modeling_qwen3_5.py):\n *\n * patch_embed: MatMul(patches[N,1536], proj_w[768,1536]) + proj_b → [N,768]\n * then + pos_embeds (bilinear-interpolated, host-precomputed)\n * per block (pre-norm, bidirectional):\n * norm1 (LayerNorm) → qkv (MatMul[2304,768]+bias) → split Q/K/V [N,768]\n * → ApplyRotaryEmb(Q), ApplyRotaryEmb(K) (precomputed cos/sin, rotate_half)\n * → Attention (NON-causal, 12 heads, head_dim 64)\n * → proj (MatMul[768,768]+bias) → residual Add\n * norm2 (LayerNorm) → fc1 (MatMul[3072,768]+bias) → GELU(tanh)\n * → fc2 (MatMul[768,3072]+bias) → residual Add\n * merger:\n * norm (LayerNorm over 768) → reshape [N,768]→[N/4,3072] (free, row-major)\n * → fc1 (MatMul[3072,3072]+bias) → GELU(exact erf) → fc2 (MatMul[1024,3072]+bias)\n * → merged image tokens [N/4, 1024]\n *\n * The bilinear pos-embed interpolation and the 2D rotary cos/sin are position-\n * (grid-)dependent only — they're cheap and computed on the host (see\n * vision-preprocess.ts), then fed as input activations. This keeps the GPU graph\n * to the weight-dependent math while staying byte-identical to HF.\n *\n * Patches must already be ordered into spatial_merge_size×spatial_merge_size\n * groups (the HF image processor does this), so the merger's reshape picks up\n * correct 2×2 blocks with no gather.\n */\n\nimport type { ModelArchConfig, ModelCapabilities, ModelGraph, OpNode, TensorDesc } from \"../ir.js\";\nimport { CANONICAL_KEYS } from \"../ir.js\";\n\nconst VIS_NORM_EPS = 1e-6;\n\nexport interface VisionGraphInfo {\n hiddenSize: number;\n numHeads: number;\n headDim: number;\n depth: number;\n intermediateSize: number;\n outHiddenSize: number;\n spatialMergeSize: number;\n patchDim: number;\n mergedDim: number;\n}\n\n/**\n * Build the ViT graph. The graph is shaped by symbolic \"N\" (number of patches),\n * resolved at run time from the input tensor's first dim — exactly like the LM's\n * symbolic \"T\".\n */\nexport function generateQwen3_5VisionGraph(rawConfig: Record<string, unknown>): ModelGraph {\n const vcfg = (rawConfig.vision_config as Record<string, unknown>) ?? rawConfig;\n\n const hidden_size = vcfg.hidden_size as number; // 768\n const num_heads = vcfg.num_heads as number; // 12\n const head_dim = Math.floor(hidden_size / num_heads); // 64\n const depth = vcfg.depth as number; // 12\n const intermediate_size = vcfg.intermediate_size as number; // 3072\n const out_hidden_size = vcfg.out_hidden_size as number; // 1024\n const spatial_merge_size = vcfg.spatial_merge_size as number; // 2\n const in_channels = vcfg.in_channels as number; // 3\n const temporal_patch_size = vcfg.temporal_patch_size as number; // 2\n const patch_size = vcfg.patch_size as number; // 16\n const patch_dim = in_channels * temporal_patch_size * patch_size * patch_size; // 1536\n const merge_unit = spatial_merge_size * spatial_merge_size; // 4\n const merged_in = hidden_size * merge_unit; // 3072\n\n const tensors: Record<string, TensorDesc> = {};\n const nodes: OpNode[] = [];\n const executionOrder: string[] = [];\n\n function addTensor(desc: TensorDesc): void {\n tensors[desc.name] = desc;\n }\n function addNode(node: OpNode): void {\n nodes.push(node);\n executionOrder.push(node.id);\n }\n function constant(name: string, shape: (number | string)[]): void {\n addTensor({ name, shape, dtype: \"f32\", storage: \"constant\", safetensorsKey: name });\n }\n function activation(name: string, shape: (number | string)[]): void {\n addTensor({ name, shape, dtype: \"f32\", storage: \"activation\" });\n }\n\n /** MatMul (no bias) → out[N, Nout] = in[N,K] @ W[Nout,K]^T. M resolved from `mTensor`. */\n function matmul(\n id: string,\n input: string,\n weight: string,\n output: string,\n K: number,\n N: number,\n mTensor?: string,\n mStatic?: number,\n ): void {\n addNode({\n id,\n opType: \"MatMul\",\n inputs: [input, weight],\n outputs: [output],\n attributes: mTensor ? { M_tensor: mTensor, K, N } : { M: mStatic, K, N },\n });\n }\n /** out[r,c] = in[r,c] + bias[c]. */\n function addBias(id: string, input: string, bias: string, output: string, width: number): void {\n addNode({\n id,\n opType: \"AddBias\",\n inputs: [input, bias],\n outputs: [output],\n attributes: { width },\n });\n }\n /**\n * Fused MatMul + row-broadcast bias → out = in @ W^T + bias. Replaces a MatMul\n * followed by a separate AddBias, removing a full read+write of the matmul\n * output per ViT linear layer.\n */\n function matmulBias(\n id: string,\n input: string,\n weight: string,\n bias: string,\n output: string,\n K: number,\n N: number,\n mTensor: string,\n ): void {\n addNode({\n id,\n opType: \"MatMulBias\",\n inputs: [input, weight, bias],\n outputs: [output],\n attributes: { M_tensor: mTensor, K, N },\n });\n }\n function layernorm(\n id: string,\n input: string,\n w: string,\n b: string,\n output: string,\n hidden: number,\n seqTensor: string,\n ): void {\n addNode({\n id,\n opType: \"LayerNorm\",\n inputs: [input, w, b],\n outputs: [output],\n attributes: { hidden_size: hidden, eps: VIS_NORM_EPS, seq_len_tensor: seqTensor },\n });\n }\n\n // ── Inputs (host-written activations) ──\n activation(\"vis_patches\", [\"N\", patch_dim]); // [N, 1536]\n activation(\"vis_pos_embeds\", [\"N\", hidden_size]); // [N, 768]\n activation(\"vis_cos\", [\"N\", head_dim]); // [N, 64]\n activation(\"vis_sin\", [\"N\", head_dim]); // [N, 64]\n\n // ── Patch embed: MatMul + bias + pos_embeds ──\n constant(CANONICAL_KEYS.visPatchEmbedWeight, [hidden_size, patch_dim]);\n constant(CANONICAL_KEYS.visPatchEmbedBias, [hidden_size]);\n activation(\"vis_patch_mm\", [\"N\", hidden_size]);\n activation(\"vis_patch_bias\", [\"N\", hidden_size]);\n activation(\"vis_h0\", [\"N\", hidden_size]);\n\n matmulBias(\n \"vis_patch_mm\",\n \"vis_patches\",\n CANONICAL_KEYS.visPatchEmbedWeight,\n CANONICAL_KEYS.visPatchEmbedBias,\n \"vis_patch_bias\",\n patch_dim,\n hidden_size,\n \"vis_patches\",\n );\n addNode({\n id: \"vis_add_pos\",\n opType: \"Add\",\n inputs: [\"vis_patch_bias\", \"vis_pos_embeds\"],\n outputs: [\"vis_h0\"],\n attributes: { count_tensor: \"vis_patch_bias\", hidden_size },\n });\n\n let prev = \"vis_h0\";\n const qkv_dim = hidden_size * 3; // 2304\n\n for (let i = 0; i < depth; i++) {\n const p = `vis_b${i}`;\n\n // norm1\n constant(CANONICAL_KEYS.visBlockNorm1W(i), [hidden_size]);\n constant(CANONICAL_KEYS.visBlockNorm1B(i), [hidden_size]);\n activation(`${p}_n1`, [\"N\", hidden_size]);\n layernorm(\n `${p}_norm1`,\n prev,\n CANONICAL_KEYS.visBlockNorm1W(i),\n CANONICAL_KEYS.visBlockNorm1B(i),\n `${p}_n1`,\n hidden_size,\n prev,\n );\n\n // qkv projection + bias\n constant(CANONICAL_KEYS.visBlockQkvW(i), [qkv_dim, hidden_size]);\n constant(CANONICAL_KEYS.visBlockQkvB(i), [qkv_dim]);\n activation(`${p}_qkv_mm`, [\"N\", qkv_dim]);\n activation(`${p}_qkv`, [\"N\", qkv_dim]);\n matmulBias(\n `${p}_qkv_proj`,\n `${p}_n1`,\n CANONICAL_KEYS.visBlockQkvW(i),\n CANONICAL_KEYS.visBlockQkvB(i),\n `${p}_qkv`,\n hidden_size,\n qkv_dim,\n `${p}_n1`,\n );\n\n // split Q/K/V [N,768] from fused [N,2304]\n activation(`${p}_q`, [\"N\", hidden_size]);\n activation(`${p}_k`, [\"N\", hidden_size]);\n activation(`${p}_v`, [\"N\", hidden_size]);\n const sliceCols = (id: string, output: string, offset: number) =>\n addNode({\n id,\n opType: \"SliceCols\",\n inputs: [`${p}_qkv`],\n outputs: [output],\n attributes: {\n in_width: qkv_dim,\n out_width: hidden_size,\n col_offset: offset,\n seq_len_tensor: `${p}_qkv`,\n },\n });\n sliceCols(`${p}_split_q`, `${p}_q`, 0);\n sliceCols(`${p}_split_k`, `${p}_k`, hidden_size);\n sliceCols(`${p}_split_v`, `${p}_v`, 2 * hidden_size);\n\n // rotary on Q, K\n activation(`${p}_qr`, [\"N\", hidden_size]);\n activation(`${p}_kr`, [\"N\", hidden_size]);\n addNode({\n id: `${p}_rope_q`,\n opType: \"ApplyRotaryEmb\",\n inputs: [`${p}_q`, \"vis_cos\", \"vis_sin\"],\n outputs: [`${p}_qr`],\n attributes: { num_heads, head_dim },\n });\n addNode({\n id: `${p}_rope_k`,\n opType: \"ApplyRotaryEmb\",\n inputs: [`${p}_k`, \"vis_cos\", \"vis_sin\"],\n outputs: [`${p}_kr`],\n attributes: { num_heads, head_dim },\n });\n\n // bidirectional attention\n activation(`${p}_attn`, [\"N\", hidden_size]);\n addNode({\n id: `${p}_attention`,\n opType: \"Attention\",\n inputs: [`${p}_qr`, `${p}_kr`, `${p}_v`],\n outputs: [`${p}_attn`],\n attributes: { num_q_heads: num_heads, num_kv_heads: num_heads, head_dim, causal: false },\n });\n\n // output projection + bias\n constant(CANONICAL_KEYS.visBlockProjW(i), [hidden_size, hidden_size]);\n constant(CANONICAL_KEYS.visBlockProjB(i), [hidden_size]);\n activation(`${p}_proj_mm`, [\"N\", hidden_size]);\n activation(`${p}_proj`, [\"N\", hidden_size]);\n matmulBias(\n `${p}_proj_op`,\n `${p}_attn`,\n CANONICAL_KEYS.visBlockProjW(i),\n CANONICAL_KEYS.visBlockProjB(i),\n `${p}_proj`,\n hidden_size,\n hidden_size,\n `${p}_attn`,\n );\n\n // residual 1\n activation(`${p}_res1`, [\"N\", hidden_size]);\n addNode({\n id: `${p}_residual1`,\n opType: \"Add\",\n inputs: [prev, `${p}_proj`],\n outputs: [`${p}_res1`],\n attributes: { count_tensor: prev, hidden_size },\n });\n\n // norm2 + MLP (GELU tanh)\n constant(CANONICAL_KEYS.visBlockNorm2W(i), [hidden_size]);\n constant(CANONICAL_KEYS.visBlockNorm2B(i), [hidden_size]);\n constant(CANONICAL_KEYS.visBlockFc1W(i), [intermediate_size, hidden_size]);\n constant(CANONICAL_KEYS.visBlockFc1B(i), [intermediate_size]);\n constant(CANONICAL_KEYS.visBlockFc2W(i), [hidden_size, intermediate_size]);\n constant(CANONICAL_KEYS.visBlockFc2B(i), [hidden_size]);\n activation(`${p}_n2`, [\"N\", hidden_size]);\n activation(`${p}_fc1_mm`, [\"N\", intermediate_size]);\n activation(`${p}_fc1`, [\"N\", intermediate_size]);\n activation(`${p}_gelu`, [\"N\", intermediate_size]);\n activation(`${p}_fc2_mm`, [\"N\", hidden_size]);\n activation(`${p}_fc2`, [\"N\", hidden_size]);\n activation(`${p}_res2`, [\"N\", hidden_size]);\n\n layernorm(\n `${p}_norm2`,\n `${p}_res1`,\n CANONICAL_KEYS.visBlockNorm2W(i),\n CANONICAL_KEYS.visBlockNorm2B(i),\n `${p}_n2`,\n hidden_size,\n `${p}_res1`,\n );\n matmulBias(\n `${p}_fc1_op`,\n `${p}_n2`,\n CANONICAL_KEYS.visBlockFc1W(i),\n CANONICAL_KEYS.visBlockFc1B(i),\n `${p}_fc1`,\n hidden_size,\n intermediate_size,\n `${p}_n2`,\n );\n addNode({\n id: `${p}_gelu_op`,\n opType: \"GELU\",\n inputs: [`${p}_fc1`],\n outputs: [`${p}_gelu`],\n attributes: { count_tensor: `${p}_fc1` },\n });\n matmulBias(\n `${p}_fc2_op`,\n `${p}_gelu`,\n CANONICAL_KEYS.visBlockFc2W(i),\n CANONICAL_KEYS.visBlockFc2B(i),\n `${p}_fc2`,\n intermediate_size,\n hidden_size,\n `${p}_gelu`,\n );\n addNode({\n id: `${p}_residual2`,\n opType: \"Add\",\n inputs: [`${p}_res1`, `${p}_fc2`],\n outputs: [`${p}_res2`],\n attributes: { count_tensor: `${p}_res1`, hidden_size },\n });\n\n prev = `${p}_res2`;\n }\n\n // ── Merger ──\n // norm over 768 (per-patch), then view [N,768]→[N/4,3072] (free), fc1 → erf-GELU → fc2.\n // M for merger matmuls is N/4 (resolved from the [N/4,*] output tensor via M_tensor).\n constant(CANONICAL_KEYS.visMergerNormW, [hidden_size]);\n constant(CANONICAL_KEYS.visMergerNormB, [hidden_size]);\n constant(CANONICAL_KEYS.visMergerFc1W, [merged_in, merged_in]);\n constant(CANONICAL_KEYS.visMergerFc1B, [merged_in]);\n constant(CANONICAL_KEYS.visMergerFc2W, [out_hidden_size, merged_in]);\n constant(CANONICAL_KEYS.visMergerFc2B, [out_hidden_size]);\n\n activation(\"vis_merge_norm\", [\"N\", hidden_size]);\n // Reshaped views: these alias the same memory as the [N,768] norm output but\n // are addressed as [N/4, 3072]. The executor allocates per-name buffers, so we\n // declare them with the merged row count and copy is implicit (same byte layout).\n activation(\"vis_merge_fc1_mm\", [\"Nm\", merged_in]);\n activation(\"vis_merge_fc1\", [\"Nm\", merged_in]);\n activation(\"vis_merge_gelu\", [\"Nm\", merged_in]);\n activation(\"vis_merge_fc2_mm\", [\"Nm\", out_hidden_size]);\n activation(\"vis_image_embeds\", [\"Nm\", out_hidden_size]);\n\n layernorm(\n \"vis_merger_norm\",\n prev,\n CANONICAL_KEYS.visMergerNormW,\n CANONICAL_KEYS.visMergerNormB,\n \"vis_merge_norm\",\n hidden_size,\n prev,\n );\n // fc1: input is vis_merge_norm viewed as [Nm, 3072]. K=3072, N=3072, M=Nm.\n matmulBias(\n \"vis_merger_fc1\",\n \"vis_merge_norm\",\n CANONICAL_KEYS.visMergerFc1W,\n CANONICAL_KEYS.visMergerFc1B,\n \"vis_merge_fc1\",\n merged_in,\n merged_in,\n \"vis_merge_fc1\",\n );\n addNode({\n id: \"vis_merger_gelu\",\n opType: \"GeluErf\",\n inputs: [\"vis_merge_fc1\"],\n outputs: [\"vis_merge_gelu\"],\n attributes: { count_tensor: \"vis_merge_fc1\" },\n });\n matmulBias(\n \"vis_merger_fc2\",\n \"vis_merge_gelu\",\n CANONICAL_KEYS.visMergerFc2W,\n CANONICAL_KEYS.visMergerFc2B,\n \"vis_image_embeds\",\n merged_in,\n out_hidden_size,\n \"vis_image_embeds\",\n );\n\n const config: ModelArchConfig = {\n hidden_size,\n num_layers: depth,\n num_heads,\n num_kv_heads: num_heads,\n head_dim,\n intermediate_size,\n vocab_size: 0,\n context_length: vcfg.num_position_embeddings as number,\n rms_norm_eps: VIS_NORM_EPS,\n norm_type: \"layernorm\",\n rope_base: 10000.0,\n rope_dim: head_dim,\n kv_layout: \"LHSd\",\n is_moe: false,\n has_vision_tower: true,\n vision_architecture: \"qwen3_5_vit\",\n vision_patch_size: patch_size,\n vision_embed_dim: hidden_size,\n };\n\n const capabilities: ModelCapabilities = { text: true, vision: true, moe: false };\n\n return {\n architecture: \"Qwen3_5VisionModel\",\n config,\n capabilities,\n tensors,\n nodes,\n executionOrder,\n inputs: [\"vis_patches\", \"vis_pos_embeds\", \"vis_cos\", \"vis_sin\"],\n outputs: [\"vis_image_embeds\"],\n };\n}\n","/**\n * KaniTTS — native text-to-speech engine for Gerbil's WebGPU backend.\n *\n * Kani-TTS-2 (nineninesix/kani-tts-2-en) is a two-stage TTS model that, like\n * Moonshine, needs more than one graph:\n *\n * 1. CODEC-LM BACKBONE (LFM2-350M body): autoregressively emits NanoCodec audio\n * tokens (4 per frame) into the same vocab as text. Reuses LFM2's block math\n * with two KaniTTS2 deltas — frame-level position IDs (the 4 audio tokens of a\n * frame share a position) and learnable per-layer RoPE (α^(l)-scaled freqs) —\n * both folded host-side into per-layer cos/sin tables fed to the MRoPE op.\n * 2. NANOCODEC DECODER (NVIDIA NeMo 22 kHz): FSQ dequant + causal HiFi-GAN conv\n * decoder → 22 kHz PCM. Validated bit-exact vs MLX (test-nanocodec-decode.mjs).\n *\n * The AR loop runs on the host with full-logit readback so each frame's 4 audio\n * tokens are sampled per-codebook (constrained to the valid codebook window), then\n * the collected codes are decoded once through the NanoCodec graph.\n *\n * Validated on Dawn (desktop) via scripts/engine/test-kani-speak.mjs.\n */\n\nimport {\n audioTokensToCodes,\n buildKaniLayerCosSin,\n computeKaniPositions,\n generateKaniTtsGraph,\n generateNanoCodecDecoderGraph,\n KANI_END_OF_HUMAN,\n KANI_START_OF_HUMAN,\n type KaniConfig,\n kaniAttentionLayerIndices,\n kaniCosTensor,\n kaniLayerAlpha,\n kaniSinTensor,\n parseKaniConfig,\n} from \"./architectures/kani_tts.js\";\nimport type { GPUContext } from \"./device.js\";\nimport { initGPU } from \"./device.js\";\nimport { Executor } from \"./executor.js\";\nimport { type LoadedKaniTTS, loadKaniTTS } from \"./model-loader.js\";\nimport type { Tokenizer } from \"./tokenizer.js\";\n\nconst KANI_SAMPLE_RATE = 22_050;\nconst KANI_HOP = 1764; // PCM samples per frame (product of up-sample rates)\nconst KANI_END_OF_TEXT = 2; // tokenizer end-of-text marker spliced after the text\n/** NanoCodec decode chunking (keeps per-dispatch under WebGPU's 65535 cap). */\nconst KANI_CODEC_CHUNK_FRAMES = 32;\nconst KANI_CODEC_LOOKBACK_FRAMES = 32;\n/** Hard cap so a stuck decode cannot loop forever (reference uses 3000). */\nconst DEFAULT_MAX_NEW_TOKENS = 3000;\n\nexport interface KaniTTSOptions {\n /** Backbone repo (default nineninesix/kani-tts-2-en). */\n repo?: string;\n /** NanoCodec repo (default the NeMo 22 kHz MLX checkpoint). */\n codecRepo?: string;\n revision?: string;\n hfToken?: string;\n cacheDir?: string;\n /** Max self-attn KV-cache length (prompt + generated). Default 2048. */\n maxSeqLen?: number;\n onProgress?: (loaded: number, total: number, message: string) => void;\n}\n\nexport interface SpeakOptions {\n /** Language/accent tag, e.g. \"en_us\" (default). Prepended as \"{tag}: {text}\". */\n languageTag?: string;\n /** Sampling temperature (default 1.0). */\n temperature?: number;\n /** Top-p nucleus threshold (default 0.95). */\n topP?: number;\n /** Repetition penalty (default 1.1). */\n repetitionPenalty?: number;\n /** Max audio frames to generate (caps duration). Default unbounded (maxSeqLen). */\n maxFrames?: number;\n /** Override max generated tokens (default 3000). */\n maxNewTokens?: number;\n}\n\nexport interface SpeakResult {\n /** Mono PCM in [-1, 1]. */\n pcm: Float32Array;\n /** Sample rate (22050). */\n sampleRate: number;\n /** Number of audio frames decoded. */\n frames: number;\n /** Audio duration in seconds. */\n audioSeconds: number;\n}\n\n/** Build a fresh map of just the constant weights a graph references (see moonshine-stt). */\nfunction selectGraphWeights(\n graph: { tensors: Record<string, { storage: string; safetensorsKey?: string }> },\n weights: Map<string, { data: ArrayBufferView; shape: number[] }>,\n): Map<string, { data: ArrayBufferView; shape: number[] }> {\n const out = new Map<string, { data: ArrayBufferView; shape: number[] }>();\n for (const [name, desc] of Object.entries(graph.tensors)) {\n if (desc.storage !== \"constant\") {\n continue;\n }\n // Resolve by name, falling back to the tensor's safetensorsKey alias (handles\n // LFM2's embedding_norm→norm rename and the tied lm_head→embed_tokens alias —\n // the aliasing loadModel() normally does, which this dedicated loader bypasses).\n const w =\n weights.get(name) ?? (desc.safetensorsKey ? weights.get(desc.safetensorsKey) : undefined);\n if (w) {\n out.set(name, w);\n }\n }\n return out;\n}\n\n/**\n * Top-p nucleus sample from a (already softmaxed) probability array over `ids`.\n * Sorts by prob desc, keeps the smallest prefix whose cumulative mass ≥ topP,\n * renormalizes, and samples one id.\n */\nfunction nucleusSample(probs: Float32Array, ids: Int32Array, topP: number): number {\n const n = probs.length;\n const order = Array.from({ length: n }, (_, i) => i).sort((a, b) => probs[b] - probs[a]);\n let cum = 0;\n let cutoff = n;\n for (let r = 0; r < n; r++) {\n cum += probs[order[r]];\n if (cum >= topP) {\n cutoff = r + 1;\n break;\n }\n }\n let keepSum = 0;\n for (let r = 0; r < cutoff; r++) {\n keepSum += probs[order[r]];\n }\n let pick = Math.random() * keepSum;\n for (let r = 0; r < cutoff; r++) {\n pick -= probs[order[r]];\n if (pick <= 0) {\n return ids[order[r]];\n }\n }\n return ids[order[0]];\n}\n\n/** In-place softmax of `scores`. */\nfunction softmaxInPlace(scores: Float32Array): void {\n let maxV = Number.NEGATIVE_INFINITY;\n for (let i = 0; i < scores.length; i++) {\n if (scores[i] > maxV) {\n maxV = scores[i];\n }\n }\n let sum = 0;\n for (let i = 0; i < scores.length; i++) {\n const e = Math.exp(scores[i] - maxV);\n scores[i] = e;\n sum += e;\n }\n for (let i = 0; i < scores.length; i++) {\n scores[i] /= sum;\n }\n}\n\nexport class KaniTTS {\n private ctx: GPUContext;\n private loaded: LoadedKaniTTS;\n private tokenizer: Tokenizer;\n private cfg: KaniConfig;\n private rawConfig: Record<string, unknown>;\n private maxSeqLen: number;\n /** Backbone executor (built once; reused across speak() calls). */\n private backboneExec: Executor;\n /** The attention layer indices (carry learnable α) and their α values. */\n private attnLayers: number[];\n private layerAlpha: Map<number, number>;\n private headDim: number;\n private ropeBase: number;\n private _destroyed = false;\n\n readonly architecture = \"KaniTTS2ForCausalLM\";\n\n private constructor(ctx: GPUContext, loaded: LoadedKaniTTS, maxSeqLen: number) {\n this.ctx = ctx;\n this.loaded = loaded;\n this.tokenizer = loaded.tokenizer;\n this.rawConfig = loaded.rawConfig;\n this.cfg = parseKaniConfig(loaded.rawConfig);\n this.maxSeqLen = maxSeqLen;\n\n const hidden = this.rawConfig.hidden_size as number;\n const heads = this.rawConfig.num_attention_heads as number;\n this.headDim = (this.rawConfig.head_dim as number) ?? Math.floor(hidden / heads);\n this.ropeBase = (this.rawConfig.rope_theta as number) ?? 1_000_000.0;\n\n // ── Resolve per-layer learnable α from the alpha_weight scalars. ──\n this.attnLayers = kaniAttentionLayerIndices(this.rawConfig);\n this.layerAlpha = new Map<number, number>();\n const useLearnable = (this.rawConfig.use_learnable_rope as boolean) ?? false;\n for (const layer of this.attnLayers) {\n let alpha = 1.0;\n if (useLearnable) {\n const w = loaded.backboneWeights.get(`learnable_rope_layers.${layer}.alpha_weight`);\n const aw = w ? ((w.data as Float32Array)[0] ?? 0) : 0;\n alpha = kaniLayerAlpha(aw, this.cfg);\n }\n this.layerAlpha.set(layer, alpha);\n }\n\n // ── Build + upload the backbone graph once. ──\n const graph = generateKaniTtsGraph(this.rawConfig);\n this.backboneExec = new Executor(ctx, graph, { maxSeqLen, kvMode: \"f32\" });\n this.backboneExec.uploadWeightsMap(selectGraphWeights(graph, loaded.backboneWeights));\n this.backboneExec.initBindGroups();\n }\n\n static async create(options: KaniTTSOptions = {}): Promise<KaniTTS> {\n const ctx = await initGPU();\n const loaded = await loadKaniTTS({\n repo: options.repo,\n codecRepo: options.codecRepo,\n revision: options.revision,\n hfToken: options.hfToken,\n cacheDir: options.cacheDir,\n onProgress: options.onProgress,\n });\n const ctxLen = (loaded.rawConfig.max_position_embeddings as number) ?? 4096;\n const maxSeqLen = Math.min(options.maxSeqLen ?? 2048, ctxLen);\n return new KaniTTS(ctx, loaded, maxSeqLen);\n }\n\n /** Write per-layer cos/sin for token rows [rowStart, rowStart+positions.length). */\n private writeCosSin(positions: Float32Array, rowStart: number): void {\n const rowBytes = this.headDim * 4; // rope_dim == head_dim, f32\n for (const layer of this.attnLayers) {\n const alpha = this.layerAlpha.get(layer) ?? 1.0;\n const { cos, sin } = buildKaniLayerCosSin(positions, this.headDim, this.ropeBase, alpha);\n if (rowStart === 0) {\n this.backboneExec.writeInput(kaniCosTensor(layer), cos);\n this.backboneExec.writeInput(kaniSinTensor(layer), sin);\n } else {\n this.backboneExec.writeInputAt(kaniCosTensor(layer), cos, rowStart * rowBytes);\n this.backboneExec.writeInputAt(kaniSinTensor(layer), sin, rowStart * rowBytes);\n }\n }\n }\n\n /**\n * Synthesize speech for `text`. Returns 22 kHz mono PCM.\n *\n * Pipeline: build the [SOH]+text+[EOT,EOH] prompt → prefill the codec-LM →\n * AR-decode 4-token frames (per-codebook constrained sampling) until end_of_speech\n * → strip markers → codes → NanoCodec decode → PCM.\n */\n async speak(text: string, opts: SpeakOptions = {}): Promise<SpeakResult> {\n if (this._destroyed) {\n throw new Error(\"KaniTTS has been destroyed.\");\n }\n const temperature = opts.temperature ?? 1.0;\n const topP = opts.topP ?? 0.95;\n const repetitionPenalty = opts.repetitionPenalty ?? 1.1;\n const tag = opts.languageTag ?? \"en_us\";\n const maxNewTokens = Math.min(opts.maxNewTokens ?? DEFAULT_MAX_NEW_TOKENS, this.maxSeqLen);\n\n // ── Build prompt: [SOH] + tokenizer(\"tag: text\") + [EOT, EOH]. ──\n // The tokenizer prepends BOS (= start_of_text = 1) per its config.\n const textIds = this.tokenizer.encode(`${tag.trim()}: ${text}`);\n const prompt: number[] = [KANI_START_OF_HUMAN, ...textIds, KANI_END_OF_TEXT, KANI_END_OF_HUMAN];\n if (prompt.length >= this.maxSeqLen) {\n throw new Error(`Kani prompt is ${prompt.length} tokens >= maxSeqLen ${this.maxSeqLen}.`);\n }\n\n // ── Prefill. ──\n this.backboneExec.reset();\n const promptPos = computeKaniPositions(prompt, this.cfg);\n this.writeCosSin(promptPos, 0);\n const { logits } = await this.backboneExec.forward(new Uint32Array(prompt));\n\n // ── AR decode loop. ──\n const startTime = performance.now();\n const { audioTokens, finished } = await this.runDecodeLoop(prompt, logits, {\n temperature,\n topP,\n repetitionPenalty,\n maxNewTokens,\n maxFrames: opts.maxFrames ?? Number.POSITIVE_INFINITY,\n });\n\n // ── Strip to whole frames, convert to codes, NanoCodec decode. ──\n const usable = audioTokens.length - (audioTokens.length % this.cfg.tokensPerFrame);\n if (usable === 0) {\n throw new Error(\n `Kani produced no complete audio frames (finished=${finished}, ` +\n `audioTokens=${audioTokens.length}). The codec-LM emitted no speech.`,\n );\n }\n const { codes, numFrames } = audioTokensToCodes(audioTokens.slice(0, usable), this.cfg);\n const pcm = await this.decodeCodes(codes, numFrames);\n\n const totalTime = (performance.now() - startTime) / 1000;\n const audioSeconds = (numFrames * KANI_HOP) / KANI_SAMPLE_RATE;\n console.log(\n `[kani] frames=${numFrames} audio=${audioSeconds.toFixed(2)}s wall=${totalTime.toFixed(\n 2,\n )}s RTF=${(audioSeconds / totalTime).toFixed(2)}x`,\n );\n\n return { pcm, sampleRate: KANI_SAMPLE_RATE, frames: numFrames, audioSeconds };\n }\n\n /**\n * Autoregressive decode: from the prefill logits, emit one token per step —\n * greedy for the structural markers ([SOA][SOS]) and per-codebook constrained\n * sampling once in speech — collecting the audio tokens between SOS and EOS.\n * Writes each step's per-layer cos/sin row (frame-level logical position) before\n * the forward. Returns the collected audio tokens and whether EOS/cap was hit.\n */\n private async runDecodeLoop(\n prompt: number[],\n prefillLogits: Float32Array,\n p: {\n temperature: number;\n topP: number;\n repetitionPenalty: number;\n maxNewTokens: number;\n maxFrames: number;\n },\n ): Promise<{ audioTokens: number[]; finished: boolean }> {\n const seq: number[] = [...prompt];\n const audioTokens: number[] = [];\n const frameCap = p.maxFrames * this.cfg.tokensPerFrame;\n let logits = prefillLogits;\n let inSpeech = false;\n let finished = false;\n\n for (let step = 0; step < p.maxNewTokens; step++) {\n const nextToken = inSpeech\n ? this.sampleAudioToken(logits, audioTokens.length % this.cfg.tokensPerFrame, {\n temperature: p.temperature,\n topP: p.topP,\n repetitionPenalty: p.repetitionPenalty,\n previous: seq,\n })\n : this.argmax(logits);\n\n if (nextToken === this.cfg.startOfSpeech) {\n inSpeech = true;\n } else if (nextToken === this.cfg.endOfSpeech) {\n seq.push(nextToken);\n finished = true;\n break;\n } else if (inSpeech && nextToken >= this.cfg.audioTokensStart) {\n audioTokens.push(nextToken);\n }\n seq.push(nextToken);\n\n if (inSpeech && audioTokens.length >= frameCap) {\n finished = true;\n break;\n }\n if (seq.length >= this.maxSeqLen) {\n break;\n }\n\n // cos/sin row for the next physical position = current seqPos, at the logical\n // (frame-level) position of the token we are about to feed.\n this.writeCosSin(\n Float32Array.of(this.logicalPositionAt(seq)),\n this.backboneExec.currentSeqPos,\n );\n const res = await this.backboneExec.forward(new Uint32Array([nextToken]));\n logits = res.logits;\n }\n return { audioTokens, finished };\n }\n\n /** Logical (frame-level) position of the LAST token in `seq`. */\n private logicalPositionAt(seq: number[]): number {\n // computeKaniPositions returns positions BEFORE counting each token; the last\n // entry is the position the final token should be rotated at.\n const positions = computeKaniPositions(seq, this.cfg);\n return positions[positions.length - 1];\n }\n\n /** Greedy argmax over a logits row. */\n private argmax(logits: Float32Array): number {\n let best = 0;\n let bestVal = logits[0];\n for (let i = 1; i < logits.length; i++) {\n if (logits[i] > bestVal) {\n bestVal = logits[i];\n best = i;\n }\n }\n return best;\n }\n\n /**\n * Sample one audio token for codebook position `codebook`, constrained to that\n * codebook's valid window [audio_tokens_start + 4032*c, +4032). Allows end_of_speech\n * only at codebook 0 (frame boundary). Applies temperature, top-p, rep-penalty.\n */\n private sampleAudioToken(\n logits: Float32Array,\n codebook: number,\n p: {\n temperature: number;\n topP: number;\n repetitionPenalty: number;\n previous: number[];\n },\n ): number {\n const winStart = this.cfg.audioTokensStart + this.cfg.codebookSize * codebook;\n const winLen = this.cfg.codebookSize;\n // Candidate window: the codebook's valid tokens (+ end_of_speech at a frame\n // boundary, codebook 0), so the sampled token is always a valid audio code.\n const allowEos = codebook === 0;\n const n = winLen + (allowEos ? 1 : 0);\n const ids = new Int32Array(n);\n for (let i = 0; i < winLen; i++) {\n ids[i] = winStart + i;\n }\n if (allowEos) {\n ids[winLen] = this.cfg.endOfSpeech;\n }\n // Repetition penalty + temperature → softmax → top-p nucleus sample.\n const prev = p.repetitionPenalty !== 1.0 ? new Set(p.previous) : null;\n const scores = new Float32Array(n);\n for (let i = 0; i < n; i++) {\n let s = logits[ids[i]];\n if (prev?.has(ids[i])) {\n s = s > 0 ? s / p.repetitionPenalty : s * p.repetitionPenalty;\n }\n scores[i] = s / p.temperature;\n }\n softmaxInPlace(scores);\n return nucleusSample(scores, ids, p.topP);\n }\n\n /**\n * Decode NanoCodec codes [groups, T] (group-major) → PCM.\n *\n * The decoder graph carries concrete lengths, and the upsampled conv activations\n * for long clips overflow WebGPU's 65535 per-dimension dispatch cap. The decoder\n * is fully causal with a small (≤ a few frames) receptive field, so we decode in\n * frame chunks with a left-context lookback and keep only each chunk's own output\n * samples — numerically identical to a single decode, but bounded per dispatch.\n */\n private async decodeCodes(codes: Uint32Array, numFrames: number): Promise<Float32Array> {\n const groups = 4; // FSQ_NUM_GROUPS (codes are group-major [groups, T])\n const lookback = KANI_CODEC_LOOKBACK_FRAMES;\n const chunk = KANI_CODEC_CHUNK_FRAMES;\n const pcm = new Float32Array(numFrames * KANI_HOP);\n\n for (let start = 0; start < numFrames; start += chunk) {\n const ctxStart = Math.max(0, start - lookback);\n const ctxFrames = start - ctxStart; // discarded warmup frames\n const end = Math.min(numFrames, start + chunk);\n const winFrames = end - ctxStart;\n\n // Slice the codes window [ctxStart, end) for all groups, group-major.\n const winCodes = new Uint32Array(groups * winFrames);\n for (let g = 0; g < groups; g++) {\n for (let t = 0; t < winFrames; t++) {\n winCodes[g * winFrames + t] = codes[g * numFrames + (ctxStart + t)];\n }\n }\n\n const win = await this.decodeCodesWindow(winCodes, winFrames);\n // Keep only this chunk's samples (drop the lookback warmup).\n const dropSamples = ctxFrames * KANI_HOP;\n const keepSamples = (end - start) * KANI_HOP;\n pcm.set(win.subarray(dropSamples, dropSamples + keepSamples), start * KANI_HOP);\n }\n\n // Clamp to [-1, 1] (NanoCodec's output activation).\n for (let i = 0; i < pcm.length; i++) {\n pcm[i] = Math.min(1, Math.max(-1, pcm[i]));\n }\n return pcm;\n }\n\n /** Run the NanoCodec decoder graph for a single (bounded) code window → PCM. */\n private async decodeCodesWindow(winCodes: Uint32Array, winFrames: number): Promise<Float32Array> {\n const graph = generateNanoCodecDecoderGraph({ numFrames: winFrames });\n const exec = new Executor(this.ctx, graph, { maxSeqLen: winFrames, kvMode: \"f32\" });\n try {\n exec.uploadWeightsMap(selectGraphWeights(graph, this.loaded.codecWeights));\n exec.initBindGroups();\n exec.reset();\n exec.writeInput(\"audio_codes\", winCodes);\n return await exec.runGraphOutput(graph.outputs[0], winFrames * KANI_HOP);\n } finally {\n exec.destroy();\n }\n }\n\n destroy(): void {\n if (this._destroyed) {\n return;\n }\n this._destroyed = true;\n this.backboneExec.destroy();\n this.loaded.backboneWeights.clear();\n this.loaded.codecWeights.clear();\n }\n}\n","/**\n * CPU-side token sampling from logits.\n *\n * Applies temperature, top-k, and top-p (nucleus) filtering,\n * then samples from the resulting probability distribution.\n *\n * Uses typed arrays and min-heap for zero-allocation top-K selection.\n * For vocab_size ~152K with topK=50, this avoids creating 152K JS tuples.\n */\n\nexport interface SamplingParams {\n temperature?: number; // Default: 0.7\n topK?: number; // Default: 50\n topP?: number; // Default: 0.9\n repetitionPenalty?: number; // Default: 1.0 (disabled)\n}\n\n// Pre-allocated buffers for top-K selection (avoid GC pressure in hot loop)\nlet _heapIndices: Uint32Array | null = null;\nlet _heapValues: Float32Array | null = null;\n\n/**\n * Sample a token ID from logits.\n *\n * Pipeline: repetition penalty → temperature → top-k (min-heap) → softmax → top-p → sample.\n */\nexport function sampleToken(\n logits: Float32Array,\n params: SamplingParams = {},\n previousTokens?: number[],\n): number {\n const temperature = params.temperature ?? 0.7;\n const topK = params.topK ?? 50;\n const topP = params.topP ?? 0.9;\n const repetitionPenalty = params.repetitionPenalty ?? 1.0;\n\n // Greedy decoding when temperature is ~0\n if (temperature < 1e-6) {\n return argmax(logits);\n }\n\n const N = logits.length;\n const K = Math.min(topK > 0 ? topK : N, N);\n\n // Ensure pre-allocated buffers are large enough\n if (!_heapIndices || _heapIndices.length < K) {\n _heapIndices = new Uint32Array(K);\n _heapValues = new Float32Array(K);\n }\n const hIdx = _heapIndices;\n const hVal = _heapValues!;\n\n // Apply repetition penalty set (built once)\n let penaltySet: Set<number> | null = null;\n if (repetitionPenalty !== 1.0 && previousTokens?.length) {\n penaltySet = new Set(previousTokens);\n }\n\n // Single pass: apply temperature + repetition penalty + top-K min-heap\n let heapSize = 0;\n\n for (let i = 0; i < N; i++) {\n let s = logits[i];\n\n // Repetition penalty\n if (penaltySet?.has(i)) {\n s = s > 0 ? s / repetitionPenalty : s * repetitionPenalty;\n }\n\n // Temperature\n s /= temperature;\n\n if (heapSize < K) {\n // Fill the heap\n hIdx[heapSize] = i;\n hVal[heapSize] = s;\n heapSize++;\n if (heapSize === K) {\n // Build min-heap\n for (let j = (K >> 1) - 1; j >= 0; j--) {\n siftDown(hIdx, hVal, j, K);\n }\n }\n } else if (s > hVal[0]) {\n // Replace min and sift down\n hIdx[0] = i;\n hVal[0] = s;\n siftDown(hIdx, hVal, 0, K);\n }\n }\n\n // Sort top-K by value descending (K is small, ~50)\n // Simple insertion sort for small K\n for (let i = 1; i < heapSize; i++) {\n const vi = hVal[i];\n const ii = hIdx[i];\n let j = i - 1;\n while (j >= 0 && hVal[j] < vi) {\n hVal[j + 1] = hVal[j];\n hIdx[j + 1] = hIdx[j];\n j--;\n }\n hVal[j + 1] = vi;\n hIdx[j + 1] = ii;\n }\n\n // Softmax over top-K candidates\n const maxScore = hVal[0];\n let sumExp = 0;\n for (let i = 0; i < heapSize; i++) {\n const p = Math.exp(hVal[i] - maxScore);\n hVal[i] = p; // Reuse hVal for probabilities\n sumExp += p;\n }\n const invSum = 1 / sumExp;\n for (let i = 0; i < heapSize; i++) {\n hVal[i] *= invSum;\n }\n\n // Apply top-p (nucleus): keep smallest set whose cumulative prob >= topP\n let candidateCount = heapSize;\n if (topP < 1.0) {\n let cumulative = 0;\n for (let i = 0; i < heapSize; i++) {\n cumulative += hVal[i];\n if (cumulative >= topP) {\n candidateCount = i + 1;\n break;\n }\n }\n // Re-normalize\n let sum = 0;\n for (let i = 0; i < candidateCount; i++) sum += hVal[i];\n const inv = 1 / sum;\n for (let i = 0; i < candidateCount; i++) hVal[i] *= inv;\n }\n\n // Weighted random sample\n const r = Math.random();\n let cumulative = 0;\n for (let i = 0; i < candidateCount; i++) {\n cumulative += hVal[i];\n if (r <= cumulative) return hIdx[i];\n }\n\n return hIdx[candidateCount - 1];\n}\n\n/**\n * Return the index of the maximum value (greedy decoding).\n */\nexport function argmax(arr: Float32Array): number {\n let maxIdx = 0;\n let maxVal = arr[0];\n for (let i = 1; i < arr.length; i++) {\n if (arr[i] > maxVal) {\n maxVal = arr[i];\n maxIdx = i;\n }\n }\n return maxIdx;\n}\n\n/** Min-heap sift down on parallel index/value typed arrays. */\nfunction siftDown(indices: Uint32Array, values: Float32Array, i: number, n: number): void {\n while (true) {\n let smallest = i;\n const left = 2 * i + 1;\n const right = 2 * i + 2;\n if (left < n && values[left] < values[smallest]) smallest = left;\n if (right < n && values[right] < values[smallest]) smallest = right;\n if (smallest === i) break;\n // Swap\n const ti = indices[i];\n indices[i] = indices[smallest];\n indices[smallest] = ti;\n const tv = values[i];\n values[i] = values[smallest];\n values[smallest] = tv;\n i = smallest;\n }\n}\n","/**\n * VisionExecutor — runs the Qwen3.5 ViT graph on WebGPU.\n *\n * Kept separate from the text `Executor` (which is specialized for\n * input_ids / logits / argmax / KV-cache decode). The vision tower is a single\n * non-autoregressive prefill over `N` patches, so this executor is deliberately\n * simple: one buffer per tensor, dispatch in executionOrder, read back the\n * merged image embeddings. It reuses the shared kernel registry and device\n * helpers, so kernel math is identical to the text path.\n *\n * Symbolic dims:\n * \"N\" → number of patches (runtime)\n * \"Nm\" → N / spatial_merge_unit (merged token count)\n */\n\nimport type { GPUContext } from \"./device.js\";\nimport {\n createBindGroup,\n createStorageBuffer,\n createUniformBuffer,\n destroyBuffers,\n getOrCreatePipeline,\n} from \"./device.js\";\nimport { CANONICAL_KEYS, DTYPE_BYTES, type ModelGraph, type OpNode } from \"./ir.js\";\nimport { KERNEL_REGISTRY, type KernelSpec, MATMUL_BIAS_F16C_SPEC } from \"./kernels/registry.js\";\n\nconst MAP_MODE_READ = 0x0001;\n\n/** Pack an f32 array to IEEE half (f16) bits (round-to-nearest-even). */\nconst _f16scratch = new ArrayBuffer(4);\nconst _f16f32 = new Float32Array(_f16scratch);\nconst _f16u32 = new Uint32Array(_f16scratch);\nfunction packF16(src: Float32Array): Uint16Array {\n const out = new Uint16Array(src.length);\n for (let i = 0; i < src.length; i++) {\n _f16f32[0] = src[i];\n const f = _f16u32[0];\n const sign = (f >>> 16) & 0x8000;\n const exp = ((f >>> 23) & 0xff) - 112; // 127 - 15\n const mant = f & 0x7fffff;\n if (exp <= 0) {\n out[i] = sign;\n } else if (exp >= 0x1f) {\n out[i] = sign | 0x7c00;\n } else {\n const round = (mant & 0x1000) !== 0;\n let half = sign | (exp << 10) | (mant >>> 13);\n if (round) half += 1;\n out[i] = half;\n }\n }\n return out;\n}\n\ninterface VisionDispatch {\n node: OpNode;\n spec: KernelSpec;\n pipeline: GPUComputePipeline;\n bindGroup: GPUBindGroup;\n uniformBuffer: GPUBuffer;\n}\n\nexport interface VisionInputs {\n /** Flattened patches [N, patch_dim]. */\n patches: Float32Array;\n /** Bilinear-interpolated learned pos embeddings [N, hidden_size]. */\n posEmbeds: Float32Array;\n /** Precomputed rotary cos [N, head_dim]. */\n cos: Float32Array;\n /** Precomputed rotary sin [N, head_dim]. */\n sin: Float32Array;\n /** Number of patches (rows). */\n numPatches: number;\n}\n\n/**\n * Coarse stage callback for the ViT encode. Fires before each transformer layer\n * (and the pre/post stages) so a host harness can localize a mobile GPU-process\n * crash to a specific layer instead of seeing only \"crashed after load\". Kept\n * synchronous and cheap; throwing is the caller's responsibility.\n */\nexport type VisionStageCallback = (\n stage: string,\n info?: { layer?: number; total?: number },\n) => void;\n\n/**\n * WebKit only: the ViT bidirectional Attention op is a single dispatch whose\n * grid is [N query rows, heads], each row looping over all N keys — O(N²) work in\n * one submission. Past a few hundred rows that can exceed Metal's ~few-second GPU\n * watchdog and kill the web-content process (indistinguishable from OOM). We split\n * the attention grid into windows of this many query rows, each its own\n * submit + onSubmittedWorkDone() drain, so no single submission runs long enough\n * to trip the watchdog. Math is unchanged: every query row computes identically\n * regardless of how the grid is partitioned.\n */\nconst WEBKIT_ATTN_CHUNK_ROWS = 64;\n\nexport class VisionExecutor {\n private ctx: GPUContext;\n private graph: ModelGraph;\n private mergeUnit: number;\n\n private weightBuffers = new Map<string, GPUBuffer>();\n private activationBuffers = new Map<string, GPUBuffer>();\n private dispatches: VisionDispatch[] = [];\n private maxPatches: number;\n /** Weight (B) names of MatMulBias nodes stored as f16 (empty without shader-f16). */\n private f16WeightNames = new Set<string>();\n\n /** Runtime pooled-token count for the Gemma 4 ViT (\"Np\" dim); 0 for Qwen. */\n private gemma4Np = 0;\n /** True when this graph is the Gemma 4 vision tower (uses \"Np\" + PoolMatMul). */\n private readonly isGemma4: boolean;\n\n constructor(ctx: GPUContext, graph: ModelGraph, maxPatches: number) {\n this.ctx = ctx;\n this.graph = graph;\n this.maxPatches = maxPatches;\n this.isGemma4 = graph.architecture === \"Gemma4VisionModel\";\n // merge unit = merger fc1 in-width / block hidden size (3072 / 768 = 4).\n // Gemma 4 has no merger (it pools via a host matrix), so fall back to 1.\n const mergedIn = graph.tensors[CANONICAL_KEYS.visMergerFc1W]?.shape[1] as number;\n const hidden = graph.config.hidden_size;\n this.mergeUnit = mergedIn && hidden ? mergedIn / hidden : 1;\n if (ctx.hasF16) {\n for (const node of graph.nodes) {\n if (node.opType === \"MatMulBias\" && node.inputs[1]) {\n this.f16WeightNames.add(node.inputs[1]);\n }\n }\n }\n this.allocateActivationBuffers();\n }\n\n uploadWeights(weights: Map<string, { data: ArrayBufferView; shape: number[] }>): void {\n for (const [name, desc] of Object.entries(this.graph.tensors)) {\n if (desc.storage !== \"constant\") continue;\n const w = weights.get(name);\n if (!w) throw new Error(`VisionExecutor: missing weight \"${name}\"`);\n let data: ArrayBufferView = w.data;\n if (this.f16WeightNames.has(name) && w.data instanceof Float32Array) {\n data = packF16(w.data);\n }\n const buffer = createStorageBuffer(this.ctx, `vweight_${name}`, data.byteLength, data);\n this.weightBuffers.set(name, buffer);\n }\n }\n\n initBindGroups(): void {\n const dummyShapes = this.resolveShapes(this.maxPatches);\n for (const nodeId of this.graph.executionOrder) {\n const node = this.graph.nodes.find((n) => n.id === nodeId)!;\n let spec = KERNEL_REGISTRY[node.opType];\n if (!spec) throw new Error(`VisionExecutor: no kernel for op \"${node.opType}\"`);\n if (node.opType === \"MatMulBias\" && this.f16WeightNames.has(node.inputs[1])) {\n spec = MATMUL_BIAS_F16C_SPEC;\n }\n\n const pipeline = getOrCreatePipeline(\n this.ctx,\n `vkernel_${nodeId}`,\n spec.shaderCode,\n spec.entryPoint,\n );\n const paramsData = spec.buildParams(node, dummyShapes, { seqPos: 0 });\n const uniformBuffer = createUniformBuffer(this.ctx, `vuniform_${nodeId}`, paramsData);\n const bufferEntries = this.gatherBuffers(spec, node, uniformBuffer);\n const bindGroup = createBindGroup(this.ctx, pipeline, bufferEntries, `vbg_${nodeId}`);\n this.dispatches.push({ node, spec, pipeline, bindGroup, uniformBuffer });\n }\n }\n\n /**\n * Encode patches → merged image embeddings [Nm, out_hidden_size].\n *\n * `onStage` (optional) fires coarse phase breadcrumbs during the WebKit path so\n * a host can localize a GPU-process crash to a specific layer.\n */\n async encode(\n inputs: VisionInputs,\n onStage?: VisionStageCallback,\n ): Promise<{ embeds: Float32Array; rows: number; dim: number }> {\n const N = inputs.numPatches;\n if (N > this.maxPatches) {\n throw new Error(`VisionExecutor: ${N} patches exceeds maxPatches=${this.maxPatches}`);\n }\n const q = this.ctx.device.queue;\n q.writeBuffer(this.activationBuffers.get(\"vis_patches\")!, 0, inputs.patches as BufferSource);\n q.writeBuffer(\n this.activationBuffers.get(\"vis_pos_embeds\")!,\n 0,\n inputs.posEmbeds as BufferSource,\n );\n q.writeBuffer(this.activationBuffers.get(\"vis_cos\")!, 0, inputs.cos as BufferSource);\n q.writeBuffer(this.activationBuffers.get(\"vis_sin\")!, 0, inputs.sin as BufferSource);\n\n const resolvedShapes = this.resolveShapes(N);\n const ctxRt = { seqPos: 0 };\n\n // Update uniforms + dispatch sizes. Attention params build with qOffset=0 here\n // (correct for the Dawn single-pass path); the WebKit path re-writes the\n // Attention uniform per query-row chunk with a nonzero q_offset below.\n const sizes: Array<[number, number, number]> = [];\n for (const d of this.dispatches) {\n const params = d.spec.buildParams(d.node, resolvedShapes, ctxRt);\n q.writeBuffer(d.uniformBuffer, 0, params);\n sizes.push(d.spec.getDispatchSize(d.node, resolvedShapes, ctxRt));\n }\n\n const multiEncoder = this.ctx.isWebKitWebGPU;\n if (multiEncoder) {\n onStage?.(\"vit-encode-start\", { total: this.dispatches.length });\n for (let i = 0; i < this.dispatches.length; i++) {\n const d = this.dispatches[i];\n if (d.node.opType === \"Attention\") {\n // Split the O(N²) attention grid into watchdog-safe query-row windows.\n // Each window: write the uniform with its q_offset, submit, drain. The\n // drain guarantees the chunk completes before the next overwrites the\n // shared uniform buffer.\n const [gridX, gridY, gridZ] = sizes[i];\n const layer = Number.parseInt(d.node.id.replace(/\\D+/g, \"\"), 10);\n if (!Number.isNaN(layer)) onStage?.(\"vit-attn\", { layer });\n for (let start = 0; start < gridX; start += WEBKIT_ATTN_CHUNK_ROWS) {\n const rows = Math.min(WEBKIT_ATTN_CHUNK_ROWS, gridX - start);\n const params = d.spec.buildParams(d.node, resolvedShapes, {\n seqPos: 0,\n qOffset: start,\n });\n q.writeBuffer(d.uniformBuffer, 0, params);\n const enc = this.ctx.device.createCommandEncoder();\n const p = enc.beginComputePass();\n p.setPipeline(d.pipeline);\n p.setBindGroup(0, d.bindGroup);\n p.dispatchWorkgroups(rows, gridY, gridZ);\n p.end();\n q.submit([enc.finish()]);\n await q.onSubmittedWorkDone();\n }\n continue;\n }\n const enc = this.ctx.device.createCommandEncoder();\n const p = enc.beginComputePass();\n p.setPipeline(d.pipeline);\n p.setBindGroup(0, d.bindGroup);\n p.dispatchWorkgroups(...sizes[i]);\n p.end();\n q.submit([enc.finish()]);\n await q.onSubmittedWorkDone();\n }\n onStage?.(\"vit-encode-done\");\n } else {\n const enc = this.ctx.device.createCommandEncoder({ label: \"vision_encode\" });\n const pass = enc.beginComputePass({ label: \"vision_pass\" });\n for (let i = 0; i < this.dispatches.length; i++) {\n pass.setPipeline(this.dispatches[i].pipeline);\n pass.setBindGroup(0, this.dispatches[i].bindGroup);\n pass.dispatchWorkgroups(...sizes[i]);\n }\n pass.end();\n q.submit([enc.finish()]);\n }\n\n // Read back the merged embeddings.\n const rows = N / this.mergeUnit;\n const dim = this.graph.config.vision_embed_dim\n ? (this.graph.tensors.vis_image_embeds.shape[1] as number)\n : (this.graph.tensors.vis_image_embeds.shape[1] as number);\n const byteLen = rows * dim * 4;\n const out = this.activationBuffers.get(\"vis_image_embeds\")!;\n const readback = this.ctx.device.createBuffer({ size: byteLen, usage: 0x0001 | 0x0008 });\n const enc = this.ctx.device.createCommandEncoder();\n enc.copyBufferToBuffer(out, 0, readback, 0, byteLen);\n this.ctx.device.queue.submit([enc.finish()]);\n await readback.mapAsync(MAP_MODE_READ, 0, byteLen);\n const embeds = new Float32Array(readback.getMappedRange(0, byteLen).slice(0));\n readback.unmap();\n readback.destroy();\n return { embeds, rows, dim };\n }\n\n /**\n * Encode patches through the Gemma 4 ViT → projected image tokens [Np, text_hidden].\n *\n * Distinct from the Qwen `encode()`: the Gemma graph has 5 inputs (patches,\n * axial pos-embeds, axial rotary cos/sin, and a host-built [Np,N] pooling matrix)\n * and its output rows (Np) are the pooled soft-token count, resolved from the\n * pooling matrix rather than an N/mergeUnit ratio. Reuses the same dispatch\n * machinery + WebKit per-dispatch-drain discipline.\n */\n async encodeGemma4(\n inputs: {\n patches: Float32Array; // [N, patch_dim]\n posEmbeds: Float32Array; // [N, hidden]\n cos: Float32Array; // [N, head_dim]\n sin: Float32Array; // [N, head_dim]\n poolMatrix: Float32Array; // [Np, N]\n numPatches: number;\n numPooled: number;\n },\n onStage?: VisionStageCallback,\n ): Promise<{ embeds: Float32Array; rows: number; dim: number }> {\n const N = inputs.numPatches;\n if (N > this.maxPatches) {\n throw new Error(`VisionExecutor(Gemma4): ${N} patches exceeds maxPatches=${this.maxPatches}`);\n }\n this.gemma4Np = inputs.numPooled;\n const q = this.ctx.device.queue;\n q.writeBuffer(this.activationBuffers.get(\"g4v_patches\")!, 0, inputs.patches as BufferSource);\n q.writeBuffer(\n this.activationBuffers.get(\"g4v_pos_embeds\")!,\n 0,\n inputs.posEmbeds as BufferSource,\n );\n q.writeBuffer(this.activationBuffers.get(\"g4v_cos\")!, 0, inputs.cos as BufferSource);\n q.writeBuffer(this.activationBuffers.get(\"g4v_sin\")!, 0, inputs.sin as BufferSource);\n q.writeBuffer(this.activationBuffers.get(\"g4v_pool_w\")!, 0, inputs.poolMatrix as BufferSource);\n\n const resolvedShapes = this.resolveShapes(N);\n const ctxRt = { seqPos: 0 };\n const sizes: Array<[number, number, number]> = [];\n for (const d of this.dispatches) {\n const params = d.spec.buildParams(d.node, resolvedShapes, ctxRt);\n q.writeBuffer(d.uniformBuffer, 0, params);\n sizes.push(d.spec.getDispatchSize(d.node, resolvedShapes, ctxRt));\n }\n\n const multiEncoder = this.ctx.isWebKitWebGPU;\n if (multiEncoder) {\n onStage?.(\"vit-encode-start\", { total: this.dispatches.length });\n for (let i = 0; i < this.dispatches.length; i++) {\n const d = this.dispatches[i];\n if (d.node.opType === \"Attention\") {\n const [gridX, gridY, gridZ] = sizes[i];\n const layer = Number.parseInt(d.node.id.replace(/\\D+/g, \"\"), 10);\n if (!Number.isNaN(layer)) onStage?.(\"vit-attn\", { layer });\n for (let start = 0; start < gridX; start += WEBKIT_ATTN_CHUNK_ROWS) {\n const rows = Math.min(WEBKIT_ATTN_CHUNK_ROWS, gridX - start);\n const params = d.spec.buildParams(d.node, resolvedShapes, {\n seqPos: 0,\n qOffset: start,\n });\n q.writeBuffer(d.uniformBuffer, 0, params);\n const enc = this.ctx.device.createCommandEncoder();\n const p = enc.beginComputePass();\n p.setPipeline(d.pipeline);\n p.setBindGroup(0, d.bindGroup);\n p.dispatchWorkgroups(rows, gridY, gridZ);\n p.end();\n q.submit([enc.finish()]);\n await q.onSubmittedWorkDone();\n }\n continue;\n }\n const enc = this.ctx.device.createCommandEncoder();\n const p = enc.beginComputePass();\n p.setPipeline(d.pipeline);\n p.setBindGroup(0, d.bindGroup);\n p.dispatchWorkgroups(...sizes[i]);\n p.end();\n q.submit([enc.finish()]);\n await q.onSubmittedWorkDone();\n }\n onStage?.(\"vit-encode-done\");\n } else {\n const enc = this.ctx.device.createCommandEncoder({ label: \"gemma4_vision_encode\" });\n const pass = enc.beginComputePass({ label: \"gemma4_vision_pass\" });\n for (let i = 0; i < this.dispatches.length; i++) {\n pass.setPipeline(this.dispatches[i].pipeline);\n pass.setBindGroup(0, this.dispatches[i].bindGroup);\n pass.dispatchWorkgroups(...sizes[i]);\n }\n pass.end();\n q.submit([enc.finish()]);\n }\n\n const rows = inputs.numPooled;\n const dim = this.graph.tensors.g4v_image_embeds.shape[1] as number;\n const byteLen = rows * dim * 4;\n const out = this.activationBuffers.get(\"g4v_image_embeds\")!;\n const readback = this.ctx.device.createBuffer({ size: byteLen, usage: 0x0001 | 0x0008 });\n const enc = this.ctx.device.createCommandEncoder();\n enc.copyBufferToBuffer(out, 0, readback, 0, byteLen);\n this.ctx.device.queue.submit([enc.finish()]);\n await readback.mapAsync(MAP_MODE_READ, 0, byteLen);\n const embeds = new Float32Array(readback.getMappedRange(0, byteLen).slice(0));\n readback.unmap();\n readback.destroy();\n return { embeds, rows, dim };\n }\n\n /** True if this executor is the Gemma 4 vision tower. */\n get gemma4(): boolean {\n return this.isGemma4;\n }\n\n /** Read back any named activation (debug). Must be called right after encode(). */\n async debugReadBuffer(name: string, maxElements?: number): Promise<Float32Array> {\n const buffer = this.activationBuffers.get(name) ?? this.weightBuffers.get(name);\n if (!buffer) throw new Error(`VisionExecutor: no buffer \"${name}\"`);\n const byteLen = maxElements ? Math.min(maxElements * 4, buffer.size) : buffer.size;\n const readback = this.ctx.device.createBuffer({ size: byteLen, usage: 0x0001 | 0x0008 });\n const enc = this.ctx.device.createCommandEncoder();\n enc.copyBufferToBuffer(buffer, 0, readback, 0, byteLen);\n this.ctx.device.queue.submit([enc.finish()]);\n await readback.mapAsync(MAP_MODE_READ, 0, byteLen);\n const data = new Float32Array(readback.getMappedRange(0, byteLen).slice(0));\n readback.unmap();\n readback.destroy();\n return data;\n }\n\n destroy(): void {\n destroyBuffers([...this.weightBuffers.values()]);\n destroyBuffers([...this.activationBuffers.values()]);\n for (const d of this.dispatches) d.uniformBuffer.destroy();\n this.weightBuffers.clear();\n this.activationBuffers.clear();\n this.dispatches = [];\n }\n\n // ── Private ──\n\n /** Max pooled tokens for buffer sizing: maxPatches with no merge/pool ratio applied. */\n private maxPooled(): number {\n // Gemma 4 pools by k² (k = pooling_kernel_size). With no merger key we cannot\n // read k here, so reserve maxPatches rows (an upper bound — Np ≤ N always).\n return this.maxPatches;\n }\n\n private resolveShapes(N: number): Record<string, number[]> {\n const Nm = N / this.mergeUnit;\n const Np = this.gemma4Np || N; // resolved per-encode for Gemma 4; ≤ N\n const resolved: Record<string, number[]> = {};\n for (const [name, desc] of Object.entries(this.graph.tensors)) {\n resolved[name] = desc.shape.map((d) => {\n if (d === \"N\") return N;\n if (d === \"Nm\") return Nm;\n if (d === \"Np\") return Np;\n return d as number;\n });\n }\n return resolved;\n }\n\n private allocateActivationBuffers(): void {\n // One dedicated buffer per activation tensor sized at maxPatches. Vision\n // graphs are tiny (≤ a few thousand patches × ≤3072 floats), so no pooling.\n for (const [name, desc] of Object.entries(this.graph.tensors)) {\n if (desc.storage !== \"activation\") continue;\n const shape = desc.shape.map((d) => {\n if (d === \"N\") return this.maxPatches;\n if (d === \"Nm\") return this.maxPatches / this.mergeUnit;\n if (d === \"Np\") return this.maxPooled();\n return d as number;\n });\n const bytes = shape.reduce((a, b) => a * b, 1) * DTYPE_BYTES[desc.dtype];\n this.activationBuffers.set(name, createStorageBuffer(this.ctx, `vact_${name}`, bytes));\n }\n }\n\n private gatherBuffers(\n spec: KernelSpec,\n node: OpNode,\n uniformBuffer: GPUBuffer,\n ): Array<{ buffer: GPUBuffer }> {\n const entries: Array<{ buffer: GPUBuffer }> = [];\n const tensorNames = [...node.inputs, ...node.outputs];\n let tensorIdx = 0;\n for (const binding of spec.bindings) {\n if (binding.type === \"uniform\") {\n entries.push({ buffer: uniformBuffer });\n } else {\n const tensorName = tensorNames[tensorIdx++];\n const buffer = this.weightBuffers.get(tensorName) ?? this.activationBuffers.get(tensorName);\n if (!buffer)\n throw new Error(`VisionExecutor: no buffer for \"${tensorName}\" in op ${node.id}`);\n entries.push({ buffer });\n }\n }\n return entries;\n }\n}\n","/**\n * Host-side vision preprocessing for the Qwen3.5 ViT.\n *\n * The learned position embeddings (bilinear-interpolated over the patch grid)\n * and the 2D rotary cos/sin tables are functions of the image grid (t, h, w)\n * ONLY — not of the model weights or pixel values. They are cheap to compute on\n * the CPU and fed to the GPU graph as input activations, keeping the graph to\n * weight-dependent math while staying byte-identical to HF transformers.\n *\n * Ports (verified against transformers 5.12 vision_utils.py + modeling_qwen3_5):\n * - get_vision_bilinear_indices_and_weights → buildPosEmbeds()\n * - get_vision_position_ids → buildPositionIds()\n * - Qwen3_5VisionRotaryEmbedding → buildRotaryCosSin()\n *\n * Patch ordering: both the pos-embed gather and the position ids reorder patches\n * into spatial_merge_size×spatial_merge_size groups, matching the HF image\n * processor's output ordering, so the merger's [N,h]→[N/u,h*u] reshape lines up.\n */\n\nexport interface VisionGridConfig {\n hiddenSize: number; // 768\n numHeads: number; // 12\n numPositionEmbeddings: number; // 2304 → num_grid_per_side = 48\n spatialMergeSize: number; // 2\n ropeTheta?: number; // 10000\n}\n\nexport interface VisionPositionTensors {\n /** [N, hidden_size] bilinear-interpolated learned position embeddings. */\n posEmbeds: Float32Array;\n /** [N, head_dim] rotary cosines. */\n cos: Float32Array;\n /** [N, head_dim] rotary sines. */\n sin: Float32Array;\n numPatches: number;\n}\n\n/** linspace(0, n-1, count) matching torch.linspace. */\nfunction linspace(stop: number, count: number): Float64Array {\n const out = new Float64Array(count);\n if (count === 1) {\n out[0] = 0;\n return out;\n }\n const step = stop / (count - 1);\n for (let i = 0; i < count; i++) out[i] = i * step;\n return out;\n}\n\n/**\n * Compute the spatial-merge reorder index for a (h, w) grid:\n * reorder[k] gives the source patch index (row-major h*w) for output slot k,\n * grouping 2×2 (merge×merge) spatial blocks contiguously. Matches the `reorder`\n * in get_vision_bilinear_indices_and_weights and the position-id reshape.\n */\nfunction spatialMergeReorder(h: number, w: number, merge: number): Int32Array {\n // HF: h_idx[h//m, m], w_idx[w//m, m];\n // (h_idx[:,:,None,None]*w + w_idx[None,None,:,:]).transpose(1,2).flatten()\n // Result dims (after transpose): [h//m, w//m, m(h), m(w)].\n const hb = h / merge;\n const wb = w / merge;\n const out = new Int32Array(h * w);\n let k = 0;\n for (let bh = 0; bh < hb; bh++) {\n for (let bw = 0; bw < wb; bw++) {\n for (let ih = 0; ih < merge; ih++) {\n for (let iw = 0; iw < merge; iw++) {\n const hh = bh * merge + ih;\n const ww = bw * merge + iw;\n out[k++] = hh * w + ww;\n }\n }\n }\n }\n return out;\n}\n\n/**\n * Build bilinear-interpolated learned position embeddings [N, hidden].\n * posEmbedTable is the raw pos_embed.weight [num_position_embeddings, hidden].\n */\nexport function buildPosEmbeds(\n gridTHW: [number, number, number],\n posEmbedTable: Float32Array,\n cfg: VisionGridConfig,\n): Float32Array {\n const [t, h, w] = gridTHW;\n const hidden = cfg.hiddenSize;\n const side = Math.round(Math.sqrt(cfg.numPositionEmbeddings)); // 48\n const merge = cfg.spatialMergeSize;\n\n const hGrid = linspace(side - 1, h);\n const wGrid = linspace(side - 1, w);\n const hFloor = new Int32Array(h);\n const hCeil = new Int32Array(h);\n const hFrac = new Float64Array(h);\n for (let i = 0; i < h; i++) {\n hFloor[i] = Math.trunc(hGrid[i]);\n hCeil[i] = Math.min(hFloor[i] + 1, side - 1);\n hFrac[i] = hGrid[i] - hFloor[i];\n }\n const wFloor = new Int32Array(w);\n const wCeil = new Int32Array(w);\n const wFrac = new Float64Array(w);\n for (let j = 0; j < w; j++) {\n wFloor[j] = Math.trunc(wGrid[j]);\n wCeil[j] = Math.min(wFloor[j] + 1, side - 1);\n wFrac[j] = wGrid[j] - wFloor[j];\n }\n\n // Per (row-major h*w) patch: 4 corner indices + weights.\n const hw = h * w;\n const cornerIdx = [\n new Int32Array(hw),\n new Int32Array(hw),\n new Int32Array(hw),\n new Int32Array(hw),\n ];\n const cornerW = [\n new Float64Array(hw),\n new Float64Array(hw),\n new Float64Array(hw),\n new Float64Array(hw),\n ];\n let p = 0;\n for (let i = 0; i < h; i++) {\n const hf = hFloor[i] * side;\n const hc = hCeil[i] * side;\n const hfr = hFrac[i];\n for (let j = 0; j < w; j++) {\n cornerIdx[0][p] = hf + wFloor[j];\n cornerIdx[1][p] = hf + wCeil[j];\n cornerIdx[2][p] = hc + wFloor[j];\n cornerIdx[3][p] = hc + wCeil[j];\n cornerW[0][p] = (1 - hfr) * (1 - wFrac[j]);\n cornerW[1][p] = (1 - hfr) * wFrac[j];\n cornerW[2][p] = hfr * (1 - wFrac[j]);\n cornerW[3][p] = hfr * wFrac[j];\n p++;\n }\n }\n\n const reorder = spatialMergeReorder(h, w, merge);\n const N = t * hw;\n const out = new Float32Array(N * hidden);\n // For each temporal repeat and reordered patch, gather the 4 weighted corners.\n for (let tt = 0; tt < t; tt++) {\n for (let k = 0; k < hw; k++) {\n const src = reorder[k];\n const dstRow = tt * hw + k;\n const dstBase = dstRow * hidden;\n for (let corner = 0; corner < 4; corner++) {\n const tableRow = cornerIdx[corner][src] * hidden;\n const wgt = cornerW[corner][src];\n if (wgt === 0) continue;\n for (let d = 0; d < hidden; d++) {\n out[dstBase + d] += posEmbedTable[tableRow + d] * wgt;\n }\n }\n }\n }\n return out;\n}\n\n/**\n * Build the reordered (row, col) position ids [N, 2] for rotary, matching\n * get_vision_position_ids.\n */\nexport function buildPositionIds(gridTHW: [number, number, number], merge: number): Int32Array {\n const [t, h, w] = gridTHW;\n const hb = h / merge;\n const wb = w / merge;\n const hw = h * w;\n const perFrame = new Int32Array(hw * 2);\n let k = 0;\n // hpos/wpos reshaped [h//m, m, w//m, m].transpose(1,2).flatten() → block-major.\n for (let bh = 0; bh < hb; bh++) {\n for (let bw = 0; bw < wb; bw++) {\n for (let ih = 0; ih < merge; ih++) {\n for (let iw = 0; iw < merge; iw++) {\n perFrame[k * 2] = bh * merge + ih; // row (h) position\n perFrame[k * 2 + 1] = bw * merge + iw; // col (w) position\n k++;\n }\n }\n }\n }\n const out = new Int32Array(t * hw * 2);\n for (let tt = 0; tt < t; tt++) out.set(perFrame, tt * hw * 2);\n return out;\n}\n\n/**\n * Build rotary cos/sin tables [N, head_dim] from position ids, matching\n * Qwen3_5VisionRotaryEmbedding + the cat((rotary, rotary)) in VisionModel.forward.\n *\n * rotary_pos_emb(position_ids) = (position_ids[..,None] * inv_freq).flatten(1)\n * where inv_freq has length (head_dim/2)/2 = head_dim/4, computed over dim=head_dim/2.\n * For each token the two position components (h, w) each produce head_dim/4 freqs,\n * concatenated → head_dim/2, then duplicated → head_dim for cos/sin.\n */\nexport function buildRotaryCosSin(\n positionIds: Int32Array,\n headDim: number,\n theta = 10000.0,\n): { cos: Float32Array; sin: Float32Array; numPatches: number } {\n // rotary embedding dim = head_dim / 2; inv_freq over arange(0, dim, 2)/dim.\n const rotaryDim = headDim / 2; // 32\n const half = rotaryDim / 2; // 16 freqs per position component\n const invFreq = new Float64Array(half);\n for (let i = 0; i < half; i++) {\n invFreq[i] = 1.0 / theta ** ((2 * i) / rotaryDim);\n }\n\n const N = positionIds.length / 2;\n // rotary_pos_emb per token = [h*invFreq (half), w*invFreq (half)] → length rotaryDim.\n // emb = cat(rotary, rotary) → length head_dim. cos/sin = cos/sin(emb).\n const cos = new Float32Array(N * headDim);\n const sin = new Float32Array(N * headDim);\n for (let n = 0; n < N; n++) {\n const hp = positionIds[n * 2];\n const wp = positionIds[n * 2 + 1];\n const base = n * headDim;\n for (let i = 0; i < half; i++) {\n const fh = hp * invFreq[i];\n const fw = wp * invFreq[i];\n // emb layout: [h-freqs(16), w-freqs(16), h-freqs(16), w-freqs(16)] = head_dim(64)\n const ch = Math.cos(fh);\n const sh = Math.sin(fh);\n const cw = Math.cos(fw);\n const sw = Math.sin(fw);\n cos[base + i] = ch;\n sin[base + i] = sh;\n cos[base + half + i] = cw;\n sin[base + half + i] = sw;\n cos[base + rotaryDim + i] = ch;\n sin[base + rotaryDim + i] = sh;\n cos[base + rotaryDim + half + i] = cw;\n sin[base + rotaryDim + half + i] = sw;\n }\n }\n return { cos, sin, numPatches: N };\n}\n\n/**\n * Build all host position tensors for a single image grid in one call.\n */\nexport function buildVisionPositionTensors(\n gridTHW: [number, number, number],\n posEmbedTable: Float32Array,\n cfg: VisionGridConfig,\n): VisionPositionTensors {\n const headDim = Math.floor(cfg.hiddenSize / cfg.numHeads);\n const posEmbeds = buildPosEmbeds(gridTHW, posEmbedTable, cfg);\n const positionIds = buildPositionIds(gridTHW, cfg.spatialMergeSize);\n const { cos, sin, numPatches } = buildRotaryCosSin(\n positionIds,\n headDim,\n cfg.ropeTheta ?? 10000.0,\n );\n return { posEmbeds, cos, sin, numPatches };\n}\n\n// ───────────────────────────────────────────────────────────────────────────\n// GEMMA 4 vision tower (`gemma4_vision`) host preprocessing.\n//\n// Distinct from the Qwen ViT above: Gemma 4 is a SigLIP-style encoder with\n// - pre-flattened [N, 3·16·16] patches (Linear patch-embed, no Conv2d),\n// - AXIAL learned position embeddings: per patch (x,y), pos = table[0][x]+table[1][y],\n// - 2D axial RoPE (θ=100): spatial_dim = head_dim/2; inv_freq over arange(0,spatial_dim,2)/spatial_dim;\n// per spatial dim emb=cat(f,f); final cos/sin = cat([emb_x, emb_y]) of length head_dim,\n// applied with the standard global-half rotate_half (the existing ApplyRotaryEmb kernel),\n// - k×k average pooling (k=pooling_kernel_size) → soft tokens, as a [Np, N] matrix.\n// Verified against transformers modeling_gemma4 + image_processing_gemma4 and the\n// live config/safetensors of google/gemma-4-E2B-it (+ MLX 4bit convert).\n// ───────────────────────────────────────────────────────────────────────────\n\nexport interface Gemma4VisionGridConfig {\n hiddenSize: number; // 768\n numHeads: number; // 12\n headDim: number; // 64\n ropeTheta: number; // 100\n poolingKernelSize: number; // 3\n}\n\nexport interface Gemma4VisionPositionTensors {\n /** [N, hidden] axial position embeddings (table[0][x] + table[1][y]). */\n posEmbeds: Float32Array;\n /** [N, headDim] axial rotary cosines. */\n cos: Float32Array;\n /** [N, headDim] axial rotary sines. */\n sin: Float32Array;\n /** [Np, N] average-pooling matrix (1/k² in-cell, 0 elsewhere). */\n poolMatrix: Float32Array;\n /** number of patches N (= gridH*gridW). */\n numPatches: number;\n /** number of pooled (soft) tokens Np (= ceil(gridH/k)*ceil(gridW/k)). */\n numPooled: number;\n}\n\n/**\n * Per-patch (x, y) grid coordinates in row-major order, matching the Gemma4\n * image processor's meshgrid(arange(width), arange(height)) reshape: patch p at\n * row r (0..gridH-1), col c (0..gridW-1) → x=c, y=r, index = r*gridW + c.\n */\nfunction gemma4PatchXY(gridH: number, gridW: number): { x: Int32Array; y: Int32Array } {\n const n = gridH * gridW;\n const x = new Int32Array(n);\n const y = new Int32Array(n);\n let p = 0;\n for (let r = 0; r < gridH; r++) {\n for (let c = 0; c < gridW; c++) {\n x[p] = c;\n y[p] = r;\n p++;\n }\n }\n return { x, y };\n}\n\n/**\n * Build axial learned position embeddings [N, hidden] from the [2, posSize, hidden]\n * table: pos[p] = table[0][x_p] + table[1][y_p]. Direct lookup, no interpolation\n * (HF F.embedding on clamped positions).\n */\nexport function buildGemma4PosEmbeds(\n gridH: number,\n gridW: number,\n posEmbedTable: Float32Array, // [2, posSize, hidden] flattened\n hidden: number,\n posSize: number,\n): Float32Array {\n const { x, y } = gemma4PatchXY(gridH, gridW);\n const n = gridH * gridW;\n const out = new Float32Array(n * hidden);\n const yPlane = posSize * hidden; // table[1] base offset\n for (let p = 0; p < n; p++) {\n const xi = Math.max(0, x[p]);\n const yi = Math.max(0, y[p]);\n const xBase = xi * hidden;\n const yBase = yPlane + yi * hidden;\n const dst = p * hidden;\n for (let d = 0; d < hidden; d++) {\n out[dst + d] = posEmbedTable[xBase + d] + posEmbedTable[yBase + d];\n }\n }\n return out;\n}\n\n/**\n * Build the 2D axial rotary cos/sin tables [N, headDim].\n * spatial_dim = headDim / 2; inv_freq[j] = 1/theta^((2j)/spatial_dim), j in [0, spatial_dim/2)\n * per spatial dim: f = pos * inv_freq (spatial_dim/2 values); emb = cat(f, f) (spatial_dim values)\n * cos/sin = cat([emb_x, emb_y]) → headDim values, layout [fx,fx,fy,fy].\n * Applied with the global-half rotate_half kernel (ApplyRotaryEmb), which computes\n * out = x*cos + rotate_half(x)*sin element-wise — exact for this layout.\n */\nexport function buildGemma4RotaryCosSin(\n gridH: number,\n gridW: number,\n headDim: number,\n theta: number,\n): { cos: Float32Array; sin: Float32Array } {\n const { x, y } = gemma4PatchXY(gridH, gridW);\n const n = gridH * gridW;\n const spatialDim = headDim / 2; // 32\n const half = spatialDim / 2; // 16 freqs per spatial dim\n const invFreq = new Float64Array(half);\n for (let j = 0; j < half; j++) {\n invFreq[j] = 1.0 / theta ** ((2 * j) / spatialDim);\n }\n const cos = new Float32Array(n * headDim);\n const sin = new Float32Array(n * headDim);\n for (let p = 0; p < n; p++) {\n const xp = Math.max(0, x[p]);\n const yp = Math.max(0, y[p]);\n const base = p * headDim;\n for (let j = 0; j < half; j++) {\n const fx = xp * invFreq[j];\n const fy = yp * invFreq[j];\n const cx = Math.cos(fx);\n const sx = Math.sin(fx);\n const cy = Math.cos(fy);\n const sy = Math.sin(fy);\n // layout: [fx(0..15), fx(16..31), fy(32..47), fy(48..63)]\n cos[base + j] = cx;\n sin[base + j] = sx;\n cos[base + half + j] = cx;\n sin[base + half + j] = sx;\n cos[base + spatialDim + j] = cy;\n sin[base + spatialDim + j] = sy;\n cos[base + spatialDim + half + j] = cy;\n sin[base + spatialDim + half + j] = sy;\n }\n }\n return { cos, sin };\n}\n\n/**\n * Build the [Np, N] average-pooling matrix for k×k spatial pooling over the real\n * (unpadded) grid, matching modeling_gemma4's kernel_idxs/one_hot pooling:\n * cell(p) = floor(x_p/k) + ceil(gridW/k) * floor(y_p/k)\n * poolMatrix[cell, p] = 1/k² (so pooled = poolMatrix @ hidden = mean over the k×k block)\n * Np = ceil(gridH/k) * ceil(gridW/k). Each pooled cell averages exactly the patches\n * that fall in it (edge cells with fewer than k² patches still divide by k², matching\n * HF's fixed 1/k² normalization).\n */\nexport function buildGemma4PoolMatrix(\n gridH: number,\n gridW: number,\n k: number,\n): { poolMatrix: Float32Array; numPooled: number } {\n const n = gridH * gridW;\n const cellsW = Math.ceil(gridW / k);\n const cellsH = Math.ceil(gridH / k);\n const np = cellsW * cellsH;\n const inv = 1.0 / (k * k);\n const poolMatrix = new Float32Array(np * n);\n const { x, y } = gemma4PatchXY(gridH, gridW);\n for (let p = 0; p < n; p++) {\n const cx = Math.floor(x[p] / k);\n const cy = Math.floor(y[p] / k);\n const cell = cx + cellsW * cy;\n poolMatrix[cell * n + p] = inv;\n }\n return { poolMatrix, numPooled: np };\n}\n\n/**\n * Build all Gemma 4 vision host tensors for one image grid in one call.\n * `posEmbedTable` is the raw [2, posSize, hidden] flattened table.\n */\nexport function buildGemma4VisionPositionTensors(\n gridH: number,\n gridW: number,\n posEmbedTable: Float32Array,\n posSize: number,\n cfg: Gemma4VisionGridConfig,\n): Gemma4VisionPositionTensors {\n const posEmbeds = buildGemma4PosEmbeds(gridH, gridW, posEmbedTable, cfg.hiddenSize, posSize);\n const { cos, sin } = buildGemma4RotaryCosSin(gridH, gridW, cfg.headDim, cfg.ropeTheta);\n const { poolMatrix, numPooled } = buildGemma4PoolMatrix(gridH, gridW, cfg.poolingKernelSize);\n return { posEmbeds, cos, sin, poolMatrix, numPatches: gridH * gridW, numPooled };\n}\n\n/** Gemma 4 image processor config (from processor_config.json). */\nexport const GEMMA4_IMAGE_PROCESSOR: ImageProcessorConfig = {\n patchSize: 16,\n temporalPatchSize: 1, // single frame, not duplicated (Gemma is not temporal)\n mergeSize: 1, // pooling (k=3) replaces Qwen's spatial-merge reshape\n imageMean: [0.0, 0.0, 0.0],\n imageStd: [1.0, 1.0, 1.0],\n rescaleFactor: 1 / 255,\n minPixels: 16 * 16, // a single 48×48-divisible tile minimum\n maxPixels: 2520 * 16 * 16, // max_patches(280·9) · patch²\n};\n\nexport interface Gemma4PreprocessedImage {\n /** Flattened patches [N, 3·patch²] row-major (row-major patch grid). */\n patches: Float32Array;\n /** Patch grid (gridH, gridW). */\n gridHW: [number, number];\n}\n\n/**\n * Preprocess a decoded RGB image for the Gemma 4 ViT: aspect-preserving resize so\n * the patch grid is ≤ max_soft_tokens·k² patches and H,W divisible by k·patch,\n * rescale ×1/255 (no normalize), patchify row-major into [N, 3·16·16].\n *\n * @param pixels row-major HWC RGB (0..255), length width*height*3.\n */\nexport function preprocessImageGemma4(\n pixels: Float32Array | Uint8ClampedArray | Uint8Array,\n width: number,\n height: number,\n maxSoftTokens = 280,\n poolingKernelSize = 3,\n patchSize = 16,\n): Gemma4PreprocessedImage {\n const factor = poolingKernelSize * patchSize; // 48\n const maxPatches = maxSoftTokens * poolingKernelSize * poolingKernelSize; // 2520\n // Largest aspect-preserving (H',W') divisible by `factor` with (H'/16)*(W'/16) ≤ maxPatches.\n const floorByFactor = (n: number) => Math.max(factor, Math.floor(n / factor) * factor);\n let outH = floorByFactor(height);\n let outW = floorByFactor(width);\n // Shrink (preserving aspect) until the patch budget is met.\n while ((outH / patchSize) * (outW / patchSize) > maxPatches) {\n const beta = Math.sqrt(((outH / patchSize) * (outW / patchSize)) / maxPatches);\n outH = floorByFactor(outH / beta);\n outW = floorByFactor(outW / beta);\n if (outH <= factor && outW <= factor) break;\n }\n const resized = bilinearResize(pixels, height, width, outH, outW);\n\n const gridH = outH / patchSize;\n const gridW = outW / patchSize;\n const ch = 3;\n const ps = patchSize;\n const patchDim = ch * ps * ps; // 768\n const numP = gridH * gridW;\n const patches = new Float32Array(numP * patchDim);\n const rescale = 1 / 255;\n\n // Row-major patch grid; per patch flatten layout [C, ps_h, ps_w] (channel-major)\n // to match the processor's (num_channels, ph, pw) → flatten ordering.\n let pIdx = 0;\n for (let pr = 0; pr < gridH; pr++) {\n for (let pc = 0; pc < gridW; pc++) {\n const base = pIdx * patchDim;\n let kk = 0;\n for (let c = 0; c < ch; c++) {\n for (let py = 0; py < ps; py++) {\n const row = (pr * ps + py) * outW;\n for (let px = 0; px < ps; px++) {\n patches[base + kk] = resized[(row + pc * ps + px) * 3 + c] * rescale;\n kk++;\n }\n }\n }\n pIdx++;\n }\n }\n return { patches, gridHW: [gridH, gridW] };\n}\n\n// ───────────────────────────────────────────────────────────────────────────\n// LM-SIDE multimodal preprocessing: image → patches, M-RoPE position ids & cos/sin.\n// These feed the text model (not the ViT). Ported from transformers 5.12:\n// - Qwen2VLImageProcessor.smart_resize / _preprocess → preprocessImage()\n// - Qwen3_5Model.get_rope_index / get_vision_position_ids → buildMRoPEPositionIds()\n// - Qwen3_5TextRotaryEmbedding.forward (interleaved M-RoPE) → buildMRoPECosSin()\n// ───────────────────────────────────────────────────────────────────────────\n\nexport interface ImageProcessorConfig {\n patchSize: number; // 16\n temporalPatchSize: number; // 2\n mergeSize: number; // 2\n imageMean: [number, number, number]; // (0.5, 0.5, 0.5)\n imageStd: [number, number, number]; // (0.5, 0.5, 0.5)\n /** rescale factor applied to raw 0..255 pixels before normalization (1/255). */\n rescaleFactor: number; // 1/255\n /** min total pixels after resize (shortest_edge). */\n minPixels: number; // 65536 (4*16*16*... HF default: 256*28*28 family → here shortest_edge)\n /** max total pixels after resize (longest_edge). */\n maxPixels: number; // 16777216\n}\n\nexport const QWEN3_5_IMAGE_PROCESSOR: ImageProcessorConfig = {\n patchSize: 16,\n temporalPatchSize: 2,\n mergeSize: 2,\n imageMean: [0.5, 0.5, 0.5],\n imageStd: [0.5, 0.5, 0.5],\n rescaleFactor: 1 / 255,\n minPixels: 65_536,\n maxPixels: 16_777_216,\n};\n\nexport interface PreprocessedImage {\n /** Flattened patches [N, 1536] in the spatial-merge order encodeImage expects. */\n patches: Float32Array;\n /** (t, h, w) patch grid. t=1 for a single image, h/w in patch units. */\n gridTHW: [number, number, number];\n}\n\n/**\n * Qwen2-VL smart-resize: round H and W to multiples of factor=patch*merge,\n * keeping aspect ratio and clamping the total pixel budget to [minPixels, maxPixels].\n * Matches transformers.models.qwen2_vl.image_processing.smart_resize.\n */\nexport function smartResize(\n height: number,\n width: number,\n factor: number,\n minPixels: number,\n maxPixels: number,\n): [number, number] {\n if (height < factor || width < factor) {\n // HF raises; clamp up to one factor so tiny images still produce a grid.\n const scale = factor / Math.min(height, width);\n height = Math.max(factor, Math.round(height * scale));\n width = Math.max(factor, Math.round(width * scale));\n }\n const roundByFactor = (n: number) => Math.round(n / factor) * factor;\n const floorByFactor = (n: number) => Math.floor(n / factor) * factor;\n const ceilByFactor = (n: number) => Math.ceil(n / factor) * factor;\n\n let hBar = Math.max(factor, roundByFactor(height));\n let wBar = Math.max(factor, roundByFactor(width));\n if (hBar * wBar > maxPixels) {\n const beta = Math.sqrt((height * width) / maxPixels);\n hBar = Math.max(factor, floorByFactor(height / beta));\n wBar = Math.max(factor, floorByFactor(width / beta));\n } else if (hBar * wBar < minPixels) {\n const beta = Math.sqrt(minPixels / (height * width));\n hBar = ceilByFactor(height * beta);\n wBar = ceilByFactor(width * beta);\n }\n return [hBar, wBar];\n}\n\n/**\n * Bilinear-resample an RGB image (HWC, 0..255 or 0..1) to (outH, outW).\n * Half-pixel-centered sampling to match PIL/torchvision BILINEAR closely.\n * `pixels` is row-major [H, W, 3].\n */\nfunction bilinearResize(\n pixels: Float32Array | Uint8ClampedArray | Uint8Array,\n inH: number,\n inW: number,\n outH: number,\n outW: number,\n): Float32Array {\n const out = new Float32Array(outH * outW * 3);\n const scaleH = inH / outH;\n const scaleW = inW / outW;\n for (let oy = 0; oy < outH; oy++) {\n let sy = (oy + 0.5) * scaleH - 0.5;\n if (sy < 0) sy = 0;\n if (sy > inH - 1) sy = inH - 1;\n const y0 = Math.floor(sy);\n const y1 = Math.min(y0 + 1, inH - 1);\n const fy = sy - y0;\n for (let ox = 0; ox < outW; ox++) {\n let sx = (ox + 0.5) * scaleW - 0.5;\n if (sx < 0) sx = 0;\n if (sx > inW - 1) sx = inW - 1;\n const x0 = Math.floor(sx);\n const x1 = Math.min(x0 + 1, inW - 1);\n const fx = sx - x0;\n const i00 = (y0 * inW + x0) * 3;\n const i01 = (y0 * inW + x1) * 3;\n const i10 = (y1 * inW + x0) * 3;\n const i11 = (y1 * inW + x1) * 3;\n const od = (oy * outW + ox) * 3;\n for (let c = 0; c < 3; c++) {\n const top = pixels[i00 + c] * (1 - fx) + pixels[i01 + c] * fx;\n const bot = pixels[i10 + c] * (1 - fx) + pixels[i11 + c] * fx;\n out[od + c] = top * (1 - fy) + bot * fy;\n }\n }\n }\n return out;\n}\n\n/**\n * Preprocess a decoded RGB image into the [N, 1536] patch tensor + grid_thw that\n * `encodeImage()` expects, matching the HF Qwen2-VL image processor:\n * smart_resize → rescale (×1/255) → normalize → temporal-pair (×temporal_patch_size)\n * → patchify into spatial_merge×spatial_merge blocks → flatten to [N, C·T·P·P].\n *\n * @param pixels row-major HWC RGB, length width*height*3. Values 0..255 (default)\n * or already 0..1 if `rescaled` is true.\n * @param width source pixel width\n * @param height source pixel height\n */\nexport function preprocessImage(\n pixels: Float32Array | Uint8ClampedArray | Uint8Array,\n width: number,\n height: number,\n cfg: ImageProcessorConfig = QWEN3_5_IMAGE_PROCESSOR,\n rescaled = false,\n): PreprocessedImage {\n const factor = cfg.patchSize * cfg.mergeSize; // 32\n const [outH, outW] = smartResize(height, width, factor, cfg.minPixels, cfg.maxPixels);\n const resized = bilinearResize(pixels, height, width, outH, outW);\n\n // rescale + normalize per channel → CHW float\n const [mr, mg, mb] = cfg.imageMean;\n const [sr, sg, sb] = cfg.imageStd;\n const mean = [mr, mg, mb];\n const std = [sr, sg, sb];\n const rescale = rescaled ? 1 : cfg.rescaleFactor;\n const chw = new Float32Array(3 * outH * outW);\n const plane = outH * outW;\n for (let y = 0; y < outH; y++) {\n for (let x = 0; x < outW; x++) {\n const src = (y * outW + x) * 3;\n const dst = y * outW + x;\n for (let c = 0; c < 3; c++) {\n chw[c * plane + dst] = (resized[src + c] * rescale - mean[c]) / std[c];\n }\n }\n }\n\n // Patchify exactly like the HF processor's reshape/transpose chain.\n // grid: t=1 (single frame, duplicated to temporal_patch_size), h/w in patches.\n const t = 1;\n const gridH = outH / cfg.patchSize;\n const gridW = outW / cfg.patchSize;\n const ps = cfg.patchSize;\n const tps = cfg.temporalPatchSize;\n const ch = 3;\n const merge = cfg.mergeSize;\n const gridHm = gridH / merge;\n const gridWm = gridW / merge;\n const patchDim = ch * tps * ps * ps; // 1536\n const numPatches = t * gridH * gridW;\n const patches = new Float32Array(numPatches * patchDim);\n\n // HF flatten layout (per patch row, contiguous): [C, T, ps_h, ps_w].\n // Output patch ordering: blocks of merge×merge, matching get_vision_*: see\n // qwen2_vl reshape (grid_t, gridHm, merge, gridWm, merge, C, tps, ps, ps).\n let pIdx = 0;\n for (let gt = 0; gt < t; gt++) {\n for (let bh = 0; bh < gridHm; bh++) {\n for (let bw = 0; bw < gridWm; bw++) {\n for (let mh = 0; mh < merge; mh++) {\n for (let mw = 0; mw < merge; mw++) {\n const patchRow = (bh * merge + mh) * ps;\n const patchCol = (bw * merge + mw) * ps;\n const base = pIdx * patchDim;\n let k = 0;\n for (let c = 0; c < ch; c++) {\n for (let tt = 0; tt < tps; tt++) {\n // single frame duplicated across the temporal dim\n for (let py = 0; py < ps; py++) {\n const row = (patchRow + py) * outW;\n for (let px = 0; px < ps; px++) {\n patches[base + k] = chw[c * plane + row + (patchCol + px)];\n k++;\n }\n }\n void tt;\n }\n }\n pIdx++;\n }\n }\n }\n }\n }\n\n return { patches, gridTHW: [t, gridH, gridW] };\n}\n\n/**\n * Build the 3D M-RoPE position ids [3, seq] for a text sequence that contains\n * `image_token_id` runs, matching Qwen3_5Model.get_rope_index for a single image.\n *\n * Text tokens advance all three (t,h,w) components together (standard 1D RoPE).\n * Each image run uses the vision grid: temporal=start (t=1), height=start+h_block,\n * width=start+w_block, in row-major (h outer, w inner) order; after the image the\n * cursor jumps by max(h_merged, w_merged). Returns Int32Array of length 3*seq\n * laid out as [T-row(seq), H-row(seq), W-row(seq)].\n */\n/**\n * Fill the 3D position rows for one image run starting at sequence index `at`\n * and logical `start`. Returns the next sequence index and the post-image cursor.\n */\nfunction fillImagePositions(\n rows: { t: Int32Array; h: Int32Array; w: Int32Array },\n at: number,\n start: number,\n grid: [number, number, number],\n mergeSize: number,\n): { next: number; cursor: number } {\n const [gt, gh, gw] = grid;\n const hm = gh / mergeSize;\n const wm = gw / mergeSize;\n let i = at;\n for (let tt = 0; tt < gt; tt++) {\n for (let h = 0; h < hm; h++) {\n for (let w = 0; w < wm; w++) {\n rows.t[i] = start + tt;\n rows.h[i] = start + h;\n rows.w[i] = start + w;\n i++;\n }\n }\n }\n return { next: i, cursor: start + Math.max(hm, wm) };\n}\n\nexport function buildMRoPEPositionIds(\n inputIds: Int32Array | Uint32Array | number[],\n imageGrids: Array<[number, number, number]>,\n imageTokenId: number,\n mergeSize: number,\n): Int32Array {\n const seq = inputIds.length;\n const rows = { t: new Int32Array(seq), h: new Int32Array(seq), w: new Int32Array(seq) };\n\n let i = 0;\n let cursor = 0;\n let imageIdx = 0;\n while (i < seq) {\n if (inputIds[i] === imageTokenId) {\n const res = fillImagePositions(rows, i, cursor, imageGrids[imageIdx++], mergeSize);\n i = res.next;\n cursor = res.cursor;\n } else {\n rows.t[i] = cursor;\n rows.h[i] = cursor;\n rows.w[i] = cursor;\n cursor++;\n i++;\n }\n }\n\n const out = new Int32Array(3 * seq);\n out.set(rows.t, 0);\n out.set(rows.h, seq);\n out.set(rows.w, 2 * seq);\n return out;\n}\n\n/**\n * Per-pair frequency→dimension assignment for interleaved M-RoPE, matching\n * Qwen3_5TextRotaryEmbedding.apply_interleaved_mrope. For pair index i in\n * [0, sum(section)) the position component is section-cyclic: T,H,W,T,H,W,...\n * but each component capped at its section count. Returns an array of length\n * (rope_dim/2) with values 0=T, 1=H, 2=W.\n */\nexport function mropeFreqDims(mropeSection: [number, number, number]): Int32Array {\n const total = mropeSection[0] + mropeSection[1] + mropeSection[2];\n const dims = new Int32Array(total); // default 0 (T)\n for (let d = 1; d <= 2; d++) {\n const length = mropeSection[d] * 3;\n for (let idx = d; idx < length && idx < total; idx += 3) {\n dims[idx] = d;\n }\n }\n return dims;\n}\n\n/**\n * Build the interleaved-M-RoPE cos/sin tables [seq, rope_dim] from 3D position\n * ids, matching Qwen3_5TextRotaryEmbedding.forward:\n * freqs[d][i] = pos[d] * inv_freq[i], inv_freq[i] = 1/theta^(2i/rope_dim)\n * freq[i] picks component mropeFreqDims[i]; emb = cat(freqs, freqs).\n * cos/sin have length seq*rope_dim. For text-only (all 3 pos rows equal) this\n * reduces exactly to standard 1D partial RoPE.\n *\n * @param positionIds3 [3, seq] as produced by buildMRoPEPositionIds.\n * @param ropeDim number of rotated dims per head (head_dim * partial_factor).\n */\nexport function buildMRoPECosSin(\n positionIds3: Int32Array,\n seq: number,\n ropeDim: number,\n theta: number,\n mropeSection: [number, number, number],\n): { cos: Float32Array; sin: Float32Array } {\n const half = ropeDim / 2; // == sum(mropeSection)\n const freqDims = mropeFreqDims(mropeSection);\n const invFreq = new Float64Array(half);\n for (let i = 0; i < half; i++) {\n invFreq[i] = 1.0 / theta ** ((2 * i) / ropeDim);\n }\n const cos = new Float32Array(seq * ropeDim);\n const sin = new Float32Array(seq * ropeDim);\n for (let n = 0; n < seq; n++) {\n const base = n * ropeDim;\n for (let i = 0; i < half; i++) {\n const dim = freqDims[i]; // 0=T,1=H,2=W\n const pos = positionIds3[dim * seq + n];\n const angle = pos * invFreq[i];\n const c = Math.cos(angle);\n const s = Math.sin(angle);\n // emb = cat(freqs, freqs): duplicate into [0,half) and [half,ropeDim)\n cos[base + i] = c;\n sin[base + i] = s;\n cos[base + half + i] = c;\n sin[base + half + i] = s;\n }\n }\n return { cos, sin };\n}\n","// ============================================\n// iOS Model Guards & Device Capability Detection\n// ============================================\n\n// ============================================\n// Real native-engine model ids\n// ============================================\n//\n// The website loads models by their actual repo id (MLX 4-bit on-device builds,\n// or the upstream Qwen/Liquid repos on desktop). The guard matches on those ids\n// — NOT the old ONNX-era shorthands — so it must stay in sync with the real\n// checkpoints the engine can run. Matching is substring-based on a normalized\n// id so both `mlx-community/Qwen3.5-0.8B-4bit` and `Qwen/Qwen3.5-0.8B` resolve.\n\n/** Recommended safe model ids per modality (used as fallbacks on mobile). */\nconst SAFE_MOBILE_CHAT = \"mlx-community/Qwen3.5-0.8B-4bit\";\n\n/**\n * Approximate on-device (INT4) memory footprint in MB for the models the native\n * engine actually ships. Used for memory-aware selection and messaging.\n */\nexport const MODEL_SIZES: Record<string, number> = {\n // Chat models (INT4, on-device)\n \"qwen3.5-0.8b\": 650, // ~0.65GB\n \"qwen3.5-2b\": 1700, // ~1.7GB\n \"gemma-4-e2b\": 3600, // ~3.6GB\n \"lfm2.5-350m\": 300, // ~0.3GB\n // TTS models\n \"kokoro-82m\": 350,\n \"supertonic-66m\": 300,\n // STT models\n \"whisper-tiny\": 150,\n \"whisper-tiny.en\": 150,\n \"whisper-small\": 500,\n // Embedding models\n \"all-minilm-l6-v2\": 100,\n};\n\n/**\n * Normalize a repo/model id to a lowercase token stream for substring matching\n * (strips org prefixes' punctuation while preserving the model name tokens).\n */\nfunction normalizeId(modelId: string): string {\n return modelId.toLowerCase().replace(/[^a-z0-9]/g, \"-\");\n}\n\n/**\n * iOS (WKWebView) model classification keyed off the REAL native-engine ids.\n *\n * - blocked: too large for the WKWebView memory ceiling on iPhone — will crash.\n * gemma-4-e2b (~3.6GB) plus any vision checkpoint (the vision encoder pushes\n * the working set well past what an iPhone can hold).\n * - risky: borderline on iPhone — Qwen3.5-2B (~1.7GB) fits on newer devices but\n * warns and can OOM on older ones.\n * - everything else (Qwen3.5-0.8B ~0.65GB, LFM2.5-350M) is allowed everywhere.\n */\nconst IOS_MODEL_LIMITS = {\n /** Substrings (normalized) of ids that HARD-BLOCK on iPhone. */\n blocked: [\"gemma-4-e2b\", \"gemma-4-e4b\"],\n /** Substrings that mark a vision checkpoint (blocked on iPhone). */\n visionMarkers: [\"vision\", \"-vl-\", \"-vl\", \"vlm\", \"image-text\", \"-it-vision\"],\n /** Substrings of ids that WARN on iPhone (Qwen3.5-2B class). */\n risky: [\"qwen3.5-2b\", \"qwen3-5-2b\"],\n /** Maximum total memory budget in MB for iOS WKWebView. */\n maxBudgetMB: 1800,\n} as const;\n\n/**\n * Check if a model is safe to load on the current device.\n * Returns guidance specific to iOS memory constraints. Matches on the real\n * native-engine repo ids (MLX 4-bit / upstream Qwen / Liquid).\n */\nexport function isModelSafeForDevice(modelId: string): {\n safe: boolean;\n reason: string;\n recommendation?: string;\n maxSafeModel?: string;\n} {\n const ua = typeof navigator !== \"undefined\" ? navigator.userAgent : \"\";\n const isIPhone = /iPhone|iPod/.test(ua);\n const isIPad = /iPad/.test(ua);\n const isIOS = isIPhone || isIPad;\n const isIOSChrome = isIOS && /CriOS/.test(ua);\n const normalizedId = normalizeId(modelId);\n\n const isVision = IOS_MODEL_LIMITS.visionMarkers.some((m) =>\n normalizedId.includes(normalizeId(m)),\n );\n const isBlocked =\n IOS_MODEL_LIMITS.blocked.some((m) => normalizedId.includes(normalizeId(m))) || isVision;\n const isRisky = IOS_MODEL_LIMITS.risky.some((m) => normalizedId.includes(normalizeId(m)));\n\n // iPhone is the hard constraint (smallest WKWebView budget). iPad has more\n // headroom, so only the genuinely huge / vision checkpoints are blocked there.\n if (isIPhone) {\n if (isBlocked) {\n const browserNote = isIOSChrome ? \" (iOS Chrome uses WKWebView, same limits as Safari)\" : \"\";\n const why = isVision\n ? \"Vision checkpoints need a separate image encoder in memory\"\n : \"It is too large (~3.6GB)\";\n return {\n safe: false,\n reason: `Model ${modelId} will crash on iPhone${browserNote}. ${why}, which exceeds the WKWebView memory ceiling.`,\n recommendation: `Use ${SAFE_MOBILE_CHAT} (Qwen3.5-0.8B) on iPhone, or run larger models on desktop.`,\n maxSafeModel: SAFE_MOBILE_CHAT,\n };\n }\n if (isRisky) {\n return {\n safe: true,\n reason: `Model ${modelId} (~1.7GB) is borderline on iPhone and may run out of memory on older devices.`,\n recommendation: `If it crashes, fall back to ${SAFE_MOBILE_CHAT} (Qwen3.5-0.8B).`,\n maxSafeModel: SAFE_MOBILE_CHAT,\n };\n }\n return { safe: true, reason: \"Model is within iPhone memory limits.\" };\n }\n\n if (isIPad) {\n // iPad tolerates the 2B class; only block the huge / vision checkpoints.\n if (isBlocked) {\n const why = isVision\n ? \"Vision checkpoints need a separate image encoder in memory\"\n : \"It is too large (~3.6GB)\";\n return {\n safe: false,\n reason: `Model ${modelId} may crash on iPad. ${why}, which can exceed the WKWebView memory ceiling.`,\n recommendation: `Use ${SAFE_MOBILE_CHAT} (Qwen3.5-0.8B) or Qwen3.5-2B on iPad.`,\n maxSafeModel: \"mlx-community/Qwen3.5-2B-4bit\",\n };\n }\n return { safe: true, reason: \"Model is within iPad memory limits.\" };\n }\n\n // Android - block the huge / vision checkpoints, allow the rest.\n const isAndroid = /Android/.test(ua);\n if (isAndroid && isBlocked) {\n return {\n safe: false,\n reason: `Model ${modelId} is very large and may crash on Android devices.`,\n recommendation: `Use ${SAFE_MOBILE_CHAT} (Qwen3.5-0.8B) or Qwen3.5-2B on Android.`,\n maxSafeModel: \"mlx-community/Qwen3.5-2B-4bit\",\n };\n }\n\n // Desktop - all models are safe.\n return { safe: true, reason: \"Desktop browser has sufficient memory.\" };\n}\n\n/**\n * Get recommended models based on device memory and capabilities.\n * Helps prevent OOM crashes on low-memory mobile devices.\n */\nexport function getRecommendedModels(): {\n chat: string;\n tts: string;\n stt: string;\n embedding: string;\n reason: string;\n deviceMemory: number | null;\n isMobile: boolean;\n} {\n const ua = typeof navigator !== \"undefined\" ? navigator.userAgent : \"\";\n const deviceMemory = typeof navigator !== \"undefined\" ? (navigator as any).deviceMemory : null;\n const isMobile = /iPhone|iPad|iPod|Android|Mobile/.test(ua);\n\n // Estimate available memory (deviceMemory reports total GB, not available)\n // Mobile devices typically have less free memory due to OS overhead\n const effectiveMemory = deviceMemory ? (isMobile ? deviceMemory * 0.4 : deviceMemory * 0.6) : 4;\n const availableMB = effectiveMemory * 1024;\n\n let chat: string;\n let reason: string;\n\n if (availableMB < 600) {\n chat = \"LiquidAI/LFM2.5-350M\";\n reason = \"Very low memory device - using smallest model (LFM2.5-350M)\";\n } else if (isMobile && availableMB < 2200) {\n chat = SAFE_MOBILE_CHAT;\n reason = \"Mobile device - using Qwen3.5-0.8B to stay within the WKWebView memory limit\";\n } else if (availableMB < 2200) {\n chat = SAFE_MOBILE_CHAT;\n reason = \"Standard model for moderate memory (Qwen3.5-0.8B)\";\n } else {\n chat = \"mlx-community/Qwen3.5-2B-4bit\";\n reason = \"High memory available - using Qwen3.5-2B for better quality\";\n }\n\n return {\n chat,\n tts: \"kokoro-82m\",\n stt: \"whisper-tiny.en\",\n embedding: \"all-MiniLM-L6-v2\",\n reason,\n deviceMemory,\n isMobile,\n };\n}\n\n// ============================================\n// Session Phase Tracking (Reload Detection)\n// ============================================\n\ntype DownloadPhase = \"idle\" | \"downloading\" | \"caching\" | \"initializing\" | \"ready\" | \"error\";\n\nexport const SESSION_STORAGE_KEY = \"gerbil_session_phase\";\n\nexport type SessionState = {\n phase: DownloadPhase;\n modelId: string | null;\n sessionId: string;\n timestamp: number;\n bytesDownloaded?: number;\n totalBytes?: number;\n};\n\n/**\n * Generate a unique session ID for tracking across reloads.\n */\nfunction generateSessionId(): string {\n return `${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;\n}\n\n/**\n * Get or create the current session ID.\n */\nfunction getSessionId(): string {\n if (typeof localStorage === \"undefined\") return generateSessionId();\n\n let sessionId = sessionStorage.getItem(\"gerbil_session_id\");\n if (!sessionId) {\n sessionId = generateSessionId();\n sessionStorage.setItem(\"gerbil_session_id\", sessionId);\n }\n return sessionId;\n}\n\n/**\n * Set the current download/initialization phase.\n * Used to detect if a reload happened during a critical operation.\n */\nexport function setDownloadPhase(\n phase: DownloadPhase,\n modelId?: string,\n progress?: { bytesDownloaded: number; totalBytes: number },\n): void {\n if (typeof localStorage === \"undefined\") return;\n\n const state: SessionState = {\n phase,\n modelId: modelId || null,\n sessionId: getSessionId(),\n timestamp: Date.now(),\n bytesDownloaded: progress?.bytesDownloaded,\n totalBytes: progress?.totalBytes,\n };\n\n localStorage.setItem(SESSION_STORAGE_KEY, JSON.stringify(state));\n}\n\n/**\n * Get the last known download phase from storage.\n */\nexport function getDownloadPhase(): SessionState | null {\n if (typeof localStorage === \"undefined\") return null;\n\n try {\n const raw = localStorage.getItem(SESSION_STORAGE_KEY);\n if (!raw) return null;\n return JSON.parse(raw) as SessionState;\n } catch {\n return null;\n }\n}\n\n/**\n * Detect if the page reloaded during a model download/initialization.\n * This typically indicates an iOS memory crash.\n *\n * @returns Detection result with recommended action\n */\nexport function detectMemoryCrash(): {\n crashed: boolean;\n phase?: DownloadPhase;\n modelId?: string;\n timeSinceCrash?: number;\n recommendation?: string;\n} {\n const lastState = getDownloadPhase();\n const currentSessionId = getSessionId();\n\n if (!lastState) {\n return { crashed: false };\n }\n\n // If session ID changed and we were in a critical phase, it's likely a crash/reload\n const criticalPhases: DownloadPhase[] = [\"downloading\", \"caching\", \"initializing\"];\n const wasInCriticalPhase = criticalPhases.includes(lastState.phase);\n const sessionChanged = lastState.sessionId !== currentSessionId;\n const timeSinceCrash = Date.now() - lastState.timestamp;\n\n // Only count as crash if it happened recently (within 5 minutes) and session changed\n const recentCrash = timeSinceCrash < 5 * 60 * 1000;\n\n if (wasInCriticalPhase && sessionChanged && recentCrash) {\n // Clear the state to prevent repeated detection\n localStorage.removeItem(SESSION_STORAGE_KEY);\n\n return {\n crashed: true,\n phase: lastState.phase,\n modelId: lastState.modelId || undefined,\n timeSinceCrash,\n recommendation:\n lastState.modelId && /2b|gemma|vision/i.test(lastState.modelId)\n ? \"The model was too large for your device. Try Qwen3.5-0.8B instead.\"\n : \"Your device ran out of memory. Try a smaller model or use a desktop browser.\",\n };\n }\n\n return { crashed: false };\n}\n\n/**\n * Clear session phase (call when model loads successfully).\n */\nexport function clearDownloadPhase(): void {\n if (typeof localStorage === \"undefined\") return;\n localStorage.removeItem(SESSION_STORAGE_KEY);\n}\n\n// ============================================\n// WebKit Submit-Granularity (group size) Probe\n// ============================================\n//\n// On WebKit/iOS WebGPU, decode speed is bound by GPU round-trips: the executor\n// groups `webkitGroupSize` dispatches per command buffer, then submits + drains\n// (queue.onSubmittedWorkDone) per group. Larger groups amortize the drains and\n// can be ~5x faster on iPad. BUT the safe ceiling is device-dependent in three\n// classes:\n// 1. Newer WebKit (iPad 26.5): high group is correct + fast.\n// 2. Older WebKit: high group yields zero/garbage logits (wrong output).\n// 3. iPhone (iOS 18.7): high group HARD-CRASHES the GPU process (page dies).\n//\n// The crash class is the hard one: a bad group kills the page before any code\n// can record the result. So we use a localStorage BREADCRUMB that OUTLIVES the\n// page kill: we persist the candidate we're about to try BEFORE running any\n// GPU-heavy work. If the page survives, a later promotion clears `trying` and\n// records it as known-good. If the page crashed, `trying` is still set on the\n// next load, so we KNOW that rung crashed and cap below it.\n//\n// The probe escalates UP from the safe floor (never starts optimistic — a\n// first-visit crash is a terrible UX) and persists per-device in localStorage.\n\nexport const WEBKIT_GROUP_PROBE_KEY = \"gerbil-webkit-group-v1\";\n\n/**\n * Candidate group-size ladder. We escalate UP one rung per page load. 128 is\n * treated as effectively \"batch-all\" for this model — kept simple on purpose.\n */\nexport const WEBKIT_GROUP_LADDER = [1, 8, 32, 64, 128] as const;\n\nexport type WebkitGroupProbe = {\n /** Largest group size proven crash-free AND correct on this device. Starts at 1. */\n knownGood: number;\n /** Candidate being attempted this page-load (null when not probing). */\n trying: number | null;\n /** True once we hit a crash/incorrect ceiling and should stop escalating. */\n capped: boolean;\n};\n\nconst DEFAULT_PROBE: WebkitGroupProbe = { knownGood: 1, trying: null, capped: false };\n\n/** Read the persisted WebKit group probe record (guarded; safe on node). */\nexport function readGroupProbe(): WebkitGroupProbe {\n if (typeof localStorage === \"undefined\") return { ...DEFAULT_PROBE };\n try {\n const raw = localStorage.getItem(WEBKIT_GROUP_PROBE_KEY);\n if (!raw) return { ...DEFAULT_PROBE };\n const parsed = JSON.parse(raw) as Partial<WebkitGroupProbe>;\n return {\n knownGood:\n typeof parsed.knownGood === \"number\" && parsed.knownGood >= 1 ? parsed.knownGood : 1,\n trying: typeof parsed.trying === \"number\" ? parsed.trying : null,\n capped: parsed.capped === true,\n };\n } catch {\n return { ...DEFAULT_PROBE };\n }\n}\n\n/** Persist the WebKit group probe record (guarded; no-op on node). */\nexport function writeGroupProbe(rec: WebkitGroupProbe): void {\n if (typeof localStorage === \"undefined\") return;\n try {\n localStorage.setItem(WEBKIT_GROUP_PROBE_KEY, JSON.stringify(rec));\n } catch {\n // Best-effort; private mode / quota can throw.\n }\n}\n\n/**\n * The validated non-phone sweet spot. iPad swept 1→7.9, 8→19, 32→24.8, 64→26.6,\n * 128→26.9 (peak), 256→26.2 tok/s — a plateau from ~64 up, so 128 is the best\n * stable target (more batching just costs memory). Non-phone WebKit jumps here\n * directly; the crash breadcrumb caps it down if a device can't sustain it.\n */\nconst NONPHONE_TARGET_GROUP = 128;\n\n/**\n * Resolve the WebKit group size to use this session, recording `trying` as a\n * side effect so a crash this load is detectable on the next load.\n *\n * Algorithm (only meaningful on WebKit; inert otherwise):\n * 1. Read the record (default {knownGood:1, trying:null, capped:false}).\n * 2. If `trying !== null` on entry → the previous load set it but never cleared\n * it → that load CRASHED at `trying`. Cap there, keep `knownGood`, clear\n * `trying`. Use `knownGood` this session.\n * 3. Else if !capped and there is a rung above `knownGood` → set `trying = next`,\n * persist BEFORE any GPU work, and use it (we're escalating).\n * 4. Else → use `knownGood`.\n *\n * @returns the group size to use this session.\n */\nexport function resolveWebkitGroupSize(args: {\n override?: number;\n isWebKit: boolean;\n /**\n * Memory-constrained devices (phones) are the \"crash class\": batching crashes\n * the GPU process, and discovering that costs one user-visible crash before the\n * breadcrumb caps it. When `conservative` is set we never auto-escalate — the\n * device stays at its proven floor (group=1 on first run) and never risks that\n * calibration crash. `?group=N` still lets such a device opt in explicitly.\n */\n conservative?: boolean;\n}): number {\n // Explicit ?group=N override wins and skips the probe entirely.\n if (args.override && args.override > 0) return args.override;\n // Non-WebKit (desktop / Dawn / node) never groups — floor of 1, no bookkeeping.\n if (!args.isWebKit) return 1;\n\n const rec = readGroupProbe();\n\n // Step 2: a stale `trying` means last load crashed at that rung.\n if (rec.trying !== null) {\n const crashedAt = rec.trying;\n const next: WebkitGroupProbe = {\n knownGood: rec.knownGood,\n trying: null,\n capped: true,\n };\n writeGroupProbe(next);\n console.log(\n `[engine] webkit group probe: previous load crashed at group=${crashedAt} → capping at knownGood=${next.knownGood}`,\n );\n return next.knownGood;\n }\n\n // Step 3: non-phone WebKit (iPad / Mac Safari) jumps straight to the validated\n // sweet spot (group=128) instead of climbing rung-by-rung — these devices\n // tolerate batching (iPad ran every rung to 256 coherently), so there's no\n // reason to crawl. The breadcrumb (Step 2) caps it down if a particular device\n // can't sustain it. Phones (conservative) NEVER escalate — they stay at the\n // proven floor (group=1) to avoid the one calibration crash batching costs on\n // the crash class.\n if (!rec.capped && !args.conservative && rec.knownGood < NONPHONE_TARGET_GROUP) {\n const next: WebkitGroupProbe = {\n knownGood: rec.knownGood,\n trying: NONPHONE_TARGET_GROUP,\n capped: false,\n };\n writeGroupProbe(next); // PERSIST BEFORE any GPU-heavy work runs\n console.log(\n `[engine] webkit group probe: trying target group=${NONPHONE_TARGET_GROUP} (knownGood=${rec.knownGood})`,\n );\n return NONPHONE_TARGET_GROUP;\n }\n\n // Step 4: nothing to escalate (capped or at top rung) — use the proven floor.\n console.log(\n `[engine] webkit group probe: knownGood=${rec.knownGood} trying=null capped=${rec.capped} → using ${rec.knownGood}`,\n );\n return rec.knownGood;\n}\n\n/**\n * Promote (or cap) the WebKit group probe after the first successful forward.\n *\n * Call this once per page-load, after the model has loaded AND a first forward\n * completed without the page dying. The breadcrumb already handles the crash\n * class (the page death leaves `trying` set for the next load); this handles the\n * wrong-output class and records success.\n *\n * @param correct true if the first forward produced non-corrupt output.\n * - correct → promote: knownGood = trying, trying = null.\n * - incorrect → cap: keep knownGood at the prior rung, trying = null, capped.\n */\nexport function promoteGroupProbe(correct: boolean): void {\n if (typeof localStorage === \"undefined\") return;\n const rec = readGroupProbe();\n // Nothing was being attempted this load — nothing to promote.\n if (rec.trying === null) return;\n\n if (correct) {\n writeGroupProbe({ knownGood: rec.trying, trying: null, capped: rec.capped });\n console.log(`[engine] webkit group probe: PROMOTED group=${rec.trying} to known-good`);\n } else {\n // Wrong-output class: this rung is incorrect (but did not crash). Keep the\n // prior knownGood and stop escalating.\n writeGroupProbe({ knownGood: rec.knownGood, trying: null, capped: true });\n console.log(\n `[engine] webkit group probe: group=${rec.trying} produced INCORRECT output → capping at knownGood=${rec.knownGood}`,\n );\n }\n}\n","/**\n * WebGPUEngine -- gerbil's native WebGPU inference engine.\n *\n * Public API:\n * const engine = await WebGPUEngine.create({ repo: \"Qwen/Qwen3.5-0.8B\" });\n * const result = await engine.generate(\"What is 2+2?\");\n * engine.destroy();\n */\n\nimport {\n dequantizeGemma4VisionProjection,\n ensureGemma4VisionEmbedderNorms,\n generateGemma4VisionGraph,\n patchGemma4VisionClips,\n resolveGemma4VisionInfo,\n} from \"./architectures/gemma4_vision.js\";\nimport type { KvMode } from \"./architectures/index.js\";\nimport { generateQwen3_5VisionGraph } from \"./architectures/qwen3_5_vision.js\";\nimport type { GPUContext, GPUDiagnosticResult } from \"./device.js\";\nimport { clearPipelineCache, initGPU, verifyGPU } from \"./device.js\";\nimport { Executor } from \"./executor.js\";\nimport type { ModelArchConfig, ModelCapabilities, ModelGraph } from \"./ir.js\";\nimport { KaniTTS } from \"./kani-tts.js\";\nimport { type LoadModelOptions, loadModel } from \"./model-loader.js\";\nimport { type SamplingParams, sampleToken } from \"./sampler.js\";\nimport type { ChatMessage, Tokenizer } from \"./tokenizer.js\";\nimport type { VisionExecutor } from \"./vision-executor.js\";\nimport { VisionExecutor as VisionExecutorImpl } from \"./vision-executor.js\";\nimport {\n buildGemma4VisionPositionTensors,\n buildMRoPECosSin,\n buildMRoPEPositionIds,\n buildVisionPositionTensors,\n type ImageProcessorConfig,\n preprocessImage,\n preprocessImageGemma4,\n QWEN3_5_IMAGE_PROCESSOR,\n} from \"./vision-preprocess.js\";\n\n// Per-capability default models live in a standalone module so the React hooks\n// can resolve defaults without statically importing this (heavy) engine module.\nexport { DEFAULT_MODELS, resolveDefaultRepo } from \"./defaults.js\";\n\nimport { promoteGroupProbe, resolveWebkitGroupSize } from \"../browser/device-guards.js\";\nimport { resolveDefaultRepo } from \"./defaults.js\";\n\nexport interface WebGPUEngineOptions extends Omit<LoadModelOptions, \"repo\"> {\n /**\n * HuggingFace repo ID (e.g. \"mlx-community/Qwen3.5-0.8B-4bit\") or full URL.\n * Optional — when omitted, a sensible default is chosen for the requested\n * capability (text, vision, or embeddings). See {@link DEFAULT_MODELS}.\n */\n repo?: string;\n /** Max sequence length (default: from model config, capped at 4096). */\n maxSeqLen?: number;\n /** Override KV mode: \"f32\", \"native-f16\", or \"packed-f16\". Auto-detected if omitted. */\n kvMode?: KvMode;\n /**\n * Build the vision encoder (Qwen3.5 ViT) alongside the text model so\n * `encodeImage()` can turn image patches into merged image-embedding tokens.\n * Only valid for vision-capable checkpoints (Qwen3.5). Downloads the ~192MB\n * vision tower. Default: false.\n */\n enableVision?: boolean;\n /** Max patches the vision encoder can process in one call (default 4096). */\n maxVisionPatches?: number;\n}\n\nexport interface EncodeImageResult {\n /** Merged image-embedding tokens, row-major [rows * dim]. */\n embeds: Float32Array;\n /** Number of merged tokens (numPatches / spatial_merge_size^2). */\n rows: number;\n /** Embedding dimension (out_hidden_size, 1024 for Qwen3.5). */\n dim: number;\n}\n\nexport interface EmbedOptions {\n /**\n * Instruction prefix for query embeddings (Qwen3-Embedding convention:\n * \"Instruct: {task}\\nQuery:{text}\"). Omit for document embeddings.\n */\n instruction?: string;\n /**\n * EmbeddingGemma task prefix. The model is asymmetric: queries and documents\n * use different prefixes (`task: search result | query: ` vs `title: none |\n * text: `). Pass \"query\" for search queries and \"document\" for the corpus\n * being searched. Defaults to \"query\" for EmbeddingGemma when omitted. Ignored\n * by non-Gemma embedding models (use `instruction` for Qwen3-Embedding).\n */\n taskType?: \"query\" | \"document\";\n /**\n * Override the raw task prefix prepended to the text (EmbeddingGemma). When\n * set, takes precedence over `taskType`. Use for non-retrieval tasks, e.g.\n * \"task: clustering | query: \" or \"task: classification | query: \".\n */\n taskPrompt?: string;\n /** Max tokens to encode (longer inputs are truncated). Default: model context, capped at maxSeqLen. */\n maxTokens?: number;\n}\n\n/** EmbeddingGemma task prefixes (from config_sentence_transformers.json prompts). */\nconst EMBEDDING_GEMMA_PROMPTS = {\n query: \"task: search result | query: \",\n document: \"title: none | text: \",\n} as const;\n\nexport interface GenerateOptions {\n /** Max tokens to generate (default: 512). */\n maxTokens?: number;\n /** Stop generation on these strings. */\n stopSequences?: string[];\n /** Sampling parameters. */\n sampling?: SamplingParams;\n /** System prompt to prepend. */\n systemPrompt?: string;\n /** Callback for each generated token (for streaming). */\n onToken?: (token: string) => void;\n}\n\nexport interface GenerateResult {\n /** Generated text. */\n text: string;\n /** Number of tokens generated. */\n tokensGenerated: number;\n /** Tokens per second. */\n tokensPerSecond: number;\n /** Total generation time in ms. */\n totalTime: number;\n /** Why generation stopped. */\n finishReason: \"eos\" | \"max_tokens\" | \"stop_sequence\";\n /** Thinking content if model produced it (future). */\n thinking?: string;\n}\n\n/**\n * A minimal JSON-schema-ish shape used by {@link WebGPUEngine.generateObject} to\n * validate generated output without pulling in a schema library. Only `required`\n * and `properties` are inspected (presence of required keys). Pass a predicate\n * function instead for arbitrary validation.\n */\nexport interface ObjectSchema {\n /** Keys that must be present on the parsed object. */\n required?: string[];\n /** Property descriptors (only the key set is used for validation). */\n properties?: Record<string, unknown>;\n /** Allow extra schema fields (type, etc.) without TS complaints. */\n [key: string]: unknown;\n}\n\n/** Validator for {@link WebGPUEngine.generateObject}: a schema object or predicate. */\nexport type ObjectValidator<T = unknown> = ObjectSchema | ((o: T) => boolean);\n\nexport interface GenerateObjectOptions extends GenerateOptions {\n /**\n * Validation target. Either a predicate `(o) => boolean` or a minimal\n * JSON-schema-ish object with `required`/`properties` (required keys must\n * exist). Omit to only require syntactically valid JSON.\n */\n schema?: ObjectValidator;\n /**\n * Max RETRIES after the first attempt (so up to `maxRetries + 1` generations).\n * Default: 4.\n */\n maxRetries?: number;\n}\n\nexport interface GenerateObjectResult<T = unknown> {\n /** The parsed + validated object (or array). */\n object: T;\n /** The raw model text the object was extracted from. */\n text: string;\n /** How many generation attempts it took (1 = first try). */\n attempts: number;\n}\n\nexport interface IntegrityCheckEntry {\n label: string;\n length: number;\n sum: number;\n first4: number[];\n argmax: number;\n maxVal: number;\n match?: \"PASS\" | \"FAIL\";\n refSum?: number;\n refArgmax?: number;\n error?: string;\n note?: string;\n}\n\nexport interface IntegrityCheckResult {\n checks: IntegrityCheckEntry[];\n allPass: boolean;\n}\n\n/**\n * Extract the first complete JSON object or array from a model's text output.\n *\n * Tolerant of prose, markdown, and ```json code fences: it scans for the first\n * `{` or `[`, then walks forward tracking string/escape state and brace/bracket\n * depth to find the matching close, returning the balanced substring. Returns\n * `undefined` when no JSON-looking span is present. Used by\n * {@link WebGPUEngine.generateObject}.\n */\nfunction extractJson(text: string): string | undefined {\n // Strip code-fence markers; the brace scan below ignores surrounding prose.\n const cleaned = text.replace(/```(?:json)?/gi, \"\");\n const start = cleaned.search(/[{[]/);\n if (start === -1) return undefined;\n const open = cleaned[start];\n const close = open === \"{\" ? \"}\" : \"]\";\n let depth = 0;\n let inString = false;\n let escaped = false;\n for (let i = start; i < cleaned.length; i++) {\n const ch = cleaned[i];\n if (inString) {\n if (escaped) {\n escaped = false;\n } else if (ch === \"\\\\\") {\n escaped = true;\n } else if (ch === '\"') {\n inString = false;\n }\n continue;\n }\n if (ch === '\"') {\n inString = true;\n } else if (ch === open) {\n depth++;\n } else if (ch === close) {\n depth--;\n if (depth === 0) return cleaned.slice(start, i + 1);\n }\n }\n return undefined;\n}\n\n/**\n * The main WebGPU inference engine.\n *\n * Usage:\n * const engine = await WebGPUEngine.create({ repo: \"Qwen/Qwen3.5-0.8B\" });\n * const result = await engine.generate(\"Hello!\");\n * console.log(result.text);\n * engine.destroy();\n */\nexport class WebGPUEngine {\n private ctx: GPUContext;\n private executor: Executor;\n private tokenizer: Tokenizer;\n private _destroyed = false;\n private _isEmbedding: boolean;\n /** HF architecture string (e.g. \"Gemma3TextModel\", \"Qwen3ForCausalLM\"). */\n private _architecture: string;\n /** Vision encoder (built only when enableVision and the model is vision-capable). */\n private visionExecutor: VisionExecutor | null;\n /** Raw vision_config (for host preprocessing of grids). */\n private visionConfig: Record<string, unknown> | null;\n /** Raw pos_embed.weight table for bilinear interpolation. */\n private visionPosEmbedTable: Float32Array | null;\n /** True when the LM graph was built with the multimodal (M-RoPE + splice) path. */\n private _multimodalGraph: boolean;\n /** Raw config.json (for M-RoPE params: mrope_section, rope_theta, partial factor). */\n private rawConfig: Record<string, unknown> | null;\n /** Effective max sequence length (cos/sin table coverage). */\n private maxSeqLen: number;\n /** Original create() options (used to lazily spin up the Kani-TTS engine for speak()). */\n private _createOptions: WebGPUEngineOptions;\n /** Lazily-created Kani-TTS engine (codec-LM + NanoCodec) backing speak(). */\n private _kaniTTS: KaniTTS | null = null;\n\n /**\n * WebKit group-size probe state. When true, a candidate group size is being\n * tried this page-load and must be promoted (or capped) after the FIRST\n * successful forward produces non-corrupt logits. Goes false once handled so\n * promotion runs at most once per session. Always false on Dawn/node.\n */\n private _groupProbePending = false;\n\n /** Model capabilities (text, vision, moe). */\n readonly capabilities: ModelCapabilities;\n /** Model architecture config. */\n readonly config: ModelArchConfig;\n\n private constructor(\n ctx: GPUContext,\n executor: Executor,\n tokenizer: Tokenizer,\n graph: ModelGraph,\n opts: {\n multimodalGraph: boolean;\n rawConfig: Record<string, unknown>;\n maxSeqLen: number;\n createOptions: WebGPUEngineOptions;\n /** True if a WebKit group-size candidate is being probed this session. */\n groupProbePending?: boolean;\n },\n vision?: {\n executor: VisionExecutor;\n config: Record<string, unknown>;\n posEmbedTable: Float32Array;\n } | null,\n ) {\n this.ctx = ctx;\n this.executor = executor;\n this.tokenizer = tokenizer;\n this.capabilities = graph.capabilities;\n this.config = graph.config;\n this._isEmbedding = graph.outputs.includes(\"embedding\");\n this._architecture = graph.architecture;\n this.visionExecutor = vision?.executor ?? null;\n this.visionConfig = vision?.config ?? null;\n this.visionPosEmbedTable = vision?.posEmbedTable ?? null;\n this._multimodalGraph = opts.multimodalGraph;\n this.rawConfig = opts.rawConfig;\n this.maxSeqLen = opts.maxSeqLen;\n this._createOptions = opts.createOptions;\n this._groupProbePending = opts.groupProbePending ?? false;\n }\n\n /** True if this engine has a vision encoder built (use encodeImage()). */\n get hasVision(): boolean {\n return this.visionExecutor !== null;\n }\n\n /** Per-opType decode GPU-time breakdown (only populated under GERBIL_PROFILE). */\n getDecodeProfile(): Array<{ opType: string; ns: number; count: number }> {\n return this.executor.getProfile();\n }\n\n /** Clear accumulated decode profiler data (e.g. to drop warm-up tokens). */\n resetDecodeProfile(): void {\n this.executor.resetProfile();\n }\n\n /** Profile ONE real decode step (the pipelined-greedy kernels). Token-independent\n * timing — pass any valid id. Only meaningful under GERBIL_PROFILE. */\n async profileDecodeStep(tokenId: number): Promise<void> {\n await this.executor.profileDecodeStep(tokenId);\n }\n\n /** Decode dispatch count + the device's storage-buffer limit (which gates the\n * INT4 projection fusions). Lets the iPad runner report whether fusions applied\n * on-device or silently fell back (8 < 9 ⇒ more dispatches ⇒ more mobile drains). */\n getDecodeStats(): { dispatches: number; maxStorageBuffers: number } {\n return {\n dispatches: this.executor.decodeDispatchCount,\n maxStorageBuffers: this.executor.maxStorageBuffers,\n };\n }\n\n /**\n * Write a coarse crash-phase breadcrumb that survives a GPU-process kill / page\n * reload. The iPad harness reads `localStorage[\"gerbil-crash-phase\"]` after a\n * crash; without these, a describe-time crash only shows the last load phase\n * (\"engine:ready\"). The describe path tags vit-encode / splice / text-decode so\n * the next run shows WHERE it died, not just \"crashed after load\".\n */\n private setPhase(phase: string): void {\n try {\n if (typeof localStorage !== \"undefined\") {\n localStorage.setItem(\"gerbil-crash-phase\", phase);\n }\n } catch {\n // localStorage can throw in private mode / cross-origin; telemetry is best-effort.\n }\n }\n\n /** True if this engine was loaded as an embedding model (use embed(), not generate()). */\n get isEmbedding(): boolean {\n return this._isEmbedding;\n }\n\n /**\n * WebKit group-size probe promotion hook. Runs at most once per session, after\n * the FIRST forward completes without the page dying. If the page had crashed\n * at this group size, this code never runs and the localStorage breadcrumb\n * (left by the resolver) caps the device on the next load — that is what makes\n * the probe survive the crash class. Here we additionally handle the\n * wrong-output class by inspecting the first forward's logits for corruption\n * (NaN / Inf / all-zero / all-same), reusing the same signals as integrityCheck().\n */\n private maybePromoteGroupProbe(logits: Float32Array): void {\n if (!this._groupProbePending) return;\n this._groupProbePending = false; // promote at most once per session\n\n // Sample a prefix (full-vocab scans are wasteful; corruption shows up early).\n const n = Math.min(logits.length, 256);\n let allZero = true;\n let allSame = true;\n let finite = true;\n const first = logits[0];\n for (let i = 0; i < n; i++) {\n const v = logits[i];\n if (!Number.isFinite(v)) {\n finite = false;\n break;\n }\n if (v !== 0) allZero = false;\n if (v !== first) allSame = false;\n }\n const correct = finite && !allZero && !allSame;\n promoteGroupProbe(correct);\n }\n\n /**\n * Create and initialize a WebGPUEngine.\n *\n * Downloads the model from HuggingFace, compiles shaders, uploads weights.\n */\n static async create(options: WebGPUEngineOptions = {}): Promise<WebGPUEngine> {\n // Fill in the default model for the requested capability when no repo is\n // given, so callers can `create()` / `create({ embedding: true })` and it\n // just works, while any explicit `repo` is still honored everywhere below.\n options = { ...options, repo: resolveDefaultRepo(options) };\n\n // Initialize WebGPU\n const ctx = await initGPU();\n\n // ── Detect environment ──\n const isBrowser = typeof navigator !== \"undefined\" && typeof location !== \"undefined\";\n const isSafari =\n isBrowser && /Safari/.test(navigator.userAgent) && !/Chrome/.test(navigator.userAgent);\n\n // ── URL parameter overrides (browser only) ──\n const params = isBrowser ? new URLSearchParams(location.search) : null;\n const forceKvF32 = params?.has(\"kvf32\");\n const maxSeqOverride = params?.get(\"maxseq\");\n const groupOverride = params?.get(\"group\");\n\n // ── Determine KV mode ──\n // Priority: explicit option > URL param > auto-detect\n let kvMode: KvMode;\n if (options.kvMode) {\n kvMode = options.kvMode;\n } else if (forceKvF32) {\n kvMode = \"f32\";\n } else if (isSafari && ctx.hasF16) {\n // Safari/WebKit miscompiles native f16 types — use packed u32 + unpack2x16float\n kvMode = \"packed-f16\";\n } else if (ctx.hasF16) {\n kvMode = \"native-f16\";\n } else {\n kvMode = \"f32\";\n }\n\n const kvDtype = kvMode === \"f32\" ? \"f32\" : \"f16\";\n\n console.log(\n `[engine] kvMode: ${kvMode}, kvDtype: ${kvDtype}, f16 supported: ${ctx.hasF16}, safari: ${isSafari}`,\n );\n\n // Crash-phase tracking for mobile debugging (survives tab crash)\n const setPhase = (phase: string) => {\n try {\n if (typeof localStorage !== \"undefined\") localStorage.setItem(\"gerbil-crash-phase\", phase);\n } catch {}\n };\n\n // Load model (config, tokenizer, weights). When vision is enabled, build the\n // multimodal LM graph variant (M-RoPE + vision-embedding splice); text-only\n // generation through it is numerically identical to the plain graph.\n setPhase(\"engine:loading-model\");\n // Mobile WebKit has a ~few-second GPU watchdog and a tight (~1.5-2GB) jetsam\n // budget. The ViT Attention op is a single O(N²) dispatch over N patches, so a\n // large N both over-reserves memory and risks a watchdog kill. The host caps\n // the longest image side (≈448px → a few hundred patches), so reserve far\n // fewer patches on WebKit. Desktop/Dawn keeps the roomy 4096 ceiling.\n const maxVisionPatches = options.maxVisionPatches ?? (ctx.isWebKitWebGPU ? 1024 : 4096);\n const multimodal = options.enableVision\n ? { maxVisionTokens: Math.ceil(maxVisionPatches / 4) }\n : undefined;\n const { graph, tokenizer, weights, rawConfig, pleSource } = await loadModel({\n ...options,\n repo: resolveDefaultRepo(options), // resolves to a string (already filled above)\n kvDtype,\n multimodal,\n });\n setPhase(`engine:model-loaded:${weights.size}-weights`);\n\n // ── Vision tower (opt-in) ──\n // Build BEFORE the text executor's uploadWeights() consumes/deletes the\n // weight entries. The vision tower needs its own f32 copy of visual.* and the\n // raw pos_embed table for host bilinear interpolation.\n let visionBundle: {\n executor: VisionExecutor;\n config: Record<string, unknown>;\n posEmbedTable: Float32Array;\n } | null = null;\n // Gate on the checkpoint actually shipping a vision tower (vision_config +\n // tower weights), not the text graph's capability flag (which describes\n // the text graph only). Two families are supported:\n // • Qwen3.5 ViT — \"visual.*\" weights, \"visual.pos_embed.weight\" table\n // • Gemma 4 ViT — \"vision_tower.*\" weights, axial pos-embed table\n // \"vision_tower.patch_embedder.position_embedding_table\"\n const hasVisionConfig = rawConfig.vision_config != null;\n const isGemma4Vision =\n ((rawConfig.vision_config as Record<string, unknown> | undefined)?.model_type as string) ===\n \"gemma4_vision\" || [...weights.keys()].some((k) => k.startsWith(\"vision_tower.\"));\n const visPosKey = isGemma4Vision\n ? \"vision_tower.patch_embedder.position_embedding_table\"\n : \"visual.pos_embed.weight\";\n const towerPrefix = isGemma4Vision ? \"vision_tower.\" : \"visual.\";\n const hasVisualWeights = weights.keys().some((k) => k.startsWith(towerPrefix));\n const wantVision = Boolean(options.enableVision && hasVisionConfig);\n if (wantVision && hasVisualWeights) {\n const visGraph = isGemma4Vision\n ? generateGemma4VisionGraph(rawConfig)\n : generateQwen3_5VisionGraph(rawConfig);\n const maxPatches = maxVisionPatches;\n // Drain the vision-tower tensors (+ projector / pos-embed) out of the\n // WeightSource into a heap Map so the existing (synchronous, Map-based)\n // vision-prep helpers and VisionExecutor.uploadWeights keep working\n // unchanged. The tower is ~201 MB BF16 and only materialized when vision is\n // explicitly enabled — bounded, and disjoint from the text weights the text\n // executor streams below.\n const visWeights = new Map<string, { data: ArrayBufferView; shape: number[] }>();\n for (const k of weights.keys()) {\n if (k.startsWith(towerPrefix) || k.startsWith(\"embed_vision.\") || k === visPosKey) {\n const w = await weights.get(k);\n if (w) visWeights.set(k, w);\n }\n }\n // Patch Gemma 4 ClippableLinear clip scalars into the graph + drop them,\n // and dequantize the MLX-int4 projector (no-op for BF16 checkpoints).\n if (isGemma4Vision) {\n patchGemma4VisionClips(visGraph, visWeights);\n const gi = resolveGemma4VisionInfo(rawConfig);\n const qc = (rawConfig.quantization_config ?? rawConfig.quantization) as\n | Record<string, unknown>\n | undefined;\n const projGroupSize = (qc?.group_size as number) ?? 64;\n dequantizeGemma4VisionProjection(visWeights, projGroupSize, gi.textHidden, gi.hiddenSize);\n // Synthesize the multimodal-embedder norm gains (post-projection norm is\n // no-scale → ones; soft_embedding_norm falls back to ones if a lean export\n // omits it). The post-projection RMSNorm rescales the image soft tokens to\n // the text-embedding magnitude so the LM actually attends to the image.\n ensureGemma4VisionEmbedderNorms(visWeights, gi.hiddenSize, gi.textHidden);\n }\n const visExec = new VisionExecutorImpl(ctx, visGraph, maxPatches);\n // Snapshot the pos-embed table before uploadWeights consumes it.\n const posW = visWeights.get(visPosKey);\n const posTable =\n posW && posW.data instanceof Float32Array\n ? new Float32Array(posW.data) // copy — weights map entry will be uploaded+freed\n : posW\n ? new Float32Array(\n posW.data.buffer,\n posW.data.byteOffset,\n posW.data.byteLength / 4,\n ).slice()\n : null;\n await visExec.uploadWeights(visWeights); // reads (does not delete) tower constants\n visExec.initBindGroups();\n if (posTable) {\n visionBundle = {\n executor: visExec,\n config: (rawConfig.vision_config as Record<string, unknown>) ?? rawConfig,\n posEmbedTable: posTable,\n };\n }\n }\n\n // ── Determine max sequence length ──\n let maxSeqLen: number;\n if (maxSeqOverride) {\n maxSeqLen = Math.min(Number.parseInt(maxSeqOverride, 10), graph.config.context_length);\n } else if (forceKvF32 && isSafari) {\n // f32 KV on Safari = 2x memory; cap at 1024 to avoid OOM\n maxSeqLen = Math.min(options.maxSeqLen ?? 1024, graph.config.context_length, 1024);\n } else if (ctx.isWebKitWebGPU) {\n // iOS/Safari: jetsam kills the web content process around 1.5-2GB.\n // Default conservatively; honor explicit requests up to 2048.\n maxSeqLen = Math.min(options.maxSeqLen ?? 512, graph.config.context_length, 2048);\n } else {\n maxSeqLen = Math.min(\n options.maxSeqLen ?? graph.config.context_length,\n graph.config.context_length,\n 4096,\n );\n }\n\n console.log(`[engine] maxSeqLen: ${maxSeqLen}, architecture: ${graph.architecture}`);\n\n // Log device limits for debugging Safari issues\n if (ctx.isWebKitWebGPU) {\n console.log(\n `[engine] device limits: maxBufferSize=${ctx.limits.maxBufferSize}, ` +\n `maxStorageBufferBindingSize=${ctx.limits.maxStorageBufferBindingSize}, ` +\n `maxComputeWorkgroupStorageSize=${ctx.limits.maxComputeWorkgroupStorageSize}`,\n );\n }\n\n // Create executor and upload weights inside error scopes — Safari reports\n // failed allocations and invalid bind groups asynchronously, and without\n // scopes the affected dispatches silently no-op.\n setPhase(\"engine:allocating-buffers\");\n ctx.device.pushErrorScope(\"out-of-memory\");\n ctx.device.pushErrorScope(\"validation\");\n // ── WebKit submit-granularity (group size) selection ──\n // On WebKit, the executor groups `webkitGroupSize` dispatches per command\n // buffer (one submit + GPU drain per group); larger groups amortize the\n // round-trip and can be ~5x faster on iPad. The safe ceiling is\n // device-dependent (some devices crash or produce garbage at high group),\n // so we run a crash-surviving, self-calibrating probe that escalates UP from\n // group=1 and persists the result per-device in localStorage. An explicit\n // ?group=N overrides everything and skips the probe.\n // - groupOverride present → use it verbatim.\n // - WebKit, no override → resolver picks the rung + records the breadcrumb.\n // - Dawn / node → undefined (executor floors at 1; probe inert).\n const groupOverrideNum = groupOverride ? Number.parseInt(groupOverride, 10) : undefined;\n let webkitGroupSize: number | undefined;\n // True only when we escalated to a new candidate this load and must promote\n // (or cap) it after the first successful forward.\n let probingGroup = false;\n if (groupOverrideNum) {\n webkitGroupSize = groupOverrideNum;\n console.log(\n `[engine] webkitGroupSize override: ${webkitGroupSize} dispatches/command buffer`,\n );\n } else if (ctx.isWebKitWebGPU) {\n // The resolver picks the rung and, when escalating, writes `trying` to the\n // localStorage breadcrumb BEFORE we touch the GPU. We then attempt a\n // promotion after the first forward; promoteGroupProbe() is itself a no-op\n // when `trying === null` (capped / at the floor), so it is always safe to\n // flag this as pending — no need to re-derive whether we actually escalated.\n //\n // Phones are NO LONGER treated as a crash class. We thought iPhone batching\n // crashed the GPU process — but that was memory pressure from the old broken\n // cache (404 MB redownloaded into RAM every load + orphaned/corrupt entries\n // filling the quota). With the IndexedDB cache that pressure is gone, and an\n // iPhone (iOS 18.7) ran Qwen3.5-0.8B at group=4096 / 38.4 tok/s, coherent.\n // So phones escalate like tablets; the crash-surviving breadcrumb still caps\n // any genuinely memory-tight device down after a single bad load.\n webkitGroupSize = resolveWebkitGroupSize({ isWebKit: true });\n probingGroup = true;\n }\n const executor = new Executor(ctx, graph, { maxSeqLen, kvMode, webkitGroupSize });\n // Gemma 4: register the CPU-resident PLE table (streamed per-step, never\n // uploaded to a GPU buffer) before uploadWeights consumes the rest.\n if (pleSource) {\n executor.setPleSource(pleSource);\n }\n setPhase(\"engine:uploading-weights\");\n await executor.uploadWeights(weights);\n // Free the browser transform-staging cache now that every weight is GPU-\n // resident. (The PLE table lives in a separate cache and is NOT evicted —\n // it is lazily streamed on first forward.)\n await weights.dispose?.();\n setPhase(\"engine:compiling-shaders\");\n executor.initBindGroups();\n const validationError = await ctx.device.popErrorScope();\n const oomError = await ctx.device.popErrorScope();\n if (oomError || validationError) {\n const detail = [\n oomError ? `out-of-memory: ${oomError.message}` : null,\n validationError ? `validation: ${validationError.message}` : null,\n ]\n .filter(Boolean)\n .join(\"; \");\n setPhase(`engine:gpu-error:${detail.slice(0, 120)}`);\n throw new Error(\n `GPU setup failed (${detail}). The model likely exceeds this device's memory budget — try a smaller maxSeqLen or a q4-quantized model.`,\n );\n }\n setPhase(\"engine:ready\");\n\n return new WebGPUEngine(\n ctx,\n executor,\n tokenizer,\n graph,\n {\n multimodalGraph: Boolean(multimodal),\n rawConfig,\n maxSeqLen,\n createOptions: options,\n groupProbePending: probingGroup,\n },\n visionBundle,\n );\n }\n\n /**\n * Encode an image (already preprocessed into patches) into merged\n * image-embedding tokens of dim `out_hidden_size` (1024 for Qwen3.5).\n *\n * This is the VISION ENCODER ONLY — it returns the image tokens; it does not\n * splice them into a text sequence or apply M-RoPE (that is the LM-side\n * integration phase). Requires `enableVision: true` at create() on a\n * vision-capable checkpoint.\n *\n * @param patches Flattened patches, row-major [numPatches, patch_dim].\n * patch_dim = in_channels * temporal_patch_size * patch_size^2 (1536 for Qwen3.5).\n * Patches must already be ordered in spatial_merge_size×spatial_merge_size\n * groups (as the HF image processor emits them).\n * @param gridTHW The (temporal, height, width) patch-grid dims for the image.\n * numPatches must equal t*h*w.\n */\n async encodeImage(\n patches: Float32Array,\n gridTHW: [number, number, number],\n onStage?: (stage: string, info?: { layer?: number; total?: number }) => void,\n ): Promise<EncodeImageResult> {\n this.checkDestroyed();\n if (!this.visionExecutor || !this.visionConfig || !this.visionPosEmbedTable) {\n throw new Error(\n \"encodeImage() requires a vision encoder. Load with { enableVision: true } \" +\n \"on a vision-capable checkpoint (e.g. Qwen/Qwen3.5-0.8B).\",\n );\n }\n const vcfg = this.visionConfig;\n\n // ── Gemma 4 ViT path ──\n if (this.visionExecutor.gemma4) {\n const info = resolveGemma4VisionInfo({ vision_config: vcfg });\n const gridH = gridTHW[1];\n const gridW = gridTHW[2];\n // The pos-embed table is [2, posSize, hidden]; posSize = total/(2*hidden).\n const posSize = Math.floor(this.visionPosEmbedTable.length / (2 * info.hiddenSize));\n const host = buildGemma4VisionPositionTensors(\n gridH,\n gridW,\n this.visionPosEmbedTable,\n posSize,\n {\n hiddenSize: info.hiddenSize,\n numHeads: info.numHeads,\n headDim: info.headDim,\n ropeTheta: info.ropeTheta,\n poolingKernelSize: info.poolingKernelSize,\n },\n );\n return this.visionExecutor.encodeGemma4(\n {\n patches,\n posEmbeds: host.posEmbeds,\n cos: host.cos,\n sin: host.sin,\n poolMatrix: host.poolMatrix,\n numPatches: host.numPatches,\n numPooled: host.numPooled,\n },\n onStage,\n );\n }\n\n // ── Qwen3.5 ViT path ──\n const hiddenSize = vcfg.hidden_size as number;\n const numHeads = vcfg.num_heads as number;\n const host = buildVisionPositionTensors(gridTHW, this.visionPosEmbedTable, {\n hiddenSize,\n numHeads,\n numPositionEmbeddings: vcfg.num_position_embeddings as number,\n spatialMergeSize: vcfg.spatial_merge_size as number,\n ropeTheta: 10_000.0,\n });\n const numPatches = gridTHW[0] * gridTHW[1] * gridTHW[2];\n return this.visionExecutor.encode(\n {\n patches,\n posEmbeds: host.posEmbeds,\n cos: host.cos,\n sin: host.sin,\n numPatches,\n },\n onStage,\n );\n }\n\n // ── Multimodal M-RoPE helpers ────────────────────────────────────────────\n\n /** Resolve M-RoPE params from rawConfig: rope_dim, theta, mrope_section. */\n private mropeParams(): {\n ropeDim: number;\n theta: number;\n section: [number, number, number];\n imageTokenId: number;\n mergeSize: number;\n } {\n const cfg = this.rawConfig ?? {};\n const textCfg = (cfg.text_config as Record<string, unknown>) ?? cfg;\n const rope = (textCfg.rope_parameters as Record<string, unknown>) ?? {};\n const headDim = this.config.head_dim;\n const partial = (rope.partial_rotary_factor as number) ?? 0.25;\n const ropeDim = Math.floor(headDim * partial);\n const theta = (rope.rope_theta as number) ?? this.config.rope_base ?? 10_000;\n const section = ((rope.mrope_section as number[]) ?? [11, 11, 10]) as [number, number, number];\n const visCfg = (cfg.vision_config as Record<string, unknown>) ?? {};\n return {\n ropeDim,\n theta,\n section,\n imageTokenId: (cfg.image_token_id as number) ?? 248_056,\n mergeSize: (visCfg.spatial_merge_size as number) ?? 2,\n };\n }\n\n /**\n * Write the M-RoPE cos/sin (token order) + image row-map for a prefill of\n * `positionIds3` ([3, seq]). `rowMap[i]` = vision-buffer row for image tokens,\n * -1 for text. Returns the logical position of the last token (for decode).\n */\n private writeMRoPEPrefill(positionIds3: Int32Array, seq: number, rowMap: Int32Array): number {\n const { ropeDim, theta, section } = this.mropeParams();\n const { cos, sin } = buildMRoPECosSin(positionIds3, seq, ropeDim, theta, section);\n this.executor.writeInput(\"mrope_cos\", cos);\n this.executor.writeInput(\"mrope_sin\", sin);\n this.executor.writeInput(\"vision_row_map\", rowMap);\n // last logical position = max over the 3 components of the final token.\n const last = seq - 1;\n return Math.max(positionIds3[last], positionIds3[seq + last], positionIds3[2 * seq + last]);\n }\n\n /**\n * Write a single decode-step M-RoPE cos/sin row at table slot `seqPos` for a\n * text token at logical position `logicalPos`, plus a -1 row-map entry.\n */\n private writeMRoPEDecodeStep(seqPos: number, logicalPos: number): void {\n const { ropeDim, theta, section } = this.mropeParams();\n // Text token: all 3 position components equal → 1D RoPE for this row.\n const pid = new Int32Array(3);\n pid[0] = logicalPos;\n pid[1] = logicalPos;\n pid[2] = logicalPos;\n const { cos, sin } = buildMRoPECosSin(pid, 1, ropeDim, theta, section);\n const rowBytes = ropeDim * 4;\n this.executor.writeInputAt(\"mrope_cos\", cos, seqPos * rowBytes);\n this.executor.writeInputAt(\"mrope_sin\", sin, seqPos * rowBytes);\n }\n\n /** Write linear-position M-RoPE inputs for a pure-text forward (no image). */\n private writeMRoPELinearText(seq: number): void {\n const { ropeDim, theta, section } = this.mropeParams();\n // Positions 0..seq-1, all three components equal (1D RoPE).\n const pid = new Int32Array(3 * seq);\n for (let i = 0; i < seq; i++) {\n pid[i] = i;\n pid[seq + i] = i;\n pid[2 * seq + i] = i;\n }\n const { cos, sin } = buildMRoPECosSin(pid, seq, ropeDim, theta, section);\n this.executor.writeInput(\"mrope_cos\", cos);\n this.executor.writeInput(\"mrope_sin\", sin);\n const rowMap = new Int32Array(seq).fill(-1);\n this.executor.writeInput(\"vision_row_map\", rowMap);\n }\n\n /**\n * Generate text from a prompt.\n */\n async generate(\n prompt: string | ChatMessage[],\n options: GenerateOptions = {},\n ): Promise<GenerateResult> {\n this.checkDestroyed();\n\n const { maxTokens = 512, stopSequences = [], sampling = {}, systemPrompt, onToken } = options;\n\n // Reset executor for new generation\n this.executor.reset();\n\n // Tokenize\n let inputIds: number[];\n if (typeof prompt === \"string\") {\n // Wrap in chat format\n const messages: ChatMessage[] = [];\n if (systemPrompt) {\n messages.push({ role: \"system\", content: systemPrompt });\n }\n messages.push({ role: \"user\", content: prompt });\n inputIds = this.tokenizer.encodeChat(messages, { addGenerationPrompt: true });\n } else {\n // Already chat messages\n const messages = systemPrompt\n ? [{ role: \"system\" as const, content: systemPrompt }, ...prompt]\n : prompt;\n inputIds = this.tokenizer.encodeChat(messages, { addGenerationPrompt: true });\n }\n\n const startTime = performance.now();\n const isGreedy = (sampling.temperature ?? 0.7) < 1e-6;\n\n // Multimodal graph, text-only prompt. Two flavors:\n // - Qwen3.5 (M-RoPE): feed linear-position cos/sin (reduces to 1D RoPE) + an\n // all-text row map; decode steps write one cos/sin row each.\n // - Gemma 4 (standard 1D RoPE, self-computed): no host cos/sin. Just clear the\n // vision row-map to all -1 so EmbedSplice passes the text embeds through.\n const hasMRoPE = this._multimodalGraph && this.executor.hasBuffer(\"mrope_cos\");\n if (this._multimodalGraph) {\n if (hasMRoPE) {\n this.writeMRoPELinearText(inputIds.length);\n } else if (this.executor.hasBuffer(\"vision_row_map\")) {\n this.executor.writeInput(\"vision_row_map\", new Int32Array(inputIds.length).fill(-1));\n }\n }\n\n // Prefill: process all input tokens at once\n let { logits } = await this.executor.forward(new Uint32Array(inputIds));\n\n // WebKit group-size probe: the first forward survived (page didn't die), so\n // promote the candidate group size if its output is non-corrupt, else cap.\n // No-op on Dawn/node and after the first call. Done before decode so a wrong\n // group is recorded even if the user stops the generation early.\n this.maybePromoteGroupProbe(logits);\n\n // Decode loop\n const generatedIds: number[] = [];\n let finishReason: GenerateResult[\"finishReason\"] = \"max_tokens\";\n let generatedText = \"\";\n\n const eosId = this.tokenizer.config.eosTokenId;\n\n // Emit one token; returns true when generation should stop.\n const consumeToken = (nextToken: number): boolean => {\n generatedIds.push(nextToken);\n\n // Check EOS\n if (eosId !== null && nextToken === eosId) {\n finishReason = \"eos\";\n return true;\n }\n\n // Decode token and check stop sequences\n const tokenText = this.tokenizer.decode([nextToken], true);\n generatedText += tokenText;\n onToken?.(tokenText);\n\n if (stopSequences.some((s) => generatedText.includes(s))) {\n // Trim the stop sequence from the end\n for (const s of stopSequences) {\n const idx = generatedText.indexOf(s);\n if (idx !== -1) {\n generatedText = generatedText.slice(0, idx);\n }\n }\n finishReason = \"stop_sequence\";\n return true;\n }\n return false;\n };\n\n // M-RoPE decode positions are logical (text continues after the image at the\n // post-image cursor), so each step needs a fresh cos/sin row — incompatible\n // with the GPU-chained pipelined path. mmDecode tracks the logical position.\n // Gemma 4's multimodal graph uses self-computed sequential RoPE (no host cos/sin),\n // so it is NOT an mmDecode case and can use the fast pipelined path.\n const mmDecode = hasMRoPE;\n let mmLogicalPos = inputIds.length; // text-only: logical == token index\n\n if (isGreedy && !this.executor.needsMultiEncoder && !mmDecode) {\n // ── Pipelined greedy decode (Dawn) ──\n // The argmax result is chained into input_ids on the GPU, so up to\n // PIPELINE_DEPTH decode steps stay in flight while tokens are read\n // back one step behind. Removes per-token CPU prep and map latency\n // from the critical path.\n const firstToken = sampleToken(logits, sampling, [...inputIds, ...generatedIds]);\n if (!consumeToken(firstToken)) {\n const depth = Executor.PIPELINE_DEPTH;\n const stepsNeeded = Math.min(maxTokens - 1, this.executor.decodeCapacityRemaining());\n let submitted = 0;\n let consumed = 0;\n while (consumed < stepsNeeded) {\n while (submitted < stepsNeeded && submitted < consumed + depth) {\n this.executor.submitGreedyDecodeStep(\n submitted === 0 ? firstToken : null,\n submitted % depth,\n );\n submitted++;\n }\n const tok = await this.executor.readDecodeToken(consumed % depth);\n consumed++;\n if (consumeToken(tok)) break;\n }\n }\n } else {\n for (let step = 0; step < maxTokens; step++) {\n let nextToken: number;\n if (step === 0 || !isGreedy) {\n nextToken = sampleToken(logits, sampling, [...inputIds, ...generatedIds]);\n } else {\n // Greedy multimodal/desktop fallback: write this step's M-RoPE row\n // (logical position) before the GPU forward reads it.\n if (mmDecode) this.writeMRoPEDecodeStep(this.executor.currentSeqPos, mmLogicalPos);\n nextToken = await this.executor.forwardArgmax(\n new Uint32Array([generatedIds[generatedIds.length - 1]]),\n );\n mmLogicalPos++;\n }\n\n if (consumeToken(nextToken)) break;\n\n if (!isGreedy) {\n if (mmDecode) this.writeMRoPEDecodeStep(this.executor.currentSeqPos, mmLogicalPos);\n const result = await this.executor.forward(new Uint32Array([nextToken]));\n logits = result.logits;\n mmLogicalPos++;\n }\n }\n }\n\n const totalTime = performance.now() - startTime;\n const tokensGenerated = generatedIds.length;\n const tokensPerSecond = tokensGenerated / (totalTime / 1000);\n\n return {\n text: generatedText,\n tokensGenerated,\n tokensPerSecond,\n totalTime,\n finishReason,\n };\n }\n\n /**\n * Generate a STRUCTURED object: generate text, extract the first JSON\n * object/array, parse it, validate it, and RETRY until it is valid (on-device\n * tokens are free, so re-rolling a malformed JSON is cheap).\n *\n * Extraction is tolerant: prose, markdown, and ```json code fences are\n * stripped, then the outermost balanced `{...}` or `[...]` is matched and\n * `JSON.parse`d. Validation is one of:\n * - a predicate `(o) => boolean` (return false to reject),\n * - a minimal JSON-schema-ish object with `required` (those keys must exist),\n * - nothing (only valid JSON is required).\n *\n * On each retry the prompt is nudged with a terse \"return ONLY valid JSON…\"\n * instruction (including the required-key shape when known). Throws a clear\n * error if it never validates within `maxRetries + 1` attempts.\n *\n * ```ts\n * const { object } = await engine.generateObject(\n * 'Extract {name, age} from: \"I am Sarah, 28\"',\n * { schema: { required: [\"name\", \"age\"] } },\n * );\n * // object === { name: \"Sarah\", age: 28 }\n * ```\n *\n * @typeParam T Expected object type (not enforced at runtime — validate via schema).\n */\n async generateObject<T = unknown>(\n prompt: string,\n options: GenerateObjectOptions = {},\n ): Promise<GenerateObjectResult<T>> {\n this.checkDestroyed();\n const { schema, maxRetries = 4, ...generateOpts } = options;\n\n const validate = (value: unknown): boolean => {\n if (typeof schema === \"function\") {\n return (schema as (o: unknown) => boolean)(value);\n }\n if (schema && typeof schema === \"object\" && Array.isArray(schema.required)) {\n if (value === null || typeof value !== \"object\") return false;\n const obj = value as Record<string, unknown>;\n return schema.required.every((key) => key in obj);\n }\n // No schema → any valid JSON value is acceptable.\n return true;\n };\n\n // Build the retry nudge once: include the required-key shape when known so\n // the model gets a concrete target to match.\n const shape =\n schema && typeof schema === \"object\" && Array.isArray(schema.required)\n ? `{ ${schema.required.join(\", \")} }`\n : \"the requested shape\";\n const nudge = `\\n\\nReturn ONLY valid JSON matching ${shape}. No prose, no markdown, no code fences.`;\n\n const attemptsMax = Math.max(0, maxRetries) + 1;\n let lastText = \"\";\n let lastError = \"\";\n for (let attempt = 1; attempt <= attemptsMax; attempt++) {\n const promptForAttempt = attempt === 1 ? prompt : prompt + nudge;\n const result = await this.generate(promptForAttempt, generateOpts);\n lastText = result.text;\n const parsed = extractJson(result.text);\n if (parsed === undefined) {\n lastError = \"no JSON object/array found in output\";\n continue;\n }\n let value: unknown;\n try {\n value = JSON.parse(parsed);\n } catch (e) {\n lastError = `JSON.parse failed: ${e instanceof Error ? e.message : String(e)}`;\n continue;\n }\n if (!validate(value)) {\n lastError = \"parsed JSON failed schema validation\";\n continue;\n }\n return { object: value as T, text: result.text, attempts: attempt };\n }\n\n throw new Error(\n `generateObject failed after ${attemptsMax} attempt(s): ${lastError}. ` +\n `Last output: ${JSON.stringify(lastText.slice(0, 200))}`,\n );\n }\n\n /**\n * Text-to-speech: text → 22 kHz PCM via Kani-TTS-2 (LFM2-350M codec-LM + NVIDIA\n * NeMo NanoCodec). Returns `{ pcm: Float32Array, sampleRate: 22050 }`.\n *\n * Runs the full pipeline: the codec-LM backbone autoregressively emits NanoCodec\n * audio tokens (4 per frame, frame-level positions + learnable per-layer RoPE),\n * then the bit-exact NanoCodec decoder (FSQ + causal HiFi-GAN) turns the codes\n * into PCM. The heavy lifting lives in {@link KaniTTS} (src/gpu/kani-tts.ts); this\n * lazily constructs that engine on first use (downloading the NanoCodec codec\n * checkpoint alongside the backbone).\n *\n * Requires a Kani-TTS-2 checkpoint (architecture \"KaniTTS2ForCausalLM\").\n */\n async speak(\n text: string,\n options: {\n languageTag?: string;\n temperature?: number;\n topP?: number;\n repetitionPenalty?: number;\n maxFrames?: number;\n } = {},\n ): Promise<{ pcm: Float32Array; sampleRate: number; frames: number; audioSeconds: number }> {\n this.checkDestroyed();\n if (this._architecture !== \"KaniTTS2ForCausalLM\") {\n throw new Error(\n `speak() requires a Kani-TTS-2 model (architecture \"KaniTTS2ForCausalLM\"), ` +\n `loaded engine is \"${this._architecture}\".`,\n );\n }\n // Lazily build the dual-graph Kani engine (codec-LM + NanoCodec). It loads its\n // own weights (the backbone repo + the separate NanoCodec checkpoint), so it is\n // independent of this engine's text executor.\n if (!this._kaniTTS) {\n this._kaniTTS = await KaniTTS.create({\n repo: this._createOptions.repo,\n revision: this._createOptions.revision,\n hfToken: this._createOptions.hfToken,\n cacheDir: this._createOptions.cacheDir,\n maxSeqLen: this.maxSeqLen,\n });\n }\n return this._kaniTTS.speak(text, options);\n }\n\n /**\n * Describe an image: image-in → text-out. Runs the vision encoder, splices the\n * merged image tokens into a text prompt, applies multimodal M-RoPE, and\n * generates a description. Requires `enableVision: true` at create().\n *\n * Image input forms:\n * - `{ pixels, width, height }` — decoded RGB (HWC, 0..255), host-preprocessed\n * (smart-resize/normalize/patchify) to match the HF image processor.\n * - `{ patches, gridTHW }` — already-built [N,1536] patch tensor + grid (e.g.\n * HF-exact pixel_values from a reference; skips host preprocessing).\n */\n async describeImage(\n image:\n | { pixels: Float32Array | Uint8ClampedArray | Uint8Array; width: number; height: number }\n | { patches: Float32Array; gridTHW: [number, number, number] },\n prompt = \"Describe this image.\",\n options: GenerateOptions & { imageProcessor?: ImageProcessorConfig } = {},\n ): Promise<GenerateResult> {\n this.checkDestroyed();\n if (!this.visionExecutor) {\n throw new Error(\n \"describeImage() requires a vision encoder. Load with { enableVision: true } \" +\n \"on a vision-capable checkpoint (Qwen3.5 or Gemma 4).\",\n );\n }\n\n // ── Gemma 4 family path ──\n // ViT encoder + projector are native + validated (cosine 1.0 vs reference).\n // Gemma 4 uses STANDARD sequential 1D RoPE (positions 0..N-1 across text+image),\n // computed inside each layer from the KV write-position — so, unlike Qwen3.5's\n // M-RoPE path, there are no host cos/sin inputs. We splice the merged vision\n // tokens into the `image_token_id` rows (EmbedSplice in the multimodal graph)\n // and run a plain self-RoPE prefill + greedy/sampled decode.\n if (this.visionExecutor.gemma4) {\n if (!this._multimodalGraph) {\n throw new Error(\n \"Gemma 4 describeImage() requires the multimodal text graph. Load with \" +\n \"{ enableVision: true } so generateGemma4Graph builds the vision_embeds + \" +\n \"EmbedSplice variant.\",\n );\n }\n let g4patches: Float32Array;\n let g4gridHW: [number, number];\n if (\"patches\" in image) {\n g4patches = image.patches;\n g4gridHW = [image.gridTHW[1], image.gridTHW[2]];\n } else {\n const pre = preprocessImageGemma4(image.pixels, image.width, image.height);\n g4patches = pre.patches;\n g4gridHW = pre.gridHW;\n }\n const gThw: [number, number, number] = [1, g4gridHW[0], g4gridHW[1]];\n const numPatches = gThw[0] * gThw[1] * gThw[2];\n this.setPhase(`describe:vit-encode:N=${numPatches}`);\n const vision = await this.encodeImage(g4patches, gThw, (stage, info) => {\n const suffix = info?.layer != null ? `:L${info.layer}` : \"\";\n this.setPhase(`describe:${stage}${suffix}`);\n });\n\n // Build the Gemma 4 multimodal token sequence. The HF Gemma processor expands\n // the `<|image|>` chat placeholder into: boi(`<|image>`) + image_token×N +\n // eoi(`<image|>`), bracketed by the \"\\n\\n\" the image template adds, all inside\n // the user turn. We mirror that exactly so token positions match HF.\n const cfg = this.rawConfig ?? {};\n const imageTokenId = (cfg.image_token_id as number) ?? 258_880;\n const boiId = this.tokenizer.tokenToId(\"<|image>\") ?? (cfg.boi_token_id as number) ?? 255_999;\n const eoiId = this.tokenizer.tokenToId(\"<image|>\") ?? (cfg.eoi_token_id as number) ?? 258_882;\n const bos = this.tokenizer.config.bosToken\n ? (this.tokenizer.tokenToId(this.tokenizer.config.bosToken) ?? [])\n : [];\n const bosIds = Array.isArray(bos) ? bos : [bos];\n // User-turn prefix/suffix around the image+prompt, matching applyGemmaTurnTemplate\n // (`<|turn>user\\n … <turn|>\\n<|turn>model\\n`). The image block is \"\\n\\n\" + boi +\n // soft tokens + eoi + \"\\n\\n\", then the text prompt.\n const turnUserOpen = this.tokenizer.encode(\"<|turn>user\\n\");\n const imgLead = this.tokenizer.encode(\"\\n\\n\");\n const imgTrail = this.tokenizer.encode(\"\\n\\n\");\n const promptIds = this.tokenizer.encode(prompt);\n const turnClose = this.tokenizer.encode(\"<turn|>\\n<|turn>model\\n\");\n const imageRun: number[] = new Array(vision.rows).fill(imageTokenId);\n const inputIds: number[] = [\n ...bosIds,\n ...turnUserOpen,\n ...imgLead,\n boiId,\n ...imageRun,\n eoiId,\n ...imgTrail,\n ...promptIds,\n ...turnClose,\n ];\n\n return this.runMultimodalGemma4(vision.embeds, inputIds, imageTokenId, options);\n }\n\n if (!this._multimodalGraph) {\n throw new Error(\n \"describeImage() requires a multimodal engine. Load with { enableVision: true } \" +\n \"on a vision-capable checkpoint (e.g. Qwen/Qwen3.5-0.8B).\",\n );\n }\n\n // 1. Image → patches → merged vision tokens [N, 1024].\n const procCfg = options.imageProcessor ?? QWEN3_5_IMAGE_PROCESSOR;\n let patches: Float32Array;\n let gridTHW: [number, number, number];\n if (\"patches\" in image) {\n patches = image.patches;\n gridTHW = image.gridTHW;\n } else {\n const pre = preprocessImage(image.pixels, image.width, image.height, procCfg);\n patches = pre.patches;\n gridTHW = pre.gridTHW;\n }\n // Phase breadcrumbs survive a GPU-process kill so the harness can attribute a\n // describe-time crash to vit-encode vs splice vs text-decode.\n const numPatches = gridTHW[0] * gridTHW[1] * gridTHW[2];\n this.setPhase(`describe:vit-encode:N=${numPatches}`);\n const vision = await this.encodeImage(patches, gridTHW, (stage, info) => {\n const suffix = info?.layer != null ? `:L${info.layer}` : \"\";\n this.setPhase(`describe:${stage}${suffix}`);\n });\n\n // 2. Build the multimodal token sequence. Match the HF chat template:\n // <|im_start|>user\\n<vision_start><image_pad × N><vision_end>{prompt}\n // <|im_end|>\\n<|im_start|>assistant\\n<think>\\n\\n</think>\\n\\n\n const visStart = this.tokenizer.tokenToId(\"<|vision_start|>\") ?? 248_053;\n const visEnd = this.tokenizer.tokenToId(\"<|vision_end|>\") ?? 248_054;\n const { imageTokenId } = this.mropeParams();\n const numImageTokens = vision.rows;\n const pre = this.tokenizer.encode(\"<|im_start|>user\\n\");\n const post = this.tokenizer.encode(\n `${prompt}<|im_end|>\\n<|im_start|>assistant\\n<think>\\n\\n</think>\\n\\n`,\n );\n const imageRun: number[] = new Array(numImageTokens).fill(imageTokenId);\n const inputIds: number[] = [...pre, visStart, ...imageRun, visEnd, ...post];\n\n return this.runMultimodal(vision.embeds, gridTHW, inputIds, options);\n }\n\n /**\n * Prepare the multimodal prefill: upload vision embeds, build the image row-map\n * and 3D M-RoPE cos/sin, reset state, and write all host inputs. Returns the\n * input ids and the post-image logical cursor for decode. Does NOT run forward.\n */\n private prepareMultimodalPrefill(\n visionEmbeds: Float32Array,\n gridTHW: [number, number, number],\n inputIds: number[],\n ): { lastLogicalPos: number } {\n const { imageTokenId, mergeSize } = this.mropeParams();\n const seq = inputIds.length;\n if (seq > this.maxSeqLen) {\n throw new Error(\n `describeImage: prompt+image is ${seq} tokens > maxSeqLen ${this.maxSeqLen}. ` +\n \"Increase maxSeqLen or use a smaller image.\",\n );\n }\n this.executor.reset();\n this.executor.writeInput(\"vision_embeds\", visionEmbeds);\n\n const rowMap = new Int32Array(seq).fill(-1);\n let v = 0;\n for (let i = 0; i < seq; i++) {\n if (inputIds[i] === imageTokenId) rowMap[i] = v++;\n }\n const positionIds3 = buildMRoPEPositionIds(inputIds, [gridTHW], imageTokenId, mergeSize);\n const lastLogicalPos = this.writeMRoPEPrefill(positionIds3, seq, rowMap);\n return { lastLogicalPos };\n }\n\n /**\n * Gemma 4 multimodal prefill + decode. Unlike Qwen3.5 (M-RoPE), Gemma 4 uses\n * STANDARD sequential 1D RoPE computed inside each layer from the KV write\n * position, so there are no host cos/sin inputs and decode positions are simply\n * the running seqPos — identical to plain text generation. We only upload the\n * merged vision embeds + an image-token row-map (EmbedSplice scatters them into\n * the image_token rows) before the forward pass.\n */\n private async runMultimodalGemma4(\n visionEmbeds: Float32Array,\n inputIds: number[],\n imageTokenId: number,\n options: GenerateOptions,\n ): Promise<GenerateResult> {\n const seq = inputIds.length;\n if (seq > this.maxSeqLen) {\n throw new Error(\n `describeImage: prompt+image is ${seq} tokens > maxSeqLen ${this.maxSeqLen}. ` +\n \"Increase maxSeqLen or use a smaller image.\",\n );\n }\n this.setPhase(`describe:splice:seq=${seq}`);\n this.executor.reset();\n this.executor.writeInput(\"vision_embeds\", visionEmbeds);\n // row_map[i] = vision-buffer row for the i-th image token, -1 for text rows.\n const rowMap = new Int32Array(seq).fill(-1);\n let v = 0;\n for (let i = 0; i < seq; i++) {\n if (inputIds[i] === imageTokenId) rowMap[i] = v++;\n }\n this.executor.writeInput(\"vision_row_map\", rowMap);\n\n this.setPhase(\"describe:text-decode\");\n const { maxTokens = 512, stopSequences = [], sampling = {}, onToken } = options;\n const startTime = performance.now();\n const isGreedy = (sampling.temperature ?? 0.7) < 1e-6;\n\n let { logits } = await this.executor.forward(new Uint32Array(inputIds));\n\n const generatedIds: number[] = [];\n let finishReason: GenerateResult[\"finishReason\"] = \"max_tokens\";\n let generatedText = \"\";\n const eosId = this.tokenizer.config.eosTokenId;\n // Gemma model turns end with the end-of-turn token `<turn|>` (id 106), NOT the\n // generic `<eos>` — treat it as a stop so chat decode doesn't run on into\n // repetition past the answer.\n const eotId = this.tokenizer.tokenToId(\"<turn|>\");\n\n const consumeToken = (nextToken: number): boolean => {\n generatedIds.push(nextToken);\n if ((eosId !== null && nextToken === eosId) || (eotId !== null && nextToken === eotId)) {\n finishReason = \"eos\";\n return true;\n }\n const tokenText = this.tokenizer.decode([nextToken], true);\n generatedText += tokenText;\n onToken?.(tokenText);\n if (stopSequences.some((s) => generatedText.includes(s))) {\n for (const s of stopSequences) {\n const idx = generatedText.indexOf(s);\n if (idx !== -1) generatedText = generatedText.slice(0, idx);\n }\n finishReason = \"stop_sequence\";\n return true;\n }\n return false;\n };\n\n for (let step = 0; step < maxTokens; step++) {\n let nextToken: number;\n if (step === 0) {\n nextToken = sampleToken(logits, sampling, [...inputIds, ...generatedIds]);\n } else if (isGreedy) {\n nextToken = await this.executor.forwardArgmax(\n new Uint32Array([generatedIds[generatedIds.length - 1]]),\n );\n } else {\n nextToken = sampleToken(logits, sampling, [...inputIds, ...generatedIds]);\n }\n\n if (consumeToken(nextToken)) break;\n\n if (!isGreedy) {\n const result = await this.executor.forward(new Uint32Array([nextToken]));\n logits = result.logits;\n }\n }\n\n const totalTime = performance.now() - startTime;\n const tokensGenerated = generatedIds.length;\n return {\n text: generatedText,\n tokensGenerated,\n tokensPerSecond: tokensGenerated / (totalTime / 1000),\n totalTime,\n finishReason,\n };\n }\n\n /** Prepare + prefill + decode for a fully-specified multimodal token sequence. */\n private async runMultimodal(\n visionEmbeds: Float32Array,\n gridTHW: [number, number, number],\n inputIds: number[],\n options: GenerateOptions,\n ): Promise<GenerateResult> {\n this.setPhase(`describe:splice:seq=${inputIds.length}`);\n const { lastLogicalPos } = this.prepareMultimodalPrefill(visionEmbeds, gridTHW, inputIds);\n this.setPhase(\"describe:text-decode\");\n return this.generateFromPrepared(inputIds, lastLogicalPos + 1, options);\n }\n\n /**\n * Debug: run ONLY the multimodal prefill for an explicit token sequence and\n * return the spliced input embeddings [seq, hidden] + first-token logits. Lets\n * tests compare the fused text+vision stream and M-RoPE numerically vs HF\n * without the decode loop overwriting intermediate buffers.\n */\n async debugMultimodalPrefill(\n patches: Float32Array,\n gridTHW: [number, number, number],\n inputIds: number[],\n ): Promise<{ splicedEmbeds: Float32Array; logits: Float32Array; seq: number }> {\n this.checkDestroyed();\n if (!this._multimodalGraph || !this.visionExecutor) {\n throw new Error(\"debugMultimodalPrefill requires a multimodal engine (enableVision: true).\");\n }\n const vision = await this.encodeImage(patches, gridTHW);\n this.prepareMultimodalPrefill(vision.embeds, gridTHW, inputIds);\n const { logits } = await this.executor.forward(new Uint32Array(inputIds));\n const splicedEmbeds = await this.executor.debugReadBuffer(\n \"embed_spliced\",\n inputIds.length * this.config.hidden_size,\n );\n return { splicedEmbeds, logits, seq: inputIds.length };\n }\n\n /**\n * Internal: run prefill (assumes M-RoPE/splice inputs already written) + decode,\n * with decode logical positions starting at `decodeStartPos`. Used by\n * describeImage so the post-image cursor is honored.\n */\n private async generateFromPrepared(\n inputIds: number[],\n decodeStartPos: number,\n options: GenerateOptions,\n ): Promise<GenerateResult> {\n const { maxTokens = 512, stopSequences = [], sampling = {}, onToken } = options;\n const startTime = performance.now();\n const isGreedy = (sampling.temperature ?? 0.7) < 1e-6;\n\n let { logits } = await this.executor.forward(new Uint32Array(inputIds));\n\n const generatedIds: number[] = [];\n let finishReason: GenerateResult[\"finishReason\"] = \"max_tokens\";\n let generatedText = \"\";\n const eosId = this.tokenizer.config.eosTokenId;\n let mmLogicalPos = decodeStartPos;\n\n const consumeToken = (nextToken: number): boolean => {\n generatedIds.push(nextToken);\n if (eosId !== null && nextToken === eosId) {\n finishReason = \"eos\";\n return true;\n }\n const tokenText = this.tokenizer.decode([nextToken], true);\n generatedText += tokenText;\n onToken?.(tokenText);\n if (stopSequences.some((s) => generatedText.includes(s))) {\n for (const s of stopSequences) {\n const idx = generatedText.indexOf(s);\n if (idx !== -1) generatedText = generatedText.slice(0, idx);\n }\n finishReason = \"stop_sequence\";\n return true;\n }\n return false;\n };\n\n for (let step = 0; step < maxTokens; step++) {\n let nextToken: number;\n if (step === 0) {\n nextToken = sampleToken(logits, sampling, [...inputIds, ...generatedIds]);\n } else if (isGreedy) {\n this.writeMRoPEDecodeStep(this.executor.currentSeqPos, mmLogicalPos);\n nextToken = await this.executor.forwardArgmax(\n new Uint32Array([generatedIds[generatedIds.length - 1]]),\n );\n mmLogicalPos++;\n } else {\n nextToken = sampleToken(logits, sampling, [...inputIds, ...generatedIds]);\n }\n\n if (consumeToken(nextToken)) break;\n\n if (!isGreedy) {\n this.writeMRoPEDecodeStep(this.executor.currentSeqPos, mmLogicalPos);\n const result = await this.executor.forward(new Uint32Array([nextToken]));\n logits = result.logits;\n mmLogicalPos++;\n }\n }\n\n const totalTime = performance.now() - startTime;\n const tokensGenerated = generatedIds.length;\n return {\n text: generatedText,\n tokensGenerated,\n tokensPerSecond: tokensGenerated / (totalTime / 1000),\n totalTime,\n finishReason,\n };\n }\n\n /**\n * Embed text into an L2-normalized vector. The pooling strategy depends on the\n * model: Qwen3-Embedding uses last-token (EOS-position) pooling, while\n * EmbeddingGemma (Gemma3 encoder) uses mean pooling over all tokens followed by\n * a 2-layer Dense head. Requires an embedding model (loaded with\n * { embedding: true }).\n *\n * The returned Float32Array has unit L2 norm, so cosine similarity reduces to a\n * dot product. Length is the model's embedding dim (768 for EmbeddingGemma;\n * config.hidden_size for Qwen3-Embedding).\n *\n * EmbeddingGemma is asymmetric — pass `{ taskType: \"query\" }` for search\n * queries and `{ taskType: \"document\" }` for the corpus, or a raw\n * `{ taskPrompt }` for other tasks (clustering/classification/STS).\n */\n async embed(text: string, options: EmbedOptions = {}): Promise<Float32Array> {\n this.checkDestroyed();\n if (!this._isEmbedding) {\n throw new Error(\n \"embed() requires an embedding model. Load with { embedding: true } \" +\n \"(e.g. repo: 'Qwen/Qwen3-Embedding-0.6B' or \" +\n \"'mlx-community/embeddinggemma-300m-4bit').\",\n );\n }\n\n // Reset non-autoregressive state before each embed call.\n this.executor.reset();\n\n const { instruction, taskType, taskPrompt, maxTokens } = options;\n const isGemmaEncoder =\n this._architecture === \"Gemma3TextModel\" || this._architecture === \"Gemma3Model\";\n\n if (isGemmaEncoder) {\n // EmbeddingGemma: apply the task prefix, mean-pool over all tokens (no EOS\n // pooling token appended — every token participates in the mean).\n const prefix =\n taskPrompt ?? EMBEDDING_GEMMA_PROMPTS[taskType ?? \"query\"] ?? EMBEDDING_GEMMA_PROMPTS.query;\n const input = `${prefix}${text}`;\n\n // The Gemma tokenizer prepends <bos> per its config (add_bos_token: true).\n let ids = this.tokenizer.encode(input);\n\n const cap = maxTokens ?? this.config.context_length;\n if (ids.length > cap) {\n ids = ids.slice(0, cap);\n }\n\n return this.executor.embed(new Uint32Array(ids));\n }\n\n // Qwen3-Embedding: optional instruction prefix + last-token (EOS) pooling.\n const input = instruction ? `Instruct: ${instruction}\\nQuery:${text}` : text;\n\n let ids = this.tokenizer.encode(input);\n\n // Last-token pooling reads the final position; Qwen3-Embedding pools the\n // hidden state at a trailing <|endoftext|> (id 151643 — the model config's\n // eos_token_id), NOT the chat <|im_end|> token the tokenizer reports as\n // eos_token. Resolve the literal token and append it if absent.\n const padId = this.tokenizer.tokenToId(\"<|endoftext|>\") ?? this.tokenizer.config.eosTokenId;\n if (padId !== null && ids[ids.length - 1] !== padId) {\n ids.push(padId);\n }\n\n // Truncate to budget, keeping the trailing pooling token as the last token.\n const cap = maxTokens ?? this.config.context_length;\n if (ids.length > cap) {\n const tail = padId !== null && ids[ids.length - 1] === padId ? [padId] : [];\n ids = [...ids.slice(0, cap - tail.length), ...tail];\n }\n\n return this.executor.embed(new Uint32Array(ids));\n }\n\n /**\n * Generate text as an async iterator (streaming).\n *\n * Uses the onToken callback from generate() to push tokens into a queue\n * that the async generator yields from. The generator returns the full\n * GenerateResult when generation completes.\n *\n * Usage:\n * const gen = engine.stream(\"Hello!\");\n * for await (const token of gen) {\n * process.stdout.write(token);\n * }\n * const result = gen.next(); // { done: true, value: GenerateResult }\n */\n async *stream(\n prompt: string | ChatMessage[],\n options: GenerateOptions = {},\n ): AsyncGenerator<string, GenerateResult, undefined> {\n this.checkDestroyed();\n\n // Token queue: onToken callback pushes here, generator yields from here.\n const tokenQueue: string[] = [];\n let resolve: (() => void) | null = null;\n let done = false;\n\n // Notify the generator that a new token is available\n const pushToken = (token: string): void => {\n tokenQueue.push(token);\n if (resolve) {\n const r = resolve;\n resolve = null;\n r();\n }\n };\n\n // Wait until at least one token is available or generation is done\n const waitForToken = (): Promise<void> => {\n if (tokenQueue.length > 0 || done) {\n return Promise.resolve();\n }\n return new Promise<void>((r) => {\n resolve = r;\n });\n };\n\n // Start generation in the background with our onToken callback\n const genPromise = this.generate(prompt, {\n ...options,\n onToken: (token) => {\n options.onToken?.(token); // Forward to user's callback too\n pushToken(token);\n },\n }).then((result) => {\n done = true;\n // Wake up the generator if it's waiting\n if (resolve) {\n const r = resolve;\n resolve = null;\n r();\n }\n return result;\n });\n\n // Yield tokens as they arrive\n let yielded = 0;\n while (true) {\n await waitForToken();\n\n // Yield all queued tokens\n while (yielded < tokenQueue.length) {\n yield tokenQueue[yielded++];\n }\n\n // If generation is done and we've yielded everything, return the result\n if (done && yielded >= tokenQueue.length) {\n break;\n }\n }\n\n return await genPromise;\n }\n\n /**\n * Debug: read back a named GPU buffer (weight or activation).\n * Call after forward() to inspect intermediate values.\n */\n async debugReadBuffer(tensorName: string, maxElements?: number): Promise<Float32Array> {\n return this.executor.debugReadBuffer(tensorName, maxElements);\n }\n\n /**\n * Run GPU diagnostics (buffer integrity, compute, shared memory).\n * Useful for isolating Safari/WebKit-specific WebGPU issues.\n */\n async diagnose(): Promise<GPUDiagnosticResult> {\n return verifyGPU(this.ctx);\n }\n\n /**\n * Run GPU diagnostics without loading a model.\n * Quick way to check if WebGPU is working correctly on this device.\n */\n static async quickDiagnose(): Promise<GPUDiagnosticResult> {\n const ctx = await initGPU();\n const result = await verifyGPU(ctx);\n clearPipelineCache(ctx.device);\n ctx.device.destroy();\n return result;\n }\n\n /**\n * Run a raw forward pass (no tokenization/chat template).\n * Returns logits for the last token.\n */\n async rawForward(inputIds: Uint32Array): Promise<{ logits: Float32Array }> {\n return this.executor.forward(inputIds);\n }\n\n /**\n * Reset executor state (SSM, positions, etc.)\n */\n resetState(): void {\n this.executor.reset();\n }\n\n /**\n * Encode text to token IDs (useful for debugging / token counting).\n */\n encode(text: string): number[] {\n return this.tokenizer.encode(text);\n }\n\n /**\n * Decode token IDs to text.\n */\n decode(ids: number[], skipSpecialTokens?: boolean): string {\n return this.tokenizer.decode(ids, skipSpecialTokens);\n }\n\n /**\n * Integrity check: reads back key weight tensors and runs a single forward pass,\n * returning checksums for comparison against a known-good reference (Dawn/Node.js).\n *\n * Use this to isolate Safari/iPad corruption:\n * - If weights mismatch → fetch/download pipeline is corrupt\n * - If weights match but logits mismatch → kernel computation bug on Metal\n *\n * Resets executor state before and after (safe to call anytime).\n */\n async integrityCheck(): Promise<IntegrityCheckResult> {\n this.checkDestroyed();\n const checks: IntegrityCheckEntry[] = [];\n const log = (label: string, data: Float32Array | Uint32Array) => {\n const f = data instanceof Float32Array ? data : new Float32Array(data.buffer);\n let sum = 0;\n let maxVal = -Infinity;\n let argmax = 0;\n for (let i = 0; i < f.length; i++) {\n sum += f[i];\n if (f[i] > maxVal) {\n maxVal = f[i];\n argmax = i;\n }\n }\n const entry: IntegrityCheckEntry = {\n label,\n length: f.length,\n sum: Math.round(sum * 1e6) / 1e6,\n first4: [\n Math.round(f[0] * 1e6) / 1e6,\n Math.round(f[1] * 1e6) / 1e6,\n Math.round(f[2] * 1e6) / 1e6,\n Math.round(f[3] * 1e6) / 1e6,\n ],\n argmax,\n maxVal: Math.round(maxVal * 1e4) / 1e4,\n };\n checks.push(entry);\n return entry;\n };\n\n // 0. Report Metal detection and adapter info\n checks.push({\n label: `webkit_detection: isWebKitWebGPU=${this.ctx.isWebKitWebGPU} needsMultiEncoder=${this.executor.needsMultiEncoder}`,\n length: 0,\n sum: 0,\n first4: [0, 0, 0, 0],\n argmax: 0,\n maxVal: 0,\n note: this.ctx.adapterDescription,\n });\n\n // 1. Read back weight tensors (no forward pass needed)\n const weightNames = [\"norm.weight\", \"layers.0.input_layernorm.weight\"];\n\n for (const name of weightNames) {\n try {\n const data = await this.debugReadBuffer(name);\n log(name, data);\n } catch {\n checks.push({\n label: name,\n length: 0,\n sum: NaN,\n first4: [NaN, NaN, NaN, NaN],\n argmax: -1,\n maxVal: NaN,\n error: \"buffer not found\",\n });\n }\n }\n\n // 2. Try to read INT4 packed weights (gate_proj AND embed_tokens)\n for (const qName of [\n \"layers.0.mlp.gate_proj.weight.q\",\n \"embed_tokens.weight.q\",\n \"embed_tokens.weight.scales\",\n \"embed_tokens.weight.zeros\",\n ]) {\n try {\n const q = await this.debugReadBuffer(qName, 16);\n log(`${qName} (reinterpret)`, q);\n } catch {\n checks.push({\n label: qName,\n length: 0,\n sum: NaN,\n first4: [NaN, NaN, NaN, NaN],\n argmax: -1,\n maxVal: NaN,\n error: \"not found\",\n });\n }\n }\n\n // 3a. Single-dispatch diagnostic: run ONLY EmbeddingInt4 with production bind group\n this.executor.reset();\n try {\n const result = await this.executor.debugFirstDispatch(new Uint32Array([1]));\n const entry = log(`single_dispatch(${result.opType})`, result.output);\n entry.note = `dispatch=${result.dispatchSize.join(\",\")} node=${result.nodeId}`;\n } catch (e: any) {\n checks.push({\n label: \"single_dispatch\",\n length: 0,\n sum: NaN,\n first4: [NaN, NaN, NaN, NaN],\n argmax: -1,\n maxVal: NaN,\n error: e.message,\n });\n }\n\n // 3a2. Run entry[1] (RMSNorm) in isolation — embed_out still has data from debugFirstDispatch.\n // If RMSNorm produces non-zero output, the bind group is correct and it's a barrier issue.\n // If zeros, the bind group is pointing to the wrong buffer.\n try {\n const result = await this.executor.debugDispatchEntry(1, 1);\n const entry = log(`isolated_entry1(${result.opType})`, result.output);\n entry.note = `node=${result.nodeId}`;\n } catch (e: any) {\n checks.push({\n label: \"isolated_entry1\",\n length: 0,\n sum: NaN,\n first4: [NaN, NaN, NaN, NaN],\n argmax: -1,\n maxVal: NaN,\n error: e.message,\n });\n }\n\n // 3a3. Run entry[2] (MatVecInt4) in isolation — layer0_norm1_out has data from isolated_entry1.\n // If non-zero: MatVecInt4 kernel works on this GPU.\n // If zeros: the kernel itself is broken on this GPU (not a barrier issue).\n try {\n const result = await this.executor.debugDispatchEntry(2, 1);\n const entry = log(`isolated_entry2(${result.opType})`, result.output);\n entry.note = `node=${result.nodeId}`;\n } catch (e: any) {\n checks.push({\n label: \"isolated_entry2\",\n length: 0,\n sum: NaN,\n first4: [NaN, NaN, NaN, NaN],\n argmax: -1,\n maxVal: NaN,\n error: e.message,\n });\n }\n\n // 3b. Show JS-computed params for first 5 entries (no GPU dispatch needed)\n this.executor.reset();\n const jsParams = this.executor.debugComputeParams(1, 5);\n for (const p of jsParams) {\n checks.push({\n label: `jsParams[${p.idx}] ${p.opType} dispatch=[${p.dispatchSize}] u32=[${p.paramsU32}]`,\n length: p.paramsU32.length,\n sum: p.paramsU32.reduce((a, b) => a + b, 0),\n first4: p.paramsU32.slice(0, 4).map(Number),\n argmax: 0,\n maxVal: 0,\n });\n }\n\n // 3c. Full forward pass with token 1 — compare logits\n this.executor.reset();\n try {\n const { logits } = await this.executor.forward(new Uint32Array([1]));\n log(\"logits(token=1)\", logits);\n\n // Also read embed_out (embedding layer output)\n try {\n const embed = await this.debugReadBuffer(\"embed_out\", 16);\n log(\"embed_out\", embed);\n } catch {\n // embed_out may not exist in all architectures\n }\n\n // 3c. Pipeline probe: read activations at ~8 points to find where data drops to zero\n try {\n const probes = await this.executor.debugPipelineProbe(1);\n for (const p of probes) {\n const paramsStr = p.uniformParams ? ` params=[${p.uniformParams.join(\",\")}]` : \"\";\n const entry: IntegrityCheckEntry = {\n label: `probe[${p.idx}] ${p.opType} → ${p.tensor}${paramsStr}`,\n length: 16,\n sum: p.sum,\n first4: p.first4,\n argmax: 0,\n maxVal: Math.max(...p.first4.map(Math.abs)),\n };\n // Flag if sum is exactly 0 (likely dead activation)\n if (p.sum === 0 && p.first4.every((v) => v === 0)) {\n entry.match = \"FAIL\";\n entry.note = \"all zeros — activation dead\";\n }\n checks.push(entry);\n }\n } catch (e: any) {\n checks.push({\n label: \"pipeline_probe\",\n length: 0,\n sum: NaN,\n first4: [NaN, NaN, NaN, NaN],\n argmax: -1,\n maxVal: NaN,\n error: e.message,\n });\n }\n } catch (e: any) {\n checks.push({\n label: \"logits(token=1)\",\n length: 0,\n sum: NaN,\n first4: [NaN, NaN, NaN, NaN],\n argmax: -1,\n maxVal: NaN,\n error: e.message,\n });\n }\n\n // Reset state so the engine is clean for actual use\n this.executor.reset();\n\n // Dawn reference values for Qwen3.5-0.8B Q4 (updated per current build).\n // Weight checksums are stable across builds; logits may drift with code changes.\n // Key diagnostic: if weights match but logits differ → kernel bug on Metal.\n // If weights don't match → data transfer/download corruption on Safari.\n const reference: Record<string, { sum: number; first4: number[] }> = {\n \"norm.weight\": {\n sum: 4412.571289,\n first4: [0.222656, 3.75, 3.640625, 4.0625],\n },\n \"layers.0.input_layernorm.weight\": {\n sum: 1267.312683,\n first4: [2.0, 1.294922, 1.535156, 1.746094],\n },\n embed_out: {\n sum: -0.067839,\n first4: [0.010059, 0.0264, 0.001888, -0.030794],\n },\n };\n\n // Compare weights against reference and flag obvious corruption\n for (const check of checks) {\n const ref = reference[check.label];\n if (ref) {\n const sumDelta = Math.abs(check.sum - ref.sum);\n const sumMatch = sumDelta < Math.max(Math.abs(ref.sum) * 0.001, 1);\n const first4Match = check.first4.every((v, i) => Math.abs(v - ref.first4[i]) < 0.01);\n check.match = sumMatch && first4Match ? \"PASS\" : \"FAIL\";\n check.refSum = ref.sum;\n } else if (check.label === \"logits(token=1)\") {\n // For logits: check for corruption signals (NaN, Inf, all-same, argmax in range)\n const hasNaN = check.first4.some((v) => Number.isNaN(v));\n const hasInf = check.first4.some((v) => !Number.isFinite(v));\n const allSame = check.first4.every((v) => v === check.first4[0]);\n const argmaxValid = check.argmax >= 0 && check.argmax < check.length;\n check.match = !hasNaN && !hasInf && !allSame && argmaxValid ? \"PASS\" : \"FAIL\";\n }\n }\n\n // Console output\n console.log(\"\\n=== INTEGRITY CHECK ===\");\n for (const c of checks) {\n const status = c.error ? \"ERR\" : (c.match ?? \"---\");\n console.log(\n `[${status}] ${c.label}: sum=${c.sum}, first4=[${c.first4.join(\", \")}], ` +\n `argmax=${c.argmax}, len=${c.length}` +\n (c.refSum !== undefined ? ` (ref sum=${c.refSum})` : \"\") +\n (c.error ? ` ERROR: ${c.error}` : \"\") +\n (c.note ? ` | ${c.note}` : \"\"),\n );\n }\n console.log(\"=== END INTEGRITY CHECK ===\\n\");\n\n const allPass = checks.every((c) => c.match === \"PASS\" || c.match === undefined);\n return { checks, allPass };\n }\n\n /**\n * Destroy the engine and free all GPU resources.\n */\n destroy(): void {\n if (this._destroyed) return;\n this._destroyed = true;\n this.executor.destroy();\n this._kaniTTS?.destroy();\n this.visionExecutor?.destroy();\n clearPipelineCache(this.ctx.device);\n this.ctx.device.destroy();\n }\n\n private checkDestroyed(): void {\n if (this._destroyed) {\n throw new Error(\"WebGPUEngine has been destroyed\");\n }\n }\n}\n\nexport {\n dequantizeGemma4VisionProjection,\n dequantizeMLXProjection,\n type Gemma4VisionGraphInfo,\n generateGemma4VisionGraph,\n patchGemma4VisionClips,\n resolveGemma4VisionInfo,\n} from \"./architectures/gemma4_vision.js\";\nexport type { GraphDType, KVDType, KvMode } from \"./architectures/index.js\";\n// Kani-TTS-2 native TTS (LFM2-350M codec-LM + NVIDIA NeMo NanoCodec). End-to-end:\n// KaniTTS.create() then engine.speak(text) → 22 kHz PCM. The NanoCodec decoder is\n// validated bit-exact; the codec-LM AR driver (frame positions + learnable RoPE)\n// lives in kani-tts.ts.\nexport {\n audioTokensToCodes,\n generateKaniTtsGraph,\n generateNanoCodecDecoderGraph,\n parseKaniConfig,\n} from \"./architectures/kani_tts.js\";\n// Moonshine native STT (encoder-decoder). End-to-end: MoonshineSTT.create() then\n// engine.transcribe(pcm16k). CrossAttention + conv-frontend kernels validated;\n// dual-graph executor wired (encoder once → frozen K/V → AR decode w/ cross-attn).\nexport {\n generateMoonshineDecoderGraph,\n generateMoonshineEncoderGraph,\n MOONSHINE_REMAINING_WORK,\n moonshineEncoderFrames,\n parseMoonshineConfig,\n} from \"./architectures/moonshine.js\";\nexport { generateQwen3_5VisionGraph } from \"./architectures/qwen3_5_vision.js\";\nexport type { GPUDiagnosticResult } from \"./device.js\";\nexport { initGPU } from \"./device.js\";\nexport { Executor } from \"./executor.js\";\nexport type { ModelArchConfig, ModelCapabilities } from \"./ir.js\";\nexport { KaniTTS, type KaniTTSOptions, type SpeakOptions, type SpeakResult } from \"./kani-tts.js\";\nexport {\n type LoadedKaniTTS,\n type LoadedMoonshine,\n loadKaniTTS,\n loadModel,\n loadMoonshine,\n} from \"./model-loader.js\";\nexport { MoonshineEncoderExecutor } from \"./moonshine-executor.js\";\nexport {\n MoonshineSTT,\n type MoonshineSTTOptions,\n type TranscribeOptions,\n type TranscribeResult,\n} from \"./moonshine-stt.js\";\nexport type { SamplingParams } from \"./sampler.js\";\n// Re-export useful types\nexport type { ChatMessage } from \"./tokenizer.js\";\nexport type { VisionInputs } from \"./vision-executor.js\";\nexport { VisionExecutor } from \"./vision-executor.js\";\nexport type {\n Gemma4VisionGridConfig,\n Gemma4VisionPositionTensors,\n ImageProcessorConfig,\n PreprocessedImage,\n VisionGridConfig,\n VisionPositionTensors,\n} from \"./vision-preprocess.js\";\nexport {\n buildGemma4PoolMatrix,\n buildGemma4PosEmbeds,\n buildGemma4RotaryCosSin,\n buildGemma4VisionPositionTensors,\n buildMRoPECosSin,\n buildMRoPEPositionIds,\n buildPosEmbeds,\n buildPositionIds,\n buildRotaryCosSin,\n buildVisionPositionTensors,\n GEMMA4_IMAGE_PROCESSOR,\n mropeFreqDims,\n preprocessImage,\n preprocessImageGemma4,\n QWEN3_5_IMAGE_PROCESSOR,\n smartResize,\n} from \"./vision-preprocess.js\";\n"],"mappings":"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"}