@tryhamster/gerbil 1.0.0-rc.1 → 1.0.0-rc.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (90) hide show
  1. package/README.md +14 -5
  2. package/dist/browser/{index.d.mts → index.d.ts} +354 -3
  3. package/dist/browser/index.d.ts.map +1 -0
  4. package/dist/browser/{index.mjs → index.js} +631 -259
  5. package/dist/browser/index.js.map +1 -0
  6. package/dist/{chrome-backend-Y9F7W5VQ.mjs → chrome-backend-CORwaIyC.mjs} +1 -1
  7. package/dist/{chrome-backend-Y9F7W5VQ.mjs.map → chrome-backend-CORwaIyC.mjs.map} +1 -1
  8. package/dist/{chrome-backend-JEPeM2YE.mjs → chrome-backend-DIKYoWj-.mjs} +1 -1
  9. package/dist/cli.mjs +14 -15
  10. package/dist/cli.mjs.map +1 -1
  11. package/dist/frameworks/express.d.mts +1 -1
  12. package/dist/frameworks/express.mjs +3 -4
  13. package/dist/frameworks/express.mjs.map +1 -1
  14. package/dist/frameworks/fastify.d.mts +1 -1
  15. package/dist/frameworks/fastify.mjs +2 -3
  16. package/dist/frameworks/fastify.mjs.map +1 -1
  17. package/dist/frameworks/hono.d.mts +1 -1
  18. package/dist/frameworks/hono.mjs +2 -3
  19. package/dist/frameworks/hono.mjs.map +1 -1
  20. package/dist/frameworks/next.d.mts +2 -2
  21. package/dist/frameworks/next.mjs +2 -3
  22. package/dist/frameworks/next.mjs.map +1 -1
  23. package/dist/frameworks/react.d.mts +1 -1
  24. package/dist/frameworks/trpc.d.mts +1 -1
  25. package/dist/frameworks/trpc.mjs +2 -3
  26. package/dist/frameworks/trpc.mjs.map +1 -1
  27. package/dist/gerbil-DJGqq7BX.mjs +4 -0
  28. package/dist/{gerbil-yoSpRHgv.mjs → gerbil-DoDGHe6Z.mjs} +187 -19
  29. package/dist/gerbil-DoDGHe6Z.mjs.map +1 -0
  30. package/dist/{gerbil-POAz8peb.d.mts → gerbil-qOTe1nl2.d.mts} +2 -2
  31. package/dist/{gerbil-POAz8peb.d.mts.map → gerbil-qOTe1nl2.d.mts.map} +1 -1
  32. package/dist/index.d.mts +19 -3
  33. package/dist/index.d.mts.map +1 -1
  34. package/dist/index.mjs +6 -7
  35. package/dist/index.mjs.map +1 -1
  36. package/dist/integrations/ai-sdk.d.mts +1 -1
  37. package/dist/integrations/ai-sdk.mjs +4 -5
  38. package/dist/integrations/ai-sdk.mjs.map +1 -1
  39. package/dist/integrations/langchain.d.mts +1 -1
  40. package/dist/integrations/langchain.mjs +2 -3
  41. package/dist/integrations/langchain.mjs.map +1 -1
  42. package/dist/integrations/llamaindex.d.mts +1 -1
  43. package/dist/integrations/llamaindex.mjs +2 -3
  44. package/dist/integrations/llamaindex.mjs.map +1 -1
  45. package/dist/integrations/mcp-client.mjs +2 -2
  46. package/dist/integrations/mcp.d.mts +2 -2
  47. package/dist/integrations/mcp.mjs +5 -6
  48. package/dist/kokoro-BNTb6egA.mjs +20210 -0
  49. package/dist/kokoro-BNTb6egA.mjs.map +1 -0
  50. package/dist/{mcp-Bitg4sjX.mjs → mcp-kzDDWIoS.mjs} +3 -3
  51. package/dist/{mcp-Bitg4sjX.mjs.map → mcp-kzDDWIoS.mjs.map} +1 -1
  52. package/dist/{one-liner-B1rmFto6.mjs → one-liner-DxnNs_JK.mjs} +2 -2
  53. package/dist/{one-liner-B1rmFto6.mjs.map → one-liner-DxnNs_JK.mjs.map} +1 -1
  54. package/dist/repl-DGUw4fCc.mjs +9 -0
  55. package/dist/skills/index.d.mts +2 -2
  56. package/dist/skills/index.d.mts.map +1 -1
  57. package/dist/skills/index.mjs +4 -5
  58. package/dist/{skills-5DxAV-rn.mjs → skills-DulrOPeP.mjs} +12 -12
  59. package/dist/skills-DulrOPeP.mjs.map +1 -0
  60. package/dist/stt-1WIefHwc.mjs +3 -0
  61. package/dist/{stt-Bv_dum-R.mjs → stt-CG_7KB_0.mjs} +3 -2
  62. package/dist/stt-CG_7KB_0.mjs.map +1 -0
  63. package/dist/{tools-IYPrqoek.mjs → tools-Bi1P7Xoy.mjs} +2 -2
  64. package/dist/{tools-IYPrqoek.mjs.map → tools-Bi1P7Xoy.mjs.map} +1 -1
  65. package/dist/{tts-5yWeP_I0.mjs → tts-B1pZMlDv.mjs} +1 -1
  66. package/dist/{tts-DG6denWG.mjs → tts-CyHhcLtN.mjs} +6 -4
  67. package/dist/tts-CyHhcLtN.mjs.map +1 -0
  68. package/dist/{types-s6Py2_DL.d.mts → types-CiTc7ez3.d.mts} +1 -1
  69. package/dist/{types-s6Py2_DL.d.mts.map → types-CiTc7ez3.d.mts.map} +1 -1
  70. package/dist/{utils-CkB4Roi6.mjs → utils-CZBZ8dgR.mjs} +1 -1
  71. package/dist/{utils-CkB4Roi6.mjs.map → utils-CZBZ8dgR.mjs.map} +1 -1
  72. package/docs/architecture/overview.md +15 -7
  73. package/docs/tts.md +11 -8
  74. package/package.json +6 -6
  75. package/dist/browser/index.d.mts.map +0 -1
  76. package/dist/browser/index.mjs.map +0 -1
  77. package/dist/gerbil-DeQlX_Mt.mjs +0 -5
  78. package/dist/gerbil-yoSpRHgv.mjs.map +0 -1
  79. package/dist/models-BAtL8qsA.mjs +0 -171
  80. package/dist/models-BAtL8qsA.mjs.map +0 -1
  81. package/dist/models-CE0fBq0U.d.mts +0 -22
  82. package/dist/models-CE0fBq0U.d.mts.map +0 -1
  83. package/dist/repl-D20JO260.mjs +0 -10
  84. package/dist/skills-5DxAV-rn.mjs.map +0 -1
  85. package/dist/stt-Bv_dum-R.mjs.map +0 -1
  86. package/dist/stt-KzSoNvwI.mjs +0 -3
  87. package/dist/tts-DG6denWG.mjs.map +0 -1
  88. /package/dist/{auto-update-DsWBBnEk.mjs → auto-update-S9s5-g0C.mjs} +0 -0
  89. /package/dist/{chunk-Ct1HF2bE.mjs → chunk-CkXuGtQK.mjs} +0 -0
  90. /package/dist/{microphone-D-6y9aiE.mjs → microphone-DaMZFRuR.mjs} +0 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stt-CG_7KB_0.mjs","names":["WHISPER_MODELS: STTModelConfig[]","tfDevice: \"webgpu\" | \"cpu\" | \"wasm\"","audioData: Float32Array","pipelineOptions: any","transcribeResult: TranscribeResult","audioBuffer: Float32Array[]","intervalId: ReturnType<typeof setInterval> | null","e: any"],"sources":["../src/core/stt.ts"],"sourcesContent":["/**\n * Speech-to-Text with Whisper\n *\n * Provides local speech recognition using Whisper ONNX models via transformers.js.\n * Supports multiple model sizes and languages.\n *\n * @example\n * ```ts\n * const stt = new WhisperSTT();\n * await stt.load({ onProgress: (p) => console.log(p.status) });\n *\n * // Transcribe audio (Float32Array at 16kHz)\n * const result = await stt.transcribe(audioData);\n * console.log(result.text);\n *\n * // With timestamps\n * const result = await stt.transcribe(audioData, { timestamps: true });\n * for (const seg of result.segments) {\n * console.log(`[${seg.start.toFixed(1)}s] ${seg.text}`);\n * }\n * ```\n */\n\nimport type {\n LoadSTTOptions,\n ProgressInfo,\n STTModelConfig,\n StreamingTranscriptionOptions,\n StreamingTranscriptionSession,\n TranscribeOptions,\n TranscribeResult,\n TranscribeSegment,\n} from \"./types.js\";\n\n// ============================================\n// Model Registry\n// ============================================\n\n/**\n * Available Whisper models\n * Ordered by size (smallest first)\n */\nexport const WHISPER_MODELS: STTModelConfig[] = [\n {\n id: \"whisper-tiny.en\",\n repo: \"onnx-community/whisper-tiny.en\",\n description: \"Tiny English-only model, fastest\",\n size: \"39M\",\n multilingual: false,\n languages: [\"en\"],\n sampleRate: 16000,\n },\n {\n id: \"whisper-tiny\",\n repo: \"onnx-community/whisper-tiny\",\n description: \"Tiny multilingual model\",\n size: \"39M\",\n multilingual: true,\n languages: [\"en\", \"es\", \"fr\", \"de\", \"it\", \"pt\", \"nl\", \"ru\", \"zh\", \"ja\", \"ko\"],\n sampleRate: 16000,\n },\n {\n id: \"whisper-base.en\",\n repo: \"onnx-community/whisper-base.en\",\n description: \"Base English-only model, good balance\",\n size: \"74M\",\n multilingual: false,\n languages: [\"en\"],\n sampleRate: 16000,\n },\n {\n id: \"whisper-base\",\n repo: \"onnx-community/whisper-base\",\n description: \"Base multilingual model\",\n size: \"74M\",\n multilingual: true,\n languages: [\"en\", \"es\", \"fr\", \"de\", \"it\", \"pt\", \"nl\", \"ru\", \"zh\", \"ja\", \"ko\"],\n sampleRate: 16000,\n },\n {\n id: \"whisper-small.en\",\n repo: \"onnx-community/whisper-small.en\",\n description: \"Small English-only model, high quality\",\n size: \"244M\",\n multilingual: false,\n languages: [\"en\"],\n sampleRate: 16000,\n },\n {\n id: \"whisper-small\",\n repo: \"onnx-community/whisper-small\",\n description: \"Small multilingual model\",\n size: \"244M\",\n multilingual: true,\n languages: [\"en\", \"es\", \"fr\", \"de\", \"it\", \"pt\", \"nl\", \"ru\", \"zh\", \"ja\", \"ko\"],\n sampleRate: 16000,\n },\n {\n id: \"whisper-large-v3-turbo\",\n repo: \"onnx-community/whisper-large-v3-turbo\",\n description: \"Large Turbo model, 5.4x faster, 80+ languages\",\n size: \"809M\",\n multilingual: true,\n languages: [\n \"en\",\n \"es\",\n \"fr\",\n \"de\",\n \"it\",\n \"pt\",\n \"nl\",\n \"ru\",\n \"zh\",\n \"ja\",\n \"ko\",\n \"ar\",\n \"hi\",\n \"vi\",\n \"th\",\n ],\n sampleRate: 16000,\n },\n];\n\n// Default model\nconst DEFAULT_MODEL = \"whisper-tiny.en\";\n\n// ============================================\n// Audio Utilities\n// ============================================\n\n/**\n * Decode WAV file to Float32Array\n * Handles stereo to mono conversion\n */\nexport function decodeWav(buffer: Uint8Array): { audio: Float32Array; sampleRate: number } {\n const view = new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength);\n\n // Validate RIFF header\n const riff = String.fromCharCode(buffer[0], buffer[1], buffer[2], buffer[3]);\n if (riff !== \"RIFF\") {\n throw new Error(\"Invalid WAV file: missing RIFF header\");\n }\n\n // Get format details\n const numChannels = view.getUint16(22, true);\n const sampleRate = view.getUint32(24, true);\n const bitsPerSample = view.getUint16(34, true);\n\n if (bitsPerSample !== 16) {\n throw new Error(`Unsupported bit depth: ${bitsPerSample}. Only 16-bit WAV is supported.`);\n }\n\n // Find data chunk\n let dataOffset = 12;\n while (dataOffset < buffer.length - 8) {\n const chunkId = String.fromCharCode(\n buffer[dataOffset],\n buffer[dataOffset + 1],\n buffer[dataOffset + 2],\n buffer[dataOffset + 3],\n );\n const chunkSize = view.getUint32(dataOffset + 4, true);\n if (chunkId === \"data\") {\n dataOffset += 8;\n break;\n }\n dataOffset += 8 + chunkSize;\n }\n\n const dataSize = buffer.length - dataOffset;\n const bytesPerSample = bitsPerSample / 8;\n const totalSamples = Math.floor(dataSize / bytesPerSample);\n const samplesPerChannel = Math.floor(totalSamples / numChannels);\n\n // Convert to mono Float32Array\n const audio = new Float32Array(samplesPerChannel);\n\n for (let i = 0; i < samplesPerChannel; i++) {\n if (numChannels === 2) {\n const left = view.getInt16(dataOffset + i * 4, true);\n const right = view.getInt16(dataOffset + i * 4 + 2, true);\n audio[i] = (left + right) / 2 / 32768;\n } else {\n const sample = view.getInt16(dataOffset + i * 2, true);\n audio[i] = sample / 32768;\n }\n }\n\n return { audio, sampleRate };\n}\n\n/**\n * Resample audio to target sample rate using linear interpolation\n */\nexport function resampleAudio(audio: Float32Array, fromRate: number, toRate: number): Float32Array {\n if (fromRate === toRate) return audio;\n\n const ratio = toRate / fromRate;\n const newLength = Math.round(audio.length * ratio);\n const result = new Float32Array(newLength);\n\n for (let i = 0; i < newLength; i++) {\n const srcIndex = i / ratio;\n const floor = Math.floor(srcIndex);\n const ceil = Math.min(floor + 1, audio.length - 1);\n const t = srcIndex - floor;\n result[i] = audio[floor] * (1 - t) + audio[ceil] * t;\n }\n\n return result;\n}\n\n// ============================================\n// WhisperSTT Class\n// ============================================\n\n/**\n * Speech-to-Text using Whisper ONNX models\n */\nexport class WhisperSTT {\n private modelConfig: STTModelConfig;\n private pipeline: any = null;\n private loadPromise: Promise<void> | null = null;\n private _isLoaded = false;\n private _deviceMode: \"webgpu\" | \"cpu\" = \"cpu\";\n\n constructor(modelId: string = DEFAULT_MODEL) {\n const config = WHISPER_MODELS.find((m) => m.id === modelId);\n if (!config) {\n const available = WHISPER_MODELS.map((m) => m.id).join(\", \");\n throw new Error(`Unknown STT model: ${modelId}. Available: ${available}`);\n }\n this.modelConfig = config;\n }\n\n /**\n * Check if model is loaded\n */\n isLoaded(): boolean {\n return this._isLoaded;\n }\n\n /**\n * Get model configuration\n */\n getModelConfig(): STTModelConfig {\n return this.modelConfig;\n }\n\n /**\n * Get model info (alias for getModelConfig)\n */\n getModelInfo(): STTModelConfig {\n return this.modelConfig;\n }\n\n /**\n * Get current device mode\n */\n getDeviceMode(): \"webgpu\" | \"cpu\" {\n return this._deviceMode;\n }\n\n /**\n * List available models\n */\n static listModels(): STTModelConfig[] {\n return [...WHISPER_MODELS];\n }\n\n /**\n * Load the STT model\n */\n async load(options: LoadSTTOptions = {}): Promise<void> {\n if (this._isLoaded) return;\n if (this.loadPromise) {\n await this.loadPromise;\n return;\n }\n\n this.loadPromise = this._load(options);\n await this.loadPromise;\n }\n\n private async _load(options: LoadSTTOptions = {}): Promise<void> {\n const { onProgress, device = \"auto\" } = options;\n\n onProgress?.({ status: \"Loading transformers.js...\" });\n\n // Check if we're in Node.js or browser\n const isNode = typeof process !== \"undefined\" && process.versions?.node;\n\n // Import transformers.js dynamically\n // tsdown handles resolution: Node.js = external, Browser = bundled\n const { pipeline, env } = await import(\"@huggingface/transformers\");\n\n // Configure environment based on runtime\n if (isNode) {\n // Node.js: allow local models (for CLI/server use)\n env.allowLocalModels = true;\n env.allowRemoteModels = true;\n } else {\n // Browser: use IndexedDB cache, fetch from HuggingFace CDN\n env.useBrowserCache = true;\n env.allowLocalModels = false;\n // Load WASM files from CDN (not bundled with gerbil)\n if (env.backends?.onnx?.wasm) {\n env.backends.onnx.wasm.wasmPaths =\n \"https://cdn.jsdelivr.net/npm/onnxruntime-web@1.21.0/dist/\";\n }\n }\n\n // Determine device\n // Note: Whisper ONNX models work best with fp32 on CPU/WASM\n // WebGPU support for ASR is limited, so we use CPU for reliability\n let tfDevice: \"webgpu\" | \"cpu\" | \"wasm\" = \"cpu\";\n\n // In browser, use WASM for better compatibility\n if (!isNode) {\n tfDevice = \"wasm\";\n }\n\n // Store device mode\n this._deviceMode = \"cpu\"; // STT always reports as CPU since WASM is CPU-based\n\n onProgress?.({ status: `Loading ${this.modelConfig.id}...` });\n\n // Load the ASR pipeline\n // Always use fp32 for Whisper models (fp16 not available for ONNX ASR)\n this.pipeline = await pipeline(\"automatic-speech-recognition\", this.modelConfig.repo, {\n dtype: \"fp32\",\n device: tfDevice,\n progress_callback: (progress: any) => {\n if (progress.status === \"progress\" && progress.file) {\n onProgress?.({\n status: `Downloading ${progress.file}`,\n progress: Math.round(progress.progress || 0),\n file: progress.file,\n });\n }\n },\n });\n\n this._isLoaded = true;\n onProgress?.({ status: `Ready (${tfDevice.toUpperCase()})!` });\n }\n\n /**\n * Transcribe audio to text\n *\n * @param audio - Audio data as Float32Array (mono, 16kHz preferred) or Uint8Array (WAV file)\n * @param options - Transcription options\n * @returns Transcription result with text and optional timestamps\n */\n async transcribe(\n audio: Float32Array | Uint8Array,\n options: TranscribeOptions = {},\n ): Promise<TranscribeResult> {\n if (!this._isLoaded) {\n throw new Error(\"STT model not loaded. Call load() first.\");\n }\n\n const { language, timestamps = false, onProgress } = options;\n const startTime = performance.now();\n\n // Convert Uint8Array (WAV) to Float32Array\n let audioData: Float32Array;\n let inputSampleRate = 16000;\n\n if (audio instanceof Uint8Array) {\n onProgress?.({ status: \"Decoding audio...\" });\n const decoded = decodeWav(audio);\n audioData = decoded.audio;\n inputSampleRate = decoded.sampleRate;\n } else {\n audioData = audio;\n }\n\n // Resample to 16kHz if needed\n if (inputSampleRate !== 16000) {\n onProgress?.({ status: \"Resampling to 16kHz...\" });\n audioData = resampleAudio(audioData, inputSampleRate, 16000);\n }\n\n const audioDuration = audioData.length / 16000;\n onProgress?.({ status: `Transcribing ${audioDuration.toFixed(1)}s of audio...` });\n\n // Build pipeline options\n const pipelineOptions: any = {};\n\n // Only set language for multilingual models\n if (language && this.modelConfig.multilingual) {\n pipelineOptions.language = language;\n pipelineOptions.task = \"transcribe\";\n }\n\n // Enable timestamps if requested\n if (timestamps) {\n pipelineOptions.return_timestamps = true;\n }\n\n // Run transcription\n const result = await this.pipeline(audioData, pipelineOptions);\n\n const totalTime = performance.now() - startTime;\n\n // Build result\n let text = result.text?.trim() || \"\";\n\n // Filter out Whisper artifacts\n if (text === \"[BLANK_AUDIO]\" || text === \"(blank audio)\" || text === \"[BLANK AUDIO]\") {\n text = \"\";\n }\n\n const transcribeResult: TranscribeResult = {\n text,\n language: language || (this.modelConfig.multilingual ? \"auto\" : \"en\"),\n duration: audioDuration,\n totalTime,\n };\n\n // Add segments if timestamps were requested\n if (timestamps && result.chunks) {\n transcribeResult.segments = result.chunks.map(\n (chunk: any): TranscribeSegment => ({\n text: chunk.text?.trim() || \"\",\n start: chunk.timestamp?.[0] || 0,\n end: chunk.timestamp?.[1] || 0,\n }),\n );\n }\n\n onProgress?.({ status: \"Done!\" });\n\n return transcribeResult;\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 = stt.createStreamingSession({\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 (Float32Array at 16kHz)\n * session.feedAudio(audioChunk);\n *\n * // Or manually trigger transcription\n * await session.flush();\n *\n * // Stop and get final transcript\n * const finalText = await session.stop();\n * ```\n */\n createStreamingSession(\n options: StreamingTranscriptionOptions = {},\n ): StreamingTranscriptionSession {\n const {\n chunkDuration = 3000,\n minChunkSize = 8000, // ~0.5 seconds at 16kHz\n onChunk,\n onTranscript,\n onError,\n language,\n } = options;\n\n let audioBuffer: Float32Array[] = [];\n let fullTranscript = \"\";\n let chunkIndex = 0;\n let intervalId: ReturnType<typeof setInterval> | null = null;\n let isRunning = false;\n\n const getBufferSize = (): number => {\n return audioBuffer.reduce((sum, chunk) => sum + chunk.length, 0);\n };\n\n const mergeBuffer = (): Float32Array => {\n const totalLength = getBufferSize();\n const merged = new Float32Array(totalLength);\n let offset = 0;\n for (const chunk of audioBuffer) {\n merged.set(chunk, offset);\n offset += chunk.length;\n }\n return merged;\n };\n\n const transcribeBuffer = async (): Promise<string> => {\n if (!this._isLoaded || getBufferSize() < minChunkSize) {\n return \"\";\n }\n\n const audio = mergeBuffer();\n audioBuffer = []; // Clear buffer\n\n try {\n const result = await this.transcribe(audio, { language });\n const text = result.text.trim();\n\n if (text) {\n chunkIndex++;\n onChunk?.(text, chunkIndex);\n\n // Append to full transcript\n fullTranscript = fullTranscript + (fullTranscript ? \" \" : \"\") + text;\n onTranscript?.(fullTranscript);\n }\n\n return text;\n } catch (e: any) {\n onError?.(e.message || \"Transcription failed\");\n return \"\";\n }\n };\n\n let aborted = false;\n\n const session: StreamingTranscriptionSession = {\n feedAudio: (audio: Float32Array) => {\n if (!aborted) {\n audioBuffer.push(audio);\n }\n },\n\n flush: async () => {\n if (aborted) return \"\";\n return transcribeBuffer();\n },\n\n start: () => {\n if (isRunning || aborted) return;\n isRunning = true;\n\n intervalId = setInterval(async () => {\n if (isRunning && !aborted) {\n await transcribeBuffer();\n }\n }, chunkDuration);\n },\n\n stop: async () => {\n isRunning = false;\n\n if (intervalId) {\n clearInterval(intervalId);\n intervalId = null;\n }\n\n // Transcribe any remaining audio (unless aborted)\n if (!aborted && getBufferSize() >= minChunkSize) {\n await transcribeBuffer();\n }\n\n return fullTranscript;\n },\n\n abort: () => {\n // Immediately stop without final transcription\n aborted = true;\n isRunning = false;\n\n if (intervalId) {\n clearInterval(intervalId);\n intervalId = null;\n }\n\n audioBuffer = [];\n },\n\n isRunning: () => isRunning,\n\n getTranscript: () => fullTranscript,\n\n getChunkCount: () => chunkIndex,\n\n reset: () => {\n audioBuffer = [];\n fullTranscript = \"\";\n chunkIndex = 0;\n },\n };\n\n return session;\n }\n\n /**\n * Dispose of resources\n */\n dispose(): void {\n this.pipeline = null;\n this._isLoaded = false;\n this.loadPromise = null;\n }\n}\n"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { n as zodToJsonSchema } from "./utils-CkB4Roi6.mjs";
1
+ import { n as zodToJsonSchema } from "./utils-CZBZ8dgR.mjs";
2
2
  import { z } from "zod";
3
3
 
4
4
  //#region src/core/docs.ts
@@ -563,4 +563,4 @@ const docsTool = defineTool({
563
563
 
564
564
  //#endregion
565
565
  export { getToolDefinitions as a, setToolContext as c, getTool as i, executeToolCall as n, loadProjectTools as o, formatToolsForPrompt as r, parseToolCall as s, defineTool as t };
566
- //# sourceMappingURL=tools-IYPrqoek.mjs.map
566
+ //# sourceMappingURL=tools-Bi1P7Xoy.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"tools-IYPrqoek.mjs","names":["GERBIL_DOCS: Record<string, string>","matches: string[]","toolContext: ToolContext | null","results: LoadedToolResult[]"],"sources":["../src/core/docs.ts","../src/core/tools.ts"],"sourcesContent":["/**\n * Gerbil Documentation Content\n *\n * Documentation strings used by the gerbil_docs tool\n * for answering questions about Gerbil features.\n */\n\nexport const GERBIL_DOCS: Record<string, string> = {\n // Core\n quickstart: `Gerbil Quick Start:\n\\`\\`\\`typescript\nimport { Gerbil } from \"gerbil\";\n\nconst g = new Gerbil();\nawait g.loadModel(\"qwen3-0.6b\");\n\n// Generate text\nconst result = await g.generate(\"Write a haiku\");\nconsole.log(result.text);\n\n// Stream responses\nfor await (const chunk of g.stream(\"Tell me a story\")) {\n process.stdout.write(chunk);\n}\n\\`\\`\\``,\n\n generate: `Generate text with options:\n\\`\\`\\`typescript\nconst result = await gerbil.generate(prompt, {\n maxTokens: 256, // Max tokens to generate\n temperature: 0.7, // 0-2, higher = more creative\n topP: 0.9, // Nucleus sampling\n topK: 50, // Top-k sampling\n thinking: false, // Enable reasoning mode (Qwen3)\n system: \"You are...\", // System prompt\n});\n\n// Result contains:\nresult.text // Generated text\nresult.thinking // Reasoning (if thinking=true)\nresult.tokensGenerated\nresult.tokensPerSecond\nresult.totalTime\n\\`\\`\\``,\n\n stream: `Streaming responses:\n\\`\\`\\`typescript\nfor await (const chunk of gerbil.stream(\"Hello\")) {\n process.stdout.write(chunk);\n}\n\n// With options\nconst stream = gerbil.stream(\"Explain React\", {\n maxTokens: 500,\n onToken: (token) => console.log(token),\n});\n\\`\\`\\``,\n\n json: `Structured JSON output with Zod validation:\n\\`\\`\\`typescript\nimport { z } from \"zod\";\n\nconst PersonSchema = z.object({\n name: z.string(),\n age: z.number(),\n city: z.string(),\n});\n\nconst data = await gerbil.json(\"Extract: John is 32 and lives in NYC\", {\n schema: PersonSchema,\n retries: 3, // Retry on validation failure\n temperature: 0.3, // Lower = more consistent\n});\n// { name: \"John\", age: 32, city: \"NYC\" }\n\\`\\`\\``,\n\n thinking: `Thinking mode (chain-of-thought reasoning) - Qwen3 models:\n\\`\\`\\`typescript\nconst result = await gerbil.generate(\"What is 127 × 43?\", {\n thinking: true,\n});\n\nconsole.log(result.thinking);\n// \"Let me calculate step by step: 127 × 43 = 127 × 40 + 127 × 3...\"\n\nconsole.log(result.text);\n// \"5461\"\n\\`\\`\\`\n\nEnable in chat with /no_think or /think commands, or enable_thinking parameter.`,\n\n embed: `Generate embeddings:\n\\`\\`\\`typescript\n// Single text\nconst { vector } = await gerbil.embed(\"Hello world\");\n// vector: number[] (384 dimensions by default)\n\n// Batch\nconst results = await gerbil.embedBatch([\"Hello\", \"World\"]);\n\\`\\`\\``,\n\n // Models\n models: `Available models:\n\n| Model | Size | Best For |\n|-------|------|----------|\n| qwen3-0.6b | ~400MB | General use, reasoning (thinking mode) |\n| qwen2.5-0.5b | ~350MB | Fast and capable |\n| qwen2.5-coder-0.5b | ~400MB | Code generation |\n| smollm2-360m | ~250MB | Very fast, simple tasks |\n| smollm2-135m | ~100MB | Fastest, basic generation |\n| phi-3-mini | ~2.1GB | High quality, larger |\n\nLoad any HuggingFace model:\n\\`\\`\\`typescript\nawait gerbil.loadModel(\"hf:org/model\");\nawait gerbil.loadModel(\"onnx-community/Qwen3-0.6B-ONNX\");\n\\`\\`\\``,\n\n load: `Loading models:\n\\`\\`\\`typescript\nawait gerbil.loadModel(\"qwen3-0.6b\", {\n device: \"auto\", // \"auto\" | \"gpu\" | \"cpu\"\n dtype: \"q4\", // \"q4\" | \"q8\" | \"fp16\" | \"fp32\"\n onProgress: (p) => {\n console.log(p.status, p.progress, p.file);\n },\n});\n\\`\\`\\``,\n\n // AI SDK\n \"ai-sdk\": `Vercel AI SDK v5 integration:\n\\`\\`\\`typescript\nimport { generateText, streamText } from \"ai\";\nimport { gerbil } from \"gerbil/ai\";\n\n// Generate\nconst { text } = await generateText({\n model: gerbil(\"qwen3-0.6b\"),\n prompt: \"Write a commit message\",\n});\n\n// Stream\nconst stream = streamText({\n model: gerbil(\"qwen3-0.6b\"),\n prompt: \"Explain React hooks\",\n});\n\nfor await (const chunk of stream.textStream) {\n process.stdout.write(chunk);\n}\n\n// With thinking mode\nconst { text } = await generateText({\n model: gerbil(\"qwen3-0.6b\", { thinking: true }),\n prompt: \"What is 127 × 43?\",\n});\n\\`\\`\\``,\n\n // Frameworks\n next: `Next.js App Router integration:\n\\`\\`\\`typescript\n// app/api/chat/route.ts\nimport { gerbil } from \"gerbil/next\";\n\nexport const POST = gerbil.handler({ \n model: \"qwen3-0.6b\",\n maxTokens: 500,\n});\n\\`\\`\\``,\n\n express: `Express.js integration:\n\\`\\`\\`typescript\nimport express from \"express\";\nimport { gerbil } from \"gerbil/express\";\n\nconst app = express();\napp.use(\"/ai\", gerbil()());\n\n// Endpoints created:\n// POST /ai/generate - Generate text\n// POST /ai/stream - Stream text\n// POST /ai/json - Structured output\n// POST /ai/embed - Embeddings\n\\`\\`\\``,\n\n react: `React hooks:\n\\`\\`\\`typescript\nimport { useGerbil, useChat } from \"gerbil/react\";\n\nfunction Chat() {\n const { generate, isLoading } = useGerbil();\n \n const handleSubmit = async (prompt: string) => {\n const result = await generate(prompt);\n console.log(result.text);\n };\n}\n\\`\\`\\``,\n\n hono: `Hono integration:\n\\`\\`\\`typescript\nimport { Hono } from \"hono\";\nimport { gerbil } from \"gerbil/hono\";\n\nconst app = new Hono();\napp.route(\"/ai\", await gerbil()());\n\\`\\`\\``,\n\n langchain: `LangChain integration:\n\\`\\`\\`typescript\nimport { GerbilLLM, GerbilEmbeddings } from \"gerbil/langchain\";\n\nconst llm = new GerbilLLM({ model: \"qwen3-0.6b\" });\nconst embeddings = new GerbilEmbeddings();\n\n// Use with chains\nconst chain = new LLMChain({ llm, prompt: template });\n\\`\\`\\``,\n\n mcp: `MCP Server for Claude Desktop & Cursor:\n\\`\\`\\`bash\ngerbil serve --mcp\n\\`\\`\\`\n\nSkills are automatically exposed as MCP tools:\n- gerbil_generate\n- gerbil_commit\n- gerbil_summarize\n- gerbil_explain\n- etc.`,\n\n // Skills\n skills: `Skills are reusable AI tasks with Zod validation:\n\\`\\`\\`typescript\nimport { commit, summarize, explain, review } from \"gerbil/skills\";\n\n// Generate commit message\nconst msg = await commit({ type: \"conventional\", maxLength: 72 });\n\n// Summarize content\nconst summary = await summarize({ \n content: doc, \n length: \"short\",\n format: \"bullets\" \n});\n\n// Explain code\nconst explanation = await explain({ \n content: code, \n level: \"beginner\" \n});\n\n// Code review\nconst feedback = await review({ \n code, \n focus: [\"security\", \"performance\"] \n});\n\\`\\`\\`\n\nBuilt-in skills: commit, summarize, explain, review, test, translate, extract, title`,\n\n \"define-skill\": `Create custom skills:\n\\`\\`\\`typescript\nimport { defineSkill } from \"gerbil/skills\";\nimport { z } from \"zod\";\n\nexport const sentiment = defineSkill({\n name: \"sentiment\",\n description: \"Analyze text sentiment\",\n input: z.object({ \n text: z.string(),\n detailed: z.boolean().default(false)\n }),\n output: z.object({\n sentiment: z.enum([\"positive\", \"negative\", \"neutral\"]),\n confidence: z.number(),\n }),\n temperature: 0.3,\n maxTokens: 100,\n \n async run({ input, gerbil }) {\n return gerbil.json(\\`Sentiment of: \\${input.text}\\`, {\n schema: this.output,\n });\n },\n});\n\\`\\`\\``,\n\n \"load-skills\": `Load skills from files:\n\\`\\`\\`typescript\nimport { loadSkills, useSkill, listSkills } from \"gerbil/skills\";\n\n// Load from directory (*.skill.ts files)\nawait loadSkills(\"./skills\");\n\n// Use by name\nconst skill = useSkill(\"my-skill\");\nconst result = await skill({ text: \"Hello\" });\n\n// List all\nconsole.log(listSkills()); // [\"commit\", \"summarize\", ...]\n\\`\\`\\``,\n\n // CLI\n cli: `CLI commands:\n\\`\\`\\`bash\ngerbil \"Write a haiku\" # Generate text\ngerbil -m qwen3-0.6b \"prompt\" # Specify model\ngerbil --thinking \"127 × 43\" # Enable reasoning\n\ngerbil commit # Git commit message\ngerbil summarize README.md # Summarize file\ngerbil explain src/index.ts # Explain code\n\ngerbil chat # Interactive chat\ngerbil chat --thinking # With reasoning\n\ngerbil serve # HTTP server\ngerbil serve --mcp # MCP server\n\ngerbil models # List models\ngerbil cache # Show cached models\ngerbil bench # Benchmark\ngerbil info # System info\n\ngerbil repl # Interactive TUI\n\\`\\`\\``,\n\n // Tools/Agents\n tools: `Gerbil supports tool calling with Qwen3 models:\n\\`\\`\\`typescript\nimport { defineTool, executeToolCall } from \"gerbil\";\n\nconst weatherTool = defineTool({\n name: \"get_weather\",\n description: \"Get current weather for a city\",\n parameters: z.object({\n city: z.string(),\n }),\n execute: async ({ city }) => {\n return \\`Weather in \\${city}: 72°F, sunny\\`;\n },\n});\n\\`\\`\\`\n\nIn the REPL, use Agent mode (Tab → Agent) to enable tools.`,\n};\n\n/**\n * Get all available documentation topics\n */\nexport function getDocTopics(): string[] {\n return Object.keys(GERBIL_DOCS);\n}\n\n/**\n * Search documentation by topic\n */\nexport function searchDocs(query: string): string | null {\n const words = query.toLowerCase().split(/\\s+/);\n const keyWords = words.filter((w) => w.length > 2);\n\n // Direct key match first\n for (const [key, content] of Object.entries(GERBIL_DOCS)) {\n const keyNorm = key.replace(/-/g, \"\");\n for (const word of keyWords) {\n if (keyNorm === word || key === word || keyNorm.includes(word)) {\n return content;\n }\n }\n }\n\n // Try each word as a potential topic\n for (const word of keyWords) {\n for (const [key, content] of Object.entries(GERBIL_DOCS)) {\n if (key.includes(word) || word.includes(key.replace(/-/g, \"\"))) {\n return content;\n }\n }\n }\n\n // Fallback: search content\n const matches: string[] = [];\n for (const [key, content] of Object.entries(GERBIL_DOCS)) {\n for (const word of keyWords) {\n if (content.toLowerCase().includes(word)) {\n matches.push(`## ${key}\\n${content}`);\n break;\n }\n }\n }\n\n if (matches.length > 0) {\n return matches.slice(0, 2).join(\"\\n\\n---\\n\\n\");\n }\n\n return null;\n}\n","/**\n * Tool Calling System for Gerbil\n *\n * Enables LLMs to call functions/tools during generation.\n * Compatible with Qwen3's tool calling format.\n */\n\nimport { z } from \"zod\";\nimport { getDocTopics, searchDocs } from \"./docs.js\";\nimport { zodToJsonSchema } from \"./utils.js\";\n\n// ============================================\n// Tool Context (for passing gerbil to tools)\n// ============================================\n\nexport type ToolContext = {\n /** Generate text using the loaded model */\n generate: (prompt: string) => Promise<string>;\n};\n\nlet toolContext: ToolContext | null = null;\n\n/**\n * Set the tool context (call this after loading a model)\n */\nexport function setToolContext(ctx: ToolContext): void {\n toolContext = ctx;\n}\n\n/**\n * Get the tool context for use in tools\n */\nexport function getToolContext(): ToolContext | null {\n return toolContext;\n}\n\n// ============================================\n// Tool Definition\n// ============================================\n\nexport type ToolDefinition<TParams = unknown> = {\n /** Tool name (function name) */\n name: string;\n\n /** Description for the LLM */\n description: string;\n\n /** Zod schema for parameters */\n parameters?: z.ZodType<TParams>;\n\n /** The function to execute (receives params and optional context) */\n execute: (params: TParams, ctx?: ToolContext | null) => Promise<string>;\n};\n\nexport type Tool<TParams = unknown> = {\n definition: ToolDefinition<TParams>;\n (params: TParams): Promise<string>;\n};\n\n// ============================================\n// Tool Registry\n// ============================================\n\nconst toolRegistry = new Map<string, Tool<any>>();\n\n/**\n * Define a tool\n */\nexport function defineTool<TParams>(def: ToolDefinition<TParams>): Tool<TParams> {\n const tool = async (params: TParams): Promise<string> => {\n // Validate params if schema provided\n if (def.parameters) {\n const parsed = def.parameters.parse(params);\n return def.execute(parsed, toolContext);\n }\n return def.execute(params, toolContext);\n };\n\n (tool as Tool<TParams>).definition = def;\n\n // Auto-register\n toolRegistry.set(def.name, tool as Tool<any>);\n\n return tool as Tool<TParams>;\n}\n\n/**\n * Get tool by name\n */\nexport function getTool(name: string): Tool<any> | undefined {\n return toolRegistry.get(name);\n}\n\n/**\n * List all tools\n */\nexport function listTools(): string[] {\n return Array.from(toolRegistry.keys());\n}\n\n/**\n * Get all tool definitions for prompt injection\n */\nexport function getToolDefinitions(): ToolDefinition<any>[] {\n return Array.from(toolRegistry.values()).map((t) => t.definition);\n}\n\nexport type LoadedToolResult = {\n name: string;\n loaded: boolean;\n error?: string;\n path: string;\n};\n\n/**\n * Simple tool config (no imports needed in tool files)\n */\nexport type SimpleToolConfig = {\n name: string;\n description: string;\n execute: (params: Record<string, any>) => Promise<string> | string;\n};\n\n/**\n * Load tools from a directory\n * Tools are simple .ts files with a default export config object\n * No module imports - we eval the execute function directly\n */\nexport async function loadTools(dir: string): Promise<LoadedToolResult[]> {\n const fs = await import(\"node:fs\");\n const pathModule = await import(\"node:path\");\n\n if (!fs.existsSync(dir)) {\n return [];\n }\n\n const files = fs.readdirSync(dir).filter((f) => f.endsWith(\".tool.ts\") || f.endsWith(\".tool.js\"));\n const results: LoadedToolResult[] = [];\n\n for (const file of files) {\n const filePath = pathModule.join(dir, file);\n const toolName = file.replace(/\\.tool\\.(ts|js)$/, \"\");\n\n try {\n // Read file content directly\n const content = fs.readFileSync(filePath, \"utf-8\");\n\n // Extract the config object using simple parsing\n const config = parseToolFile(content, toolName);\n\n if (config) {\n // Register the tool\n defineTool({\n name: config.name,\n description: config.description,\n execute: async (params, ctx) => {\n // Project tools may or may not accept ctx parameter\n const executeFn = config.execute as any;\n const result = await executeFn(params, ctx);\n return typeof result === \"string\" ? result : String(result);\n },\n });\n results.push({ name: config.name, loaded: true, path: filePath });\n } else {\n results.push({\n name: toolName,\n loaded: false,\n error: \"Could not parse tool config\",\n path: filePath,\n });\n }\n } catch (e) {\n const errorMsg = e instanceof Error ? e.message.split(\"\\n\")[0] : String(e);\n results.push({ name: toolName, loaded: false, error: errorMsg, path: filePath });\n }\n }\n\n return results;\n}\n\n/**\n * Parse a simple tool file and extract the config\n * Expected format:\n * ```\n * export default {\n * name: \"tool_name\",\n * description: \"...\",\n * execute: async (params) => { ... }\n * }\n * ```\n */\nfunction parseToolFile(content: string, fallbackName: string): SimpleToolConfig | null {\n try {\n // Remove TypeScript type annotations for eval\n let cleaned = content\n .replace(/:\\s*\\{[^}]+\\}/g, \"\") // Remove type objects like : { theme?: string }\n .replace(/:\\s*Record<[^>]+>/g, \"\") // Remove Record types\n .replace(/:\\s*string\\b/g, \"\")\n .replace(/:\\s*number\\b/g, \"\")\n .replace(/:\\s*boolean\\b/g, \"\")\n .replace(/:\\s*any\\b/g, \"\")\n .replace(/:\\s*Promise<[^>]+>/g, \"\")\n .replace(/export\\s+default\\s+/, \"return \");\n\n // Remove comments\n cleaned = cleaned.replace(/\\/\\/.*$/gm, \"\").replace(/\\/\\*[\\s\\S]*?\\*\\//g, \"\");\n\n // Wrap in function and evaluate\n const fn = new Function(cleaned);\n const config = fn();\n\n if (config && typeof config === \"object\" && config.name && config.execute) {\n return {\n name: config.name || fallbackName,\n description: config.description || \"No description\",\n execute: config.execute,\n };\n }\n return null;\n } catch (_e) {\n return null;\n }\n}\n\n/**\n * Load tools from project .gerbil/tools directory\n */\nexport async function loadProjectTools(): Promise<LoadedToolResult[]> {\n const pathModule = await import(\"node:path\");\n const toolsDir = pathModule.join(process.cwd(), \".gerbil\", \"tools\");\n return loadTools(toolsDir);\n}\n\n// Re-export for backwards compatibility\nexport { zodToJsonSchema } from \"./utils.js\";\n\n/**\n * Format tools for Qwen3 prompt\n */\nexport function formatToolsForPrompt(tools: ToolDefinition<any>[]): string {\n if (tools.length === 0) {\n return \"\";\n }\n\n const toolDefs = tools\n .map((t) => {\n const params = t.parameters\n ? zodToJsonSchema(t.parameters)\n : { type: \"object\", properties: {} };\n return `## ${t.name}\n\nDescription: ${t.description}\nParameters: ${JSON.stringify(params, null, 2)}`;\n })\n .join(\"\\n\\n\");\n\n return `You are a helpful assistant with access to tools.\n\n# Tools\n\n${toolDefs}\n\n## How to call tools\n\nTo call a tool, use this exact format:\n<tool_call>\n{\"name\": \"tool_name\", \"arguments\": {\"param\": \"value\"}}\n</tool_call>\n\nWhen the user asks about Gerbil (documentation, features, how to use), call gerbil_docs with a topic like: tools, skills, ai-sdk, next, express, generate, stream, models, cli.\n\nFor other questions, respond normally without calling tools.`;\n}\n\n/**\n * Parse tool call from response\n */\nexport function parseToolCall(response: string): { tool: string; params: any } | null {\n try {\n // Try <tool_call> format first (Qwen style)\n const toolCallMatch = response.match(/<tool_call>\\s*([\\s\\S]*?)\\s*<\\/tool_call>/);\n if (toolCallMatch) {\n const parsed = JSON.parse(toolCallMatch[1]);\n if (parsed.name) {\n return { tool: parsed.name, params: parsed.arguments || {} };\n }\n }\n\n // Fallback: look for {\"name\": \"...\", \"arguments\": ...} pattern\n const nameArgsMatch = response.match(\n /\\{\\s*\"name\"\\s*:\\s*\"([^\"]+)\"[^}]*\"arguments\"\\s*:\\s*(\\{[^}]*\\})/,\n );\n if (nameArgsMatch) {\n return { tool: nameArgsMatch[1], params: JSON.parse(nameArgsMatch[2]) };\n }\n\n // Fallback: {\"tool\": \"...\", \"params\": ...} pattern\n const toolParamsMatch = response.match(\n /\\{\\s*\"tool\"\\s*:\\s*\"([^\"]+)\"[^}]*\"params\"\\s*:\\s*(\\{[^}]*\\})/,\n );\n if (toolParamsMatch) {\n return { tool: toolParamsMatch[1], params: JSON.parse(toolParamsMatch[2]) };\n }\n\n // Simple fallback\n const simpleMatch = response.match(/\\{\\s*\"(?:tool|name)\"\\s*:\\s*\"([^\"]+)\"/);\n if (simpleMatch) {\n return { tool: simpleMatch[1], params: {} };\n }\n } catch {}\n return null;\n}\n\n/**\n * Execute a tool call\n */\nexport async function executeToolCall(toolName: string, params: any): Promise<string> {\n const tool = getTool(toolName);\n if (!tool) {\n return `Error: Unknown tool \"${toolName}\"`;\n }\n\n try {\n return await tool(params);\n } catch (e) {\n return `Error executing ${toolName}: ${e}`;\n }\n}\n\n// ============================================\n// Built-in Tools\n// ============================================\n\n/**\n * Docs tool - searches gerbil documentation\n * Documentation content is in core/docs.ts\n */\nexport const docsTool = defineTool({\n name: \"gerbil_docs\",\n description:\n \"Search Gerbil documentation for information about features, API, integrations, skills, and usage examples\",\n parameters: z.object({\n query: z\n .string()\n .optional()\n .default(\"quickstart\")\n .describe(\n \"Topic: quickstart, generate, stream, json, thinking, embed, models, ai-sdk, next, express, react, skills, define-skill, cli, tools\",\n ),\n }),\n execute: async ({ query = \"quickstart\" }) => {\n const result = searchDocs(query);\n if (result) {\n return result;\n }\n const topics = getDocTopics().join(\", \");\n return `No documentation found for \"${query}\". Available topics: ${topics}`;\n },\n});\n"],"mappings":"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"}
1
+ {"version":3,"file":"tools-Bi1P7Xoy.mjs","names":["GERBIL_DOCS: Record<string, string>","matches: string[]","toolContext: ToolContext | null","results: LoadedToolResult[]"],"sources":["../src/core/docs.ts","../src/core/tools.ts"],"sourcesContent":["/**\n * Gerbil Documentation Content\n *\n * Documentation strings used by the gerbil_docs tool\n * for answering questions about Gerbil features.\n */\n\nexport const GERBIL_DOCS: Record<string, string> = {\n // Core\n quickstart: `Gerbil Quick Start:\n\\`\\`\\`typescript\nimport { Gerbil } from \"gerbil\";\n\nconst g = new Gerbil();\nawait g.loadModel(\"qwen3-0.6b\");\n\n// Generate text\nconst result = await g.generate(\"Write a haiku\");\nconsole.log(result.text);\n\n// Stream responses\nfor await (const chunk of g.stream(\"Tell me a story\")) {\n process.stdout.write(chunk);\n}\n\\`\\`\\``,\n\n generate: `Generate text with options:\n\\`\\`\\`typescript\nconst result = await gerbil.generate(prompt, {\n maxTokens: 256, // Max tokens to generate\n temperature: 0.7, // 0-2, higher = more creative\n topP: 0.9, // Nucleus sampling\n topK: 50, // Top-k sampling\n thinking: false, // Enable reasoning mode (Qwen3)\n system: \"You are...\", // System prompt\n});\n\n// Result contains:\nresult.text // Generated text\nresult.thinking // Reasoning (if thinking=true)\nresult.tokensGenerated\nresult.tokensPerSecond\nresult.totalTime\n\\`\\`\\``,\n\n stream: `Streaming responses:\n\\`\\`\\`typescript\nfor await (const chunk of gerbil.stream(\"Hello\")) {\n process.stdout.write(chunk);\n}\n\n// With options\nconst stream = gerbil.stream(\"Explain React\", {\n maxTokens: 500,\n onToken: (token) => console.log(token),\n});\n\\`\\`\\``,\n\n json: `Structured JSON output with Zod validation:\n\\`\\`\\`typescript\nimport { z } from \"zod\";\n\nconst PersonSchema = z.object({\n name: z.string(),\n age: z.number(),\n city: z.string(),\n});\n\nconst data = await gerbil.json(\"Extract: John is 32 and lives in NYC\", {\n schema: PersonSchema,\n retries: 3, // Retry on validation failure\n temperature: 0.3, // Lower = more consistent\n});\n// { name: \"John\", age: 32, city: \"NYC\" }\n\\`\\`\\``,\n\n thinking: `Thinking mode (chain-of-thought reasoning) - Qwen3 models:\n\\`\\`\\`typescript\nconst result = await gerbil.generate(\"What is 127 × 43?\", {\n thinking: true,\n});\n\nconsole.log(result.thinking);\n// \"Let me calculate step by step: 127 × 43 = 127 × 40 + 127 × 3...\"\n\nconsole.log(result.text);\n// \"5461\"\n\\`\\`\\`\n\nEnable in chat with /no_think or /think commands, or enable_thinking parameter.`,\n\n embed: `Generate embeddings:\n\\`\\`\\`typescript\n// Single text\nconst { vector } = await gerbil.embed(\"Hello world\");\n// vector: number[] (384 dimensions by default)\n\n// Batch\nconst results = await gerbil.embedBatch([\"Hello\", \"World\"]);\n\\`\\`\\``,\n\n // Models\n models: `Available models:\n\n| Model | Size | Best For |\n|-------|------|----------|\n| qwen3-0.6b | ~400MB | General use, reasoning (thinking mode) |\n| qwen2.5-0.5b | ~350MB | Fast and capable |\n| qwen2.5-coder-0.5b | ~400MB | Code generation |\n| smollm2-360m | ~250MB | Very fast, simple tasks |\n| smollm2-135m | ~100MB | Fastest, basic generation |\n| phi-3-mini | ~2.1GB | High quality, larger |\n\nLoad any HuggingFace model:\n\\`\\`\\`typescript\nawait gerbil.loadModel(\"hf:org/model\");\nawait gerbil.loadModel(\"onnx-community/Qwen3-0.6B-ONNX\");\n\\`\\`\\``,\n\n load: `Loading models:\n\\`\\`\\`typescript\nawait gerbil.loadModel(\"qwen3-0.6b\", {\n device: \"auto\", // \"auto\" | \"gpu\" | \"cpu\"\n dtype: \"q4\", // \"q4\" | \"q8\" | \"fp16\" | \"fp32\"\n onProgress: (p) => {\n console.log(p.status, p.progress, p.file);\n },\n});\n\\`\\`\\``,\n\n // AI SDK\n \"ai-sdk\": `Vercel AI SDK v5 integration:\n\\`\\`\\`typescript\nimport { generateText, streamText } from \"ai\";\nimport { gerbil } from \"gerbil/ai\";\n\n// Generate\nconst { text } = await generateText({\n model: gerbil(\"qwen3-0.6b\"),\n prompt: \"Write a commit message\",\n});\n\n// Stream\nconst stream = streamText({\n model: gerbil(\"qwen3-0.6b\"),\n prompt: \"Explain React hooks\",\n});\n\nfor await (const chunk of stream.textStream) {\n process.stdout.write(chunk);\n}\n\n// With thinking mode\nconst { text } = await generateText({\n model: gerbil(\"qwen3-0.6b\", { thinking: true }),\n prompt: \"What is 127 × 43?\",\n});\n\\`\\`\\``,\n\n // Frameworks\n next: `Next.js App Router integration:\n\\`\\`\\`typescript\n// app/api/chat/route.ts\nimport { gerbil } from \"gerbil/next\";\n\nexport const POST = gerbil.handler({ \n model: \"qwen3-0.6b\",\n maxTokens: 500,\n});\n\\`\\`\\``,\n\n express: `Express.js integration:\n\\`\\`\\`typescript\nimport express from \"express\";\nimport { gerbil } from \"gerbil/express\";\n\nconst app = express();\napp.use(\"/ai\", gerbil()());\n\n// Endpoints created:\n// POST /ai/generate - Generate text\n// POST /ai/stream - Stream text\n// POST /ai/json - Structured output\n// POST /ai/embed - Embeddings\n\\`\\`\\``,\n\n react: `React hooks:\n\\`\\`\\`typescript\nimport { useGerbil, useChat } from \"gerbil/react\";\n\nfunction Chat() {\n const { generate, isLoading } = useGerbil();\n \n const handleSubmit = async (prompt: string) => {\n const result = await generate(prompt);\n console.log(result.text);\n };\n}\n\\`\\`\\``,\n\n hono: `Hono integration:\n\\`\\`\\`typescript\nimport { Hono } from \"hono\";\nimport { gerbil } from \"gerbil/hono\";\n\nconst app = new Hono();\napp.route(\"/ai\", await gerbil()());\n\\`\\`\\``,\n\n langchain: `LangChain integration:\n\\`\\`\\`typescript\nimport { GerbilLLM, GerbilEmbeddings } from \"gerbil/langchain\";\n\nconst llm = new GerbilLLM({ model: \"qwen3-0.6b\" });\nconst embeddings = new GerbilEmbeddings();\n\n// Use with chains\nconst chain = new LLMChain({ llm, prompt: template });\n\\`\\`\\``,\n\n mcp: `MCP Server for Claude Desktop & Cursor:\n\\`\\`\\`bash\ngerbil serve --mcp\n\\`\\`\\`\n\nSkills are automatically exposed as MCP tools:\n- gerbil_generate\n- gerbil_commit\n- gerbil_summarize\n- gerbil_explain\n- etc.`,\n\n // Skills\n skills: `Skills are reusable AI tasks with Zod validation:\n\\`\\`\\`typescript\nimport { commit, summarize, explain, review } from \"gerbil/skills\";\n\n// Generate commit message\nconst msg = await commit({ type: \"conventional\", maxLength: 72 });\n\n// Summarize content\nconst summary = await summarize({ \n content: doc, \n length: \"short\",\n format: \"bullets\" \n});\n\n// Explain code\nconst explanation = await explain({ \n content: code, \n level: \"beginner\" \n});\n\n// Code review\nconst feedback = await review({ \n code, \n focus: [\"security\", \"performance\"] \n});\n\\`\\`\\`\n\nBuilt-in skills: commit, summarize, explain, review, test, translate, extract, title`,\n\n \"define-skill\": `Create custom skills:\n\\`\\`\\`typescript\nimport { defineSkill } from \"gerbil/skills\";\nimport { z } from \"zod\";\n\nexport const sentiment = defineSkill({\n name: \"sentiment\",\n description: \"Analyze text sentiment\",\n input: z.object({ \n text: z.string(),\n detailed: z.boolean().default(false)\n }),\n output: z.object({\n sentiment: z.enum([\"positive\", \"negative\", \"neutral\"]),\n confidence: z.number(),\n }),\n temperature: 0.3,\n maxTokens: 100,\n \n async run({ input, gerbil }) {\n return gerbil.json(\\`Sentiment of: \\${input.text}\\`, {\n schema: this.output,\n });\n },\n});\n\\`\\`\\``,\n\n \"load-skills\": `Load skills from files:\n\\`\\`\\`typescript\nimport { loadSkills, useSkill, listSkills } from \"gerbil/skills\";\n\n// Load from directory (*.skill.ts files)\nawait loadSkills(\"./skills\");\n\n// Use by name\nconst skill = useSkill(\"my-skill\");\nconst result = await skill({ text: \"Hello\" });\n\n// List all\nconsole.log(listSkills()); // [\"commit\", \"summarize\", ...]\n\\`\\`\\``,\n\n // CLI\n cli: `CLI commands:\n\\`\\`\\`bash\ngerbil \"Write a haiku\" # Generate text\ngerbil -m qwen3-0.6b \"prompt\" # Specify model\ngerbil --thinking \"127 × 43\" # Enable reasoning\n\ngerbil commit # Git commit message\ngerbil summarize README.md # Summarize file\ngerbil explain src/index.ts # Explain code\n\ngerbil chat # Interactive chat\ngerbil chat --thinking # With reasoning\n\ngerbil serve # HTTP server\ngerbil serve --mcp # MCP server\n\ngerbil models # List models\ngerbil cache # Show cached models\ngerbil bench # Benchmark\ngerbil info # System info\n\ngerbil repl # Interactive TUI\n\\`\\`\\``,\n\n // Tools/Agents\n tools: `Gerbil supports tool calling with Qwen3 models:\n\\`\\`\\`typescript\nimport { defineTool, executeToolCall } from \"gerbil\";\n\nconst weatherTool = defineTool({\n name: \"get_weather\",\n description: \"Get current weather for a city\",\n parameters: z.object({\n city: z.string(),\n }),\n execute: async ({ city }) => {\n return \\`Weather in \\${city}: 72°F, sunny\\`;\n },\n});\n\\`\\`\\`\n\nIn the REPL, use Agent mode (Tab → Agent) to enable tools.`,\n};\n\n/**\n * Get all available documentation topics\n */\nexport function getDocTopics(): string[] {\n return Object.keys(GERBIL_DOCS);\n}\n\n/**\n * Search documentation by topic\n */\nexport function searchDocs(query: string): string | null {\n const words = query.toLowerCase().split(/\\s+/);\n const keyWords = words.filter((w) => w.length > 2);\n\n // Direct key match first\n for (const [key, content] of Object.entries(GERBIL_DOCS)) {\n const keyNorm = key.replace(/-/g, \"\");\n for (const word of keyWords) {\n if (keyNorm === word || key === word || keyNorm.includes(word)) {\n return content;\n }\n }\n }\n\n // Try each word as a potential topic\n for (const word of keyWords) {\n for (const [key, content] of Object.entries(GERBIL_DOCS)) {\n if (key.includes(word) || word.includes(key.replace(/-/g, \"\"))) {\n return content;\n }\n }\n }\n\n // Fallback: search content\n const matches: string[] = [];\n for (const [key, content] of Object.entries(GERBIL_DOCS)) {\n for (const word of keyWords) {\n if (content.toLowerCase().includes(word)) {\n matches.push(`## ${key}\\n${content}`);\n break;\n }\n }\n }\n\n if (matches.length > 0) {\n return matches.slice(0, 2).join(\"\\n\\n---\\n\\n\");\n }\n\n return null;\n}\n","/**\n * Tool Calling System for Gerbil\n *\n * Enables LLMs to call functions/tools during generation.\n * Compatible with Qwen3's tool calling format.\n */\n\nimport { z } from \"zod\";\nimport { getDocTopics, searchDocs } from \"./docs.js\";\nimport { zodToJsonSchema } from \"./utils.js\";\n\n// ============================================\n// Tool Context (for passing gerbil to tools)\n// ============================================\n\nexport type ToolContext = {\n /** Generate text using the loaded model */\n generate: (prompt: string) => Promise<string>;\n};\n\nlet toolContext: ToolContext | null = null;\n\n/**\n * Set the tool context (call this after loading a model)\n */\nexport function setToolContext(ctx: ToolContext): void {\n toolContext = ctx;\n}\n\n/**\n * Get the tool context for use in tools\n */\nexport function getToolContext(): ToolContext | null {\n return toolContext;\n}\n\n// ============================================\n// Tool Definition\n// ============================================\n\nexport type ToolDefinition<TParams = unknown> = {\n /** Tool name (function name) */\n name: string;\n\n /** Description for the LLM */\n description: string;\n\n /** Zod schema for parameters */\n parameters?: z.ZodType<TParams>;\n\n /** The function to execute (receives params and optional context) */\n execute: (params: TParams, ctx?: ToolContext | null) => Promise<string>;\n};\n\nexport type Tool<TParams = unknown> = {\n definition: ToolDefinition<TParams>;\n (params: TParams): Promise<string>;\n};\n\n// ============================================\n// Tool Registry\n// ============================================\n\nconst toolRegistry = new Map<string, Tool<any>>();\n\n/**\n * Define a tool\n */\nexport function defineTool<TParams>(def: ToolDefinition<TParams>): Tool<TParams> {\n const tool = async (params: TParams): Promise<string> => {\n // Validate params if schema provided\n if (def.parameters) {\n const parsed = def.parameters.parse(params);\n return def.execute(parsed, toolContext);\n }\n return def.execute(params, toolContext);\n };\n\n (tool as Tool<TParams>).definition = def;\n\n // Auto-register\n toolRegistry.set(def.name, tool as Tool<any>);\n\n return tool as Tool<TParams>;\n}\n\n/**\n * Get tool by name\n */\nexport function getTool(name: string): Tool<any> | undefined {\n return toolRegistry.get(name);\n}\n\n/**\n * List all tools\n */\nexport function listTools(): string[] {\n return Array.from(toolRegistry.keys());\n}\n\n/**\n * Get all tool definitions for prompt injection\n */\nexport function getToolDefinitions(): ToolDefinition<any>[] {\n return Array.from(toolRegistry.values()).map((t) => t.definition);\n}\n\nexport type LoadedToolResult = {\n name: string;\n loaded: boolean;\n error?: string;\n path: string;\n};\n\n/**\n * Simple tool config (no imports needed in tool files)\n */\nexport type SimpleToolConfig = {\n name: string;\n description: string;\n execute: (params: Record<string, any>) => Promise<string> | string;\n};\n\n/**\n * Load tools from a directory\n * Tools are simple .ts files with a default export config object\n * No module imports - we eval the execute function directly\n */\nexport async function loadTools(dir: string): Promise<LoadedToolResult[]> {\n const fs = await import(\"node:fs\");\n const pathModule = await import(\"node:path\");\n\n if (!fs.existsSync(dir)) {\n return [];\n }\n\n const files = fs.readdirSync(dir).filter((f) => f.endsWith(\".tool.ts\") || f.endsWith(\".tool.js\"));\n const results: LoadedToolResult[] = [];\n\n for (const file of files) {\n const filePath = pathModule.join(dir, file);\n const toolName = file.replace(/\\.tool\\.(ts|js)$/, \"\");\n\n try {\n // Read file content directly\n const content = fs.readFileSync(filePath, \"utf-8\");\n\n // Extract the config object using simple parsing\n const config = parseToolFile(content, toolName);\n\n if (config) {\n // Register the tool\n defineTool({\n name: config.name,\n description: config.description,\n execute: async (params, ctx) => {\n // Project tools may or may not accept ctx parameter\n const executeFn = config.execute as any;\n const result = await executeFn(params, ctx);\n return typeof result === \"string\" ? result : String(result);\n },\n });\n results.push({ name: config.name, loaded: true, path: filePath });\n } else {\n results.push({\n name: toolName,\n loaded: false,\n error: \"Could not parse tool config\",\n path: filePath,\n });\n }\n } catch (e) {\n const errorMsg = e instanceof Error ? e.message.split(\"\\n\")[0] : String(e);\n results.push({ name: toolName, loaded: false, error: errorMsg, path: filePath });\n }\n }\n\n return results;\n}\n\n/**\n * Parse a simple tool file and extract the config\n * Expected format:\n * ```\n * export default {\n * name: \"tool_name\",\n * description: \"...\",\n * execute: async (params) => { ... }\n * }\n * ```\n */\nfunction parseToolFile(content: string, fallbackName: string): SimpleToolConfig | null {\n try {\n // Remove TypeScript type annotations for eval\n let cleaned = content\n .replace(/:\\s*\\{[^}]+\\}/g, \"\") // Remove type objects like : { theme?: string }\n .replace(/:\\s*Record<[^>]+>/g, \"\") // Remove Record types\n .replace(/:\\s*string\\b/g, \"\")\n .replace(/:\\s*number\\b/g, \"\")\n .replace(/:\\s*boolean\\b/g, \"\")\n .replace(/:\\s*any\\b/g, \"\")\n .replace(/:\\s*Promise<[^>]+>/g, \"\")\n .replace(/export\\s+default\\s+/, \"return \");\n\n // Remove comments\n cleaned = cleaned.replace(/\\/\\/.*$/gm, \"\").replace(/\\/\\*[\\s\\S]*?\\*\\//g, \"\");\n\n // Wrap in function and evaluate\n const fn = new Function(cleaned);\n const config = fn();\n\n if (config && typeof config === \"object\" && config.name && config.execute) {\n return {\n name: config.name || fallbackName,\n description: config.description || \"No description\",\n execute: config.execute,\n };\n }\n return null;\n } catch (_e) {\n return null;\n }\n}\n\n/**\n * Load tools from project .gerbil/tools directory\n */\nexport async function loadProjectTools(): Promise<LoadedToolResult[]> {\n const pathModule = await import(\"node:path\");\n const toolsDir = pathModule.join(process.cwd(), \".gerbil\", \"tools\");\n return loadTools(toolsDir);\n}\n\n// Re-export for backwards compatibility\nexport { zodToJsonSchema } from \"./utils.js\";\n\n/**\n * Format tools for Qwen3 prompt\n */\nexport function formatToolsForPrompt(tools: ToolDefinition<any>[]): string {\n if (tools.length === 0) {\n return \"\";\n }\n\n const toolDefs = tools\n .map((t) => {\n const params = t.parameters\n ? zodToJsonSchema(t.parameters)\n : { type: \"object\", properties: {} };\n return `## ${t.name}\n\nDescription: ${t.description}\nParameters: ${JSON.stringify(params, null, 2)}`;\n })\n .join(\"\\n\\n\");\n\n return `You are a helpful assistant with access to tools.\n\n# Tools\n\n${toolDefs}\n\n## How to call tools\n\nTo call a tool, use this exact format:\n<tool_call>\n{\"name\": \"tool_name\", \"arguments\": {\"param\": \"value\"}}\n</tool_call>\n\nWhen the user asks about Gerbil (documentation, features, how to use), call gerbil_docs with a topic like: tools, skills, ai-sdk, next, express, generate, stream, models, cli.\n\nFor other questions, respond normally without calling tools.`;\n}\n\n/**\n * Parse tool call from response\n */\nexport function parseToolCall(response: string): { tool: string; params: any } | null {\n try {\n // Try <tool_call> format first (Qwen style)\n const toolCallMatch = response.match(/<tool_call>\\s*([\\s\\S]*?)\\s*<\\/tool_call>/);\n if (toolCallMatch) {\n const parsed = JSON.parse(toolCallMatch[1]);\n if (parsed.name) {\n return { tool: parsed.name, params: parsed.arguments || {} };\n }\n }\n\n // Fallback: look for {\"name\": \"...\", \"arguments\": ...} pattern\n const nameArgsMatch = response.match(\n /\\{\\s*\"name\"\\s*:\\s*\"([^\"]+)\"[^}]*\"arguments\"\\s*:\\s*(\\{[^}]*\\})/,\n );\n if (nameArgsMatch) {\n return { tool: nameArgsMatch[1], params: JSON.parse(nameArgsMatch[2]) };\n }\n\n // Fallback: {\"tool\": \"...\", \"params\": ...} pattern\n const toolParamsMatch = response.match(\n /\\{\\s*\"tool\"\\s*:\\s*\"([^\"]+)\"[^}]*\"params\"\\s*:\\s*(\\{[^}]*\\})/,\n );\n if (toolParamsMatch) {\n return { tool: toolParamsMatch[1], params: JSON.parse(toolParamsMatch[2]) };\n }\n\n // Simple fallback\n const simpleMatch = response.match(/\\{\\s*\"(?:tool|name)\"\\s*:\\s*\"([^\"]+)\"/);\n if (simpleMatch) {\n return { tool: simpleMatch[1], params: {} };\n }\n } catch {}\n return null;\n}\n\n/**\n * Execute a tool call\n */\nexport async function executeToolCall(toolName: string, params: any): Promise<string> {\n const tool = getTool(toolName);\n if (!tool) {\n return `Error: Unknown tool \"${toolName}\"`;\n }\n\n try {\n return await tool(params);\n } catch (e) {\n return `Error executing ${toolName}: ${e}`;\n }\n}\n\n// ============================================\n// Built-in Tools\n// ============================================\n\n/**\n * Docs tool - searches gerbil documentation\n * Documentation content is in core/docs.ts\n */\nexport const docsTool = defineTool({\n name: \"gerbil_docs\",\n description:\n \"Search Gerbil documentation for information about features, API, integrations, skills, and usage examples\",\n parameters: z.object({\n query: z\n .string()\n .optional()\n .default(\"quickstart\")\n .describe(\n \"Topic: quickstart, generate, stream, json, thinking, embed, models, ai-sdk, next, express, react, skills, define-skill, cli, tools\",\n ),\n }),\n execute: async ({ query = \"quickstart\" }) => {\n const result = searchDocs(query);\n if (result) {\n return result;\n }\n const topics = getDocTopics().join(\", \");\n return `No documentation found for \"${query}\". Available topics: ${topics}`;\n },\n});\n"],"mappings":"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"}
@@ -1,3 +1,3 @@
1
- import { a as TTS_MODELS, c as listTTSModels, i as SupertonicTTS, n as KokoroTTS, o as createTTS, r as SUPERTONIC_VOICES, s as getTTSModelConfig, t as KOKORO_VOICES } from "./tts-DG6denWG.mjs";
1
+ import { a as TTS_MODELS, c as listTTSModels, i as SupertonicTTS, n as KokoroTTS, o as createTTS, r as SUPERTONIC_VOICES, s as getTTSModelConfig, t as KOKORO_VOICES } from "./tts-CyHhcLtN.mjs";
2
2
 
3
3
  export { TTS_MODELS, createTTS };
@@ -344,7 +344,7 @@ var KokoroTTS = class {
344
344
  const { onProgress, device = "auto" } = options;
345
345
  onProgress?.({ status: `Loading TTS model (${this.modelConfig.id})...` });
346
346
  try {
347
- const { KokoroTTS: KokoroJS } = await import("kokoro-js");
347
+ const { KokoroTTS: KokoroJS } = await import("./kokoro-BNTb6egA.mjs");
348
348
  const isBrowser = typeof window !== "undefined";
349
349
  let dtype = "fp32";
350
350
  if (device === "webgpu" || device === "auto" && isBrowser && "gpu" in navigator) {
@@ -576,8 +576,10 @@ var SupertonicTTS = class {
576
576
  const { onProgress, device = "auto" } = options;
577
577
  onProgress?.({ status: `Loading TTS model (${this.modelConfig.id})...` });
578
578
  try {
579
- const { pipeline } = await import("@huggingface/transformers");
580
- if (device === "webgpu" || device === "auto" && typeof window !== "undefined" && "gpu" in navigator) this._deviceMode = "webgpu";
579
+ const isBrowser = typeof window !== "undefined";
580
+ const { pipeline, env } = await import("@huggingface/transformers");
581
+ if (isBrowser && env.backends?.onnx?.wasm) env.backends.onnx.wasm.wasmPaths = "https://cdn.jsdelivr.net/npm/onnxruntime-web@1.21.0/dist/";
582
+ if (device === "webgpu" || device === "auto" && isBrowser && "gpu" in navigator) this._deviceMode = "webgpu";
581
583
  else this._deviceMode = "cpu";
582
584
  onProgress?.({ status: `Loading Supertonic model...` });
583
585
  this.pipeline = await pipeline("text-to-speech", this.modelConfig.repo, {
@@ -726,4 +728,4 @@ function createTTS(modelId = "kokoro-82m") {
726
728
 
727
729
  //#endregion
728
730
  export { TTS_MODELS as a, listTTSModels as c, SupertonicTTS as i, KokoroTTS as n, createTTS as o, SUPERTONIC_VOICES as r, getTTSModelConfig as s, KOKORO_VOICES as t };
729
- //# sourceMappingURL=tts-DG6denWG.mjs.map
731
+ //# sourceMappingURL=tts-CyHhcLtN.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tts-CyHhcLtN.mjs","names":["KOKORO_VOICES: VoiceInfo[]","SUPERTONIC_VOICES: VoiceInfo[]","TTS_MODELS: Record<string, TTSModelConfig>","dtype: \"fp32\" | \"fp16\" | \"q8\" | \"q4\" | \"q4f16\"","allAudio: Float32Array[]","chunk: AudioChunk"],"sources":["../src/core/tts.ts"],"sourcesContent":["/**\n * Text-to-Speech with Kokoro-82M\n *\n * Provides local TTS using the Kokoro-82M model with multiple voice options.\n * Uses kokoro-js for proper phoneme conversion (G2P) and audio generation.\n * Supports streaming audio generation and works in both Node.js and browser.\n *\n * @example\n * ```ts\n * const tts = new KokoroTTS();\n * await tts.load({ onProgress: (p) => console.log(p.status) });\n *\n * // List available voices\n * const voices = tts.listVoices();\n *\n * // Generate audio\n * const result = await tts.speak(\"Hello world\", { voice: \"af_bella\" });\n * // result.audio = Float32Array, result.sampleRate = 24000\n *\n * // Stream audio chunks\n * for await (const chunk of tts.speakStream(\"Long text...\")) {\n * playAudioChunk(chunk);\n * }\n * ```\n */\n\nimport type {\n AudioChunk,\n LoadTTSOptions,\n SpeakOptions,\n SpeakResult,\n TTSModelConfig,\n VoiceInfo,\n} from \"./types.js\";\n\n// Regex for sentence splitting (defined at module level for performance)\nconst SENTENCE_SPLIT_REGEX = /(?<=[.!?])\\s+/;\n\n// ============================================\n// Voice Registry\n// ============================================\n\n/**\n * Kokoro voice definitions\n * Voice IDs follow pattern: {language}{gender}_{name}\n * - a = American English\n * - b = British English\n * - f = female, m = male\n */\nexport const KOKORO_VOICES: VoiceInfo[] = [\n // American English - Female (ordered by quality)\n {\n id: \"af_heart\",\n name: \"Heart\",\n gender: \"female\",\n language: \"en-us\",\n description: \"American female, highest quality voice (Grade A)\",\n embeddingFile: \"voices/af_heart.bin\",\n },\n {\n id: \"af_bella\",\n name: \"Bella\",\n gender: \"female\",\n language: \"en-us\",\n description: \"American female, warm and friendly (Grade A-)\",\n embeddingFile: \"voices/af_bella.bin\",\n },\n {\n id: \"af_nicole\",\n name: \"Nicole\",\n gender: \"female\",\n language: \"en-us\",\n description: \"American female, soft and gentle (Grade B-)\",\n embeddingFile: \"voices/af_nicole.bin\",\n },\n {\n id: \"af_sarah\",\n name: \"Sarah\",\n gender: \"female\",\n language: \"en-us\",\n description: \"American female, clear and professional (Grade C+)\",\n embeddingFile: \"voices/af_sarah.bin\",\n },\n {\n id: \"af_sky\",\n name: \"Sky\",\n gender: \"female\",\n language: \"en-us\",\n description: \"American female, young and energetic (Grade C-)\",\n embeddingFile: \"voices/af_sky.bin\",\n },\n {\n id: \"af_alloy\",\n name: \"Alloy\",\n gender: \"female\",\n language: \"en-us\",\n description: \"American female (Grade C)\",\n embeddingFile: \"voices/af_alloy.bin\",\n },\n {\n id: \"af_aoede\",\n name: \"Aoede\",\n gender: \"female\",\n language: \"en-us\",\n description: \"American female (Grade C+)\",\n embeddingFile: \"voices/af_aoede.bin\",\n },\n {\n id: \"af_kore\",\n name: \"Kore\",\n gender: \"female\",\n language: \"en-us\",\n description: \"American female (Grade C+)\",\n embeddingFile: \"voices/af_kore.bin\",\n },\n {\n id: \"af_nova\",\n name: \"Nova\",\n gender: \"female\",\n language: \"en-us\",\n description: \"American female (Grade C)\",\n embeddingFile: \"voices/af_nova.bin\",\n },\n {\n id: \"af_river\",\n name: \"River\",\n gender: \"female\",\n language: \"en-us\",\n description: \"American female (Grade D)\",\n embeddingFile: \"voices/af_river.bin\",\n },\n {\n id: \"af_jessica\",\n name: \"Jessica\",\n gender: \"female\",\n language: \"en-us\",\n description: \"American female (Grade D)\",\n embeddingFile: \"voices/af_jessica.bin\",\n },\n // American English - Male\n {\n id: \"am_fenrir\",\n name: \"Fenrir\",\n gender: \"male\",\n language: \"en-us\",\n description: \"American male, best quality (Grade C+)\",\n embeddingFile: \"voices/am_fenrir.bin\",\n },\n {\n id: \"am_michael\",\n name: \"Michael\",\n gender: \"male\",\n language: \"en-us\",\n description: \"American male, warm and friendly (Grade C+)\",\n embeddingFile: \"voices/am_michael.bin\",\n },\n {\n id: \"am_puck\",\n name: \"Puck\",\n gender: \"male\",\n language: \"en-us\",\n description: \"American male (Grade C+)\",\n embeddingFile: \"voices/am_puck.bin\",\n },\n {\n id: \"am_adam\",\n name: \"Adam\",\n gender: \"male\",\n language: \"en-us\",\n description: \"American male, deep voice (Grade F+)\",\n embeddingFile: \"voices/am_adam.bin\",\n },\n {\n id: \"am_echo\",\n name: \"Echo\",\n gender: \"male\",\n language: \"en-us\",\n description: \"American male (Grade D)\",\n embeddingFile: \"voices/am_echo.bin\",\n },\n {\n id: \"am_eric\",\n name: \"Eric\",\n gender: \"male\",\n language: \"en-us\",\n description: \"American male (Grade D)\",\n embeddingFile: \"voices/am_eric.bin\",\n },\n {\n id: \"am_liam\",\n name: \"Liam\",\n gender: \"male\",\n language: \"en-us\",\n description: \"American male (Grade D)\",\n embeddingFile: \"voices/am_liam.bin\",\n },\n {\n id: \"am_onyx\",\n name: \"Onyx\",\n gender: \"male\",\n language: \"en-us\",\n description: \"American male (Grade D)\",\n embeddingFile: \"voices/am_onyx.bin\",\n },\n {\n id: \"am_santa\",\n name: \"Santa\",\n gender: \"male\",\n language: \"en-us\",\n description: \"American male, festive (Grade D-)\",\n embeddingFile: \"voices/am_santa.bin\",\n },\n // British English - Female\n {\n id: \"bf_emma\",\n name: \"Emma\",\n gender: \"female\",\n language: \"en-gb\",\n description: \"British female, elegant and clear (Grade B-)\",\n embeddingFile: \"voices/bf_emma.bin\",\n },\n {\n id: \"bf_isabella\",\n name: \"Isabella\",\n gender: \"female\",\n language: \"en-gb\",\n description: \"British female, sophisticated (Grade C)\",\n embeddingFile: \"voices/bf_isabella.bin\",\n },\n {\n id: \"bf_alice\",\n name: \"Alice\",\n gender: \"female\",\n language: \"en-gb\",\n description: \"British female (Grade D)\",\n embeddingFile: \"voices/bf_alice.bin\",\n },\n {\n id: \"bf_lily\",\n name: \"Lily\",\n gender: \"female\",\n language: \"en-gb\",\n description: \"British female (Grade D)\",\n embeddingFile: \"voices/bf_lily.bin\",\n },\n // British English - Male\n {\n id: \"bm_george\",\n name: \"George\",\n gender: \"male\",\n language: \"en-gb\",\n description: \"British male, distinguished (Grade C)\",\n embeddingFile: \"voices/bm_george.bin\",\n },\n {\n id: \"bm_fable\",\n name: \"Fable\",\n gender: \"male\",\n language: \"en-gb\",\n description: \"British male (Grade C)\",\n embeddingFile: \"voices/bm_fable.bin\",\n },\n {\n id: \"bm_lewis\",\n name: \"Lewis\",\n gender: \"male\",\n language: \"en-gb\",\n description: \"British male, friendly (Grade D+)\",\n embeddingFile: \"voices/bm_lewis.bin\",\n },\n {\n id: \"bm_daniel\",\n name: \"Daniel\",\n gender: \"male\",\n language: \"en-gb\",\n description: \"British male (Grade D)\",\n embeddingFile: \"voices/bm_daniel.bin\",\n },\n];\n\n// ============================================\n// Supertonic Voice Registry\n// ============================================\n\n/**\n * Supertonic voice definitions\n * 4 built-in voices: F1, F2 (female), M1, M2 (male)\n */\nexport const SUPERTONIC_VOICES: VoiceInfo[] = [\n {\n id: \"F1\",\n name: \"Female 1\",\n gender: \"female\",\n language: \"en\",\n description: \"Female voice 1 - Clear and natural\",\n embeddingFile: \"voices/F1.bin\",\n },\n {\n id: \"F2\",\n name: \"Female 2\",\n gender: \"female\",\n language: \"en\",\n description: \"Female voice 2 - Warm and expressive\",\n embeddingFile: \"voices/F2.bin\",\n },\n {\n id: \"M1\",\n name: \"Male 1\",\n gender: \"male\",\n language: \"en\",\n description: \"Male voice 1 - Deep and confident\",\n embeddingFile: \"voices/M1.bin\",\n },\n {\n id: \"M2\",\n name: \"Male 2\",\n gender: \"male\",\n language: \"en\",\n description: \"Male voice 2 - Friendly and casual\",\n embeddingFile: \"voices/M2.bin\",\n },\n];\n\n// ============================================\n// TTS Model Registry\n// ============================================\n\nexport const TTS_MODELS: Record<string, TTSModelConfig> = {\n \"kokoro-82m\": {\n id: \"kokoro-82m\",\n repo: \"onnx-community/Kokoro-82M-v1.0-ONNX\",\n description: \"Kokoro 82M - High-quality multilingual TTS\",\n size: \"~330MB\",\n sampleRate: 24000,\n voices: KOKORO_VOICES,\n defaultVoice: \"af_heart\",\n languages: [\"en-us\", \"en-gb\"],\n },\n \"supertonic-66m\": {\n id: \"supertonic-66m\",\n repo: \"onnx-community/Supertonic-TTS-ONNX\",\n description: \"Supertonic 66M - Fast on-device TTS (167x realtime)\",\n size: \"~250MB\",\n sampleRate: 44100,\n voices: SUPERTONIC_VOICES,\n defaultVoice: \"F1\",\n languages: [\"en\"],\n },\n};\n\n/**\n * Get TTS model config by ID\n */\nexport function getTTSModelConfig(modelId: string): TTSModelConfig | null {\n return TTS_MODELS[modelId] || null;\n}\n\n/**\n * List all available TTS models\n */\nexport function listTTSModels(): TTSModelConfig[] {\n return Object.values(TTS_MODELS);\n}\n\n// ============================================\n// Kokoro TTS Class (wraps kokoro-js)\n// ============================================\n\n// kokoro-js types\ninterface KokoroJSAudio {\n audio: Float32Array;\n sampling_rate: number;\n}\n\ninterface KokoroJSInstance {\n generate(text: string, options?: { voice?: string; speed?: number }): Promise<KokoroJSAudio>;\n list_voices(): Array<{\n name: string;\n language: string;\n gender: string;\n traits: string;\n targetQuality: string;\n overallGrade: string;\n }>;\n}\n\n/**\n * Kokoro TTS - Local text-to-speech with voice selection\n *\n * Uses kokoro-js (official Kokoro library by xenova) for high-quality speech synthesis.\n * Includes proper G2P (grapheme-to-phoneme) conversion for accurate pronunciation.\n */\nexport class KokoroTTS {\n private kokoroInstance: KokoroJSInstance | null = null;\n private modelConfig: TTSModelConfig;\n private loadPromise: Promise<void> | null = null;\n private _isLoaded = false;\n private _deviceMode: \"webgpu\" | \"cpu\" = \"cpu\";\n\n constructor(modelId = \"kokoro-82m\") {\n const config = getTTSModelConfig(modelId);\n if (!config) {\n throw new Error(\n `Unknown TTS model: ${modelId}. Available: ${Object.keys(TTS_MODELS).join(\", \")}`,\n );\n }\n this.modelConfig = config;\n }\n\n // ============================================\n // Model Loading\n // ============================================\n\n /**\n * Load the TTS model\n *\n * @example\n * ```ts\n * const tts = new KokoroTTS();\n * await tts.load({\n * onProgress: (p) => console.log(p.status, p.progress),\n * device: \"webgpu\",\n * });\n * ```\n */\n async load(options: LoadTTSOptions = {}): Promise<void> {\n if (this._isLoaded) {\n return;\n }\n\n // Prevent duplicate loads\n if (this.loadPromise) {\n return this.loadPromise;\n }\n\n this.loadPromise = this._load(options);\n await this.loadPromise;\n }\n\n private async _load(options: LoadTTSOptions = {}): Promise<void> {\n const { onProgress, device = \"auto\" } = options;\n\n onProgress?.({ status: `Loading TTS model (${this.modelConfig.id})...` });\n\n try {\n // Dynamically import kokoro-js\n // tsdown handles resolution: Node.js = external, Browser = bundled\n const kokoroModule = await import(\"kokoro-js\");\n const { KokoroTTS: KokoroJS } = kokoroModule;\n\n // Determine device/dtype\n const isBrowser = typeof window !== \"undefined\";\n let dtype: \"fp32\" | \"fp16\" | \"q8\" | \"q4\" | \"q4f16\" = \"fp32\";\n\n if (device === \"webgpu\" || (device === \"auto\" && isBrowser && \"gpu\" in navigator)) {\n dtype = \"fp16\";\n this._deviceMode = \"webgpu\";\n } else {\n dtype = \"fp32\";\n this._deviceMode = \"cpu\";\n }\n\n onProgress?.({ status: `Loading model with ${dtype} precision...` });\n\n // Load the model using kokoro-js\n // kokoro-js handles:\n // - Model loading with proper quantization\n // - Phoneme tokenization (G2P) via phonemizer\n // - Voice embeddings\n this.kokoroInstance = (await KokoroJS.from_pretrained(this.modelConfig.repo, {\n dtype,\n progress_callback: (progress: any) => {\n if (progress.status === \"progress\" && progress.file) {\n onProgress?.({\n status: `Downloading ${progress.file}`,\n progress: Math.round(progress.progress || 0),\n file: progress.file,\n });\n } else if (progress.status === \"ready\") {\n onProgress?.({ status: \"Model ready\" });\n }\n },\n })) as unknown as KokoroJSInstance;\n\n this._isLoaded = true;\n onProgress?.({ status: `Ready (${this._deviceMode.toUpperCase()})!` });\n } catch (error) {\n this.loadPromise = null;\n throw error;\n }\n }\n\n /**\n * Ensure model is loaded (lazy loading)\n */\n async ensureLoaded(options?: LoadTTSOptions): Promise<void> {\n if (!this._isLoaded) {\n await this.load(options);\n }\n }\n\n // ============================================\n // Voice Management\n // ============================================\n\n /**\n * Get list of available voices\n *\n * @example\n * ```ts\n * const voices = tts.listVoices();\n * // [{ id: \"af_heart\", name: \"Heart\", gender: \"female\", ... }, ...]\n * ```\n */\n listVoices(): VoiceInfo[] {\n return [...this.modelConfig.voices];\n }\n\n /**\n * Get a specific voice by ID\n */\n getVoice(voiceId: string): VoiceInfo | null {\n return this.modelConfig.voices.find((v) => v.id === voiceId) || null;\n }\n\n /**\n * Get voices by gender\n */\n getVoicesByGender(gender: \"male\" | \"female\"): VoiceInfo[] {\n return this.modelConfig.voices.filter((v) => v.gender === gender);\n }\n\n /**\n * Get voices by language\n */\n getVoicesByLanguage(language: string): VoiceInfo[] {\n return this.modelConfig.voices.filter(\n (v) => v.language === language || v.language.startsWith(language),\n );\n }\n\n // ============================================\n // Speech Generation\n // ============================================\n\n /**\n * Generate speech from text\n *\n * @example\n * ```ts\n * const result = await tts.speak(\"Hello world\", {\n * voice: \"af_heart\",\n * speed: 1.0,\n * });\n *\n * // Play in browser\n * const audioContext = new AudioContext();\n * const buffer = audioContext.createBuffer(1, result.audio.length, result.sampleRate);\n * buffer.copyToChannel(result.audio, 0);\n * const source = audioContext.createBufferSource();\n * source.buffer = buffer;\n * source.connect(audioContext.destination);\n * source.start();\n * ```\n */\n async speak(text: string, options: SpeakOptions = {}): Promise<SpeakResult> {\n await this.ensureLoaded({ onProgress: options.onProgress });\n\n const { voice = this.modelConfig.defaultVoice, speed = 1.0 } = options;\n\n // Validate voice exists in our registry\n const voiceInfo = this.getVoice(voice);\n if (!voiceInfo) {\n throw new Error(`Unknown voice: ${voice}. Use listVoices() to see available options.`);\n }\n\n if (!this.kokoroInstance) {\n throw new Error(\"Model not loaded\");\n }\n\n const startTime = performance.now();\n\n // Generate audio using kokoro-js\n const result = await this.kokoroInstance.generate(text, {\n voice,\n speed,\n });\n\n const totalTime = performance.now() - startTime;\n\n return {\n audio: result.audio,\n sampleRate: result.sampling_rate,\n duration: result.audio.length / result.sampling_rate,\n voice,\n totalTime,\n };\n }\n\n /**\n * Stream speech generation (yields audio chunks as they're generated)\n *\n * @example\n * ```ts\n * for await (const chunk of tts.speakStream(\"Long text...\")) {\n * // chunk.samples = Float32Array\n * // chunk.sampleRate = 24000\n * // chunk.isFinal = boolean\n * playChunk(chunk);\n * }\n * ```\n */\n async *speakStream(\n text: string,\n options: SpeakOptions = {},\n ): AsyncGenerator<AudioChunk, SpeakResult, unknown> {\n await this.ensureLoaded({ onProgress: options.onProgress });\n\n const { voice = this.modelConfig.defaultVoice, speed = 1.0 } = options;\n\n // Validate voice\n const voiceInfo = this.getVoice(voice);\n if (!voiceInfo) {\n throw new Error(`Unknown voice: ${voice}. Use listVoices() to see available options.`);\n }\n\n if (!this.kokoroInstance) {\n throw new Error(\"Model not loaded\");\n }\n\n const startTime = performance.now();\n\n // Split text into sentences for streaming\n const sentences = this.splitIntoSentences(text);\n const allAudio: Float32Array[] = [];\n let chunkIndex = 0;\n let sampleRate = this.modelConfig.sampleRate;\n\n for (let i = 0; i < sentences.length; i++) {\n const sentence = sentences[i];\n if (!sentence.trim()) {\n continue;\n }\n\n const result = await this.kokoroInstance.generate(sentence, {\n voice,\n speed,\n });\n\n sampleRate = result.sampling_rate;\n allAudio.push(result.audio);\n\n const chunk: AudioChunk = {\n samples: result.audio,\n sampleRate: result.sampling_rate,\n index: chunkIndex++,\n isFinal: i === sentences.length - 1,\n };\n\n yield chunk;\n options.onAudioChunk?.(chunk);\n }\n\n // Concatenate all audio\n const totalLength = allAudio.reduce((sum, arr) => sum + arr.length, 0);\n const fullAudio = new Float32Array(totalLength);\n let offset = 0;\n for (const chunk of allAudio) {\n fullAudio.set(chunk, offset);\n offset += chunk.length;\n }\n\n const totalTime = performance.now() - startTime;\n\n return {\n audio: fullAudio,\n sampleRate,\n duration: fullAudio.length / sampleRate,\n voice,\n totalTime,\n };\n }\n\n /**\n * Split text into sentences for streaming\n */\n private splitIntoSentences(text: string): string[] {\n // Split on sentence boundaries while preserving the delimiters\n return text.split(SENTENCE_SPLIT_REGEX).filter((s) => s.trim());\n }\n\n // ============================================\n // Status & Info\n // ============================================\n\n /**\n * Check if model is loaded\n */\n isLoaded(): boolean {\n return this._isLoaded;\n }\n\n /**\n * Get current device mode\n */\n getDeviceMode(): \"webgpu\" | \"cpu\" {\n return this._deviceMode;\n }\n\n /**\n * Get model configuration\n */\n getModelInfo(): TTSModelConfig {\n return { ...this.modelConfig };\n }\n\n /**\n * Get sample rate\n */\n getSampleRate(): number {\n return this.modelConfig.sampleRate;\n }\n\n // ============================================\n // Cleanup\n // ============================================\n\n /**\n * Dispose of resources\n */\n async dispose(): Promise<void> {\n // kokoro-js doesn't expose a dispose method currently\n // but we clear our reference\n this.kokoroInstance = null;\n this._isLoaded = false;\n this.loadPromise = null;\n }\n}\n\n// ============================================\n// Supertonic TTS Class (uses transformers.js)\n// ============================================\n\n/**\n * Supertonic TTS - Fast on-device text-to-speech\n *\n * Uses transformers.js with the Supertonic-TTS-ONNX model.\n * Generates speech at 167x realtime with 66M parameters.\n * Outputs at 44100 Hz sample rate.\n */\nexport class SupertonicTTS {\n private pipeline: any = null;\n private modelConfig: TTSModelConfig;\n private loadPromise: Promise<void> | null = null;\n private _isLoaded = false;\n private _deviceMode: \"webgpu\" | \"cpu\" = \"cpu\";\n private voiceEmbeddings: Map<string, Float32Array> = new Map();\n\n constructor(modelId = \"supertonic-66m\") {\n const config = getTTSModelConfig(modelId);\n if (!config) {\n throw new Error(\n `Unknown TTS model: ${modelId}. Available: ${Object.keys(TTS_MODELS).join(\", \")}`,\n );\n }\n this.modelConfig = config;\n }\n\n /**\n * Load the TTS model\n */\n async load(options: LoadTTSOptions = {}): Promise<void> {\n if (this._isLoaded) {\n return;\n }\n\n if (this.loadPromise) {\n return this.loadPromise;\n }\n\n this.loadPromise = this._load(options);\n await this.loadPromise;\n }\n\n private async _load(options: LoadTTSOptions = {}): Promise<void> {\n const { onProgress, device = \"auto\" } = options;\n\n onProgress?.({ status: `Loading TTS model (${this.modelConfig.id})...` });\n\n try {\n // Determine device\n const isBrowser = typeof window !== \"undefined\";\n\n // Dynamically import transformers.js\n // tsdown handles resolution: Node.js = external, Browser = bundled\n const { pipeline, env } = await import(\"@huggingface/transformers\");\n\n // Configure WASM paths for browser (load from CDN, not bundled)\n if (isBrowser && env.backends?.onnx?.wasm) {\n env.backends.onnx.wasm.wasmPaths =\n \"https://cdn.jsdelivr.net/npm/onnxruntime-web@1.21.0/dist/\";\n }\n\n if (device === \"webgpu\" || (device === \"auto\" && isBrowser && \"gpu\" in navigator)) {\n this._deviceMode = \"webgpu\";\n } else {\n this._deviceMode = \"cpu\";\n }\n\n onProgress?.({ status: `Loading Supertonic model...` });\n\n // Create TTS pipeline\n this.pipeline = await pipeline(\"text-to-speech\", this.modelConfig.repo, {\n dtype: \"fp32\", // Supertonic works best with fp32\n device: this._deviceMode,\n progress_callback: (progress: any) => {\n if (progress.status === \"progress\" && progress.file) {\n onProgress?.({\n status: `Downloading ${progress.file}`,\n progress: Math.round(progress.progress || 0),\n file: progress.file,\n });\n }\n },\n });\n\n // Load voice embeddings\n onProgress?.({ status: \"Loading voice embeddings...\" });\n await this.loadVoiceEmbeddings();\n\n this._isLoaded = true;\n onProgress?.({ status: `Ready (${this._deviceMode.toUpperCase()})!` });\n } catch (error) {\n this.loadPromise = null;\n throw error;\n }\n }\n\n /**\n * Load speaker embeddings for all voices\n * Supertonic uses 101x128 = 12,928 floats per voice\n */\n private async loadVoiceEmbeddings(): Promise<void> {\n // In browser, we'd fetch from HuggingFace Hub\n // In Node.js, we'd load from cache\n // For now, we'll lazy-load embeddings when needed\n // The pipeline handles this internally when we pass speaker_embeddings\n }\n\n async ensureLoaded(options?: LoadTTSOptions): Promise<void> {\n if (!this._isLoaded) {\n await this.load(options);\n }\n }\n\n listVoices(): VoiceInfo[] {\n return [...this.modelConfig.voices];\n }\n\n getVoice(voiceId: string): VoiceInfo | null {\n return this.modelConfig.voices.find((v) => v.id === voiceId) || null;\n }\n\n getVoicesByGender(gender: \"male\" | \"female\"): VoiceInfo[] {\n return this.modelConfig.voices.filter((v) => v.gender === gender);\n }\n\n /**\n * Generate speech from text\n */\n async speak(text: string, options: SpeakOptions = {}): Promise<SpeakResult> {\n await this.ensureLoaded({ onProgress: options.onProgress });\n\n const { voice = this.modelConfig.defaultVoice } = options;\n\n // Validate voice\n const voiceInfo = this.getVoice(voice);\n if (!voiceInfo) {\n throw new Error(`Unknown voice: ${voice}. Use listVoices() to see available options.`);\n }\n\n if (!this.pipeline) {\n throw new Error(\"Model not loaded\");\n }\n\n const startTime = performance.now();\n\n // Get or create speaker embedding\n // Supertonic expects [101, 128] = 12,928 floats\n let speakerEmbedding = this.voiceEmbeddings.get(voice);\n if (!speakerEmbedding) {\n // Load from HuggingFace Hub via fetch\n try {\n const voiceUrl = `https://huggingface.co/${this.modelConfig.repo}/resolve/main/voices/${voice}.bin`;\n const response = await fetch(voiceUrl);\n if (response.ok) {\n const buffer = await response.arrayBuffer();\n speakerEmbedding = new Float32Array(buffer);\n this.voiceEmbeddings.set(voice, speakerEmbedding);\n } else {\n throw new Error(`Failed to load voice: ${response.status}`);\n }\n } catch {\n // Fallback: create neutral embedding (not ideal but works)\n speakerEmbedding = new Float32Array(101 * 128).fill(0.1);\n this.voiceEmbeddings.set(voice, speakerEmbedding);\n }\n }\n\n // Generate audio\n const result = await this.pipeline(text, {\n speaker_embeddings: speakerEmbedding,\n });\n\n const totalTime = performance.now() - startTime;\n const audio = result.audio as Float32Array;\n const sampleRate = result.sampling_rate as number;\n\n return {\n audio,\n sampleRate,\n duration: audio.length / sampleRate,\n voice,\n totalTime,\n };\n }\n\n /**\n * Stream speech generation\n */\n async *speakStream(\n text: string,\n options: SpeakOptions = {},\n ): AsyncGenerator<AudioChunk, SpeakResult, unknown> {\n await this.ensureLoaded({ onProgress: options.onProgress });\n\n const { voice = this.modelConfig.defaultVoice, speed = 1.0 } = options;\n\n const voiceInfo = this.getVoice(voice);\n if (!voiceInfo) {\n throw new Error(`Unknown voice: ${voice}. Use listVoices() to see available options.`);\n }\n\n const startTime = performance.now();\n\n // Split text into sentences for streaming\n const sentences = text.split(SENTENCE_SPLIT_REGEX).filter((s) => s.trim());\n const allAudio: Float32Array[] = [];\n let chunkIndex = 0;\n let sampleRate = this.modelConfig.sampleRate;\n\n for (let i = 0; i < sentences.length; i++) {\n const sentence = sentences[i];\n if (!sentence.trim()) continue;\n\n const result = await this.speak(sentence, { voice, speed });\n sampleRate = result.sampleRate;\n allAudio.push(result.audio);\n\n const chunk: AudioChunk = {\n samples: result.audio,\n sampleRate: result.sampleRate,\n index: chunkIndex++,\n isFinal: i === sentences.length - 1,\n };\n\n yield chunk;\n options.onAudioChunk?.(chunk);\n }\n\n // Concatenate all audio\n const totalLength = allAudio.reduce((sum, arr) => sum + arr.length, 0);\n const fullAudio = new Float32Array(totalLength);\n let offset = 0;\n for (const chunk of allAudio) {\n fullAudio.set(chunk, offset);\n offset += chunk.length;\n }\n\n const totalTime = performance.now() - startTime;\n\n return {\n audio: fullAudio,\n sampleRate,\n duration: fullAudio.length / sampleRate,\n voice,\n totalTime,\n };\n }\n\n isLoaded(): boolean {\n return this._isLoaded;\n }\n\n getDeviceMode(): \"webgpu\" | \"cpu\" {\n return this._deviceMode;\n }\n\n getModelInfo(): TTSModelConfig {\n return { ...this.modelConfig };\n }\n\n getSampleRate(): number {\n return this.modelConfig.sampleRate;\n }\n\n async dispose(): Promise<void> {\n this.pipeline = null;\n this.voiceEmbeddings.clear();\n this._isLoaded = false;\n this.loadPromise = null;\n }\n}\n\n// ============================================\n// Unified TTS Factory\n// ============================================\n\nexport type TTSBackend = KokoroTTS | SupertonicTTS;\n\n/**\n * Create a TTS instance based on model ID\n */\nexport function createTTS(modelId: string = \"kokoro-82m\"): TTSBackend {\n if (modelId.startsWith(\"supertonic\")) {\n return new SupertonicTTS(modelId);\n }\n return new KokoroTTS(modelId);\n}\n\nexport default KokoroTTS;\n"],"mappings":"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"}
@@ -350,4 +350,4 @@ type StreamingTranscriptionSession = {
350
350
  };
351
351
  //#endregion
352
352
  export { TranscribeSegment as A, SpeakResult as C, TTSModelConfig as D, SystemInfo as E, TranscribeOptions as O, SpeakOptions as S, StreamingTranscriptionSession as T, ModelSource as _, FallbackConfig as a, STTModelConfig as b, GerbilConfig as c, ImageInput as d, JsonOptions as f, ModelConfig as g, LoadTTSOptions as h, EmbedResult as i, VoiceInfo as j, TranscribeResult as k, GerbilModelSettings as l, LoadSTTOptions as m, CacheConfig as n, GenerateOptions as o, LoadOptions as p, EmbedOptions as r, GenerateResult as s, AudioChunk as t, GerbilProviderSettings as u, ModelStats as v, StreamingTranscriptionOptions as w, SessionStats as x, ProgressInfo as y };
353
- //# sourceMappingURL=types-s6Py2_DL.d.mts.map
353
+ //# sourceMappingURL=types-CiTc7ez3.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types-s6Py2_DL.d.mts","names":[],"sources":["../src/core/types.ts"],"sourcesContent":[],"mappings":";;;;AAyBY,KAfA,WAAA,GAeW;EASX,EAAA,EAAA,MAAA;EAWA,IAAA,EAAA,MAAA;EAmCA,WAAA,EAAA,MAAc;EA8Bd,IAAA,EAAA,MAAA;EAkBA,aAAA,EAAY,MAAA;EAQZ,gBAAW,EAAA,OAAA;EAeX,YAAA,EAAW,OAAA;EAcX;EAYA,cAAA,CAAY,EAAA,OAAA;EAiBZ;EAiBA,iBAAc,CAAA,EAAA,MAAA;EAqBd,MAAA,EAAA,MAAA,GAAY,QAAA,GAAA,KAAA,GAAA,SAAA,GAAA,OAAA,GAAA,OAAA;AAUxB,CAAA;AAOY,KAhOA,WAAA,GAgOU;EAyBV,IAAA,EAAA,SAAA,GAAA,aAAmB,GAAA,OAAA;EAWnB,IAAA,EAAA,MAAA;AAYZ,CAAA;AAeY,KAtRA,UAAA,GAsRc;EAmBd;EAWA,MAAA,EAAA,MAAU;EAWV;EAaA,GAAA,CAAA,EAAA,MAAA;AAWZ,CAAA;AAiBY,KA7VA,eAAA,GA6ViB;EASjB;EASA,SAAA,CAAA,EAAA,MAAA;EAaA;EAWA,WAAA,CAAA,EAAA,MAAA;EAeA;EAES,IAAA,CAAA,EAAA,MAAA;EAEN;EAID,IAAA,CAAA,EAAA,MAAA;EAAO;;;;;;;;;WApYV;;;;;;KASC,cAAA;;;;;;;;;;;;;;;;;;KA8BA;;UAEF,CAAA,CAAE,QAAQ;;;;;;;;KAgBR,YAAA;;;;;;KAQA,WAAA;;;;;;;;KAeA,WAAA;;sBAEU;;;;;;;;KAYV,YAAA;;;;;;;KAYA,YAAA;;;;;;;;UAWF;;aAGG;;KAGD,WAAA;;;;;;;;;;;;KAiBA,cAAA;;;;;;;;;;;;KAqBA,YAAA;;;;;;;;;KAUA,UAAA;;;;;;KAOA,UAAA;;SAEH;;;;;;;;;;;;;;;;;;KAuBG,mBAAA;;;;;;;;KAWA,sBAAA;;;;;;KAYA,SAAA;;;;;;;;;;;;;;KAeA,cAAA;;;;;;;;;;;;UAYF;;;;;;KAOE,YAAA;;;;;;sBAMU;;yBAEG;;KAGb,UAAA;;WAED;;;;;;;;KASC,WAAA;;SAEH;;;;;;;;;;KAWG,cAAA;;sBAEU;;;;KASV,cAAA;;;;;;;;;;;;;;;;KAiBA,iBAAA;;;;;;sBAMU;;KAGV,iBAAA;;;;;;;;KASA,gBAAA;;;;;;aAMC;;;;;;KAOD,cAAA;;sBAEU;;;;KASV,6BAAA;;;;;;;;;;;;;;KAeA,6BAAA;;qBAES;;eAEN;;;;cAID"}
1
+ {"version":3,"file":"types-CiTc7ez3.d.mts","names":[],"sources":["../src/core/types.ts"],"sourcesContent":[],"mappings":";;;;AAyBY,KAfA,WAAA,GAeW;EASX,EAAA,EAAA,MAAA;EAWA,IAAA,EAAA,MAAA;EAmCA,WAAA,EAAA,MAAc;EA8Bd,IAAA,EAAA,MAAA;EAkBA,aAAA,EAAY,MAAA;EAQZ,gBAAW,EAAA,OAAA;EAeX,YAAA,EAAW,OAAA;EAcX;EAYA,cAAA,CAAY,EAAA,OAAA;EAiBZ;EAiBA,iBAAc,CAAA,EAAA,MAAA;EAqBd,MAAA,EAAA,MAAA,GAAY,QAAA,GAAA,KAAA,GAAA,SAAA,GAAA,OAAA,GAAA,OAAA;AAUxB,CAAA;AAOY,KAhOA,WAAA,GAgOU;EAyBV,IAAA,EAAA,SAAA,GAAA,aAAmB,GAAA,OAAA;EAWnB,IAAA,EAAA,MAAA;AAYZ,CAAA;AAeY,KAtRA,UAAA,GAsRc;EAmBd;EAWA,MAAA,EAAA,MAAU;EAWV;EAaA,GAAA,CAAA,EAAA,MAAA;AAWZ,CAAA;AAiBY,KA7VA,eAAA,GA6ViB;EASjB;EASA,SAAA,CAAA,EAAA,MAAA;EAaA;EAWA,WAAA,CAAA,EAAA,MAAA;EAeA;EAES,IAAA,CAAA,EAAA,MAAA;EAEN;EAID,IAAA,CAAA,EAAA,MAAA;EAAO;;;;;;;;;WApYV;;;;;;KASC,cAAA;;;;;;;;;;;;;;;;;;KA8BA;;UAEF,CAAA,CAAE,QAAQ;;;;;;;;KAgBR,YAAA;;;;;;KAQA,WAAA;;;;;;;;KAeA,WAAA;;sBAEU;;;;;;;;KAYV,YAAA;;;;;;;KAYA,YAAA;;;;;;;;UAWF;;aAGG;;KAGD,WAAA;;;;;;;;;;;;KAiBA,cAAA;;;;;;;;;;;;KAqBA,YAAA;;;;;;;;;KAUA,UAAA;;;;;;KAOA,UAAA;;SAEH;;;;;;;;;;;;;;;;;;KAuBG,mBAAA;;;;;;;;KAWA,sBAAA;;;;;;KAYA,SAAA;;;;;;;;;;;;;;KAeA,cAAA;;;;;;;;;;;;UAYF;;;;;;KAOE,YAAA;;;;;;sBAMU;;yBAEG;;KAGb,UAAA;;WAED;;;;;;;;KASC,WAAA;;SAEH;;;;;;;;;;KAWG,cAAA;;sBAEU;;;;KASV,cAAA;;;;;;;;;;;;;;;;KAiBA,iBAAA;;;;;;sBAMU;;KAGV,iBAAA;;;;;;;;KASA,gBAAA;;;;;;aAMC;;;;;;KAOD,cAAA;;sBAEU;;;;KASV,6BAAA;;;;;;;;;;;;;;KAeA,6BAAA;;qBAES;;eAEN;;;;cAID"}
@@ -60,4 +60,4 @@ function extractJson(text) {
60
60
 
61
61
  //#endregion
62
62
  export { zodToJsonSchema as n, extractJson as t };
63
- //# sourceMappingURL=utils-CkB4Roi6.mjs.map
63
+ //# sourceMappingURL=utils-CZBZ8dgR.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"utils-CkB4Roi6.mjs","names":["properties: Record<string, any>","required: string[]"],"sources":["../src/core/utils.ts"],"sourcesContent":["/**\n * Shared utility functions for Gerbil core\n */\n\nimport type { z } from \"zod\";\n\n/**\n * Convert Zod schema to JSON Schema (simplified)\n * Handles objects, arrays, primitives, enums, optionals, and defaults\n */\nexport function zodToJsonSchema(schema: z.ZodType<any>): object {\n try {\n if (\"_def\" in schema) {\n const def = (schema as any)._def;\n\n if (def.typeName === \"ZodObject\") {\n const shape = def.shape();\n const properties: Record<string, any> = {};\n const required: string[] = [];\n\n for (const [key, value] of Object.entries(shape)) {\n properties[key] = zodToJsonSchema(value as z.ZodType<any>);\n // Check if required (not optional)\n if (!(value as any)._def?.typeName?.includes(\"Optional\")) {\n required.push(key);\n }\n }\n\n return { type: \"object\", properties, required };\n }\n if (def.typeName === \"ZodString\") {\n return { type: \"string\", description: def.description };\n }\n if (def.typeName === \"ZodNumber\") {\n return { type: \"number\", description: def.description };\n }\n if (def.typeName === \"ZodBoolean\") {\n return { type: \"boolean\" };\n }\n if (def.typeName === \"ZodArray\") {\n return { type: \"array\", items: zodToJsonSchema(def.type) };\n }\n if (def.typeName === \"ZodEnum\") {\n return { type: \"string\", enum: def.values };\n }\n if (def.typeName === \"ZodOptional\") {\n return zodToJsonSchema(def.innerType);\n }\n if (def.typeName === \"ZodDefault\") {\n const inner = zodToJsonSchema(def.innerType);\n return { ...inner, default: def.defaultValue() };\n }\n }\n } catch {}\n\n return { type: \"string\" };\n}\n\n/**\n * Extract JSON from text (finds first { } or [ ] block)\n */\nexport function extractJson(text: string): string {\n const jsonMatch = text.match(/\\{[\\s\\S]*\\}/);\n if (jsonMatch) {\n return jsonMatch[0];\n }\n\n const arrayMatch = text.match(/\\[[\\s\\S]*\\]/);\n if (arrayMatch) {\n return arrayMatch[0];\n }\n\n return text;\n}\n"],"mappings":";;;;;AAUA,SAAgB,gBAAgB,QAAgC;AAC9D,KAAI;AACF,MAAI,UAAU,QAAQ;GACpB,MAAM,MAAO,OAAe;AAE5B,OAAI,IAAI,aAAa,aAAa;IAChC,MAAM,QAAQ,IAAI,OAAO;IACzB,MAAMA,aAAkC,EAAE;IAC1C,MAAMC,WAAqB,EAAE;AAE7B,SAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,MAAM,EAAE;AAChD,gBAAW,OAAO,gBAAgB,MAAwB;AAE1D,SAAI,CAAE,MAAc,MAAM,UAAU,SAAS,WAAW,CACtD,UAAS,KAAK,IAAI;;AAItB,WAAO;KAAE,MAAM;KAAU;KAAY;KAAU;;AAEjD,OAAI,IAAI,aAAa,YACnB,QAAO;IAAE,MAAM;IAAU,aAAa,IAAI;IAAa;AAEzD,OAAI,IAAI,aAAa,YACnB,QAAO;IAAE,MAAM;IAAU,aAAa,IAAI;IAAa;AAEzD,OAAI,IAAI,aAAa,aACnB,QAAO,EAAE,MAAM,WAAW;AAE5B,OAAI,IAAI,aAAa,WACnB,QAAO;IAAE,MAAM;IAAS,OAAO,gBAAgB,IAAI,KAAK;IAAE;AAE5D,OAAI,IAAI,aAAa,UACnB,QAAO;IAAE,MAAM;IAAU,MAAM,IAAI;IAAQ;AAE7C,OAAI,IAAI,aAAa,cACnB,QAAO,gBAAgB,IAAI,UAAU;AAEvC,OAAI,IAAI,aAAa,aAEnB,QAAO;IAAE,GADK,gBAAgB,IAAI,UAAU;IACzB,SAAS,IAAI,cAAc;IAAE;;SAG9C;AAER,QAAO,EAAE,MAAM,UAAU;;;;;AAM3B,SAAgB,YAAY,MAAsB;CAChD,MAAM,YAAY,KAAK,MAAM,cAAc;AAC3C,KAAI,UACF,QAAO,UAAU;CAGnB,MAAM,aAAa,KAAK,MAAM,cAAc;AAC5C,KAAI,WACF,QAAO,WAAW;AAGpB,QAAO"}
1
+ {"version":3,"file":"utils-CZBZ8dgR.mjs","names":["properties: Record<string, any>","required: string[]"],"sources":["../src/core/utils.ts"],"sourcesContent":["/**\n * Shared utility functions for Gerbil core\n */\n\nimport type { z } from \"zod\";\n\n/**\n * Convert Zod schema to JSON Schema (simplified)\n * Handles objects, arrays, primitives, enums, optionals, and defaults\n */\nexport function zodToJsonSchema(schema: z.ZodType<any>): object {\n try {\n if (\"_def\" in schema) {\n const def = (schema as any)._def;\n\n if (def.typeName === \"ZodObject\") {\n const shape = def.shape();\n const properties: Record<string, any> = {};\n const required: string[] = [];\n\n for (const [key, value] of Object.entries(shape)) {\n properties[key] = zodToJsonSchema(value as z.ZodType<any>);\n // Check if required (not optional)\n if (!(value as any)._def?.typeName?.includes(\"Optional\")) {\n required.push(key);\n }\n }\n\n return { type: \"object\", properties, required };\n }\n if (def.typeName === \"ZodString\") {\n return { type: \"string\", description: def.description };\n }\n if (def.typeName === \"ZodNumber\") {\n return { type: \"number\", description: def.description };\n }\n if (def.typeName === \"ZodBoolean\") {\n return { type: \"boolean\" };\n }\n if (def.typeName === \"ZodArray\") {\n return { type: \"array\", items: zodToJsonSchema(def.type) };\n }\n if (def.typeName === \"ZodEnum\") {\n return { type: \"string\", enum: def.values };\n }\n if (def.typeName === \"ZodOptional\") {\n return zodToJsonSchema(def.innerType);\n }\n if (def.typeName === \"ZodDefault\") {\n const inner = zodToJsonSchema(def.innerType);\n return { ...inner, default: def.defaultValue() };\n }\n }\n } catch {}\n\n return { type: \"string\" };\n}\n\n/**\n * Extract JSON from text (finds first { } or [ ] block)\n */\nexport function extractJson(text: string): string {\n const jsonMatch = text.match(/\\{[\\s\\S]*\\}/);\n if (jsonMatch) {\n return jsonMatch[0];\n }\n\n const arrayMatch = text.match(/\\[[\\s\\S]*\\]/);\n if (arrayMatch) {\n return arrayMatch[0];\n }\n\n return text;\n}\n"],"mappings":";;;;;AAUA,SAAgB,gBAAgB,QAAgC;AAC9D,KAAI;AACF,MAAI,UAAU,QAAQ;GACpB,MAAM,MAAO,OAAe;AAE5B,OAAI,IAAI,aAAa,aAAa;IAChC,MAAM,QAAQ,IAAI,OAAO;IACzB,MAAMA,aAAkC,EAAE;IAC1C,MAAMC,WAAqB,EAAE;AAE7B,SAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,MAAM,EAAE;AAChD,gBAAW,OAAO,gBAAgB,MAAwB;AAE1D,SAAI,CAAE,MAAc,MAAM,UAAU,SAAS,WAAW,CACtD,UAAS,KAAK,IAAI;;AAItB,WAAO;KAAE,MAAM;KAAU;KAAY;KAAU;;AAEjD,OAAI,IAAI,aAAa,YACnB,QAAO;IAAE,MAAM;IAAU,aAAa,IAAI;IAAa;AAEzD,OAAI,IAAI,aAAa,YACnB,QAAO;IAAE,MAAM;IAAU,aAAa,IAAI;IAAa;AAEzD,OAAI,IAAI,aAAa,aACnB,QAAO,EAAE,MAAM,WAAW;AAE5B,OAAI,IAAI,aAAa,WACnB,QAAO;IAAE,MAAM;IAAS,OAAO,gBAAgB,IAAI,KAAK;IAAE;AAE5D,OAAI,IAAI,aAAa,UACnB,QAAO;IAAE,MAAM;IAAU,MAAM,IAAI;IAAQ;AAE7C,OAAI,IAAI,aAAa,cACnB,QAAO,gBAAgB,IAAI,UAAU;AAEvC,OAAI,IAAI,aAAa,aAEnB,QAAO;IAAE,GADK,gBAAgB,IAAI,UAAU;IACzB,SAAS,IAAI,cAAc;IAAE;;SAG9C;AAER,QAAO,EAAE,MAAM,UAAU;;;;;AAM3B,SAAgB,YAAY,MAAsB;CAChD,MAAM,YAAY,KAAK,MAAM,cAAc;AAC3C,KAAI,UACF,QAAO,UAAU;CAGnB,MAAM,aAAa,KAAK,MAAM,cAAc;AAC5C,KAAI,WACF,QAAO,WAAW;AAGpB,QAAO"}
@@ -57,21 +57,29 @@ Enables WebGPU in Node.js by using headless Chrome as a GPU accelerator:
57
57
  └───────────┘
58
58
  ```
59
59
 
60
- ### 4. Browser Worker (`src/browser/index.ts`)
60
+ ### 4. Browser Module (`src/browser/index.ts`)
61
61
 
62
- Provides `createGerbilWorker()` for browser applications:
62
+ Provides React hooks and workers for browser applications:
63
63
 
64
64
  ```typescript
65
+ // LLM worker
65
66
  const gerbil = await createGerbilWorker({
66
67
  modelId: "qwen3-0.6b",
67
68
  onToken: (token) => console.log(token.text),
68
69
  });
70
+
71
+ // React hooks
72
+ const { messages, handleSubmit } = useChat();
73
+ const { speak, isSpeaking } = useSpeech();
74
+ const { startRecording, transcript } = useVoiceInput();
69
75
  ```
70
76
 
71
- Uses an inline Web Worker to:
72
- - Load models without blocking the UI
73
- - Stream tokens in real-time
74
- - Manage GPU memory separately from main thread
77
+ Uses inline Web Workers for:
78
+ - **LLM**: Model loading, token streaming, GPU memory management
79
+ - **TTS**: Kokoro/Supertonic speech synthesis
80
+ - **STT**: Whisper transcription
81
+
82
+ All workers load dependencies from CDN to avoid bundler issues with onnxruntime-web.
75
83
 
76
84
  ## Execution Paths
77
85
 
@@ -166,7 +174,7 @@ src/
166
174
  │ ├── tools.ts # Tool calling system
167
175
  │ └── chrome-backend.ts # Node.js WebGPU via Chrome
168
176
  ├── browser/
169
- │ └── index.ts # createGerbilWorker + utilities
177
+ │ └── index.ts # React hooks + LLM/TTS/STT workers
170
178
  ├── skills/
171
179
  │ └── ... # Built-in skills (commit, summarize, etc.)
172
180
  ├── integrations/
package/docs/tts.md CHANGED
@@ -500,7 +500,7 @@ export function createTTS(modelId: string = "kokoro-82m"): TTSBackend {
500
500
 
501
501
  ### 2. Browser Hook (`src/browser/index.ts`)
502
502
 
503
- Add browser support in the useSpeech hook:
503
+ **Important:** Browser TTS runs in an inline Web Worker that loads from CDN. Direct imports don't work due to bundler issues with onnxruntime-web.
504
504
 
505
505
  ```typescript
506
506
  // 1. Add voice definitions for browser
@@ -522,10 +522,15 @@ const TTS_MODELS: Record<TTSModelId, {...}> = {
522
522
  // 3. Update TTSModelId type
523
523
  export type TTSModelId = "kokoro-82m" | "supertonic-66m" | "my-model-50m";
524
524
 
525
- // 4. Add loading logic in useSpeech useEffect
526
- if (modelId === "my-model-50m") {
527
- // Load using appropriate method (pipeline, custom loader, etc.)
528
- }
525
+ // 4. Update TTS_WORKER_CODE to handle the new model
526
+ const TTS_WORKER_CODE = `
527
+ import { pipeline } from "https://cdn.jsdelivr.net/npm/@huggingface/transformers@3.8.1";
528
+
529
+ // Add loading logic for new model
530
+ if (modelType === "my-model") {
531
+ // Load from CDN, not npm
532
+ }
533
+ `;
529
534
  ```
530
535
 
531
536
  ### 3. Gerbil Class (`src/core/gerbil.ts`)
@@ -553,17 +558,15 @@ Update these integration files if applicable:
553
558
  Update documentation:
554
559
 
555
560
  - `docs/tts.md`: Add model to tables and examples
556
- - `.cursor/rules/gerbil/tts.mdc`: Update implementation patterns
557
561
 
558
562
  ### File Summary
559
563
 
560
564
  | File | Purpose |
561
565
  |------|---------|
562
566
  | `src/core/tts.ts` | Core TTS classes, voice registry, model config |
563
- | `src/browser/index.ts` | React hooks (useSpeech, useVoiceChat) |
567
+ | `src/browser/index.ts` | React hooks (useSpeech, useVoiceChat) + TTS worker |
564
568
  | `src/core/gerbil.ts` | Main Gerbil class integration |
565
569
  | `src/integrations/ai-sdk.ts` | AI SDK v5 speech model |
566
570
  | `src/skills/builtin/speak.ts` | Speak skill voice options |
567
571
  | `docs/tts.md` | User documentation |
568
- | `.cursor/rules/gerbil/tts.mdc` | Developer patterns |
569
572
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tryhamster/gerbil",
3
- "version": "1.0.0-rc.1",
3
+ "version": "1.0.0-rc.11",
4
4
  "description": "Local LLM inference for Node.js. GPU-accelerated. Zero config. Works standalone or with Vercel AI SDK.",
5
5
  "type": "module",
6
6
  "main": "dist/index.mjs",
@@ -62,18 +62,15 @@
62
62
  "types": "./dist/integrations/mcp-client.d.mts"
63
63
  },
64
64
  "./browser": {
65
- "import": "./dist/browser/index.mjs",
66
- "types": "./dist/browser/index.d.mts"
65
+ "import": "./dist/browser/index.js",
66
+ "types": "./dist/browser/index.d.ts"
67
67
  }
68
68
  },
69
69
  "dependencies": {
70
70
  "@huggingface/hub": "^2.7.1",
71
- "@huggingface/transformers": "^3.8.0",
72
71
  "chalk": "^5.3.0",
73
72
  "cli-progress": "^3.12.0",
74
73
  "commander": "^12.1.0",
75
- "kokoro-js": "^1.2.1",
76
- "onnxruntime-web": "^1.21.0-dev.20250114-228dd16893",
77
74
  "ora": "^8.0.1",
78
75
  "puppeteer-core": "^24.31.0",
79
76
  "react": "^19.0.0",
@@ -130,6 +127,9 @@
130
127
  "devDependencies": {
131
128
  "@ai-sdk/provider": "^2.0.0",
132
129
  "@biomejs/biome": "^2.3.8",
130
+ "@huggingface/transformers": "^3.8.0",
131
+ "kokoro-js": "^1.2.1",
132
+ "onnxruntime-web": "^1.21.0-dev.20250114-228dd16893",
133
133
  "@changesets/changelog-github": "^0.5.1",
134
134
  "@changesets/cli": "^2.28.1",
135
135
  "@types/cli-progress": "^3.11.6",
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../../src/browser/index.ts"],"sourcesContent":[],"mappings":";;;;;AAuhEgB,KA19DJ,cAAA,GA09DqB;EAgErB,MAAA,EAAA,SAAW,GAAA,aAAA,GAAA,OAAA,GAAA,OAAA;EAUX,OAAA,CAAA,EAAA,MAAA;EAwBA,IAAA,CAAA,EAAA,MAAA;EAEY,QAAA,CAAA,EAAA,MAAA;EAED;EAID,aAAA,CAAA,EAAA,MAAA;EAAiB;EAgBpB,UAAA,CAAA,EAAA,MAAA;EAAW,KAAA,CAAA,EAAA,MAAA;AAiD9B,CAAA;AA2iBY,KApqFA,WAAA,GAoqFmB;EA4BnB,MAAA,EAAA,OAAA;EAWA,IAAA,EAAA,MAAA;EAEA,KAAA,EAAA,UAAA,GAAA,WAAA;EAEY,SAAA,EAAA,MAAA;EAED,GAAA,EAAA,MAAA;CAAO;AAoEd,KA7wFJ,cAAA,GA6wFgB;EAgcZ,MAAA,EAAA,UAAA;EAUM,IAAA,EAAA,MAAA;EAwBrB,SAAA,EAAA,MAAA;;;;KAvuGW,mBAAA;;;;0BAIc;;oBAEN;;wBAEI;;;;;;KAOZ,qBAAA;;;;;;;;;;;;;;;;YAgBA;;;;;KAGA,YAAA;;uCAE2B,0BAA0B;;;;;;;;;;;;;;;;iBAqB3C,kBAAA,WAA4B,sBAA2B,QAAQ;;KA+ezE,OAAA;;;;;;;;;KAUA,eAAA;;;;;;;;;;;KAYA,cAAA;;;;;;;;;;;;oBAYQ;;;;;;;;;KAUR,aAAA;;YAEA;;;;;;;;;;;;mBAUO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAuDH,OAAA,WAAiB,iBAAsB;;KAwS3C,oBAAA;;;;;;;;;;;;;;;;;;;KAoBA,eAAA;;;;;KAMA,mBAAA;;;;;;uCAM2B,oBAAoB;;;;mBAIxC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAoCH,aAAA,WAAuB,uBAA4B;;KAsMvD,WAAA;;;;;;;;KASA,UAAA;;KAGA,gBAAA;;;;;;;;KA0NA,gBAAA;;UAEF;;;;;;;;;;;;;;;;;KAkBE,eAAA;;;;;QAE6D;;;;;;mBAMtD;;;;;;;;;;oBAUC;;;;;;;;;;gBAUJ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAuCA,SAAA,WAAmB,mBAAwB;;;;;;;;;;;;;;;iBA8XrC,SAAA,QACb,oCAEN;;WAAqC;;;;;;;;;;;;;;;;;;;iBAmDxB,iBAAA;iBACC;;;;;;;KA+DL,WAAA;;;;;;;;;KAUA,oBAAA;;;;;;;;;;;;0BAYc;;;;;;;;;;;KAYd,mBAAA;;wBAEY;;uBAED;;;;sBAID,iBAAiB;;;;;;;;;;;;;;;;mBAgBpB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAiDH,aAAA,WAAuB,uBAA4B;;;;KA2iBvD,mBAAA;;;;;;aAMC;;;;;;;;;;;;;;;;;;;;;KAsBD,gBAAA;;;;;;;;;;KAWA,kBAAA;;YAEA;;wBAEY;;uBAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAoEP,YAAA,WAAsB,sBAA2B;;;;iBAgcjD,iBAAA,CAAA;;;;iBAUM,aAAA,CAAA,GAAiB;;;;;cAwBtC"}