@tryhamster/gerbil 1.0.3 → 1.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.mjs +10 -10
- package/dist/cli.mjs.map +1 -1
- package/dist/frameworks/express.d.mts +1 -1
- package/dist/frameworks/express.mjs +1 -1
- package/dist/frameworks/fastify.d.mts +1 -1
- package/dist/frameworks/fastify.mjs +1 -1
- package/dist/frameworks/hono.d.mts +1 -1
- package/dist/frameworks/hono.mjs +1 -1
- package/dist/frameworks/next.d.mts +3 -3
- package/dist/frameworks/next.mjs +1 -1
- package/dist/frameworks/react.d.mts +1 -1
- package/dist/frameworks/trpc.d.mts +1 -1
- package/dist/frameworks/trpc.mjs +1 -1
- package/dist/gerbil-BgppGzKa.mjs +4 -0
- package/dist/{gerbil-D4eIu8hx.mjs → gerbil-CZYYq4Sq.mjs} +4 -4
- package/dist/{gerbil-D4eIu8hx.mjs.map → gerbil-CZYYq4Sq.mjs.map} +1 -1
- package/dist/{gerbil-BetB5xb0.d.mts → gerbil-CvNqpVg0.d.mts} +3 -3
- package/dist/{gerbil-BetB5xb0.d.mts.map → gerbil-CvNqpVg0.d.mts.map} +1 -1
- package/dist/gpu/hooks.d.mts +2 -2
- package/dist/gpu/hooks.d.mts.map +1 -1
- package/dist/gpu/hooks.mjs +4 -5
- package/dist/gpu/hooks.mjs.map +1 -1
- package/dist/gpu/index.d.mts +1 -1
- package/dist/gpu/index.mjs +2 -3
- package/dist/{gpu-rziY7czu.mjs → gpu-Ch2VuLdN.mjs} +25 -4
- package/dist/gpu-Ch2VuLdN.mjs.map +1 -0
- package/dist/{index-Dgmb2kE3.d.mts → index-Dy_9rDd5.d.mts} +2 -2
- package/dist/{index-Dgmb2kE3.d.mts.map → index-Dy_9rDd5.d.mts.map} +1 -1
- package/dist/{index-DukkJRMj.d.mts → index-Nl40RdSs.d.mts} +1 -1
- package/dist/{index-DukkJRMj.d.mts.map → index-Nl40RdSs.d.mts.map} +1 -1
- package/dist/index.d.mts +5 -5
- package/dist/index.mjs +8 -9
- package/dist/index.mjs.map +1 -1
- package/dist/{indexeddb-store-BWIMtxxH.mjs → indexeddb-store-BEylGAex.mjs} +2 -2
- package/dist/{indexeddb-store-BWIMtxxH.mjs.map → indexeddb-store-BEylGAex.mjs.map} +1 -1
- package/dist/indexeddb-store-ClSHQxwz.mjs +4 -0
- package/dist/integrations/ai-sdk.d.mts +1 -1
- package/dist/integrations/ai-sdk.mjs +1 -1
- package/dist/integrations/langchain.d.mts +1 -1
- package/dist/integrations/langchain.mjs +1 -1
- package/dist/integrations/llamaindex.d.mts +1 -1
- package/dist/integrations/llamaindex.mjs +1 -1
- package/dist/integrations/mcp-client.mjs +1 -1
- package/dist/integrations/mcp.d.mts +3 -3
- package/dist/integrations/mcp.mjs +4 -4
- package/dist/{mcp-DujeDKfA.mjs → mcp-DUgk28Kp.mjs} +3 -3
- package/dist/{mcp-DujeDKfA.mjs.map → mcp-DUgk28Kp.mjs.map} +1 -1
- package/dist/memory/index.d.mts +2 -2
- package/dist/memory/index.mjs +4 -4
- package/dist/memory-CGRo6zY5.mjs +4 -0
- package/dist/{memory-DVN0MnIG.mjs → memory-Deo99oKh.mjs} +2 -2
- package/dist/{memory-DVN0MnIG.mjs.map → memory-Deo99oKh.mjs.map} +1 -1
- package/dist/{memory-Dj0J1v88.mjs → memory-wgo2ZK3M.mjs} +2 -2
- package/dist/{memory-Dj0J1v88.mjs.map → memory-wgo2ZK3M.mjs.map} +1 -1
- package/dist/moonshine-stt-DFIP4tIb.mjs +4 -0
- package/dist/{moonshine-stt-BdccCmYH.mjs → moonshine-stt-DR0-JbVW.mjs} +2 -6
- package/dist/{moonshine-stt-BdccCmYH.mjs.map → moonshine-stt-DR0-JbVW.mjs.map} +1 -1
- package/dist/{one-liner-D8cM1Mg2.mjs → one-liner-sTDG9Hum.mjs} +2 -2
- package/dist/{one-liner-D8cM1Mg2.mjs.map → one-liner-sTDG9Hum.mjs.map} +1 -1
- package/dist/repl-njlFQs4-.mjs +9 -0
- package/dist/skills/index.d.mts +3 -3
- package/dist/skills/index.mjs +3 -3
- package/dist/{skills-FEBy8-pu.mjs → skills-CEUuOUlm.mjs} +2 -2
- package/dist/{skills-FEBy8-pu.mjs.map → skills-CEUuOUlm.mjs.map} +1 -1
- package/dist/{tools-DQ1mPUw5.mjs → tools-DbbNCJ5y.mjs} +1 -1
- package/dist/{tools-DQ1mPUw5.mjs.map → tools-DbbNCJ5y.mjs.map} +1 -1
- package/dist/{types-LlyYILII.d.mts → types-Bxwe_uS7.d.mts} +1 -1
- package/dist/{types-LlyYILII.d.mts.map → types-Bxwe_uS7.d.mts.map} +1 -1
- package/dist/{types-DQBe2lFo.d.mts → types-D7kn-0i2.d.mts} +1 -1
- package/dist/{types-DQBe2lFo.d.mts.map → types-D7kn-0i2.d.mts.map} +1 -1
- package/dist/{vector-B0panuy6.mjs → vector-C1U9vVVS.mjs} +1 -1
- package/dist/{vector-B0panuy6.mjs.map → vector-C1U9vVVS.mjs.map} +1 -1
- package/package.json +1 -1
- package/dist/defaults-9komdrbY.mjs +0 -24
- package/dist/defaults-9komdrbY.mjs.map +0 -1
- package/dist/gerbil-Dk6sTA7P.mjs +0 -4
- package/dist/gpu-rziY7czu.mjs.map +0 -1
- package/dist/indexeddb-store-ClH12Xnl.mjs +0 -4
- package/dist/memory-D1P7Tmda.mjs +0 -4
- package/dist/moonshine-stt-CBX849nI.mjs +0 -4
- package/dist/repl-DuACpxEj.mjs +0 -9
- /package/dist/{auto-update-BVaLXcDE.mjs → auto-update-DNbr3GLz.mjs} +0 -0
- /package/dist/{microphone-Bqmoz9_K.mjs → microphone-sj_bnRm-.mjs} +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gerbil-D4eIu8hx.mjs","names":["globalCache: ResponseCache | null","BUILTIN_MODELS: Record<string, ModelConfig>","FAMILY_CONTEXT_DEFAULTS: Record<string, number>","family: ModelConfig[\"family\"]","idat: Uint8Array[]","v: number","KOKORO_VOICES_DEFAULT: VoiceInfo[]","concurrency: ConcurrencyConfig","result","result: GenerateResult","tokenQueue: string[]","resolveNext: ((value: string | null) => void) | null","results: EmbedResult[]"],"sources":["../src/core/cache.ts","../src/core/models.ts","../src/core/gerbil.ts"],"sourcesContent":["/**\n * Response Cache for Gerbil\n *\n * LRU cache with TTL expiration for caching inference responses.\n * Enables instant responses for repeated prompts.\n */\n\nimport type { GenerateResult } from \"./types.js\";\n\n// ============================================\n// Types\n// ============================================\n\ntype CacheEntry = {\n result: GenerateResult;\n createdAt: number;\n ttl: number;\n};\n\ntype CacheStats = {\n hits: number;\n misses: number;\n size: number;\n maxSize: number;\n};\n\n// ============================================\n// Cache Key Generation\n// ============================================\n\n/**\n * Generate a deterministic cache key from prompt and options.\n * Key includes all parameters that affect the output.\n */\nexport function generateCacheKey(\n prompt: string,\n modelId: string,\n options: {\n maxTokens?: number;\n temperature?: number;\n topP?: number;\n topK?: number;\n system?: string;\n thinking?: boolean;\n },\n): string {\n const keyParts = [\n prompt,\n modelId,\n options.maxTokens ?? 256,\n options.temperature ?? 0.7,\n options.topP ?? 0.9,\n options.topK ?? 50,\n options.system ?? \"\",\n options.thinking ?? false,\n ];\n\n // Simple hash function for cache key\n const str = JSON.stringify(keyParts);\n let hash = 0;\n for (let i = 0; i < str.length; i++) {\n const char = str.charCodeAt(i);\n hash = (hash << 5) - hash + char;\n hash = hash & hash; // Convert to 32bit integer\n }\n return `gerbil:${hash.toString(16)}`;\n}\n\n// ============================================\n// Response Cache\n// ============================================\n\n/**\n * LRU cache with TTL expiration for inference responses.\n */\nexport class ResponseCache {\n private cache: Map<string, CacheEntry> = new Map();\n private maxSize: number;\n private defaultTtl: number;\n private hits = 0;\n private misses = 0;\n\n /**\n * Create a new response cache.\n * @param maxSize Maximum number of entries (default: 100)\n * @param defaultTtl Default TTL in ms (default: 5 minutes)\n */\n constructor(maxSize = 100, defaultTtl = 5 * 60 * 1000) {\n this.maxSize = maxSize;\n this.defaultTtl = defaultTtl;\n }\n\n /**\n * Get a cached response if it exists and hasn't expired.\n */\n get(key: string): GenerateResult | null {\n const entry = this.cache.get(key);\n\n if (!entry) {\n this.misses++;\n return null;\n }\n\n // Check if expired\n if (Date.now() - entry.createdAt > entry.ttl) {\n this.cache.delete(key);\n this.misses++;\n return null;\n }\n\n // Move to end for LRU (delete and re-add)\n this.cache.delete(key);\n this.cache.set(key, entry);\n\n this.hits++;\n return { ...entry.result, cached: true };\n }\n\n /**\n * Store a response in the cache.\n */\n set(key: string, result: GenerateResult, ttl?: number): void {\n // Evict oldest entries if at capacity\n while (this.cache.size >= this.maxSize) {\n const firstKey = this.cache.keys().next().value;\n if (firstKey) {\n this.cache.delete(firstKey);\n }\n }\n\n this.cache.set(key, {\n result,\n createdAt: Date.now(),\n ttl: ttl ?? this.defaultTtl,\n });\n }\n\n /**\n * Check if a key exists and is not expired.\n */\n has(key: string): boolean {\n const entry = this.cache.get(key);\n if (!entry) return false;\n\n if (Date.now() - entry.createdAt > entry.ttl) {\n this.cache.delete(key);\n return false;\n }\n\n return true;\n }\n\n /**\n * Remove a specific key from the cache.\n */\n delete(key: string): boolean {\n return this.cache.delete(key);\n }\n\n /**\n * Clear all entries from the cache.\n */\n clear(): void {\n this.cache.clear();\n this.hits = 0;\n this.misses = 0;\n }\n\n /**\n * Remove all expired entries.\n */\n prune(): number {\n const now = Date.now();\n let pruned = 0;\n\n for (const [key, entry] of this.cache) {\n if (now - entry.createdAt > entry.ttl) {\n this.cache.delete(key);\n pruned++;\n }\n }\n\n return pruned;\n }\n\n /**\n * Get cache statistics.\n */\n getStats(): CacheStats {\n return {\n hits: this.hits,\n misses: this.misses,\n size: this.cache.size,\n maxSize: this.maxSize,\n };\n }\n\n /**\n * Get hit rate as a percentage.\n */\n getHitRate(): number {\n const total = this.hits + this.misses;\n if (total === 0) return 0;\n return (this.hits / total) * 100;\n }\n}\n\n// ============================================\n// Global Cache Instance\n// ============================================\n\nlet globalCache: ResponseCache | null = null;\n\n/**\n * Get the global response cache instance.\n * Creates one if it doesn't exist.\n */\nexport function getGlobalCache(): ResponseCache {\n if (!globalCache) {\n globalCache = new ResponseCache();\n }\n return globalCache;\n}\n\n/**\n * Configure the global cache with custom settings.\n */\nexport function configureGlobalCache(maxSize?: number, defaultTtl?: number): ResponseCache {\n globalCache = new ResponseCache(maxSize, defaultTtl);\n return globalCache;\n}\n\n/**\n * Clear and reset the global cache.\n */\nexport function clearGlobalCache(): void {\n if (globalCache) {\n globalCache.clear();\n }\n}\n","/**\n * Model Registry\n *\n * Supports built-in models and any HuggingFace model via hf:org/model syntax\n */\n\nimport type { ModelConfig, ModelSource } from \"./types.js\";\n\n// ============================================\n// Canonical default model\n// ============================================\n\n/**\n * The default model used everywhere a model id is not explicitly provided\n * (CLI flags, REPL, framework adapters, integrations, one-liner). This is the\n * e2e-validated model; reference this constant instead of hard-coding the id.\n */\nexport const DEFAULT_MODEL = \"qwen3.5-0.8b\";\n\n// ============================================\n// Built-in Models (curated & tested)\n// ============================================\n\n// Every entry is a standard HuggingFace safetensors repo whose architecture the\n// native WebGPU engine supports (Qwen2/Qwen3/Qwen3.5, LFM2 — see\n// src/gpu/architectures/index.ts). The engine quantizes weights to INT4 on load;\n// `size` is the bf16/fp16 download size. Only add a repo here whose architecture\n// has a graph generator in the registry.\nexport const BUILTIN_MODELS: Record<string, ModelConfig> = {\n \"qwen3.5-0.8b\": {\n id: \"qwen3.5-0.8b\",\n repo: \"Qwen/Qwen3.5-0.8B\",\n description:\n \"Qwen3.5 0.8B - Fast, multimodal (vision), 262k context, supports thinking (default)\",\n size: \"~1.6GB\",\n contextLength: 262_144,\n supportsThinking: true,\n supportsJson: true,\n supportsVision: true,\n family: \"qwen\",\n },\n \"qwen3.5-2b\": {\n id: \"qwen3.5-2b\",\n repo: \"Qwen/Qwen3.5-2B\",\n description:\n \"Qwen3.5 2B - Higher quality, multimodal (vision), 262k context, supports thinking\",\n size: \"~4GB\",\n contextLength: 262_144,\n supportsThinking: true,\n supportsJson: true,\n supportsVision: true,\n family: \"qwen\",\n },\n \"lfm2.5-1.2b-thinking\": {\n id: \"lfm2.5-1.2b-thinking\",\n repo: \"LiquidAI/LFM2.5-1.2B-Thinking\",\n description: \"LFM2.5 1.2B Thinking - Efficient reasoning model, 128k context\",\n size: \"~2.4GB\",\n contextLength: 128_000,\n supportsThinking: true,\n supportsJson: false,\n family: \"other\",\n },\n};\n\n// ============================================\n// Model Resolution\n// ============================================\n\n/**\n * Parse model identifier and resolve to source\n *\n * Supported formats:\n * - \"qwen3.5-0.8b\" (built-in)\n * - \"hf:org/model\" (HuggingFace shorthand)\n * - \"https://huggingface.co/org/model\" (full URL)\n * - \"file:./path/to/model\" (local path)\n */\nexport function resolveModel(modelId: string): ModelSource {\n // Built-in model\n if (BUILTIN_MODELS[modelId]) {\n return {\n type: \"builtin\",\n path: BUILTIN_MODELS[modelId].repo,\n };\n }\n\n // HuggingFace shorthand: hf:org/model\n if (modelId.startsWith(\"hf:\")) {\n const repo = modelId.slice(3);\n return {\n type: \"huggingface\",\n path: repo,\n };\n }\n\n // HuggingFace URL\n if (modelId.startsWith(\"https://huggingface.co/\")) {\n const repo = modelId.replace(\"https://huggingface.co/\", \"\");\n return {\n type: \"huggingface\",\n path: repo,\n };\n }\n\n // Local file\n if (modelId.startsWith(\"file:\")) {\n const path = modelId.slice(5);\n return {\n type: \"local\",\n path,\n };\n }\n\n // Assume it's a HuggingFace repo if it contains a slash\n if (modelId.includes(\"/\")) {\n return {\n type: \"huggingface\",\n path: modelId,\n };\n }\n\n // Unknown - treat as HuggingFace\n return {\n type: \"huggingface\",\n path: modelId,\n };\n}\n\n/**\n * Get model config (built-in only)\n */\nexport function getModelConfig(modelId: string): ModelConfig | null {\n return BUILTIN_MODELS[modelId] || null;\n}\n\n// Default context lengths for the families the native engine actually supports\n// (a graph generator exists in src/gpu/architectures). Other families fall back\n// to a conservative default.\nconst FAMILY_CONTEXT_DEFAULTS: Record<string, number> = {\n qwen: 32_768,\n other: 32_768, // LFM2 supports 128k but config.json is the real source of truth\n};\n\n/**\n * Create model config for an external HuggingFace model.\n *\n * Inference is restricted to families the engine can actually run — Qwen\n * (Qwen2/Qwen3/Qwen3.5) and LFM2 (Liquid). Everything else is left as \"other\"\n * with conservative capability flags so the REPL doesn't advertise features the\n * engine can't deliver.\n */\nexport function createExternalModelConfig(\n modelId: string,\n repo: string,\n contextLength?: number,\n): ModelConfig {\n const repoLower = repo.toLowerCase();\n\n // Only infer families that have a graph generator in the registry.\n let family: ModelConfig[\"family\"] = \"other\";\n if (repoLower.includes(\"qwen\")) {\n family = \"qwen\";\n }\n\n const isLiquid = repoLower.includes(\"lfm\") || repoLower.includes(\"liquid\");\n const isQwen = family === \"qwen\";\n\n return {\n id: modelId,\n repo,\n description: `External model: ${repo}`,\n size: \"Unknown\",\n contextLength: contextLength || FAMILY_CONTEXT_DEFAULTS[family] || 32_768,\n // Qwen3/Qwen3.5 and LFM2.5-Thinking expose thinking; nothing here is vision.\n supportsThinking: isQwen || isLiquid,\n supportsJson: isQwen,\n family,\n };\n}\n\n/**\n * Fetch context length from HuggingFace model config\n */\nexport async function fetchModelContextLength(repo: string): Promise<number | null> {\n try {\n const res = await fetch(`https://huggingface.co/${repo}/raw/main/config.json`);\n if (!res.ok) {\n return null;\n }\n\n const config = await res.json();\n\n // Different models use different field names\n return (\n config.max_position_embeddings ||\n config.n_positions ||\n config.max_seq_len ||\n config.sliding_window || // Some models use this\n config.context_length ||\n null\n );\n } catch {\n return null;\n }\n}\n\n/**\n * List all built-in models\n */\nexport function listBuiltinModels(): ModelConfig[] {\n return Object.values(BUILTIN_MODELS);\n}\n\n/**\n * Search HuggingFace models (placeholder - would need HF API)\n */\nexport async function searchModels(query: string): Promise<ModelConfig[]> {\n // TODO: Implement HuggingFace API search\n // For now, filter built-in models\n const q = query.toLowerCase();\n return listBuiltinModels().filter(\n (m) =>\n m.id.toLowerCase().includes(q) ||\n m.description.toLowerCase().includes(q) ||\n m.family.toLowerCase().includes(q),\n );\n}\n","/**\n * Gerbil - Local GPU-accelerated LLM inference\n */\n\nimport { existsSync } from \"node:fs\";\nimport os from \"node:os\";\nimport path from \"node:path\";\nimport zlib from \"node:zlib\";\nimport PQueue from \"p-queue\";\nimport { generateCacheKey, getGlobalCache } from \"./cache.js\";\nimport {\n BUILTIN_MODELS,\n createExternalModelConfig,\n DEFAULT_MODEL,\n fetchModelContextLength,\n getModelConfig,\n resolveModel,\n} from \"./models.js\";\nimport type {\n AudioChunk,\n ConcurrencyConfig,\n EmbedOptions,\n EmbedResult,\n ErrorContext,\n GenerateOptions,\n GenerateResult,\n GerbilConfig,\n JsonOptions,\n LoadOptions,\n LoadSTTOptions,\n LoadTTSOptions,\n ModelConfig,\n PreloadOptions,\n SessionStats,\n SpeakOptions,\n SpeakResult,\n STTModelConfig,\n StreamingTranscriptionOptions,\n StreamingTranscriptionSession,\n SystemInfo,\n TelemetryConfig,\n TranscribeOptions,\n TranscribeResult,\n VoiceInfo,\n} from \"./types.js\";\n\n/**\n * Minimal PNG decoder: 8-bit, non-interlaced, color type 2 (RGB) or 6 (RGBA).\n * Returns packed RGB pixels for the native vision encoder. Replaces the\n * transformers.js RawImage decoder for the common PNG case.\n */\nfunction decodePng(buf: Uint8Array): { pixels: Uint8Array; width: number; height: number } {\n const view = new DataView(buf.buffer, buf.byteOffset, buf.byteLength);\n const w = view.getUint32(16);\n const h = view.getUint32(20);\n const bitDepth = buf[24];\n const colorType = buf[25];\n if (bitDepth !== 8 || (colorType !== 2 && colorType !== 6)) {\n throw new Error(\n `Unsupported PNG (bitDepth=${bitDepth} colorType=${colorType}); need 8-bit RGB/RGBA`,\n );\n }\n const channels = colorType === 6 ? 4 : 3;\n let off = 8;\n const idat: Uint8Array[] = [];\n while (off < buf.length) {\n const len = view.getUint32(off);\n const type = String.fromCharCode(buf[off + 4], buf[off + 5], buf[off + 6], buf[off + 7]);\n if (type === \"IDAT\") {\n idat.push(buf.subarray(off + 8, off + 8 + len));\n }\n off += 12 + len;\n if (type === \"IEND\") {\n break;\n }\n }\n const raw = zlib.inflateSync(Buffer.concat(idat));\n const stride = w * channels;\n const out = new Uint8Array(w * h * 3);\n const line = new Uint8Array(stride);\n const prev = new Uint8Array(stride);\n let p = 0;\n for (let y = 0; y < h; y += 1) {\n const filter = raw[p];\n p += 1;\n for (let i = 0; i < stride; i += 1) {\n const x = raw[p];\n p += 1;\n const a = i >= channels ? line[i - channels] : 0;\n const b = prev[i];\n const c = i >= channels ? prev[i - channels] : 0;\n let v: number;\n switch (filter) {\n case 0:\n v = x;\n break;\n case 1:\n v = x + a;\n break;\n case 2:\n v = x + b;\n break;\n case 3:\n v = x + ((a + b) >> 1);\n break;\n case 4: {\n const pp = a + b - c;\n const pa = Math.abs(pp - a);\n const pb = Math.abs(pp - b);\n const pc = Math.abs(pp - c);\n let pred = c;\n if (pa <= pb && pa <= pc) {\n pred = a;\n } else if (pb <= pc) {\n pred = b;\n }\n v = x + pred;\n break;\n }\n default:\n throw new Error(`bad PNG filter ${filter}`);\n }\n line[i] = v & 0xff;\n }\n for (let x = 0; x < w; x += 1) {\n out[(y * w + x) * 3 + 0] = line[x * channels + 0];\n out[(y * w + x) * 3 + 1] = line[x * channels + 1];\n out[(y * w + x) * 3 + 2] = line[x * channels + 2];\n }\n prev.set(line);\n }\n return { pixels: out, width: w, height: h };\n}\n\n// Default voices for listVoices() when native TTS not loaded\nconst KOKORO_VOICES_DEFAULT: VoiceInfo[] = [\n {\n id: \"af_bella\",\n name: \"Bella\",\n gender: \"female\",\n language: \"en-us\",\n description: \"American female, warm and friendly\",\n },\n {\n id: \"af_sarah\",\n name: \"Sarah\",\n gender: \"female\",\n language: \"en-us\",\n description: \"American female, clear and professional\",\n },\n {\n id: \"af_nicole\",\n name: \"Nicole\",\n gender: \"female\",\n language: \"en-us\",\n description: \"American female, soft and gentle\",\n },\n {\n id: \"af_sky\",\n name: \"Sky\",\n gender: \"female\",\n language: \"en-us\",\n description: \"American female, young and energetic\",\n },\n {\n id: \"am_adam\",\n name: \"Adam\",\n gender: \"male\",\n language: \"en-us\",\n description: \"American male, deep and confident\",\n },\n {\n id: \"am_michael\",\n name: \"Michael\",\n gender: \"male\",\n language: \"en-us\",\n description: \"American male, warm and friendly\",\n },\n {\n id: \"bf_emma\",\n name: \"Emma\",\n gender: \"female\",\n language: \"en-gb\",\n description: \"British female, elegant and clear\",\n },\n {\n id: \"bf_isabella\",\n name: \"Isabella\",\n gender: \"female\",\n language: \"en-gb\",\n description: \"British female, sophisticated\",\n },\n {\n id: \"bm_george\",\n name: \"George\",\n gender: \"male\",\n language: \"en-gb\",\n description: \"British male, distinguished\",\n },\n {\n id: \"bm_lewis\",\n name: \"Lewis\",\n gender: \"male\",\n language: \"en-gb\",\n description: \"British male, friendly and warm\",\n },\n];\n\nimport type { GenerateObjectOptions, GenerateObjectResult } from \"../gpu/index.js\";\nimport { extractJson, zodToJsonSchema } from \"./utils.js\";\n\n// ============================================\n// Gerbil Class\n// ============================================\n\nexport class Gerbil {\n private currentModel: string | null = null;\n private modelConfig: ModelConfig | null = null;\n private readonly config: GerbilConfig;\n private stats: SessionStats;\n private _deviceMode: \"webgpu\" | \"cpu\" | \"wasm\" = \"cpu\"; // Track which backend is active\n private webgpuEngine: any = null; // Native WebGPU engine (src/gpu)\n\n // ── Native (src/gpu) capability engines, lazily created and independent of the\n // loaded text model. These let embed / transcribe / speak run on the native\n // WebGPU engine even when the active model isn't that capability. They are torn\n // down in dispose().\n private nativeEmbedEngine: any = null; // Native WebGPUEngine loaded as an embedder\n private nativeEmbedRepo: string | null = null; // Repo backing nativeEmbedEngine (for cache-busting on model switch)\n private nativeSTT: any = null; // Native MoonshineSTT (src/gpu/moonshine-stt)\n private nativeTTSEngine: any = null; // Native WebGPUEngine (Kani-TTS) backing speak()\n\n private isVisionModel = false; // Whether current model supports vision\n\n // Request queue for concurrency control (prevents GPU OOM under load)\n private readonly queue: PQueue;\n private readonly telemetry: TelemetryConfig;\n\n constructor(config: GerbilConfig = {}) {\n this.config = config;\n this.stats = {\n prompts: 0,\n tokensIn: 0,\n tokensOut: 0,\n avgSpeed: 0,\n totalTime: 0,\n cacheHits: 0,\n cacheMisses: 0,\n };\n\n // Initialize request queue (default: 1 concurrent for LLM, 5 min timeout)\n const concurrency: ConcurrencyConfig = config.concurrency || {};\n this.queue = new PQueue({\n concurrency: concurrency.maxConcurrent ?? 1,\n timeout: concurrency.timeout ?? 300_000, // 5 minutes (auto-throws on timeout)\n });\n\n // Store telemetry hooks\n this.telemetry = config.telemetry || {};\n }\n\n // ============================================\n // Telemetry Helpers\n // ============================================\n\n private reportError(error: Error, context: ErrorContext): void {\n try {\n this.telemetry.onError?.(error, context);\n } catch {\n // Never throw from telemetry\n }\n }\n\n /**\n * Whether the native (src/gpu) WebGPU engine should be used for a capability\n * (embed / transcribe / speak / vision). The native WebGPU engine is the only\n * inference backend, so this is always true; kept as a seam for callers.\n */\n private preferNative(): boolean {\n return true;\n }\n\n // ============================================\n // Static Methods\n // ============================================\n\n static listModels(): ModelConfig[] {\n return Object.values(BUILTIN_MODELS);\n }\n\n static getModel(modelId: string): ModelConfig | undefined {\n return BUILTIN_MODELS[modelId];\n }\n\n // ============================================\n // Model Loading\n // ============================================\n\n /**\n * Load a model\n *\n * @example\n * ```ts\n * // Built-in model\n * await g.loadModel(\"qwen3.5-0.8b\");\n *\n * // HuggingFace model\n * await g.loadModel(\"hf:microsoft/Phi-3-mini\");\n *\n * // Local model\n * await g.loadModel(\"file:./models/my-model\");\n *\n * // Vision model\n * await g.loadModel(\"ministral-3b\");\n * ```\n */\n async loadModel(modelId = DEFAULT_MODEL, options: LoadOptions = {}): Promise<void> {\n const loadStartTime = performance.now();\n\n // Dispose any existing model/backend before loading a new one\n // This prevents zombie Chrome pages when switching models\n if (this.isLoaded()) {\n await this.dispose();\n }\n\n const source = resolveModel(modelId);\n const { onProgress, device = \"auto\", dtype: userDtype } = options;\n\n // Get or create model config\n let config = getModelConfig(modelId);\n if (!config) {\n // Try to fetch actual context length from HuggingFace config.json\n const contextLength = await fetchModelContextLength(source.path).catch(() => null);\n config = createExternalModelConfig(modelId, source.path, contextLength || undefined);\n }\n\n // Route to vision model loading if needed\n if (config.supportsVision) {\n return this.loadVisionModel(modelId, source.path, config, options);\n }\n\n // The native WebGPU engine is the only inference backend. CPU/WASM and the\n // legacy ONNX path have been removed — guard against legacy runtime values.\n if ((device as string) === \"cpu\" || (device as string) === \"gpu\") {\n throw new Error(\n 'Gerbil requires WebGPU. CPU/WASM and the legacy ONNX backend have been removed; use device \"webgpu\" or \"auto\".',\n );\n }\n\n onProgress?.({ status: `Loading ${modelId}...` });\n\n try {\n onProgress?.({ status: \"Initializing WebGPU engine...\" });\n const { WebGPUEngine } = await import(\"../gpu/index.js\");\n\n // Resolve the HF repo — for built-in models, use the standard HF repo\n // (not the ONNX repo) since the native engine loads safetensors directly\n let hfRepo = source.path;\n // If it points to an ONNX repo, map to the standard repo\n if (hfRepo.includes(\"onnx-community/\") || hfRepo.includes(\"-ONNX\")) {\n const nativeRepoMap: Record<string, string> = {\n \"onnx-community/Qwen3.5-0.8B-ONNX\": \"Qwen/Qwen3.5-0.8B\",\n \"onnx-community/Qwen3-0.6B-ONNX\": \"Qwen/Qwen3-0.6B\",\n \"onnx-community/Qwen3-1.7B-ONNX\": \"Qwen/Qwen3-1.7B\",\n \"onnx-community/Qwen3.5-2B-ONNX\": \"Qwen/Qwen3.5-2B\",\n };\n hfRepo = nativeRepoMap[hfRepo] || hfRepo;\n }\n\n // Map user dtype to GPU engine dtype (\"q4\" for INT4, \"f32\" default)\n const gpuDtype = userDtype === \"q4\" ? (\"q4\" as const) : undefined;\n\n this.webgpuEngine = await WebGPUEngine.create({\n repo: hfRepo,\n maxSeqLen: options.contextLength ?? config.contextLength ?? 4096,\n dtype: gpuDtype,\n onProgress: (loaded, total, message) => {\n onProgress?.({\n status: message,\n progress: total > 0 ? Math.round((loaded / total) * 100) : undefined,\n });\n },\n });\n\n this._deviceMode = \"webgpu\";\n this.isVisionModel = false;\n this.currentModel = modelId;\n this.modelConfig = config;\n onProgress?.({ status: \"Ready (WebGPU Native)!\" });\n\n // Report successful model load\n if (this.telemetry.onModelLoad) {\n try {\n this.telemetry.onModelLoad({\n modelId,\n loadTimeMs: performance.now() - loadStartTime,\n fromCache: false, // TODO: detect if loaded from cache\n device: this._deviceMode,\n success: true,\n });\n } catch {\n // Never throw from telemetry\n }\n }\n } catch (err) {\n // No CPU/ONNX fallback — surface the load failure.\n this.reportError(err instanceof Error ? err : new Error(String(err)), {\n operation: \"load\",\n modelId,\n });\n if (this.telemetry.onModelLoad) {\n try {\n this.telemetry.onModelLoad({\n modelId,\n loadTimeMs: performance.now() - loadStartTime,\n fromCache: false,\n device: this._deviceMode,\n success: false,\n error: err instanceof Error ? err.message : String(err),\n });\n } catch {\n // Never throw from telemetry\n }\n }\n throw err;\n }\n }\n\n /**\n * Load a vision model (VLM) on the native WebGPU engine.\n * The native engine loads the vision-capable safetensors checkpoint directly\n * and builds its ViT tower on demand (enableVision: true). describeImage() then\n * runs encode → splice → decode entirely in WebGPU compute.\n */\n private async loadVisionModel(\n modelId: string,\n repoPath: string,\n config: ModelConfig,\n options: LoadOptions = {},\n ): Promise<void> {\n const { onProgress, device = \"auto\" } = options;\n\n onProgress?.({ status: `Loading ${modelId} (vision model)...` });\n\n // The native WebGPU engine is the only backend. CPU/WASM/ONNX are unsupported.\n if ((device as string) === \"cpu\" || (device as string) === \"gpu\") {\n throw new Error(\n 'Gerbil vision models require WebGPU. CPU/WASM and the legacy ONNX backend have been removed; use device \"webgpu\" or \"auto\".',\n );\n }\n\n onProgress?.({ status: \"Initializing WebGPU vision engine...\" });\n const { WebGPUEngine } = await import(\"../gpu/index.js\");\n // Map an ONNX vision repo to its standard HF checkpoint when possible;\n // otherwise honor the configured path (defaults resolve inside create()).\n let visRepo = repoPath;\n if (visRepo.includes(\"onnx-community/\") || visRepo.includes(\"-ONNX\")) {\n const map: Record<string, string> = {\n \"onnx-community/Qwen3.5-0.8B-ONNX\": \"Qwen/Qwen3.5-0.8B\",\n };\n visRepo = map[visRepo] || visRepo;\n }\n this.webgpuEngine = await WebGPUEngine.create({\n repo: visRepo,\n enableVision: true,\n maxSeqLen: options.contextLength ?? config.contextLength ?? 4096,\n onProgress: (loaded, total, message) =>\n onProgress?.({\n status: message,\n progress: total > 0 ? Math.round((loaded / total) * 100) : undefined,\n }),\n });\n this._deviceMode = \"webgpu\";\n this.isVisionModel = true;\n this.currentModel = modelId;\n this.modelConfig = config;\n onProgress?.({ status: \"Ready (Vision, WebGPU Native)!\" });\n }\n\n /**\n * Check if a model is loaded\n */\n isLoaded(): boolean {\n return this.webgpuEngine !== null;\n }\n\n /**\n * Check if current model supports vision\n */\n supportsVision(): boolean {\n return this.isVisionModel && this.modelConfig?.supportsVision === true;\n }\n\n /**\n * Get current model info\n */\n getModelInfo(): ModelConfig | null {\n return this.modelConfig;\n }\n\n /**\n * Get current device mode (webgpu, cpu, or wasm)\n */\n getDeviceMode(): \"webgpu\" | \"cpu\" | \"wasm\" {\n return this._deviceMode;\n }\n\n /**\n * Get the in-memory weight quantization the native engine uses for the loaded\n * model. The WebGPU engine quantizes weights to INT4 (\"q4\") on load; the KV\n * cache precision (f16/f32) is separate and device-detected.\n */\n getDtype(): string {\n return \"q4\";\n }\n\n /**\n * Get response cache statistics\n */\n getResponseCacheStats(): { hits: number; misses: number; size: number; hitRate: number } {\n const cache = getGlobalCache();\n const stats = cache.getStats();\n return {\n hits: stats.hits,\n misses: stats.misses,\n size: stats.size,\n hitRate: cache.getHitRate(),\n };\n }\n\n /**\n * Clear the response cache (for cached generate() results)\n */\n clearResponseCache(): void {\n getGlobalCache().clear();\n }\n\n // ============================================\n // Model Preloading & Cache Checking\n // ============================================\n\n /**\n * Check if a model is cached (downloaded) without loading it\n *\n * @example\n * ```ts\n * if (await g.isModelCached(\"qwen3.5-0.8b\")) {\n * console.log(\"Model ready, will load instantly\");\n * } else {\n * console.log(\"Model needs to download (~400MB)\");\n * }\n * ```\n */\n async isModelCached(modelId: string): Promise<boolean> {\n const source = resolveModel(modelId);\n return this.isNativeRepoCached(source.path);\n }\n\n /**\n * Check whether the native WebGPU engine has a repo cached on disk.\n * The native loader stores files under ~/.cache/gerbil/<repo>/<revision>/.\n */\n private isNativeRepoCached(repo: string, revision = \"main\"): boolean {\n try {\n const home = process.env.HOME || process.env.USERPROFILE || os.homedir();\n if (!home) {\n return false;\n }\n const modelDir = path.join(home, \".cache\", \"gerbil\", repo.replace(/\\//g, \"_\"), revision);\n // config.json is the first file the native loader writes for any repo.\n return existsSync(path.join(modelDir, \"config.json\".replace(/\\//g, \"_\")));\n } catch {\n return false;\n }\n }\n\n /**\n * Preload a model (download without initializing for inference)\n *\n * Use this to download models ahead of time, e.g., during app startup,\n * so users don't wait when they first use AI.\n *\n * @example\n * ```ts\n * // Preload for later (download only, free memory)\n * await g.preloadModel(\"qwen3.5-0.8b\", {\n * onProgress: (p) => console.log(p.status, p.progress),\n * });\n *\n * // Preload and keep in memory for instant use\n * await g.preloadModel(\"qwen3.5-0.8b\", { keepLoaded: true });\n * await g.generate(\"Hello\"); // Instant, no loading needed\n * ```\n */\n async preloadModel(modelId: string, options: PreloadOptions = {}): Promise<void> {\n const source = resolveModel(modelId);\n const { onProgress, keepLoaded = false } = options;\n\n // Already loaded? Nothing to do\n if (keepLoaded && this.isLoaded() && this.currentModel === modelId) {\n onProgress?.({ status: \"Model already loaded\" });\n return;\n }\n\n // Already cached and not keeping loaded? Nothing to do\n if (!keepLoaded && (await this.isModelCached(modelId))) {\n onProgress?.({ status: \"Model already cached\" });\n return;\n }\n\n // If keepLoaded, use the regular loadModel path\n if (keepLoaded) {\n await this.loadModel(modelId, { onProgress });\n return;\n }\n\n onProgress?.({ status: `Preloading ${modelId}...` });\n\n // The native engine has no \"download-only\" mode, so load it (which fetches\n // and caches all weight files to ~/.cache/gerbil) then dispose to free GPU\n // memory. The cached files remain on disk for an instant subsequent load.\n await this.loadModel(modelId, { onProgress });\n await this.dispose();\n\n onProgress?.({ status: \"Preload complete\" });\n }\n\n /**\n * Check if the native TTS model is cached. The native engine always uses the\n * Kani-TTS-2 checkpoint, so `modelId` is accepted for API compatibility only.\n */\n async isTTSCached(_modelId?: string): Promise<boolean> {\n const { DEFAULT_MODELS } = await import(\"../gpu/index.js\");\n return this.isNativeRepoCached(DEFAULT_MODELS.tts);\n }\n\n /**\n * Preload the native TTS model (downloads Kani-TTS-2 weights to disk cache).\n *\n * @param modelId - Accepted for API compatibility; native TTS uses Kani-TTS-2.\n * @param options.keepLoaded - Keep the engine in memory for instant use.\n */\n async preloadTTS(modelId?: string, options: PreloadOptions = {}): Promise<void> {\n const { onProgress, keepLoaded = false } = options;\n\n if (keepLoaded && this.isTTSLoaded()) {\n onProgress?.({ status: \"TTS model already loaded\" });\n return;\n }\n\n if (!keepLoaded && (await this.isTTSCached(modelId))) {\n onProgress?.({ status: \"TTS model already cached\" });\n return;\n }\n\n onProgress?.({ status: \"Preloading TTS model...\" });\n // Build the native TTS engine (downloads + caches weights).\n await this.ensureNativeTTSEngine();\n\n if (!keepLoaded && this.nativeTTSEngine) {\n try {\n this.nativeTTSEngine.destroy();\n } catch {\n // best-effort\n }\n this.nativeTTSEngine = null;\n }\n onProgress?.({ status: \"Preload complete\" });\n }\n\n /**\n * Check if the native STT model is cached. The native engine always uses the\n * Moonshine checkpoint, so `modelId` is accepted for API compatibility only.\n */\n async isSTTCached(_modelId?: string): Promise<boolean> {\n const { DEFAULT_MODELS } = await import(\"../gpu/index.js\");\n return this.isNativeRepoCached(DEFAULT_MODELS.stt);\n }\n\n /**\n * Preload the native STT model (downloads Moonshine weights to disk cache).\n *\n * @param modelId - Accepted for API compatibility; native STT uses Moonshine.\n * @param options.keepLoaded - Keep the engine in memory for instant use.\n */\n async preloadSTT(modelId?: string, options: PreloadOptions = {}): Promise<void> {\n const { onProgress, keepLoaded = false } = options;\n\n if (keepLoaded && this.isSTTLoaded()) {\n onProgress?.({ status: \"STT model already loaded\" });\n return;\n }\n\n if (!keepLoaded && (await this.isSTTCached(modelId))) {\n onProgress?.({ status: \"STT model already cached\" });\n return;\n }\n\n onProgress?.({ status: \"Preloading STT model...\" });\n // Build the native STT engine (downloads + caches weights).\n await this.ensureNativeSTT();\n\n if (!keepLoaded && this.nativeSTT) {\n try {\n this.nativeSTT.destroy?.();\n } catch {\n // best-effort\n }\n this.nativeSTT = null;\n }\n onProgress?.({ status: \"Preload complete\" });\n }\n\n /**\n * Check if a native embedding model is cached. Defaults to the native\n * EmbeddingGemma checkpoint when no repo is provided.\n */\n async isEmbeddingCached(modelId?: string): Promise<boolean> {\n const { DEFAULT_MODELS } = await import(\"../gpu/index.js\");\n return this.isNativeRepoCached(modelId || DEFAULT_MODELS.embedding);\n }\n\n /**\n * Preload a native embedding model (downloads weights to disk cache).\n *\n * @param modelId - Embedding repo (default: native EmbeddingGemma).\n * @param options.keepLoaded - Keep the engine in memory for instant use.\n */\n async preloadEmbedding(modelId?: string, options: PreloadOptions = {}): Promise<void> {\n const { onProgress, keepLoaded = false } = options;\n\n if (keepLoaded && this.nativeEmbedEngine) {\n onProgress?.({ status: \"Embedding model already loaded\" });\n return;\n }\n\n if (!keepLoaded && (await this.isEmbeddingCached(modelId))) {\n onProgress?.({ status: \"Embedding model already cached\" });\n return;\n }\n\n onProgress?.({ status: \"Preloading embedding model...\" });\n // Build the native embedding engine (downloads + caches weights).\n await this.ensureNativeEmbedEngine(modelId);\n\n if (!keepLoaded && this.nativeEmbedEngine) {\n try {\n this.nativeEmbedEngine.destroy();\n } catch {\n // best-effort\n }\n this.nativeEmbedEngine = null;\n this.nativeEmbedRepo = null;\n }\n onProgress?.({ status: \"Preload complete\" });\n }\n\n /**\n * Clear KV cache to free memory.\n * The native engine manages its own KV cache; this is a no-op kept for API\n * compatibility.\n */\n // biome-ignore lint/suspicious/useAwait: kept async for API compatibility\n async clearCache(): Promise<void> {\n // no-op\n }\n\n // ============================================\n // Text Generation\n // ============================================\n\n /**\n * Generate text (automatically routes to vision generation if images provided)\n *\n * @example\n * ```ts\n * // Text generation\n * const result = await g.generate(\"Hello!\");\n *\n * // Vision generation (with vision model)\n * const result = await g.generate(\"What's in this image?\", {\n * images: [{ source: \"https://example.com/cat.jpg\" }]\n * });\n * ```\n */\n async generate(prompt: string, options: GenerateOptions = {}): Promise<GenerateResult> {\n const queueStartTime = performance.now();\n\n try {\n // Queue the generation request (prevents GPU OOM under concurrent load)\n const result = await this.queue.add(async () => {\n const queueWaitTime = performance.now() - queueStartTime;\n\n // Report queue wait time if significant (>100ms)\n if (queueWaitTime > 100 && this.telemetry.onQueueWait) {\n try {\n this.telemetry.onQueueWait(queueWaitTime);\n } catch {\n // Never throw from telemetry\n }\n }\n\n const generatedResult = await this.generateInternal(prompt, options);\n\n // Report successful generation\n if (this.telemetry.onGenerate) {\n try {\n this.telemetry.onGenerate({\n modelId: this.currentModel || \"unknown\",\n result: generatedResult,\n cached: generatedResult.cached ?? false,\n queueTimeMs: queueWaitTime > 100 ? queueWaitTime : undefined,\n });\n } catch {\n // Never throw from telemetry\n }\n }\n\n return generatedResult;\n });\n\n return result as GenerateResult;\n } catch (error) {\n // Report errors to telemetry (including queue timeouts)\n if (this.telemetry.onError) {\n try {\n this.telemetry.onError(error instanceof Error ? error : new Error(String(error)), {\n method: \"generate\",\n modelId: this.currentModel || \"unknown\",\n prompt: prompt.slice(0, 100), // Truncate for privacy\n queueWaitTime: performance.now() - queueStartTime,\n });\n } catch {\n // Never throw from telemetry\n }\n }\n throw error;\n }\n }\n\n /**\n * Internal generate implementation (called within queue)\n */\n private async generateInternal(\n prompt: string,\n options: GenerateOptions = {},\n ): Promise<GenerateResult> {\n if (!this.isLoaded()) {\n // Auto-load default model\n await this.loadModel(this.config.model || DEFAULT_MODEL);\n }\n\n const { images } = options;\n\n // Route to native vision generation when images are provided and the loaded\n // model supports vision.\n if (images?.length && this.isVisionModel) {\n return this.generateWithVision(prompt, options);\n }\n\n const {\n maxTokens = 256,\n temperature = 0.7,\n topP = 0.9,\n topK = 50,\n thinking = false,\n system,\n cache = false,\n cacheTtl,\n } = options;\n\n // Check cache if enabled (skip for streaming/vision)\n if (cache && !options.onToken && !images?.length) {\n const cacheKey = generateCacheKey(prompt, this.currentModel || \"\", {\n maxTokens,\n temperature,\n topP,\n topK,\n system,\n thinking,\n });\n const cached = getGlobalCache().get(cacheKey);\n if (cached) {\n return cached;\n }\n }\n\n const startTime = performance.now();\n\n try {\n let rawText = \"\";\n // Real token counts come from the engine, not a length heuristic.\n let engineTokensGenerated = 0;\n let engineTokensPerSecond = 0;\n\n if (this.webgpuEngine) {\n // Native WebGPU engine — pure compute shaders, no ONNX\n const result = await this.webgpuEngine.generate(prompt, {\n maxTokens,\n sampling: {\n temperature,\n topP,\n topK,\n },\n systemPrompt: system,\n onToken: options.onToken ? (t: string) => options.onToken?.(t) : undefined,\n });\n rawText = result.text;\n engineTokensGenerated = result.tokensGenerated;\n engineTokensPerSecond = result.tokensPerSecond;\n } else {\n throw new Error(\"No model loaded\");\n }\n\n const endTime = performance.now();\n const totalTime = endTime - startTime;\n\n rawText = this.cleanOutput(rawText);\n\n // Always parse thinking to strip <think> tags from output\n // (model may generate them even without thinking mode enabled)\n const { thinking: thinkingText, response } = this.parseThinking(rawText);\n\n // Only include thinking in result if mode was enabled\n const finalThinking = thinking ? thinkingText : undefined;\n\n // Use the engine's true decoded-token count and throughput.\n const tokensGenerated = engineTokensGenerated;\n\n // Update stats\n this.stats.prompts += 1;\n this.stats.tokensOut += tokensGenerated;\n this.stats.totalTime += totalTime;\n this.stats.avgSpeed = (this.stats.tokensOut / this.stats.totalTime) * 1000;\n\n const result: GenerateResult = {\n text: response,\n thinking: finalThinking,\n tokensGenerated,\n tokensPerSecond: engineTokensPerSecond,\n totalTime,\n finishReason: \"stop\",\n provider: \"local\",\n cached: false,\n };\n\n // Store in cache if enabled\n if (cache && !options.onToken && !images?.length) {\n const cacheKey = generateCacheKey(prompt, this.currentModel || \"\", {\n maxTokens,\n temperature,\n topP,\n topK,\n system,\n thinking,\n });\n getGlobalCache().set(cacheKey, result, cacheTtl);\n }\n\n return result;\n } catch (error) {\n // Report error to telemetry\n this.reportError(error instanceof Error ? error : new Error(String(error)), {\n operation: \"generate\",\n modelId: this.currentModel || undefined,\n });\n\n return {\n text: \"\",\n tokensGenerated: 0,\n tokensPerSecond: 0,\n totalTime: performance.now() - startTime,\n finishReason: \"error\",\n provider: \"local\",\n cached: false,\n };\n }\n }\n\n /**\n * Stream text generation (simulated token-by-token)\n *\n * Note: Yields the raw output including <think> tags if thinking mode is enabled.\n * The final result has parsed thinking separated out.\n */\n async *stream(\n prompt: string,\n options: GenerateOptions = {},\n ): AsyncGenerator<string, GenerateResult, unknown> {\n if (!this.isLoaded()) {\n await this.loadModel(this.config.model || DEFAULT_MODEL);\n }\n\n const startTime = performance.now();\n\n // For native WebGPU engine, use real streaming via onToken callback\n if (this.webgpuEngine) {\n let fullText = \"\";\n const tokenQueue: string[] = [];\n let resolveNext: ((value: string | null) => void) | null = null;\n let done = false;\n // Capture the engine's true token count / throughput for the final result.\n let engineTokensGenerated = 0;\n let engineTokensPerSecond = 0;\n\n const generatePromise = this.webgpuEngine\n .generate(prompt, {\n ...options,\n sampling: {\n temperature: options.temperature,\n topP: options.topP,\n topK: options.topK,\n },\n systemPrompt: options.system,\n onToken: (token: string) => {\n fullText += token;\n if (resolveNext) {\n resolveNext(token);\n resolveNext = null;\n } else {\n tokenQueue.push(token);\n }\n },\n })\n .then((result: { tokensGenerated: number; tokensPerSecond: number }) => {\n engineTokensGenerated = result.tokensGenerated;\n engineTokensPerSecond = result.tokensPerSecond;\n done = true;\n if (resolveNext) {\n resolveNext(null);\n }\n })\n .catch((err: Error) => {\n done = true;\n if (resolveNext) {\n resolveNext(null);\n }\n throw err;\n });\n\n while (!done || tokenQueue.length > 0) {\n if (tokenQueue.length > 0) {\n const token = tokenQueue.shift()!;\n yield token;\n options.onToken?.(token);\n } else if (!done) {\n const token = await new Promise<string | null>((resolve) => {\n resolveNext = resolve;\n });\n if (token) {\n yield token;\n options.onToken?.(token);\n }\n }\n }\n\n await generatePromise;\n\n const { thinking: thinkingText, response } = this.parseThinking(fullText);\n const totalTime = performance.now() - startTime;\n\n return {\n text: response,\n thinking: options.thinking ? thinkingText : undefined,\n tokensGenerated: engineTokensGenerated,\n totalTime,\n tokensPerSecond: engineTokensPerSecond,\n finishReason: \"stop\" as const,\n };\n }\n\n // The native WebGPU engine is the only backend. If we reach here, no model\n // is loaded (auto-load happens at the top of stream()).\n throw new Error(\"No model loaded\");\n }\n\n // ============================================\n // Vision Generation\n // ============================================\n\n /**\n * Generate text from images using a vision model\n * Called automatically by generate() when images are provided\n */\n private async generateWithVision(\n prompt: string,\n options: GenerateOptions,\n ): Promise<GenerateResult> {\n // ── Native WebGPU vision ──\n // loadModel() brought up the native vision engine (this.webgpuEngine has its\n // ViT tower built via enableVision: true). Decode the image to RGB pixels and\n // run the native encode → splice → decode path (engine.describeImage). The\n // native engine currently supports a single image per request.\n if (!(this.webgpuEngine && typeof this.webgpuEngine.describeImage === \"function\")) {\n throw new Error(\n \"Vision model not loaded. Load a vision-capable model with device 'webgpu' first.\",\n );\n }\n\n const imgs = options.images ?? [];\n if (imgs.length !== 1) {\n throw new Error(\n `Native WebGPU vision supports exactly one image per request (got ${imgs.length}).`,\n );\n }\n\n const startTime = performance.now();\n const { pixels, width, height } = await this.decodeImageToPixels(imgs[0].source);\n const result = await this.webgpuEngine.describeImage({ pixels, width, height }, prompt, {\n maxTokens: options.maxTokens ?? 512,\n sampling: {\n temperature: options.temperature ?? 0.7,\n topP: options.topP ?? 0.9,\n topK: options.topK ?? 20,\n },\n onToken: options.onToken ? (t: string) => options.onToken?.(t) : undefined,\n });\n const totalTime = performance.now() - startTime;\n\n // Update stats\n this.stats.prompts += 1;\n this.stats.tokensOut += result.tokensGenerated;\n this.stats.totalTime += totalTime;\n this.stats.avgSpeed = (this.stats.tokensOut / this.stats.totalTime) * 1000;\n\n return {\n text: this.cleanOutput(result.text),\n tokensGenerated: result.tokensGenerated,\n tokensPerSecond: result.tokensPerSecond,\n totalTime,\n finishReason: \"stop\",\n provider: \"local\",\n cached: false,\n };\n }\n\n /**\n * Decode an image source (http(s) URL, file path, or data URI) to raw RGB\n * pixels for the native vision encoder. Supports 8-bit non-interlaced PNG\n * (color types 2/RGB and 6/RGBA). Other formats throw a clear error — callers\n * can pre-decode and use the lower-level WebGPUEngine.describeImage() with\n * pixels directly.\n */\n private async decodeImageToPixels(\n source: string,\n ): Promise<{ pixels: Uint8Array; width: number; height: number }> {\n const bytes = await this.fetchImageBytes(source);\n // PNG magic number: 89 50 4E 47 0D 0A 1A 0A\n const isPng =\n bytes.length > 8 &&\n bytes[0] === 0x89 &&\n bytes[1] === 0x50 &&\n bytes[2] === 0x4e &&\n bytes[3] === 0x47;\n if (!isPng) {\n throw new Error(\n \"Native vision currently decodes PNG images only. For other formats, pre-decode \" +\n \"to RGB pixels and call the GPU engine's describeImage() directly.\",\n );\n }\n return decodePng(bytes);\n }\n\n /** Fetch an image source to raw bytes (URL, data URI, or local file path). */\n private async fetchImageBytes(source: string): Promise<Uint8Array> {\n if (source.startsWith(\"data:\")) {\n const comma = source.indexOf(\",\");\n const meta = source.slice(5, comma);\n const data = source.slice(comma + 1);\n if (meta.includes(\"base64\")) {\n return Uint8Array.from(Buffer.from(data, \"base64\"));\n }\n return Uint8Array.from(Buffer.from(decodeURIComponent(data), \"binary\"));\n }\n if (source.startsWith(\"http://\") || source.startsWith(\"https://\")) {\n const res = await fetch(source);\n if (!res.ok) {\n throw new Error(`Failed to fetch image (${res.status}): ${source}`);\n }\n return new Uint8Array(await res.arrayBuffer());\n }\n // Local file path (Node.js only)\n const { readFile } = await import(\"node:fs/promises\");\n return new Uint8Array(await readFile(source));\n }\n\n // ============================================\n // Structured Output (JSON)\n // ============================================\n\n /**\n * Generate structured JSON output\n */\n async json<T>(prompt: string, options: JsonOptions<T>): Promise<T> {\n const { schema, retries = 3, temperature = 0.3 } = options;\n\n const systemPrompt = `You are a JSON generator. You MUST respond with valid JSON only.\nNo explanations, no markdown, no code blocks. Just pure JSON.\nThe JSON must conform to this schema: ${JSON.stringify(zodToJsonSchema(schema))}`;\n\n for (let attempt = 0; attempt < retries; attempt += 1) {\n const result = await this.generate(prompt, {\n system: options.system || systemPrompt,\n temperature,\n maxTokens: 1000,\n });\n\n try {\n // Try to extract JSON from response\n const jsonStr = extractJson(result.text);\n const parsed = JSON.parse(jsonStr);\n const validated = schema.parse(parsed);\n return validated;\n } catch (error) {\n if (attempt === retries - 1) {\n throw new Error(`Failed to generate valid JSON after ${retries} attempts: ${error}`);\n }\n }\n }\n\n throw new Error(\"Failed to generate valid JSON\");\n }\n\n /**\n * Generate a structured object via the native engine's retrying\n * `generateObject` (extract JSON → validate → retry with a nudge).\n *\n * Unlike {@link json} (which is Zod-driven), this passes through to the engine\n * and accepts either a predicate validator `(o) => boolean` or a minimal\n * `{ required: [...] }` schema; omit `schema` to accept any valid JSON.\n *\n * @example\n * ```ts\n * const { object } = await g.generateObject<{ name: string; age: number }>(\n * 'Extract {name, age} from: \"I am Sarah, 28\"',\n * { schema: { required: [\"name\", \"age\"] } },\n * );\n * ```\n */\n async generateObject<T = unknown>(\n prompt: string,\n options: GenerateObjectOptions = {},\n ): Promise<GenerateObjectResult<T>> {\n if (!this.isLoaded()) {\n await this.loadModel(this.config.model || DEFAULT_MODEL);\n }\n if (!this.webgpuEngine) {\n throw new Error(\"No model loaded\");\n }\n return this.webgpuEngine.generateObject(prompt, options) as Promise<GenerateObjectResult<T>>;\n }\n\n // ============================================\n // Embeddings\n // ============================================\n\n /**\n * Generate embeddings\n */\n async embed(text: string, options: EmbedOptions = {}): Promise<EmbedResult> {\n // ── Native WebGPU embedding ──\n // The active text model may not be an embedder, so embeddings run on a\n // dedicated native engine loaded with { embedding: true }. It is created\n // lazily on first embed(), keyed by repo: if the caller asks for a different\n // embedding model than the cached one, the old engine is torn down and a new\n // one is built. The engine is owned by this Gerbil instance and released in\n // dispose(). Native engine.embed() returns an L2-normalized Float32Array.\n if (!this.preferNative()) {\n throw new Error(\n \"Embeddings require WebGPU. CPU/WASM and the legacy ONNX backend have been removed.\",\n );\n }\n\n const native = await this.ensureNativeEmbedEngine(options.model);\n const startTime = performance.now();\n const vec = await native.embed(text);\n return {\n vector: Array.from(vec as Float32Array),\n text,\n totalTime: performance.now() - startTime,\n };\n }\n\n /**\n * Lazily build (or reuse) the native embedding engine. Re-creates it when the\n * requested repo differs from the cached one. The default native embedding\n * model is resolved by the engine itself (EmbeddingGemma) when no repo given.\n */\n private async ensureNativeEmbedEngine(repo?: string): Promise<any> {\n if (this.nativeEmbedEngine && (!repo || repo === this.nativeEmbedRepo)) {\n return this.nativeEmbedEngine;\n }\n if (this.nativeEmbedEngine) {\n // Switching embedding models — release the previous native engine.\n try {\n this.nativeEmbedEngine.destroy();\n } catch {\n // best-effort cleanup\n }\n this.nativeEmbedEngine = null;\n this.nativeEmbedRepo = null;\n }\n const { WebGPUEngine } = await import(\"../gpu/index.js\");\n this.nativeEmbedEngine = await WebGPUEngine.create({ repo, embedding: true });\n this.nativeEmbedRepo = repo ?? null;\n return this.nativeEmbedEngine;\n }\n\n /**\n * Generate embeddings for multiple texts\n */\n async embedBatch(texts: string[], options: EmbedOptions = {}): Promise<EmbedResult[]> {\n const results: EmbedResult[] = [];\n for (const text of texts) {\n results.push(await this.embed(text, options));\n }\n return results;\n }\n\n /**\n * Compute cosine similarity between two vectors\n *\n * @example\n * ```ts\n * const sim = g.cosineSimilarity([1, 0, 0], [1, 0, 0]); // 1.0\n * const sim2 = g.cosineSimilarity([1, 0, 0], [0, 1, 0]); // 0.0\n * ```\n */\n cosineSimilarity(a: number[], b: number[]): number {\n if (a.length !== b.length) {\n throw new Error(`Vector dimensions must match: ${a.length} vs ${b.length}`);\n }\n\n let dotProduct = 0;\n let normA = 0;\n let normB = 0;\n\n for (let i = 0; i < a.length; i++) {\n dotProduct += a[i] * b[i];\n normA += a[i] * a[i];\n normB += b[i] * b[i];\n }\n\n const magnitude = Math.sqrt(normA) * Math.sqrt(normB);\n if (magnitude === 0) return 0;\n\n return dotProduct / magnitude;\n }\n\n /**\n * Compare similarity between two texts\n *\n * @example\n * ```ts\n * const result = await g.similarity(\"Hello world\", \"Hi there\");\n * console.log(result.score); // 0.85\n * ```\n */\n async similarity(\n textA: string,\n textB: string,\n options: EmbedOptions = {},\n ): Promise<import(\"./types.js\").SimilarityResult> {\n const startTime = performance.now();\n\n const [embedA, embedB] = await Promise.all([\n this.embed(textA, options),\n this.embed(textB, options),\n ]);\n\n const score = this.cosineSimilarity(embedA.vector, embedB.vector);\n\n return {\n score,\n textA,\n textB,\n totalTime: performance.now() - startTime,\n };\n }\n\n /**\n * Semantic search - find most similar texts from a corpus\n *\n * @example\n * ```ts\n * const results = await g.search(\"capital of France\", [\n * \"Paris is beautiful\",\n * \"London is in England\",\n * \"Dogs are pets\"\n * ]);\n * // [{ text: \"Paris is beautiful\", score: 0.89, index: 0 }, ...]\n * ```\n */\n async search(\n query: string,\n corpus: string[],\n options: EmbedOptions & { topK?: number } = {},\n ): Promise<import(\"./types.js\").SearchResult[]> {\n const { topK = corpus.length, ...embedOptions } = options;\n\n // Embed query and all documents\n const queryEmbedding = await this.embed(query, embedOptions);\n const corpusEmbeddings = await this.embedBatch(corpus, embedOptions);\n\n // Calculate similarities\n const results: import(\"./types.js\").SearchResult[] = corpusEmbeddings.map((doc, index) => ({\n text: doc.text,\n score: this.cosineSimilarity(queryEmbedding.vector, doc.vector),\n index,\n }));\n\n // Sort by score descending and take top K\n return results.sort((a, b) => b.score - a.score).slice(0, topK);\n }\n\n /**\n * Find the nearest text to an embedding vector\n *\n * @example\n * ```ts\n * const embedding = (await g.embed(\"dog\")).vector;\n * const match = await g.findNearest(embedding, [\"cat\", \"car\", \"tree\"]);\n * // { text: \"cat\", score: 0.85, index: 0 }\n * ```\n */\n async findNearest(\n embedding: number[],\n candidates: string[],\n options: EmbedOptions & { topK?: number } = {},\n ): Promise<import(\"./types.js\").SearchResult[]> {\n const { topK = candidates.length, ...embedOptions } = options;\n\n // Embed all candidates\n const candidateEmbeddings = await this.embedBatch(candidates, embedOptions);\n\n // Calculate similarities\n const results: import(\"./types.js\").SearchResult[] = candidateEmbeddings.map((doc, index) => ({\n text: doc.text,\n score: this.cosineSimilarity(embedding, doc.vector),\n index,\n }));\n\n // Sort by score descending and take top K\n return results.sort((a, b) => b.score - a.score).slice(0, topK);\n }\n\n // ============================================\n // Stats & Info\n // ============================================\n\n /**\n * Get session stats\n */\n getStats(): SessionStats {\n return { ...this.stats };\n }\n\n /**\n * Get system info\n */\n getInfo(): SystemInfo {\n return {\n version: \"1.0.0\",\n model: this.modelConfig,\n device: {\n backend: \"webgpu-native\",\n gpu: null, // TODO: surface Dawn adapter info\n vram: null,\n status: this.isLoaded() ? \"ready\" : \"loading\",\n },\n context: {\n max: this.modelConfig?.contextLength || 0,\n used: 0,\n available: this.modelConfig?.contextLength || 0,\n },\n cache: {\n location: \"~/.cache/gerbil\",\n size: \"0 MB\",\n modelCount: 0,\n },\n };\n }\n\n /**\n * Reset stats\n */\n resetStats(): void {\n this.stats = {\n prompts: 0,\n tokensIn: 0,\n tokensOut: 0,\n avgSpeed: 0,\n totalTime: 0,\n cacheHits: 0,\n cacheMisses: 0,\n };\n }\n\n // ============================================\n // Text-to-Speech (TTS)\n // ============================================\n\n private readonly ttsModelId = \"kani-tts-2\";\n\n /**\n * Load the native TTS model (Kani-TTS-2) for text-to-speech synthesis.\n *\n * @example\n * ```ts\n * await g.loadTTS({ onProgress: (p) => console.log(p.status) });\n * const result = await g.speak(\"Hello world\");\n * // result.audio = Float32Array PCM, result.sampleRate = 22050\n * ```\n */\n // biome-ignore lint/suspicious/useAwait: native TTS is built lazily by ensureNativeTTSEngine\n async loadTTS(_options: LoadTTSOptions & { model?: string } = {}): Promise<void> {\n await this.ensureNativeTTSEngine();\n }\n\n /**\n * Ensure TTS model is loaded (lazy loading)\n */\n async ensureTTSLoaded(_options?: LoadTTSOptions): Promise<void> {\n await this.ensureNativeTTSEngine();\n }\n\n /**\n * Generate speech from text using the native Kani-TTS-2 WebGPU engine.\n *\n * @example\n * ```ts\n * const result = await g.speak(\"Hello world\");\n * // result.audio = Float32Array PCM, result.sampleRate = 22050\n * ```\n */\n async speak(text: string, options: SpeakOptions = {}): Promise<SpeakResult> {\n // ── Native WebGPU TTS ──\n // Native speech runs through a dedicated WebGPUEngine loaded with the\n // Kani-TTS-2 checkpoint; engine.speak() returns { pcm, sampleRate,\n // audioSeconds }. The engine is lazily built on first speak() and owned by\n // this instance (released in dispose()).\n if (!this.preferNative()) {\n throw new Error(\n \"Speech synthesis requires WebGPU. CPU/WASM and the legacy ONNX backend have been removed.\",\n );\n }\n\n const native = await this.ensureNativeTTSEngine();\n const startTime = performance.now();\n const out = await native.speak(text, {});\n return {\n audio: out.pcm,\n sampleRate: out.sampleRate,\n duration: out.audioSeconds,\n voice: options.voice ?? \"default\",\n totalTime: performance.now() - startTime,\n };\n }\n\n /** Lazily build (or reuse) the native Kani-TTS WebGPUEngine (default repo). */\n private async ensureNativeTTSEngine(): Promise<any> {\n if (!this.nativeTTSEngine) {\n const { WebGPUEngine, DEFAULT_MODELS } = await import(\"../gpu/index.js\");\n this.nativeTTSEngine = await WebGPUEngine.create({ repo: DEFAULT_MODELS.tts });\n }\n return this.nativeTTSEngine;\n }\n\n /**\n * Stream speech generation. The native engine synthesizes the full clip, so a\n * single final audio chunk is yielded.\n */\n async *speakStream(\n text: string,\n options: SpeakOptions = {},\n ): AsyncGenerator<AudioChunk, SpeakResult, unknown> {\n const result = await this.speak(text, options);\n yield {\n samples: result.audio,\n sampleRate: result.sampleRate,\n index: 0,\n isFinal: true,\n };\n return result;\n }\n\n /**\n * Get list of available TTS voices (native Kani-TTS-2 default voice).\n */\n listVoices(): VoiceInfo[] {\n return KOKORO_VOICES_DEFAULT;\n }\n\n /**\n * Check if TTS model is loaded\n */\n isTTSLoaded(): boolean {\n return this.nativeTTSEngine !== null;\n }\n\n /**\n * Get current TTS model info\n */\n getTTSModelInfo(): { id: string; loaded: boolean; device?: \"webgpu\" | \"cpu\" } | null {\n if (!this.nativeTTSEngine) {\n return null;\n }\n return {\n id: this.ttsModelId,\n loaded: true,\n device: \"webgpu\",\n };\n }\n\n /**\n * List available TTS models (native Kani-TTS-2).\n */\n // biome-ignore lint/suspicious/useAwait: async kept for API compatibility\n async listTTSModels(): Promise<\n Array<{ id: string; description: string; sampleRate: number; voiceCount: number }>\n > {\n return [\n {\n id: this.ttsModelId,\n description: \"Kani-TTS-2 native WebGPU TTS\",\n sampleRate: 22_050,\n voiceCount: 1,\n },\n ];\n }\n\n // ============================================\n // Speech-to-Text (STT)\n // ============================================\n\n /**\n * Load the native STT model (Moonshine) for speech-to-text transcription.\n *\n * @example\n * ```ts\n * await g.loadSTT();\n * const result = await g.transcribe(audioData);\n * console.log(result.text);\n * ```\n */\n // biome-ignore lint/suspicious/useAwait: native STT is built lazily by ensureNativeSTT\n async loadSTT(_modelId?: string, _options: LoadSTTOptions = {}): Promise<void> {\n // Native STT uses the Moonshine checkpoint, built lazily on first transcribe().\n await this.ensureNativeSTT();\n }\n\n /**\n * Ensure STT model is loaded (lazy loading)\n */\n public async ensureSTTLoaded(_modelId?: string, _options?: LoadSTTOptions): Promise<void> {\n await this.ensureNativeSTT();\n }\n\n /**\n * Transcribe audio to text\n *\n * @param audio - Audio data as Float32Array (16kHz mono) or Uint8Array (WAV file)\n * @param options - Transcription options\n *\n * @example\n * ```ts\n * // From Float32Array (16kHz mono)\n * const result = await g.transcribe(audioData);\n * console.log(result.text);\n *\n * // With timestamps\n * const result = await g.transcribe(audioData, { timestamps: true });\n * for (const seg of result.segments) {\n * console.log(`[${seg.start}s] ${seg.text}`);\n * }\n *\n * // From WAV file\n * const wavData = fs.readFileSync(\"audio.wav\");\n * const result = await g.transcribe(new Uint8Array(wavData));\n * ```\n */\n async transcribe(\n audio: Float32Array | Uint8Array,\n options: TranscribeOptions = {},\n ): Promise<TranscribeResult> {\n // ── Native WebGPU STT ──\n // MoonshineSTT consumes raw 16 kHz mono Float32 PCM. A Uint8Array WAV\n // container cannot be decoded natively, and timestamps are not produced by\n // the native path — both are unsupported now that the ONNX backend is gone.\n if (!this.preferNative()) {\n throw new Error(\n \"Transcription requires WebGPU. CPU/WASM and the legacy ONNX backend have been removed.\",\n );\n }\n if (!(audio instanceof Float32Array)) {\n throw new Error(\n \"Native transcription requires 16 kHz mono Float32Array PCM. Decode WAV bytes to PCM first.\",\n );\n }\n if (options.timestamps) {\n throw new Error(\"Native transcription does not produce timestamps.\");\n }\n\n const native = await this.ensureNativeSTT();\n const startTime = performance.now();\n const out = await native.transcribe(audio);\n return {\n text: out.text,\n language: options.language ?? \"en\",\n duration: out.audioSeconds,\n totalTime: performance.now() - startTime,\n };\n }\n\n /** Lazily build (or reuse) the native MoonshineSTT engine (default repo). */\n private async ensureNativeSTT(): Promise<any> {\n if (!this.nativeSTT) {\n const { MoonshineSTT } = await import(\"../gpu/moonshine-stt.js\");\n this.nativeSTT = await MoonshineSTT.create();\n }\n return this.nativeSTT;\n }\n\n /**\n * Create a streaming transcription session\n *\n * Transcribes audio in real-time by processing chunks at regular intervals.\n * Perfect for live captioning, call transcription, or real-time subtitles.\n *\n * @param options - Streaming options\n * @returns Streaming session controller\n *\n * @example\n * ```ts\n * const session = await g.createStreamingTranscription({\n * chunkDuration: 3000, // Transcribe every 3 seconds\n * onChunk: (text, idx) => console.log(`Chunk ${idx}: ${text}`),\n * onTranscript: (fullText) => console.log(\"Full:\", fullText),\n * });\n *\n * // Feed audio data as it comes in\n * session.feedAudio(audioChunk);\n *\n * // Start automatic interval-based transcription\n * session.start();\n *\n * // Later, stop and get final transcript\n * const finalText = await session.stop();\n * ```\n */\n // biome-ignore lint/suspicious/useAwait: async kept for API compatibility\n async createStreamingTranscription(\n _options: StreamingTranscriptionOptions = {},\n ): Promise<StreamingTranscriptionSession> {\n throw new Error(\n \"Streaming transcription is not supported by the native WebGPU STT engine. \" +\n \"Use transcribe() on buffered 16 kHz Float32Array PCM instead.\",\n );\n }\n\n /**\n * Get list of available STT models (native Moonshine).\n */\n // biome-ignore lint/suspicious/useAwait: async kept for API compatibility\n async listSTTModels(): Promise<STTModelConfig[]> {\n return [\n {\n id: \"moonshine-base\",\n repo: \"UsefulSensors/moonshine-base\",\n description: \"Moonshine native WebGPU STT\",\n size: \"61M\",\n multilingual: false,\n languages: [\"en\"],\n sampleRate: 16000,\n },\n ];\n }\n\n /**\n * Check if STT model is loaded\n */\n isSTTLoaded(): boolean {\n return this.nativeSTT !== null;\n }\n\n /**\n * Get current STT model info\n */\n getSTTModelInfo(): { id: string; loaded: boolean; device?: \"webgpu\" | \"cpu\" } | null {\n if (!this.nativeSTT) {\n return null;\n }\n return {\n id: \"moonshine-base\",\n loaded: true,\n device: \"webgpu\",\n };\n }\n\n // ============================================\n // Microphone Input\n // ============================================\n\n /**\n * Record audio from microphone and transcribe\n *\n * @example\n * ```ts\n * // Record for 5 seconds and transcribe\n * const result = await g.listen(5000);\n * console.log(result.text);\n *\n * // Use with voice chat\n * const userInput = await g.listen(10000);\n * const response = await g.generate(userInput.text);\n * await g.speak(response.text);\n * ```\n */\n async listen(\n durationMs: number = 5000,\n options: { onProgress?: (status: string) => void } = {},\n ): Promise<TranscribeResult> {\n // Dynamic import for microphone (avoids bundling when not used)\n const { Microphone, isSoxAvailable } = await import(\"./microphone.js\");\n\n if (!isSoxAvailable()) {\n throw new Error(\n \"Microphone recording requires SoX. Install with:\\n\" +\n \" macOS: brew install sox\\n\" +\n \" Ubuntu: sudo apt install sox\\n\" +\n \" Windows: https://sox.sourceforge.net/\",\n );\n }\n\n options.onProgress?.(\"Starting microphone...\");\n\n const mic = new Microphone({ sampleRate: 16000 });\n await mic.start();\n\n options.onProgress?.(`Recording for ${(durationMs / 1000).toFixed(1)}s...`);\n\n // Wait for the specified duration\n await new Promise((r) => setTimeout(r, durationMs));\n\n options.onProgress?.(\"Processing audio...\");\n const { audio } = await mic.stop();\n\n options.onProgress?.(\"Transcribing...\");\n return this.transcribe(audio, {\n onProgress: (p) => options.onProgress?.(p.status || \"Transcribing...\"),\n });\n }\n\n /**\n * Check if microphone recording is available\n */\n async isMicrophoneAvailable(): Promise<boolean> {\n try {\n const { isSoxAvailable } = await import(\"./microphone.js\");\n return isSoxAvailable();\n } catch {\n return false;\n }\n }\n\n // ============================================\n // Cleanup\n // ============================================\n\n /**\n * Dispose of resources (releases all native WebGPU engines and their devices).\n * @param _disconnect Accepted for API compatibility; no longer used.\n */\n // biome-ignore lint/suspicious/useAwait: async kept for API compatibility\n async dispose(_disconnect = false): Promise<void> {\n // Clean up native WebGPU engine\n if (this.webgpuEngine) {\n try {\n this.webgpuEngine.destroy();\n } catch {\n // Ignore errors during cleanup\n }\n this.webgpuEngine = null;\n }\n\n // Clean up the lazily-created native capability engines (embed / STT / TTS).\n // These are independent of this.webgpuEngine (they back embed()/transcribe()/\n // speak() even when the active model isn't that capability), so they need\n // their own teardown. MoonshineSTT has no GPU device of its own to destroy\n // beyond its weight maps; WebGPUEngine.destroy() releases its device.\n if (this.nativeEmbedEngine) {\n try {\n this.nativeEmbedEngine.destroy();\n } catch {\n // Ignore errors during cleanup\n }\n this.nativeEmbedEngine = null;\n this.nativeEmbedRepo = null;\n }\n if (this.nativeSTT) {\n try {\n this.nativeSTT.destroy?.();\n } catch {\n // Ignore errors during cleanup\n }\n this.nativeSTT = null;\n }\n if (this.nativeTTSEngine) {\n try {\n this.nativeTTSEngine.destroy();\n } catch {\n // Ignore errors during cleanup\n }\n this.nativeTTSEngine = null;\n }\n\n this.currentModel = null;\n this.modelConfig = null;\n this.isVisionModel = false;\n }\n\n /**\n * @deprecated The shared Chrome backend was removed; this is now a no-op.\n */\n // biome-ignore lint/suspicious/useAwait: async kept for API compatibility\n static async shutdown(): Promise<void> {\n // no-op\n }\n\n // ============================================\n // Private Methods\n // ============================================\n\n private parseThinking(text: string): {\n thinking?: string;\n response: string;\n } {\n // Handle complete <think>...</think> blocks\n const match = text.match(/<think>([\\s\\S]*?)<\\/think>/);\n if (match) {\n const thinking = match[1].trim();\n const response = text.replace(/<think>[\\s\\S]*?<\\/think>/, \"\").trim();\n return { thinking, response };\n }\n\n // Handle unclosed <think> tags (model stopped mid-thought)\n const unclosedMatch = text.match(/<think>([\\s\\S]*)$/);\n if (unclosedMatch) {\n const thinking = unclosedMatch[1].trim();\n const response = text.replace(/<think>[\\s\\S]*$/, \"\").trim();\n return { thinking: thinking || undefined, response };\n }\n\n // Handle any remaining think tags\n const response = text.replace(/<\\/?think>/g, \"\").trim();\n return { response };\n }\n\n private cleanOutput(text: string): string {\n return (\n text\n .replace(/<\\|im_end\\|>/g, \"\")\n .replace(/<\\|im_start\\|>/g, \"\")\n .replace(/<\\|endoftext\\|>/g, \"\")\n .replace(/<\\/s>/g, \"\")\n // Clean up artifacts from direct model output\n .replace(/^\\/no_think\\s*/i, \"\")\n .replace(/^assistant\\s*/i, \"\")\n .replace(/^\\s*\\/no_think\\s*/gim, \"\")\n .replace(/^\\s*assistant\\s*/gim, \"\")\n // Clean up role markers that might appear\n .replace(/^(system|user|assistant):\\s*/gim, \"\")\n .trim()\n );\n }\n}\n\nexport default Gerbil;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"gerbil-CZYYq4Sq.mjs","names":["globalCache: ResponseCache | null","BUILTIN_MODELS: Record<string, ModelConfig>","FAMILY_CONTEXT_DEFAULTS: Record<string, number>","family: ModelConfig[\"family\"]","idat: Uint8Array[]","v: number","KOKORO_VOICES_DEFAULT: VoiceInfo[]","concurrency: ConcurrencyConfig","result","result: GenerateResult","tokenQueue: string[]","resolveNext: ((value: string | null) => void) | null","results: EmbedResult[]"],"sources":["../src/core/cache.ts","../src/core/models.ts","../src/core/gerbil.ts"],"sourcesContent":["/**\n * Response Cache for Gerbil\n *\n * LRU cache with TTL expiration for caching inference responses.\n * Enables instant responses for repeated prompts.\n */\n\nimport type { GenerateResult } from \"./types.js\";\n\n// ============================================\n// Types\n// ============================================\n\ntype CacheEntry = {\n result: GenerateResult;\n createdAt: number;\n ttl: number;\n};\n\ntype CacheStats = {\n hits: number;\n misses: number;\n size: number;\n maxSize: number;\n};\n\n// ============================================\n// Cache Key Generation\n// ============================================\n\n/**\n * Generate a deterministic cache key from prompt and options.\n * Key includes all parameters that affect the output.\n */\nexport function generateCacheKey(\n prompt: string,\n modelId: string,\n options: {\n maxTokens?: number;\n temperature?: number;\n topP?: number;\n topK?: number;\n system?: string;\n thinking?: boolean;\n },\n): string {\n const keyParts = [\n prompt,\n modelId,\n options.maxTokens ?? 256,\n options.temperature ?? 0.7,\n options.topP ?? 0.9,\n options.topK ?? 50,\n options.system ?? \"\",\n options.thinking ?? false,\n ];\n\n // Simple hash function for cache key\n const str = JSON.stringify(keyParts);\n let hash = 0;\n for (let i = 0; i < str.length; i++) {\n const char = str.charCodeAt(i);\n hash = (hash << 5) - hash + char;\n hash = hash & hash; // Convert to 32bit integer\n }\n return `gerbil:${hash.toString(16)}`;\n}\n\n// ============================================\n// Response Cache\n// ============================================\n\n/**\n * LRU cache with TTL expiration for inference responses.\n */\nexport class ResponseCache {\n private cache: Map<string, CacheEntry> = new Map();\n private maxSize: number;\n private defaultTtl: number;\n private hits = 0;\n private misses = 0;\n\n /**\n * Create a new response cache.\n * @param maxSize Maximum number of entries (default: 100)\n * @param defaultTtl Default TTL in ms (default: 5 minutes)\n */\n constructor(maxSize = 100, defaultTtl = 5 * 60 * 1000) {\n this.maxSize = maxSize;\n this.defaultTtl = defaultTtl;\n }\n\n /**\n * Get a cached response if it exists and hasn't expired.\n */\n get(key: string): GenerateResult | null {\n const entry = this.cache.get(key);\n\n if (!entry) {\n this.misses++;\n return null;\n }\n\n // Check if expired\n if (Date.now() - entry.createdAt > entry.ttl) {\n this.cache.delete(key);\n this.misses++;\n return null;\n }\n\n // Move to end for LRU (delete and re-add)\n this.cache.delete(key);\n this.cache.set(key, entry);\n\n this.hits++;\n return { ...entry.result, cached: true };\n }\n\n /**\n * Store a response in the cache.\n */\n set(key: string, result: GenerateResult, ttl?: number): void {\n // Evict oldest entries if at capacity\n while (this.cache.size >= this.maxSize) {\n const firstKey = this.cache.keys().next().value;\n if (firstKey) {\n this.cache.delete(firstKey);\n }\n }\n\n this.cache.set(key, {\n result,\n createdAt: Date.now(),\n ttl: ttl ?? this.defaultTtl,\n });\n }\n\n /**\n * Check if a key exists and is not expired.\n */\n has(key: string): boolean {\n const entry = this.cache.get(key);\n if (!entry) return false;\n\n if (Date.now() - entry.createdAt > entry.ttl) {\n this.cache.delete(key);\n return false;\n }\n\n return true;\n }\n\n /**\n * Remove a specific key from the cache.\n */\n delete(key: string): boolean {\n return this.cache.delete(key);\n }\n\n /**\n * Clear all entries from the cache.\n */\n clear(): void {\n this.cache.clear();\n this.hits = 0;\n this.misses = 0;\n }\n\n /**\n * Remove all expired entries.\n */\n prune(): number {\n const now = Date.now();\n let pruned = 0;\n\n for (const [key, entry] of this.cache) {\n if (now - entry.createdAt > entry.ttl) {\n this.cache.delete(key);\n pruned++;\n }\n }\n\n return pruned;\n }\n\n /**\n * Get cache statistics.\n */\n getStats(): CacheStats {\n return {\n hits: this.hits,\n misses: this.misses,\n size: this.cache.size,\n maxSize: this.maxSize,\n };\n }\n\n /**\n * Get hit rate as a percentage.\n */\n getHitRate(): number {\n const total = this.hits + this.misses;\n if (total === 0) return 0;\n return (this.hits / total) * 100;\n }\n}\n\n// ============================================\n// Global Cache Instance\n// ============================================\n\nlet globalCache: ResponseCache | null = null;\n\n/**\n * Get the global response cache instance.\n * Creates one if it doesn't exist.\n */\nexport function getGlobalCache(): ResponseCache {\n if (!globalCache) {\n globalCache = new ResponseCache();\n }\n return globalCache;\n}\n\n/**\n * Configure the global cache with custom settings.\n */\nexport function configureGlobalCache(maxSize?: number, defaultTtl?: number): ResponseCache {\n globalCache = new ResponseCache(maxSize, defaultTtl);\n return globalCache;\n}\n\n/**\n * Clear and reset the global cache.\n */\nexport function clearGlobalCache(): void {\n if (globalCache) {\n globalCache.clear();\n }\n}\n","/**\n * Model Registry\n *\n * Supports built-in models and any HuggingFace model via hf:org/model syntax\n */\n\nimport type { ModelConfig, ModelSource } from \"./types.js\";\n\n// ============================================\n// Canonical default model\n// ============================================\n\n/**\n * The default model used everywhere a model id is not explicitly provided\n * (CLI flags, REPL, framework adapters, integrations, one-liner). This is the\n * e2e-validated model; reference this constant instead of hard-coding the id.\n */\nexport const DEFAULT_MODEL = \"qwen3.5-0.8b\";\n\n// ============================================\n// Built-in Models (curated & tested)\n// ============================================\n\n// Every entry is a standard HuggingFace safetensors repo whose architecture the\n// native WebGPU engine supports (Qwen2/Qwen3/Qwen3.5, LFM2 — see\n// src/gpu/architectures/index.ts). The engine quantizes weights to INT4 on load;\n// `size` is the bf16/fp16 download size. Only add a repo here whose architecture\n// has a graph generator in the registry.\nexport const BUILTIN_MODELS: Record<string, ModelConfig> = {\n \"qwen3.5-0.8b\": {\n id: \"qwen3.5-0.8b\",\n repo: \"Qwen/Qwen3.5-0.8B\",\n description:\n \"Qwen3.5 0.8B - Fast, multimodal (vision), 262k context, supports thinking (default)\",\n size: \"~1.6GB\",\n contextLength: 262_144,\n supportsThinking: true,\n supportsJson: true,\n supportsVision: true,\n family: \"qwen\",\n },\n \"qwen3.5-2b\": {\n id: \"qwen3.5-2b\",\n repo: \"Qwen/Qwen3.5-2B\",\n description:\n \"Qwen3.5 2B - Higher quality, multimodal (vision), 262k context, supports thinking\",\n size: \"~4GB\",\n contextLength: 262_144,\n supportsThinking: true,\n supportsJson: true,\n supportsVision: true,\n family: \"qwen\",\n },\n \"lfm2.5-1.2b-thinking\": {\n id: \"lfm2.5-1.2b-thinking\",\n repo: \"LiquidAI/LFM2.5-1.2B-Thinking\",\n description: \"LFM2.5 1.2B Thinking - Efficient reasoning model, 128k context\",\n size: \"~2.4GB\",\n contextLength: 128_000,\n supportsThinking: true,\n supportsJson: false,\n family: \"other\",\n },\n};\n\n// ============================================\n// Model Resolution\n// ============================================\n\n/**\n * Parse model identifier and resolve to source\n *\n * Supported formats:\n * - \"qwen3.5-0.8b\" (built-in)\n * - \"hf:org/model\" (HuggingFace shorthand)\n * - \"https://huggingface.co/org/model\" (full URL)\n * - \"file:./path/to/model\" (local path)\n */\nexport function resolveModel(modelId: string): ModelSource {\n // Built-in model\n if (BUILTIN_MODELS[modelId]) {\n return {\n type: \"builtin\",\n path: BUILTIN_MODELS[modelId].repo,\n };\n }\n\n // HuggingFace shorthand: hf:org/model\n if (modelId.startsWith(\"hf:\")) {\n const repo = modelId.slice(3);\n return {\n type: \"huggingface\",\n path: repo,\n };\n }\n\n // HuggingFace URL\n if (modelId.startsWith(\"https://huggingface.co/\")) {\n const repo = modelId.replace(\"https://huggingface.co/\", \"\");\n return {\n type: \"huggingface\",\n path: repo,\n };\n }\n\n // Local file\n if (modelId.startsWith(\"file:\")) {\n const path = modelId.slice(5);\n return {\n type: \"local\",\n path,\n };\n }\n\n // Assume it's a HuggingFace repo if it contains a slash\n if (modelId.includes(\"/\")) {\n return {\n type: \"huggingface\",\n path: modelId,\n };\n }\n\n // Unknown - treat as HuggingFace\n return {\n type: \"huggingface\",\n path: modelId,\n };\n}\n\n/**\n * Get model config (built-in only)\n */\nexport function getModelConfig(modelId: string): ModelConfig | null {\n return BUILTIN_MODELS[modelId] || null;\n}\n\n// Default context lengths for the families the native engine actually supports\n// (a graph generator exists in src/gpu/architectures). Other families fall back\n// to a conservative default.\nconst FAMILY_CONTEXT_DEFAULTS: Record<string, number> = {\n qwen: 32_768,\n other: 32_768, // LFM2 supports 128k but config.json is the real source of truth\n};\n\n/**\n * Create model config for an external HuggingFace model.\n *\n * Inference is restricted to families the engine can actually run — Qwen\n * (Qwen2/Qwen3/Qwen3.5) and LFM2 (Liquid). Everything else is left as \"other\"\n * with conservative capability flags so the REPL doesn't advertise features the\n * engine can't deliver.\n */\nexport function createExternalModelConfig(\n modelId: string,\n repo: string,\n contextLength?: number,\n): ModelConfig {\n const repoLower = repo.toLowerCase();\n\n // Only infer families that have a graph generator in the registry.\n let family: ModelConfig[\"family\"] = \"other\";\n if (repoLower.includes(\"qwen\")) {\n family = \"qwen\";\n }\n\n const isLiquid = repoLower.includes(\"lfm\") || repoLower.includes(\"liquid\");\n const isQwen = family === \"qwen\";\n\n return {\n id: modelId,\n repo,\n description: `External model: ${repo}`,\n size: \"Unknown\",\n contextLength: contextLength || FAMILY_CONTEXT_DEFAULTS[family] || 32_768,\n // Qwen3/Qwen3.5 and LFM2.5-Thinking expose thinking; nothing here is vision.\n supportsThinking: isQwen || isLiquid,\n supportsJson: isQwen,\n family,\n };\n}\n\n/**\n * Fetch context length from HuggingFace model config\n */\nexport async function fetchModelContextLength(repo: string): Promise<number | null> {\n try {\n const res = await fetch(`https://huggingface.co/${repo}/raw/main/config.json`);\n if (!res.ok) {\n return null;\n }\n\n const config = await res.json();\n\n // Different models use different field names\n return (\n config.max_position_embeddings ||\n config.n_positions ||\n config.max_seq_len ||\n config.sliding_window || // Some models use this\n config.context_length ||\n null\n );\n } catch {\n return null;\n }\n}\n\n/**\n * List all built-in models\n */\nexport function listBuiltinModels(): ModelConfig[] {\n return Object.values(BUILTIN_MODELS);\n}\n\n/**\n * Search HuggingFace models (placeholder - would need HF API)\n */\nexport async function searchModels(query: string): Promise<ModelConfig[]> {\n // TODO: Implement HuggingFace API search\n // For now, filter built-in models\n const q = query.toLowerCase();\n return listBuiltinModels().filter(\n (m) =>\n m.id.toLowerCase().includes(q) ||\n m.description.toLowerCase().includes(q) ||\n m.family.toLowerCase().includes(q),\n );\n}\n","/**\n * Gerbil - Local GPU-accelerated LLM inference\n */\n\nimport { existsSync } from \"node:fs\";\nimport os from \"node:os\";\nimport path from \"node:path\";\nimport zlib from \"node:zlib\";\nimport PQueue from \"p-queue\";\nimport { generateCacheKey, getGlobalCache } from \"./cache.js\";\nimport {\n BUILTIN_MODELS,\n createExternalModelConfig,\n DEFAULT_MODEL,\n fetchModelContextLength,\n getModelConfig,\n resolveModel,\n} from \"./models.js\";\nimport type {\n AudioChunk,\n ConcurrencyConfig,\n EmbedOptions,\n EmbedResult,\n ErrorContext,\n GenerateOptions,\n GenerateResult,\n GerbilConfig,\n JsonOptions,\n LoadOptions,\n LoadSTTOptions,\n LoadTTSOptions,\n ModelConfig,\n PreloadOptions,\n SessionStats,\n SpeakOptions,\n SpeakResult,\n STTModelConfig,\n StreamingTranscriptionOptions,\n StreamingTranscriptionSession,\n SystemInfo,\n TelemetryConfig,\n TranscribeOptions,\n TranscribeResult,\n VoiceInfo,\n} from \"./types.js\";\n\n/**\n * Minimal PNG decoder: 8-bit, non-interlaced, color type 2 (RGB) or 6 (RGBA).\n * Returns packed RGB pixels for the native vision encoder. Replaces the\n * transformers.js RawImage decoder for the common PNG case.\n */\nfunction decodePng(buf: Uint8Array): { pixels: Uint8Array; width: number; height: number } {\n const view = new DataView(buf.buffer, buf.byteOffset, buf.byteLength);\n const w = view.getUint32(16);\n const h = view.getUint32(20);\n const bitDepth = buf[24];\n const colorType = buf[25];\n if (bitDepth !== 8 || (colorType !== 2 && colorType !== 6)) {\n throw new Error(\n `Unsupported PNG (bitDepth=${bitDepth} colorType=${colorType}); need 8-bit RGB/RGBA`,\n );\n }\n const channels = colorType === 6 ? 4 : 3;\n let off = 8;\n const idat: Uint8Array[] = [];\n while (off < buf.length) {\n const len = view.getUint32(off);\n const type = String.fromCharCode(buf[off + 4], buf[off + 5], buf[off + 6], buf[off + 7]);\n if (type === \"IDAT\") {\n idat.push(buf.subarray(off + 8, off + 8 + len));\n }\n off += 12 + len;\n if (type === \"IEND\") {\n break;\n }\n }\n const raw = zlib.inflateSync(Buffer.concat(idat));\n const stride = w * channels;\n const out = new Uint8Array(w * h * 3);\n const line = new Uint8Array(stride);\n const prev = new Uint8Array(stride);\n let p = 0;\n for (let y = 0; y < h; y += 1) {\n const filter = raw[p];\n p += 1;\n for (let i = 0; i < stride; i += 1) {\n const x = raw[p];\n p += 1;\n const a = i >= channels ? line[i - channels] : 0;\n const b = prev[i];\n const c = i >= channels ? prev[i - channels] : 0;\n let v: number;\n switch (filter) {\n case 0:\n v = x;\n break;\n case 1:\n v = x + a;\n break;\n case 2:\n v = x + b;\n break;\n case 3:\n v = x + ((a + b) >> 1);\n break;\n case 4: {\n const pp = a + b - c;\n const pa = Math.abs(pp - a);\n const pb = Math.abs(pp - b);\n const pc = Math.abs(pp - c);\n let pred = c;\n if (pa <= pb && pa <= pc) {\n pred = a;\n } else if (pb <= pc) {\n pred = b;\n }\n v = x + pred;\n break;\n }\n default:\n throw new Error(`bad PNG filter ${filter}`);\n }\n line[i] = v & 0xff;\n }\n for (let x = 0; x < w; x += 1) {\n out[(y * w + x) * 3 + 0] = line[x * channels + 0];\n out[(y * w + x) * 3 + 1] = line[x * channels + 1];\n out[(y * w + x) * 3 + 2] = line[x * channels + 2];\n }\n prev.set(line);\n }\n return { pixels: out, width: w, height: h };\n}\n\n// Default voices for listVoices() when native TTS not loaded\nconst KOKORO_VOICES_DEFAULT: VoiceInfo[] = [\n {\n id: \"af_bella\",\n name: \"Bella\",\n gender: \"female\",\n language: \"en-us\",\n description: \"American female, warm and friendly\",\n },\n {\n id: \"af_sarah\",\n name: \"Sarah\",\n gender: \"female\",\n language: \"en-us\",\n description: \"American female, clear and professional\",\n },\n {\n id: \"af_nicole\",\n name: \"Nicole\",\n gender: \"female\",\n language: \"en-us\",\n description: \"American female, soft and gentle\",\n },\n {\n id: \"af_sky\",\n name: \"Sky\",\n gender: \"female\",\n language: \"en-us\",\n description: \"American female, young and energetic\",\n },\n {\n id: \"am_adam\",\n name: \"Adam\",\n gender: \"male\",\n language: \"en-us\",\n description: \"American male, deep and confident\",\n },\n {\n id: \"am_michael\",\n name: \"Michael\",\n gender: \"male\",\n language: \"en-us\",\n description: \"American male, warm and friendly\",\n },\n {\n id: \"bf_emma\",\n name: \"Emma\",\n gender: \"female\",\n language: \"en-gb\",\n description: \"British female, elegant and clear\",\n },\n {\n id: \"bf_isabella\",\n name: \"Isabella\",\n gender: \"female\",\n language: \"en-gb\",\n description: \"British female, sophisticated\",\n },\n {\n id: \"bm_george\",\n name: \"George\",\n gender: \"male\",\n language: \"en-gb\",\n description: \"British male, distinguished\",\n },\n {\n id: \"bm_lewis\",\n name: \"Lewis\",\n gender: \"male\",\n language: \"en-gb\",\n description: \"British male, friendly and warm\",\n },\n];\n\nimport type { GenerateObjectOptions, GenerateObjectResult } from \"../gpu/index.js\";\nimport { extractJson, zodToJsonSchema } from \"./utils.js\";\n\n// ============================================\n// Gerbil Class\n// ============================================\n\nexport class Gerbil {\n private currentModel: string | null = null;\n private modelConfig: ModelConfig | null = null;\n private readonly config: GerbilConfig;\n private stats: SessionStats;\n private _deviceMode: \"webgpu\" | \"cpu\" | \"wasm\" = \"cpu\"; // Track which backend is active\n private webgpuEngine: any = null; // Native WebGPU engine (src/gpu)\n\n // ── Native (src/gpu) capability engines, lazily created and independent of the\n // loaded text model. These let embed / transcribe / speak run on the native\n // WebGPU engine even when the active model isn't that capability. They are torn\n // down in dispose().\n private nativeEmbedEngine: any = null; // Native WebGPUEngine loaded as an embedder\n private nativeEmbedRepo: string | null = null; // Repo backing nativeEmbedEngine (for cache-busting on model switch)\n private nativeSTT: any = null; // Native MoonshineSTT (src/gpu/moonshine-stt)\n private nativeTTSEngine: any = null; // Native WebGPUEngine (Kani-TTS) backing speak()\n\n private isVisionModel = false; // Whether current model supports vision\n\n // Request queue for concurrency control (prevents GPU OOM under load)\n private readonly queue: PQueue;\n private readonly telemetry: TelemetryConfig;\n\n constructor(config: GerbilConfig = {}) {\n this.config = config;\n this.stats = {\n prompts: 0,\n tokensIn: 0,\n tokensOut: 0,\n avgSpeed: 0,\n totalTime: 0,\n cacheHits: 0,\n cacheMisses: 0,\n };\n\n // Initialize request queue (default: 1 concurrent for LLM, 5 min timeout)\n const concurrency: ConcurrencyConfig = config.concurrency || {};\n this.queue = new PQueue({\n concurrency: concurrency.maxConcurrent ?? 1,\n timeout: concurrency.timeout ?? 300_000, // 5 minutes (auto-throws on timeout)\n });\n\n // Store telemetry hooks\n this.telemetry = config.telemetry || {};\n }\n\n // ============================================\n // Telemetry Helpers\n // ============================================\n\n private reportError(error: Error, context: ErrorContext): void {\n try {\n this.telemetry.onError?.(error, context);\n } catch {\n // Never throw from telemetry\n }\n }\n\n /**\n * Whether the native (src/gpu) WebGPU engine should be used for a capability\n * (embed / transcribe / speak / vision). The native WebGPU engine is the only\n * inference backend, so this is always true; kept as a seam for callers.\n */\n private preferNative(): boolean {\n return true;\n }\n\n // ============================================\n // Static Methods\n // ============================================\n\n static listModels(): ModelConfig[] {\n return Object.values(BUILTIN_MODELS);\n }\n\n static getModel(modelId: string): ModelConfig | undefined {\n return BUILTIN_MODELS[modelId];\n }\n\n // ============================================\n // Model Loading\n // ============================================\n\n /**\n * Load a model\n *\n * @example\n * ```ts\n * // Built-in model\n * await g.loadModel(\"qwen3.5-0.8b\");\n *\n * // HuggingFace model\n * await g.loadModel(\"hf:microsoft/Phi-3-mini\");\n *\n * // Local model\n * await g.loadModel(\"file:./models/my-model\");\n *\n * // Vision model\n * await g.loadModel(\"ministral-3b\");\n * ```\n */\n async loadModel(modelId = DEFAULT_MODEL, options: LoadOptions = {}): Promise<void> {\n const loadStartTime = performance.now();\n\n // Dispose any existing model/backend before loading a new one\n // This prevents zombie Chrome pages when switching models\n if (this.isLoaded()) {\n await this.dispose();\n }\n\n const source = resolveModel(modelId);\n const { onProgress, device = \"auto\", dtype: userDtype } = options;\n\n // Get or create model config\n let config = getModelConfig(modelId);\n if (!config) {\n // Try to fetch actual context length from HuggingFace config.json\n const contextLength = await fetchModelContextLength(source.path).catch(() => null);\n config = createExternalModelConfig(modelId, source.path, contextLength || undefined);\n }\n\n // Route to vision model loading if needed\n if (config.supportsVision) {\n return this.loadVisionModel(modelId, source.path, config, options);\n }\n\n // The native WebGPU engine is the only inference backend. CPU/WASM and the\n // legacy ONNX path have been removed — guard against legacy runtime values.\n if ((device as string) === \"cpu\" || (device as string) === \"gpu\") {\n throw new Error(\n 'Gerbil requires WebGPU. CPU/WASM and the legacy ONNX backend have been removed; use device \"webgpu\" or \"auto\".',\n );\n }\n\n onProgress?.({ status: `Loading ${modelId}...` });\n\n try {\n onProgress?.({ status: \"Initializing WebGPU engine...\" });\n const { WebGPUEngine } = await import(\"../gpu/index.js\");\n\n // Resolve the HF repo — for built-in models, use the standard HF repo\n // (not the ONNX repo) since the native engine loads safetensors directly\n let hfRepo = source.path;\n // If it points to an ONNX repo, map to the standard repo\n if (hfRepo.includes(\"onnx-community/\") || hfRepo.includes(\"-ONNX\")) {\n const nativeRepoMap: Record<string, string> = {\n \"onnx-community/Qwen3.5-0.8B-ONNX\": \"Qwen/Qwen3.5-0.8B\",\n \"onnx-community/Qwen3-0.6B-ONNX\": \"Qwen/Qwen3-0.6B\",\n \"onnx-community/Qwen3-1.7B-ONNX\": \"Qwen/Qwen3-1.7B\",\n \"onnx-community/Qwen3.5-2B-ONNX\": \"Qwen/Qwen3.5-2B\",\n };\n hfRepo = nativeRepoMap[hfRepo] || hfRepo;\n }\n\n // Map user dtype to GPU engine dtype (\"q4\" for INT4, \"f32\" default)\n const gpuDtype = userDtype === \"q4\" ? (\"q4\" as const) : undefined;\n\n this.webgpuEngine = await WebGPUEngine.create({\n repo: hfRepo,\n maxSeqLen: options.contextLength ?? config.contextLength ?? 4096,\n dtype: gpuDtype,\n onProgress: (loaded, total, message) => {\n onProgress?.({\n status: message,\n progress: total > 0 ? Math.round((loaded / total) * 100) : undefined,\n });\n },\n });\n\n this._deviceMode = \"webgpu\";\n this.isVisionModel = false;\n this.currentModel = modelId;\n this.modelConfig = config;\n onProgress?.({ status: \"Ready (WebGPU Native)!\" });\n\n // Report successful model load\n if (this.telemetry.onModelLoad) {\n try {\n this.telemetry.onModelLoad({\n modelId,\n loadTimeMs: performance.now() - loadStartTime,\n fromCache: false, // TODO: detect if loaded from cache\n device: this._deviceMode,\n success: true,\n });\n } catch {\n // Never throw from telemetry\n }\n }\n } catch (err) {\n // No CPU/ONNX fallback — surface the load failure.\n this.reportError(err instanceof Error ? err : new Error(String(err)), {\n operation: \"load\",\n modelId,\n });\n if (this.telemetry.onModelLoad) {\n try {\n this.telemetry.onModelLoad({\n modelId,\n loadTimeMs: performance.now() - loadStartTime,\n fromCache: false,\n device: this._deviceMode,\n success: false,\n error: err instanceof Error ? err.message : String(err),\n });\n } catch {\n // Never throw from telemetry\n }\n }\n throw err;\n }\n }\n\n /**\n * Load a vision model (VLM) on the native WebGPU engine.\n * The native engine loads the vision-capable safetensors checkpoint directly\n * and builds its ViT tower on demand (enableVision: true). describeImage() then\n * runs encode → splice → decode entirely in WebGPU compute.\n */\n private async loadVisionModel(\n modelId: string,\n repoPath: string,\n config: ModelConfig,\n options: LoadOptions = {},\n ): Promise<void> {\n const { onProgress, device = \"auto\" } = options;\n\n onProgress?.({ status: `Loading ${modelId} (vision model)...` });\n\n // The native WebGPU engine is the only backend. CPU/WASM/ONNX are unsupported.\n if ((device as string) === \"cpu\" || (device as string) === \"gpu\") {\n throw new Error(\n 'Gerbil vision models require WebGPU. CPU/WASM and the legacy ONNX backend have been removed; use device \"webgpu\" or \"auto\".',\n );\n }\n\n onProgress?.({ status: \"Initializing WebGPU vision engine...\" });\n const { WebGPUEngine } = await import(\"../gpu/index.js\");\n // Map an ONNX vision repo to its standard HF checkpoint when possible;\n // otherwise honor the configured path (defaults resolve inside create()).\n let visRepo = repoPath;\n if (visRepo.includes(\"onnx-community/\") || visRepo.includes(\"-ONNX\")) {\n const map: Record<string, string> = {\n \"onnx-community/Qwen3.5-0.8B-ONNX\": \"Qwen/Qwen3.5-0.8B\",\n };\n visRepo = map[visRepo] || visRepo;\n }\n this.webgpuEngine = await WebGPUEngine.create({\n repo: visRepo,\n enableVision: true,\n maxSeqLen: options.contextLength ?? config.contextLength ?? 4096,\n onProgress: (loaded, total, message) =>\n onProgress?.({\n status: message,\n progress: total > 0 ? Math.round((loaded / total) * 100) : undefined,\n }),\n });\n this._deviceMode = \"webgpu\";\n this.isVisionModel = true;\n this.currentModel = modelId;\n this.modelConfig = config;\n onProgress?.({ status: \"Ready (Vision, WebGPU Native)!\" });\n }\n\n /**\n * Check if a model is loaded\n */\n isLoaded(): boolean {\n return this.webgpuEngine !== null;\n }\n\n /**\n * Check if current model supports vision\n */\n supportsVision(): boolean {\n return this.isVisionModel && this.modelConfig?.supportsVision === true;\n }\n\n /**\n * Get current model info\n */\n getModelInfo(): ModelConfig | null {\n return this.modelConfig;\n }\n\n /**\n * Get current device mode (webgpu, cpu, or wasm)\n */\n getDeviceMode(): \"webgpu\" | \"cpu\" | \"wasm\" {\n return this._deviceMode;\n }\n\n /**\n * Get the in-memory weight quantization the native engine uses for the loaded\n * model. The WebGPU engine quantizes weights to INT4 (\"q4\") on load; the KV\n * cache precision (f16/f32) is separate and device-detected.\n */\n getDtype(): string {\n return \"q4\";\n }\n\n /**\n * Get response cache statistics\n */\n getResponseCacheStats(): { hits: number; misses: number; size: number; hitRate: number } {\n const cache = getGlobalCache();\n const stats = cache.getStats();\n return {\n hits: stats.hits,\n misses: stats.misses,\n size: stats.size,\n hitRate: cache.getHitRate(),\n };\n }\n\n /**\n * Clear the response cache (for cached generate() results)\n */\n clearResponseCache(): void {\n getGlobalCache().clear();\n }\n\n // ============================================\n // Model Preloading & Cache Checking\n // ============================================\n\n /**\n * Check if a model is cached (downloaded) without loading it\n *\n * @example\n * ```ts\n * if (await g.isModelCached(\"qwen3.5-0.8b\")) {\n * console.log(\"Model ready, will load instantly\");\n * } else {\n * console.log(\"Model needs to download (~400MB)\");\n * }\n * ```\n */\n async isModelCached(modelId: string): Promise<boolean> {\n const source = resolveModel(modelId);\n return this.isNativeRepoCached(source.path);\n }\n\n /**\n * Check whether the native WebGPU engine has a repo cached on disk.\n * The native loader stores files under ~/.cache/gerbil/<repo>/<revision>/.\n */\n private isNativeRepoCached(repo: string, revision = \"main\"): boolean {\n try {\n const home = process.env.HOME || process.env.USERPROFILE || os.homedir();\n if (!home) {\n return false;\n }\n const modelDir = path.join(home, \".cache\", \"gerbil\", repo.replace(/\\//g, \"_\"), revision);\n // config.json is the first file the native loader writes for any repo.\n return existsSync(path.join(modelDir, \"config.json\".replace(/\\//g, \"_\")));\n } catch {\n return false;\n }\n }\n\n /**\n * Preload a model (download without initializing for inference)\n *\n * Use this to download models ahead of time, e.g., during app startup,\n * so users don't wait when they first use AI.\n *\n * @example\n * ```ts\n * // Preload for later (download only, free memory)\n * await g.preloadModel(\"qwen3.5-0.8b\", {\n * onProgress: (p) => console.log(p.status, p.progress),\n * });\n *\n * // Preload and keep in memory for instant use\n * await g.preloadModel(\"qwen3.5-0.8b\", { keepLoaded: true });\n * await g.generate(\"Hello\"); // Instant, no loading needed\n * ```\n */\n async preloadModel(modelId: string, options: PreloadOptions = {}): Promise<void> {\n const source = resolveModel(modelId);\n const { onProgress, keepLoaded = false } = options;\n\n // Already loaded? Nothing to do\n if (keepLoaded && this.isLoaded() && this.currentModel === modelId) {\n onProgress?.({ status: \"Model already loaded\" });\n return;\n }\n\n // Already cached and not keeping loaded? Nothing to do\n if (!keepLoaded && (await this.isModelCached(modelId))) {\n onProgress?.({ status: \"Model already cached\" });\n return;\n }\n\n // If keepLoaded, use the regular loadModel path\n if (keepLoaded) {\n await this.loadModel(modelId, { onProgress });\n return;\n }\n\n onProgress?.({ status: `Preloading ${modelId}...` });\n\n // The native engine has no \"download-only\" mode, so load it (which fetches\n // and caches all weight files to ~/.cache/gerbil) then dispose to free GPU\n // memory. The cached files remain on disk for an instant subsequent load.\n await this.loadModel(modelId, { onProgress });\n await this.dispose();\n\n onProgress?.({ status: \"Preload complete\" });\n }\n\n /**\n * Check if the native TTS model is cached. The native engine always uses the\n * Kani-TTS-2 checkpoint, so `modelId` is accepted for API compatibility only.\n */\n async isTTSCached(_modelId?: string): Promise<boolean> {\n const { DEFAULT_MODELS } = await import(\"../gpu/index.js\");\n return this.isNativeRepoCached(DEFAULT_MODELS.tts);\n }\n\n /**\n * Preload the native TTS model (downloads Kani-TTS-2 weights to disk cache).\n *\n * @param modelId - Accepted for API compatibility; native TTS uses Kani-TTS-2.\n * @param options.keepLoaded - Keep the engine in memory for instant use.\n */\n async preloadTTS(modelId?: string, options: PreloadOptions = {}): Promise<void> {\n const { onProgress, keepLoaded = false } = options;\n\n if (keepLoaded && this.isTTSLoaded()) {\n onProgress?.({ status: \"TTS model already loaded\" });\n return;\n }\n\n if (!keepLoaded && (await this.isTTSCached(modelId))) {\n onProgress?.({ status: \"TTS model already cached\" });\n return;\n }\n\n onProgress?.({ status: \"Preloading TTS model...\" });\n // Build the native TTS engine (downloads + caches weights).\n await this.ensureNativeTTSEngine();\n\n if (!keepLoaded && this.nativeTTSEngine) {\n try {\n this.nativeTTSEngine.destroy();\n } catch {\n // best-effort\n }\n this.nativeTTSEngine = null;\n }\n onProgress?.({ status: \"Preload complete\" });\n }\n\n /**\n * Check if the native STT model is cached. The native engine always uses the\n * Moonshine checkpoint, so `modelId` is accepted for API compatibility only.\n */\n async isSTTCached(_modelId?: string): Promise<boolean> {\n const { DEFAULT_MODELS } = await import(\"../gpu/index.js\");\n return this.isNativeRepoCached(DEFAULT_MODELS.stt);\n }\n\n /**\n * Preload the native STT model (downloads Moonshine weights to disk cache).\n *\n * @param modelId - Accepted for API compatibility; native STT uses Moonshine.\n * @param options.keepLoaded - Keep the engine in memory for instant use.\n */\n async preloadSTT(modelId?: string, options: PreloadOptions = {}): Promise<void> {\n const { onProgress, keepLoaded = false } = options;\n\n if (keepLoaded && this.isSTTLoaded()) {\n onProgress?.({ status: \"STT model already loaded\" });\n return;\n }\n\n if (!keepLoaded && (await this.isSTTCached(modelId))) {\n onProgress?.({ status: \"STT model already cached\" });\n return;\n }\n\n onProgress?.({ status: \"Preloading STT model...\" });\n // Build the native STT engine (downloads + caches weights).\n await this.ensureNativeSTT();\n\n if (!keepLoaded && this.nativeSTT) {\n try {\n this.nativeSTT.destroy?.();\n } catch {\n // best-effort\n }\n this.nativeSTT = null;\n }\n onProgress?.({ status: \"Preload complete\" });\n }\n\n /**\n * Check if a native embedding model is cached. Defaults to the native\n * EmbeddingGemma checkpoint when no repo is provided.\n */\n async isEmbeddingCached(modelId?: string): Promise<boolean> {\n const { DEFAULT_MODELS } = await import(\"../gpu/index.js\");\n return this.isNativeRepoCached(modelId || DEFAULT_MODELS.embedding);\n }\n\n /**\n * Preload a native embedding model (downloads weights to disk cache).\n *\n * @param modelId - Embedding repo (default: native EmbeddingGemma).\n * @param options.keepLoaded - Keep the engine in memory for instant use.\n */\n async preloadEmbedding(modelId?: string, options: PreloadOptions = {}): Promise<void> {\n const { onProgress, keepLoaded = false } = options;\n\n if (keepLoaded && this.nativeEmbedEngine) {\n onProgress?.({ status: \"Embedding model already loaded\" });\n return;\n }\n\n if (!keepLoaded && (await this.isEmbeddingCached(modelId))) {\n onProgress?.({ status: \"Embedding model already cached\" });\n return;\n }\n\n onProgress?.({ status: \"Preloading embedding model...\" });\n // Build the native embedding engine (downloads + caches weights).\n await this.ensureNativeEmbedEngine(modelId);\n\n if (!keepLoaded && this.nativeEmbedEngine) {\n try {\n this.nativeEmbedEngine.destroy();\n } catch {\n // best-effort\n }\n this.nativeEmbedEngine = null;\n this.nativeEmbedRepo = null;\n }\n onProgress?.({ status: \"Preload complete\" });\n }\n\n /**\n * Clear KV cache to free memory.\n * The native engine manages its own KV cache; this is a no-op kept for API\n * compatibility.\n */\n // biome-ignore lint/suspicious/useAwait: kept async for API compatibility\n async clearCache(): Promise<void> {\n // no-op\n }\n\n // ============================================\n // Text Generation\n // ============================================\n\n /**\n * Generate text (automatically routes to vision generation if images provided)\n *\n * @example\n * ```ts\n * // Text generation\n * const result = await g.generate(\"Hello!\");\n *\n * // Vision generation (with vision model)\n * const result = await g.generate(\"What's in this image?\", {\n * images: [{ source: \"https://example.com/cat.jpg\" }]\n * });\n * ```\n */\n async generate(prompt: string, options: GenerateOptions = {}): Promise<GenerateResult> {\n const queueStartTime = performance.now();\n\n try {\n // Queue the generation request (prevents GPU OOM under concurrent load)\n const result = await this.queue.add(async () => {\n const queueWaitTime = performance.now() - queueStartTime;\n\n // Report queue wait time if significant (>100ms)\n if (queueWaitTime > 100 && this.telemetry.onQueueWait) {\n try {\n this.telemetry.onQueueWait(queueWaitTime);\n } catch {\n // Never throw from telemetry\n }\n }\n\n const generatedResult = await this.generateInternal(prompt, options);\n\n // Report successful generation\n if (this.telemetry.onGenerate) {\n try {\n this.telemetry.onGenerate({\n modelId: this.currentModel || \"unknown\",\n result: generatedResult,\n cached: generatedResult.cached ?? false,\n queueTimeMs: queueWaitTime > 100 ? queueWaitTime : undefined,\n });\n } catch {\n // Never throw from telemetry\n }\n }\n\n return generatedResult;\n });\n\n return result as GenerateResult;\n } catch (error) {\n // Report errors to telemetry (including queue timeouts)\n if (this.telemetry.onError) {\n try {\n this.telemetry.onError(error instanceof Error ? error : new Error(String(error)), {\n method: \"generate\",\n modelId: this.currentModel || \"unknown\",\n prompt: prompt.slice(0, 100), // Truncate for privacy\n queueWaitTime: performance.now() - queueStartTime,\n });\n } catch {\n // Never throw from telemetry\n }\n }\n throw error;\n }\n }\n\n /**\n * Internal generate implementation (called within queue)\n */\n private async generateInternal(\n prompt: string,\n options: GenerateOptions = {},\n ): Promise<GenerateResult> {\n if (!this.isLoaded()) {\n // Auto-load default model\n await this.loadModel(this.config.model || DEFAULT_MODEL);\n }\n\n const { images } = options;\n\n // Route to native vision generation when images are provided and the loaded\n // model supports vision.\n if (images?.length && this.isVisionModel) {\n return this.generateWithVision(prompt, options);\n }\n\n const {\n maxTokens = 256,\n temperature = 0.7,\n topP = 0.9,\n topK = 50,\n thinking = false,\n system,\n cache = false,\n cacheTtl,\n } = options;\n\n // Check cache if enabled (skip for streaming/vision)\n if (cache && !options.onToken && !images?.length) {\n const cacheKey = generateCacheKey(prompt, this.currentModel || \"\", {\n maxTokens,\n temperature,\n topP,\n topK,\n system,\n thinking,\n });\n const cached = getGlobalCache().get(cacheKey);\n if (cached) {\n return cached;\n }\n }\n\n const startTime = performance.now();\n\n try {\n let rawText = \"\";\n // Real token counts come from the engine, not a length heuristic.\n let engineTokensGenerated = 0;\n let engineTokensPerSecond = 0;\n\n if (this.webgpuEngine) {\n // Native WebGPU engine — pure compute shaders, no ONNX\n const result = await this.webgpuEngine.generate(prompt, {\n maxTokens,\n sampling: {\n temperature,\n topP,\n topK,\n },\n systemPrompt: system,\n onToken: options.onToken ? (t: string) => options.onToken?.(t) : undefined,\n });\n rawText = result.text;\n engineTokensGenerated = result.tokensGenerated;\n engineTokensPerSecond = result.tokensPerSecond;\n } else {\n throw new Error(\"No model loaded\");\n }\n\n const endTime = performance.now();\n const totalTime = endTime - startTime;\n\n rawText = this.cleanOutput(rawText);\n\n // Always parse thinking to strip <think> tags from output\n // (model may generate them even without thinking mode enabled)\n const { thinking: thinkingText, response } = this.parseThinking(rawText);\n\n // Only include thinking in result if mode was enabled\n const finalThinking = thinking ? thinkingText : undefined;\n\n // Use the engine's true decoded-token count and throughput.\n const tokensGenerated = engineTokensGenerated;\n\n // Update stats\n this.stats.prompts += 1;\n this.stats.tokensOut += tokensGenerated;\n this.stats.totalTime += totalTime;\n this.stats.avgSpeed = (this.stats.tokensOut / this.stats.totalTime) * 1000;\n\n const result: GenerateResult = {\n text: response,\n thinking: finalThinking,\n tokensGenerated,\n tokensPerSecond: engineTokensPerSecond,\n totalTime,\n finishReason: \"stop\",\n provider: \"local\",\n cached: false,\n };\n\n // Store in cache if enabled\n if (cache && !options.onToken && !images?.length) {\n const cacheKey = generateCacheKey(prompt, this.currentModel || \"\", {\n maxTokens,\n temperature,\n topP,\n topK,\n system,\n thinking,\n });\n getGlobalCache().set(cacheKey, result, cacheTtl);\n }\n\n return result;\n } catch (error) {\n // Report error to telemetry\n this.reportError(error instanceof Error ? error : new Error(String(error)), {\n operation: \"generate\",\n modelId: this.currentModel || undefined,\n });\n\n return {\n text: \"\",\n tokensGenerated: 0,\n tokensPerSecond: 0,\n totalTime: performance.now() - startTime,\n finishReason: \"error\",\n provider: \"local\",\n cached: false,\n };\n }\n }\n\n /**\n * Stream text generation (simulated token-by-token)\n *\n * Note: Yields the raw output including <think> tags if thinking mode is enabled.\n * The final result has parsed thinking separated out.\n */\n async *stream(\n prompt: string,\n options: GenerateOptions = {},\n ): AsyncGenerator<string, GenerateResult, unknown> {\n if (!this.isLoaded()) {\n await this.loadModel(this.config.model || DEFAULT_MODEL);\n }\n\n const startTime = performance.now();\n\n // For native WebGPU engine, use real streaming via onToken callback\n if (this.webgpuEngine) {\n let fullText = \"\";\n const tokenQueue: string[] = [];\n let resolveNext: ((value: string | null) => void) | null = null;\n let done = false;\n // Capture the engine's true token count / throughput for the final result.\n let engineTokensGenerated = 0;\n let engineTokensPerSecond = 0;\n\n const generatePromise = this.webgpuEngine\n .generate(prompt, {\n ...options,\n sampling: {\n temperature: options.temperature,\n topP: options.topP,\n topK: options.topK,\n },\n systemPrompt: options.system,\n onToken: (token: string) => {\n fullText += token;\n if (resolveNext) {\n resolveNext(token);\n resolveNext = null;\n } else {\n tokenQueue.push(token);\n }\n },\n })\n .then((result: { tokensGenerated: number; tokensPerSecond: number }) => {\n engineTokensGenerated = result.tokensGenerated;\n engineTokensPerSecond = result.tokensPerSecond;\n done = true;\n if (resolveNext) {\n resolveNext(null);\n }\n })\n .catch((err: Error) => {\n done = true;\n if (resolveNext) {\n resolveNext(null);\n }\n throw err;\n });\n\n while (!done || tokenQueue.length > 0) {\n if (tokenQueue.length > 0) {\n const token = tokenQueue.shift()!;\n yield token;\n options.onToken?.(token);\n } else if (!done) {\n const token = await new Promise<string | null>((resolve) => {\n resolveNext = resolve;\n });\n if (token) {\n yield token;\n options.onToken?.(token);\n }\n }\n }\n\n await generatePromise;\n\n const { thinking: thinkingText, response } = this.parseThinking(fullText);\n const totalTime = performance.now() - startTime;\n\n return {\n text: response,\n thinking: options.thinking ? thinkingText : undefined,\n tokensGenerated: engineTokensGenerated,\n totalTime,\n tokensPerSecond: engineTokensPerSecond,\n finishReason: \"stop\" as const,\n };\n }\n\n // The native WebGPU engine is the only backend. If we reach here, no model\n // is loaded (auto-load happens at the top of stream()).\n throw new Error(\"No model loaded\");\n }\n\n // ============================================\n // Vision Generation\n // ============================================\n\n /**\n * Generate text from images using a vision model\n * Called automatically by generate() when images are provided\n */\n private async generateWithVision(\n prompt: string,\n options: GenerateOptions,\n ): Promise<GenerateResult> {\n // ── Native WebGPU vision ──\n // loadModel() brought up the native vision engine (this.webgpuEngine has its\n // ViT tower built via enableVision: true). Decode the image to RGB pixels and\n // run the native encode → splice → decode path (engine.describeImage). The\n // native engine currently supports a single image per request.\n if (!(this.webgpuEngine && typeof this.webgpuEngine.describeImage === \"function\")) {\n throw new Error(\n \"Vision model not loaded. Load a vision-capable model with device 'webgpu' first.\",\n );\n }\n\n const imgs = options.images ?? [];\n if (imgs.length !== 1) {\n throw new Error(\n `Native WebGPU vision supports exactly one image per request (got ${imgs.length}).`,\n );\n }\n\n const startTime = performance.now();\n const { pixels, width, height } = await this.decodeImageToPixels(imgs[0].source);\n const result = await this.webgpuEngine.describeImage({ pixels, width, height }, prompt, {\n maxTokens: options.maxTokens ?? 512,\n sampling: {\n temperature: options.temperature ?? 0.7,\n topP: options.topP ?? 0.9,\n topK: options.topK ?? 20,\n },\n onToken: options.onToken ? (t: string) => options.onToken?.(t) : undefined,\n });\n const totalTime = performance.now() - startTime;\n\n // Update stats\n this.stats.prompts += 1;\n this.stats.tokensOut += result.tokensGenerated;\n this.stats.totalTime += totalTime;\n this.stats.avgSpeed = (this.stats.tokensOut / this.stats.totalTime) * 1000;\n\n return {\n text: this.cleanOutput(result.text),\n tokensGenerated: result.tokensGenerated,\n tokensPerSecond: result.tokensPerSecond,\n totalTime,\n finishReason: \"stop\",\n provider: \"local\",\n cached: false,\n };\n }\n\n /**\n * Decode an image source (http(s) URL, file path, or data URI) to raw RGB\n * pixels for the native vision encoder. Supports 8-bit non-interlaced PNG\n * (color types 2/RGB and 6/RGBA). Other formats throw a clear error — callers\n * can pre-decode and use the lower-level WebGPUEngine.describeImage() with\n * pixels directly.\n */\n private async decodeImageToPixels(\n source: string,\n ): Promise<{ pixels: Uint8Array; width: number; height: number }> {\n const bytes = await this.fetchImageBytes(source);\n // PNG magic number: 89 50 4E 47 0D 0A 1A 0A\n const isPng =\n bytes.length > 8 &&\n bytes[0] === 0x89 &&\n bytes[1] === 0x50 &&\n bytes[2] === 0x4e &&\n bytes[3] === 0x47;\n if (!isPng) {\n throw new Error(\n \"Native vision currently decodes PNG images only. For other formats, pre-decode \" +\n \"to RGB pixels and call the GPU engine's describeImage() directly.\",\n );\n }\n return decodePng(bytes);\n }\n\n /** Fetch an image source to raw bytes (URL, data URI, or local file path). */\n private async fetchImageBytes(source: string): Promise<Uint8Array> {\n if (source.startsWith(\"data:\")) {\n const comma = source.indexOf(\",\");\n const meta = source.slice(5, comma);\n const data = source.slice(comma + 1);\n if (meta.includes(\"base64\")) {\n return Uint8Array.from(Buffer.from(data, \"base64\"));\n }\n return Uint8Array.from(Buffer.from(decodeURIComponent(data), \"binary\"));\n }\n if (source.startsWith(\"http://\") || source.startsWith(\"https://\")) {\n const res = await fetch(source);\n if (!res.ok) {\n throw new Error(`Failed to fetch image (${res.status}): ${source}`);\n }\n return new Uint8Array(await res.arrayBuffer());\n }\n // Local file path (Node.js only)\n const { readFile } = await import(\"node:fs/promises\");\n return new Uint8Array(await readFile(source));\n }\n\n // ============================================\n // Structured Output (JSON)\n // ============================================\n\n /**\n * Generate structured JSON output\n */\n async json<T>(prompt: string, options: JsonOptions<T>): Promise<T> {\n const { schema, retries = 3, temperature = 0.3 } = options;\n\n const systemPrompt = `You are a JSON generator. You MUST respond with valid JSON only.\nNo explanations, no markdown, no code blocks. Just pure JSON.\nThe JSON must conform to this schema: ${JSON.stringify(zodToJsonSchema(schema))}`;\n\n for (let attempt = 0; attempt < retries; attempt += 1) {\n const result = await this.generate(prompt, {\n system: options.system || systemPrompt,\n temperature,\n maxTokens: 1000,\n });\n\n try {\n // Try to extract JSON from response\n const jsonStr = extractJson(result.text);\n const parsed = JSON.parse(jsonStr);\n const validated = schema.parse(parsed);\n return validated;\n } catch (error) {\n if (attempt === retries - 1) {\n throw new Error(`Failed to generate valid JSON after ${retries} attempts: ${error}`);\n }\n }\n }\n\n throw new Error(\"Failed to generate valid JSON\");\n }\n\n /**\n * Generate a structured object via the native engine's retrying\n * `generateObject` (extract JSON → validate → retry with a nudge).\n *\n * Unlike {@link json} (which is Zod-driven), this passes through to the engine\n * and accepts either a predicate validator `(o) => boolean` or a minimal\n * `{ required: [...] }` schema; omit `schema` to accept any valid JSON.\n *\n * @example\n * ```ts\n * const { object } = await g.generateObject<{ name: string; age: number }>(\n * 'Extract {name, age} from: \"I am Sarah, 28\"',\n * { schema: { required: [\"name\", \"age\"] } },\n * );\n * ```\n */\n async generateObject<T = unknown>(\n prompt: string,\n options: GenerateObjectOptions = {},\n ): Promise<GenerateObjectResult<T>> {\n if (!this.isLoaded()) {\n await this.loadModel(this.config.model || DEFAULT_MODEL);\n }\n if (!this.webgpuEngine) {\n throw new Error(\"No model loaded\");\n }\n return this.webgpuEngine.generateObject(prompt, options) as Promise<GenerateObjectResult<T>>;\n }\n\n // ============================================\n // Embeddings\n // ============================================\n\n /**\n * Generate embeddings\n */\n async embed(text: string, options: EmbedOptions = {}): Promise<EmbedResult> {\n // ── Native WebGPU embedding ──\n // The active text model may not be an embedder, so embeddings run on a\n // dedicated native engine loaded with { embedding: true }. It is created\n // lazily on first embed(), keyed by repo: if the caller asks for a different\n // embedding model than the cached one, the old engine is torn down and a new\n // one is built. The engine is owned by this Gerbil instance and released in\n // dispose(). Native engine.embed() returns an L2-normalized Float32Array.\n if (!this.preferNative()) {\n throw new Error(\n \"Embeddings require WebGPU. CPU/WASM and the legacy ONNX backend have been removed.\",\n );\n }\n\n const native = await this.ensureNativeEmbedEngine(options.model);\n const startTime = performance.now();\n const vec = await native.embed(text);\n return {\n vector: Array.from(vec as Float32Array),\n text,\n totalTime: performance.now() - startTime,\n };\n }\n\n /**\n * Lazily build (or reuse) the native embedding engine. Re-creates it when the\n * requested repo differs from the cached one. The default native embedding\n * model is resolved by the engine itself (EmbeddingGemma) when no repo given.\n */\n private async ensureNativeEmbedEngine(repo?: string): Promise<any> {\n if (this.nativeEmbedEngine && (!repo || repo === this.nativeEmbedRepo)) {\n return this.nativeEmbedEngine;\n }\n if (this.nativeEmbedEngine) {\n // Switching embedding models — release the previous native engine.\n try {\n this.nativeEmbedEngine.destroy();\n } catch {\n // best-effort cleanup\n }\n this.nativeEmbedEngine = null;\n this.nativeEmbedRepo = null;\n }\n const { WebGPUEngine } = await import(\"../gpu/index.js\");\n this.nativeEmbedEngine = await WebGPUEngine.create({ repo, embedding: true });\n this.nativeEmbedRepo = repo ?? null;\n return this.nativeEmbedEngine;\n }\n\n /**\n * Generate embeddings for multiple texts\n */\n async embedBatch(texts: string[], options: EmbedOptions = {}): Promise<EmbedResult[]> {\n const results: EmbedResult[] = [];\n for (const text of texts) {\n results.push(await this.embed(text, options));\n }\n return results;\n }\n\n /**\n * Compute cosine similarity between two vectors\n *\n * @example\n * ```ts\n * const sim = g.cosineSimilarity([1, 0, 0], [1, 0, 0]); // 1.0\n * const sim2 = g.cosineSimilarity([1, 0, 0], [0, 1, 0]); // 0.0\n * ```\n */\n cosineSimilarity(a: number[], b: number[]): number {\n if (a.length !== b.length) {\n throw new Error(`Vector dimensions must match: ${a.length} vs ${b.length}`);\n }\n\n let dotProduct = 0;\n let normA = 0;\n let normB = 0;\n\n for (let i = 0; i < a.length; i++) {\n dotProduct += a[i] * b[i];\n normA += a[i] * a[i];\n normB += b[i] * b[i];\n }\n\n const magnitude = Math.sqrt(normA) * Math.sqrt(normB);\n if (magnitude === 0) return 0;\n\n return dotProduct / magnitude;\n }\n\n /**\n * Compare similarity between two texts\n *\n * @example\n * ```ts\n * const result = await g.similarity(\"Hello world\", \"Hi there\");\n * console.log(result.score); // 0.85\n * ```\n */\n async similarity(\n textA: string,\n textB: string,\n options: EmbedOptions = {},\n ): Promise<import(\"./types.js\").SimilarityResult> {\n const startTime = performance.now();\n\n const [embedA, embedB] = await Promise.all([\n this.embed(textA, options),\n this.embed(textB, options),\n ]);\n\n const score = this.cosineSimilarity(embedA.vector, embedB.vector);\n\n return {\n score,\n textA,\n textB,\n totalTime: performance.now() - startTime,\n };\n }\n\n /**\n * Semantic search - find most similar texts from a corpus\n *\n * @example\n * ```ts\n * const results = await g.search(\"capital of France\", [\n * \"Paris is beautiful\",\n * \"London is in England\",\n * \"Dogs are pets\"\n * ]);\n * // [{ text: \"Paris is beautiful\", score: 0.89, index: 0 }, ...]\n * ```\n */\n async search(\n query: string,\n corpus: string[],\n options: EmbedOptions & { topK?: number } = {},\n ): Promise<import(\"./types.js\").SearchResult[]> {\n const { topK = corpus.length, ...embedOptions } = options;\n\n // Embed query and all documents\n const queryEmbedding = await this.embed(query, embedOptions);\n const corpusEmbeddings = await this.embedBatch(corpus, embedOptions);\n\n // Calculate similarities\n const results: import(\"./types.js\").SearchResult[] = corpusEmbeddings.map((doc, index) => ({\n text: doc.text,\n score: this.cosineSimilarity(queryEmbedding.vector, doc.vector),\n index,\n }));\n\n // Sort by score descending and take top K\n return results.sort((a, b) => b.score - a.score).slice(0, topK);\n }\n\n /**\n * Find the nearest text to an embedding vector\n *\n * @example\n * ```ts\n * const embedding = (await g.embed(\"dog\")).vector;\n * const match = await g.findNearest(embedding, [\"cat\", \"car\", \"tree\"]);\n * // { text: \"cat\", score: 0.85, index: 0 }\n * ```\n */\n async findNearest(\n embedding: number[],\n candidates: string[],\n options: EmbedOptions & { topK?: number } = {},\n ): Promise<import(\"./types.js\").SearchResult[]> {\n const { topK = candidates.length, ...embedOptions } = options;\n\n // Embed all candidates\n const candidateEmbeddings = await this.embedBatch(candidates, embedOptions);\n\n // Calculate similarities\n const results: import(\"./types.js\").SearchResult[] = candidateEmbeddings.map((doc, index) => ({\n text: doc.text,\n score: this.cosineSimilarity(embedding, doc.vector),\n index,\n }));\n\n // Sort by score descending and take top K\n return results.sort((a, b) => b.score - a.score).slice(0, topK);\n }\n\n // ============================================\n // Stats & Info\n // ============================================\n\n /**\n * Get session stats\n */\n getStats(): SessionStats {\n return { ...this.stats };\n }\n\n /**\n * Get system info\n */\n getInfo(): SystemInfo {\n return {\n version: \"1.0.0\",\n model: this.modelConfig,\n device: {\n backend: \"webgpu-native\",\n gpu: null, // TODO: surface Dawn adapter info\n vram: null,\n status: this.isLoaded() ? \"ready\" : \"loading\",\n },\n context: {\n max: this.modelConfig?.contextLength || 0,\n used: 0,\n available: this.modelConfig?.contextLength || 0,\n },\n cache: {\n location: \"~/.cache/gerbil\",\n size: \"0 MB\",\n modelCount: 0,\n },\n };\n }\n\n /**\n * Reset stats\n */\n resetStats(): void {\n this.stats = {\n prompts: 0,\n tokensIn: 0,\n tokensOut: 0,\n avgSpeed: 0,\n totalTime: 0,\n cacheHits: 0,\n cacheMisses: 0,\n };\n }\n\n // ============================================\n // Text-to-Speech (TTS)\n // ============================================\n\n private readonly ttsModelId = \"kani-tts-2\";\n\n /**\n * Load the native TTS model (Kani-TTS-2) for text-to-speech synthesis.\n *\n * @example\n * ```ts\n * await g.loadTTS({ onProgress: (p) => console.log(p.status) });\n * const result = await g.speak(\"Hello world\");\n * // result.audio = Float32Array PCM, result.sampleRate = 22050\n * ```\n */\n // biome-ignore lint/suspicious/useAwait: native TTS is built lazily by ensureNativeTTSEngine\n async loadTTS(_options: LoadTTSOptions & { model?: string } = {}): Promise<void> {\n await this.ensureNativeTTSEngine();\n }\n\n /**\n * Ensure TTS model is loaded (lazy loading)\n */\n async ensureTTSLoaded(_options?: LoadTTSOptions): Promise<void> {\n await this.ensureNativeTTSEngine();\n }\n\n /**\n * Generate speech from text using the native Kani-TTS-2 WebGPU engine.\n *\n * @example\n * ```ts\n * const result = await g.speak(\"Hello world\");\n * // result.audio = Float32Array PCM, result.sampleRate = 22050\n * ```\n */\n async speak(text: string, options: SpeakOptions = {}): Promise<SpeakResult> {\n // ── Native WebGPU TTS ──\n // Native speech runs through a dedicated WebGPUEngine loaded with the\n // Kani-TTS-2 checkpoint; engine.speak() returns { pcm, sampleRate,\n // audioSeconds }. The engine is lazily built on first speak() and owned by\n // this instance (released in dispose()).\n if (!this.preferNative()) {\n throw new Error(\n \"Speech synthesis requires WebGPU. CPU/WASM and the legacy ONNX backend have been removed.\",\n );\n }\n\n const native = await this.ensureNativeTTSEngine();\n const startTime = performance.now();\n const out = await native.speak(text, {});\n return {\n audio: out.pcm,\n sampleRate: out.sampleRate,\n duration: out.audioSeconds,\n voice: options.voice ?? \"default\",\n totalTime: performance.now() - startTime,\n };\n }\n\n /** Lazily build (or reuse) the native Kani-TTS WebGPUEngine (default repo). */\n private async ensureNativeTTSEngine(): Promise<any> {\n if (!this.nativeTTSEngine) {\n const { WebGPUEngine, DEFAULT_MODELS } = await import(\"../gpu/index.js\");\n this.nativeTTSEngine = await WebGPUEngine.create({ repo: DEFAULT_MODELS.tts });\n }\n return this.nativeTTSEngine;\n }\n\n /**\n * Stream speech generation. The native engine synthesizes the full clip, so a\n * single final audio chunk is yielded.\n */\n async *speakStream(\n text: string,\n options: SpeakOptions = {},\n ): AsyncGenerator<AudioChunk, SpeakResult, unknown> {\n const result = await this.speak(text, options);\n yield {\n samples: result.audio,\n sampleRate: result.sampleRate,\n index: 0,\n isFinal: true,\n };\n return result;\n }\n\n /**\n * Get list of available TTS voices (native Kani-TTS-2 default voice).\n */\n listVoices(): VoiceInfo[] {\n return KOKORO_VOICES_DEFAULT;\n }\n\n /**\n * Check if TTS model is loaded\n */\n isTTSLoaded(): boolean {\n return this.nativeTTSEngine !== null;\n }\n\n /**\n * Get current TTS model info\n */\n getTTSModelInfo(): { id: string; loaded: boolean; device?: \"webgpu\" | \"cpu\" } | null {\n if (!this.nativeTTSEngine) {\n return null;\n }\n return {\n id: this.ttsModelId,\n loaded: true,\n device: \"webgpu\",\n };\n }\n\n /**\n * List available TTS models (native Kani-TTS-2).\n */\n // biome-ignore lint/suspicious/useAwait: async kept for API compatibility\n async listTTSModels(): Promise<\n Array<{ id: string; description: string; sampleRate: number; voiceCount: number }>\n > {\n return [\n {\n id: this.ttsModelId,\n description: \"Kani-TTS-2 native WebGPU TTS\",\n sampleRate: 22_050,\n voiceCount: 1,\n },\n ];\n }\n\n // ============================================\n // Speech-to-Text (STT)\n // ============================================\n\n /**\n * Load the native STT model (Moonshine) for speech-to-text transcription.\n *\n * @example\n * ```ts\n * await g.loadSTT();\n * const result = await g.transcribe(audioData);\n * console.log(result.text);\n * ```\n */\n // biome-ignore lint/suspicious/useAwait: native STT is built lazily by ensureNativeSTT\n async loadSTT(_modelId?: string, _options: LoadSTTOptions = {}): Promise<void> {\n // Native STT uses the Moonshine checkpoint, built lazily on first transcribe().\n await this.ensureNativeSTT();\n }\n\n /**\n * Ensure STT model is loaded (lazy loading)\n */\n public async ensureSTTLoaded(_modelId?: string, _options?: LoadSTTOptions): Promise<void> {\n await this.ensureNativeSTT();\n }\n\n /**\n * Transcribe audio to text\n *\n * @param audio - Audio data as Float32Array (16kHz mono) or Uint8Array (WAV file)\n * @param options - Transcription options\n *\n * @example\n * ```ts\n * // From Float32Array (16kHz mono)\n * const result = await g.transcribe(audioData);\n * console.log(result.text);\n *\n * // With timestamps\n * const result = await g.transcribe(audioData, { timestamps: true });\n * for (const seg of result.segments) {\n * console.log(`[${seg.start}s] ${seg.text}`);\n * }\n *\n * // From WAV file\n * const wavData = fs.readFileSync(\"audio.wav\");\n * const result = await g.transcribe(new Uint8Array(wavData));\n * ```\n */\n async transcribe(\n audio: Float32Array | Uint8Array,\n options: TranscribeOptions = {},\n ): Promise<TranscribeResult> {\n // ── Native WebGPU STT ──\n // MoonshineSTT consumes raw 16 kHz mono Float32 PCM. A Uint8Array WAV\n // container cannot be decoded natively, and timestamps are not produced by\n // the native path — both are unsupported now that the ONNX backend is gone.\n if (!this.preferNative()) {\n throw new Error(\n \"Transcription requires WebGPU. CPU/WASM and the legacy ONNX backend have been removed.\",\n );\n }\n if (!(audio instanceof Float32Array)) {\n throw new Error(\n \"Native transcription requires 16 kHz mono Float32Array PCM. Decode WAV bytes to PCM first.\",\n );\n }\n if (options.timestamps) {\n throw new Error(\"Native transcription does not produce timestamps.\");\n }\n\n const native = await this.ensureNativeSTT();\n const startTime = performance.now();\n const out = await native.transcribe(audio);\n return {\n text: out.text,\n language: options.language ?? \"en\",\n duration: out.audioSeconds,\n totalTime: performance.now() - startTime,\n };\n }\n\n /** Lazily build (or reuse) the native MoonshineSTT engine (default repo). */\n private async ensureNativeSTT(): Promise<any> {\n if (!this.nativeSTT) {\n const { MoonshineSTT } = await import(\"../gpu/moonshine-stt.js\");\n this.nativeSTT = await MoonshineSTT.create();\n }\n return this.nativeSTT;\n }\n\n /**\n * Create a streaming transcription session\n *\n * Transcribes audio in real-time by processing chunks at regular intervals.\n * Perfect for live captioning, call transcription, or real-time subtitles.\n *\n * @param options - Streaming options\n * @returns Streaming session controller\n *\n * @example\n * ```ts\n * const session = await g.createStreamingTranscription({\n * chunkDuration: 3000, // Transcribe every 3 seconds\n * onChunk: (text, idx) => console.log(`Chunk ${idx}: ${text}`),\n * onTranscript: (fullText) => console.log(\"Full:\", fullText),\n * });\n *\n * // Feed audio data as it comes in\n * session.feedAudio(audioChunk);\n *\n * // Start automatic interval-based transcription\n * session.start();\n *\n * // Later, stop and get final transcript\n * const finalText = await session.stop();\n * ```\n */\n // biome-ignore lint/suspicious/useAwait: async kept for API compatibility\n async createStreamingTranscription(\n _options: StreamingTranscriptionOptions = {},\n ): Promise<StreamingTranscriptionSession> {\n throw new Error(\n \"Streaming transcription is not supported by the native WebGPU STT engine. \" +\n \"Use transcribe() on buffered 16 kHz Float32Array PCM instead.\",\n );\n }\n\n /**\n * Get list of available STT models (native Moonshine).\n */\n // biome-ignore lint/suspicious/useAwait: async kept for API compatibility\n async listSTTModels(): Promise<STTModelConfig[]> {\n return [\n {\n id: \"moonshine-base\",\n repo: \"UsefulSensors/moonshine-base\",\n description: \"Moonshine native WebGPU STT\",\n size: \"61M\",\n multilingual: false,\n languages: [\"en\"],\n sampleRate: 16000,\n },\n ];\n }\n\n /**\n * Check if STT model is loaded\n */\n isSTTLoaded(): boolean {\n return this.nativeSTT !== null;\n }\n\n /**\n * Get current STT model info\n */\n getSTTModelInfo(): { id: string; loaded: boolean; device?: \"webgpu\" | \"cpu\" } | null {\n if (!this.nativeSTT) {\n return null;\n }\n return {\n id: \"moonshine-base\",\n loaded: true,\n device: \"webgpu\",\n };\n }\n\n // ============================================\n // Microphone Input\n // ============================================\n\n /**\n * Record audio from microphone and transcribe\n *\n * @example\n * ```ts\n * // Record for 5 seconds and transcribe\n * const result = await g.listen(5000);\n * console.log(result.text);\n *\n * // Use with voice chat\n * const userInput = await g.listen(10000);\n * const response = await g.generate(userInput.text);\n * await g.speak(response.text);\n * ```\n */\n async listen(\n durationMs: number = 5000,\n options: { onProgress?: (status: string) => void } = {},\n ): Promise<TranscribeResult> {\n // Dynamic import for microphone (avoids bundling when not used)\n const { Microphone, isSoxAvailable } = await import(\"./microphone.js\");\n\n if (!isSoxAvailable()) {\n throw new Error(\n \"Microphone recording requires SoX. Install with:\\n\" +\n \" macOS: brew install sox\\n\" +\n \" Ubuntu: sudo apt install sox\\n\" +\n \" Windows: https://sox.sourceforge.net/\",\n );\n }\n\n options.onProgress?.(\"Starting microphone...\");\n\n const mic = new Microphone({ sampleRate: 16000 });\n await mic.start();\n\n options.onProgress?.(`Recording for ${(durationMs / 1000).toFixed(1)}s...`);\n\n // Wait for the specified duration\n await new Promise((r) => setTimeout(r, durationMs));\n\n options.onProgress?.(\"Processing audio...\");\n const { audio } = await mic.stop();\n\n options.onProgress?.(\"Transcribing...\");\n return this.transcribe(audio, {\n onProgress: (p) => options.onProgress?.(p.status || \"Transcribing...\"),\n });\n }\n\n /**\n * Check if microphone recording is available\n */\n async isMicrophoneAvailable(): Promise<boolean> {\n try {\n const { isSoxAvailable } = await import(\"./microphone.js\");\n return isSoxAvailable();\n } catch {\n return false;\n }\n }\n\n // ============================================\n // Cleanup\n // ============================================\n\n /**\n * Dispose of resources (releases all native WebGPU engines and their devices).\n * @param _disconnect Accepted for API compatibility; no longer used.\n */\n // biome-ignore lint/suspicious/useAwait: async kept for API compatibility\n async dispose(_disconnect = false): Promise<void> {\n // Clean up native WebGPU engine\n if (this.webgpuEngine) {\n try {\n this.webgpuEngine.destroy();\n } catch {\n // Ignore errors during cleanup\n }\n this.webgpuEngine = null;\n }\n\n // Clean up the lazily-created native capability engines (embed / STT / TTS).\n // These are independent of this.webgpuEngine (they back embed()/transcribe()/\n // speak() even when the active model isn't that capability), so they need\n // their own teardown. MoonshineSTT has no GPU device of its own to destroy\n // beyond its weight maps; WebGPUEngine.destroy() releases its device.\n if (this.nativeEmbedEngine) {\n try {\n this.nativeEmbedEngine.destroy();\n } catch {\n // Ignore errors during cleanup\n }\n this.nativeEmbedEngine = null;\n this.nativeEmbedRepo = null;\n }\n if (this.nativeSTT) {\n try {\n this.nativeSTT.destroy?.();\n } catch {\n // Ignore errors during cleanup\n }\n this.nativeSTT = null;\n }\n if (this.nativeTTSEngine) {\n try {\n this.nativeTTSEngine.destroy();\n } catch {\n // Ignore errors during cleanup\n }\n this.nativeTTSEngine = null;\n }\n\n this.currentModel = null;\n this.modelConfig = null;\n this.isVisionModel = false;\n }\n\n /**\n * @deprecated The shared Chrome backend was removed; this is now a no-op.\n */\n // biome-ignore lint/suspicious/useAwait: async kept for API compatibility\n static async shutdown(): Promise<void> {\n // no-op\n }\n\n // ============================================\n // Private Methods\n // ============================================\n\n private parseThinking(text: string): {\n thinking?: string;\n response: string;\n } {\n // Handle complete <think>...</think> blocks\n const match = text.match(/<think>([\\s\\S]*?)<\\/think>/);\n if (match) {\n const thinking = match[1].trim();\n const response = text.replace(/<think>[\\s\\S]*?<\\/think>/, \"\").trim();\n return { thinking, response };\n }\n\n // Handle unclosed <think> tags (model stopped mid-thought)\n const unclosedMatch = text.match(/<think>([\\s\\S]*)$/);\n if (unclosedMatch) {\n const thinking = unclosedMatch[1].trim();\n const response = text.replace(/<think>[\\s\\S]*$/, \"\").trim();\n return { thinking: thinking || undefined, response };\n }\n\n // Handle any remaining think tags\n const response = text.replace(/<\\/?think>/g, \"\").trim();\n return { response };\n }\n\n private cleanOutput(text: string): string {\n return (\n text\n .replace(/<\\|im_end\\|>/g, \"\")\n .replace(/<\\|im_start\\|>/g, \"\")\n .replace(/<\\|endoftext\\|>/g, \"\")\n .replace(/<\\/s>/g, \"\")\n // Clean up artifacts from direct model output\n .replace(/^\\/no_think\\s*/i, \"\")\n .replace(/^assistant\\s*/i, \"\")\n .replace(/^\\s*\\/no_think\\s*/gim, \"\")\n .replace(/^\\s*assistant\\s*/gim, \"\")\n // Clean up role markers that might appear\n .replace(/^(system|user|assistant):\\s*/gim, \"\")\n .trim()\n );\n }\n}\n\nexport default Gerbil;\n"],"mappings":"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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { A as SpeakResult, C as PreloadOptions, D as SessionStats, E as SearchResult, I as TranscribeOptions, L as TranscribeResult, M as StreamingTranscriptionSession, N as SystemInfo, O as SimilarityResult, T as STTModelConfig, _ as LoadSTTOptions, a as EmbedResult, d as GerbilConfig, g as LoadOptions, h as JsonOptions, i as EmbedOptions, j as StreamingTranscriptionOptions, k as SpeakOptions, l as GenerateOptions, t as AudioChunk, u as GenerateResult, v as LoadTTSOptions, y as ModelConfig, z as VoiceInfo } from "./types-
|
|
2
|
-
import { a as GenerateObjectOptions, o as GenerateObjectResult } from "./index-
|
|
1
|
+
import { A as SpeakResult, C as PreloadOptions, D as SessionStats, E as SearchResult, I as TranscribeOptions, L as TranscribeResult, M as StreamingTranscriptionSession, N as SystemInfo, O as SimilarityResult, T as STTModelConfig, _ as LoadSTTOptions, a as EmbedResult, d as GerbilConfig, g as LoadOptions, h as JsonOptions, i as EmbedOptions, j as StreamingTranscriptionOptions, k as SpeakOptions, l as GenerateOptions, t as AudioChunk, u as GenerateResult, v as LoadTTSOptions, y as ModelConfig, z as VoiceInfo } from "./types-Bxwe_uS7.mjs";
|
|
2
|
+
import { a as GenerateObjectOptions, o as GenerateObjectResult } from "./index-Nl40RdSs.mjs";
|
|
3
3
|
|
|
4
4
|
//#region src/core/gerbil.d.ts
|
|
5
5
|
|
|
@@ -485,4 +485,4 @@ declare class Gerbil {
|
|
|
485
485
|
}
|
|
486
486
|
//#endregion
|
|
487
487
|
export { Gerbil as t };
|
|
488
|
-
//# sourceMappingURL=gerbil-
|
|
488
|
+
//# sourceMappingURL=gerbil-CvNqpVg0.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gerbil-
|
|
1
|
+
{"version":3,"file":"gerbil-CvNqpVg0.d.mts","names":[],"sources":["../src/core/gerbil.ts"],"sourcesContent":[],"mappings":";;;;;AA8OsB,cAvBT,MAAA,CAuBS;EAgDC,QAAA,YAAA;EAIa,QAAA,WAAA;EA0BgB,iBAAA,MAAA;EAAmB,QAAA,KAAA;EAoLrD,QAAA,WAAA;EAyDsB,QAAA,YAAA;EAyCO,QAAA,iBAAA;EAAsB,QAAA,eAAA;EAqC7B,QAAA,SAAA;EAWM,QAAA,eAAA;EAAsB,QAAA,aAAA;EAgC5B,iBAAA,KAAA;EAWM,iBAAA,SAAA;EAAsB,WAAA,CAAA,MAAA,CAAA,EA/b9C,YA+b8C;EAgCvB,QAAA,WAAA;EAWO;;;;;EAyDa,QAAA,YAAA;EA0MpD,OAAA,UAAA,CAAA,CAAA,EA7rBU,WA6rBV,EAAA;EACe,OAAA,QAAA,CAAA,OAAA,EAAA,MAAA,CAAA,EA1rBQ,WA0rBR,GAAA,SAAA;EAAvB;;;;;;;;;;;;;;;;;;EAwZQ,SAAA,CAAA,OAAA,CAAA,EAAA,MAAA,EAAA,OAAA,CAAA,EAxjCuC,WAwjCvC,CAAA,EAxjC0D,OAwjC1D,CAAA,IAAA,CAAA;EAAY;;;;;;EAgEZ,QAAA,eAAA;EAuDa;;;EAO0B,QAAA,CAAA,CAAA,EAAA,OAAA;EAaf;;;EAuCxB,cAAA,CAAA,CAAA,EAAA,OAAA;EACO;;;EAcJ,YAAA,CAAA,CAAA,EArkCE,WAqkCF,GAAA,IAAA;EA8BZ;;;EA2B+D,aAAA,CAAA,CAAA,EAAA,QAAA,GAAA,KAAA,GAAA,MAAA;EAQN;;;;;EA8BhD,QAAA,CAAA,CAAA,EAAA,MAAA;EAAR;;;EAqEA,qBAAA,CAAA,CAAA,EAAA;IAW4B,IAAA,EAAA,MAAA;IAAR,MAAA,EAAA,MAAA;IAyDZ,IAAA,EAAA,MAAA;IAAR,OAAA,EAAA,MAAA;EAmC4B,CAAA;EAkBK;;;;;;;;;;;;;;;;kCAzyCE;;;;;;;;;;;;;;;;;;;;;;;;0CAyCO,iBAAsB;;;;;kCAqC7B;;;;;;;yCAWM,iBAAsB;;;;;kCAgC5B;;;;;;;yCAWM,iBAAsB;;;;;uCAgCvB;;;;;;;+CAWO,iBAAsB;;;;;;gBAmCpD;;;;;;;;;;;;;;;qCAsBoB,kBAAuB,QAAQ;;;;;;;;;;;mCA0M5D,kBACR,uBAAuB;;;;;;;;;;;;;;;;;;;mCA6Ma,YAAY,KAAK,QAAQ;;;;;;;;;;;;;;;;;wDAgDrD,wBACR,QAAQ,qBAAqB;;;;gCAiBG,eAAoB,QAAQ;;;;;;;;;;wCAoDpB,eAAoB,QAAQ;;;;;;;;;;;;;;;;;;;;qDAkD5D,eACR,QADyB,gBAAA;;;;;;;;;;;;;;oDAmCjB;;MACR,QADoB,YAAA;;;;;;;;;;;mEAgCZ;;MACR,QADoB,YAAA;;;;cAyBX;;;;aAOD;;;;;;;;;;;;;;;;qBAuDa;;MAA2C;;;;6BAOlC,iBAAiB;;;;;;;;;;gCAaf,eAAoB,QAAQ;;;;;;;sCAuCpD,eACR,eAAe,YAAY;;;;gBAchB;;;;;;;;;;;;;;;;mBA6BS,QACrB;;;;;;;;;;;;;;;;wCA2ByC,iBAAsB;;;;gDAQN,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;oBA4BnE,eAAe,sBACb,oBACR,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;0CAoEC,gCACT,QAAQ;;;;mBAWY,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;MAyD5B,QAAQ;;;;2BAmCoB;;;;;kCAkBK;;;;qBAmDX"}
|
package/dist/gpu/hooks.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { n as AgentTool, t as AgentStep } from "../index-
|
|
2
|
-
import { c as MemorySearchResult, d as RecallOptions, f as RecallResult, m as SearchOptions, s as MemoryRecord, t as AddOptions } from "../types-
|
|
1
|
+
import { n as AgentTool, t as AgentStep } from "../index-Nl40RdSs.mjs";
|
|
2
|
+
import { c as MemorySearchResult, d as RecallOptions, f as RecallResult, m as SearchOptions, s as MemoryRecord, t as AddOptions } from "../types-D7kn-0i2.mjs";
|
|
3
3
|
import { ReactNode } from "react";
|
|
4
4
|
import * as react_jsx_runtime0 from "react/jsx-runtime";
|
|
5
5
|
|