{"_id":"@aituber-onair/transcription","_rev":"3-b8914445f723c1761534d0ed52475f89","name":"@aituber-onair/transcription","dist-tags":{"latest":"0.0.3"},"versions":{"0.0.1":{"name":"@aituber-onair/transcription","version":"0.0.1","keywords":["aituber","transcription","speech-to-text","webrtc"],"author":{"url":"https://github.com/shinshin86","name":"shinshin86"},"license":"MIT","_id":"@aituber-onair/transcription@0.0.1","maintainers":[{"name":"shinshin86","email":"shinshin86npm@gmail.com"}],"homepage":"https://github.com/shinshin86/aituber-onair#readme","bugs":{"url":"https://github.com/shinshin86/aituber-onair/issues"},"dist":{"shasum":"ee83c04d30f520794dd488e0d201fcc3bf38634d","tarball":"https://registry.npmjs.org/@aituber-onair/transcription/-/transcription-0.0.1.tgz","fileCount":9,"integrity":"sha512-PohbCAJFHvnIigphkP8RmXubOPCpJTDY58WJpS2GHZDr/YX1idjkv1w6KiZuXIN+cvI6m+WhpuLSyyqFVuk+/g==","signatures":[{"sig":"MEUCIQCUapkyZkT7RhPoGIvymckebVvl21veSzn3OwL1NffC9QIgJ/WJLPy9TIQ/FdjjzaV5UAHlQM0whatWgzq9RJRqg7M=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":213652},"main":"dist/index.cjs","type":"module","types":"dist/index.d.ts","module":"dist/index.js","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"},"./package.json":"./package.json"},"gitHead":"dae09fb414c1ea210ebec15e4ecc9cd4f2d9e1d7","scripts":{"fmt":"biome format . --write","lint":"biome lint .","test":"npm run typecheck && vitest run","build":"npm run build:package && npm run example:build","fmt:check":"biome format .","typecheck":"tsc --noEmit && tsc --project examples/browser-basic/tsconfig.json --noEmit","test:watch":"vitest","example:dev":"vite --config examples/browser-basic/vite.config.ts","build:package":"tsup src/index.ts --format esm,cjs --dts --sourcemap --clean --out-dir dist","example:build":"tsc --project examples/browser-basic/tsconfig.json --noEmit && vite build --config examples/browser-basic/vite.config.ts","test:coverage":"vitest run --coverage","prepublishOnly":"npm run build"},"_npmUser":{"name":"shinshin86","email":"shinshin86npm@gmail.com"},"repository":{"url":"git+https://github.com/shinshin86/aituber-onair.git","type":"git","directory":"packages/transcription"},"_npmVersion":"10.8.2","description":"Provider-neutral realtime transcription for AITuber OnAir","directories":{},"sideEffects":false,"_nodeVersion":"20.20.2","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.0.1","vite":"^5.4.21","jsdom":"^22.1.0","vitest":"^1.3.1","typescript":"^5.0.0","@types/node":"^18.15.0","@biomejs/biome":"1.9.4","@vitest/coverage-v8":"^1.3.1"},"_npmOperationalInternal":{"tmp":"tmp/transcription_0.0.1_1786695870922_0.8752386542934265","host":"s3://npm-registry-packages-npm-production"}},"0.0.2":{"name":"@aituber-onair/transcription","version":"0.0.2","keywords":["aituber","transcription","speech-to-text","webrtc"],"author":{"url":"https://github.com/shinshin86","name":"shinshin86"},"license":"MIT","_id":"@aituber-onair/transcription@0.0.2","maintainers":[{"name":"shinshin86","email":"shinshin86npm@gmail.com"}],"homepage":"https://github.com/shinshin86/aituber-onair#readme","bugs":{"url":"https://github.com/shinshin86/aituber-onair/issues"},"dist":{"shasum":"e8d1a775b5a2f1bd9a439a8afb6efa3e26a0e4c0","tarball":"https://registry.npmjs.org/@aituber-onair/transcription/-/transcription-0.0.2.tgz","fileCount":10,"integrity":"sha512-ZvnIVq3f8GS6TvYzbEh6hEAW6cvC4F0VDElTRVAeut+gdt4dJLGkvHAr143t/qhQDdJk1qjmv35rN1IyNKQAkQ==","signatures":[{"sig":"MEUCIB0q7YIB+1GKFwkA0V83yuCqmoqL+EXvUm2WekTF7bxzAiEAoxjux9XkmFU5bb8/m+kJF77P428vJdn2L6ULcJZeqCQ=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":931927},"main":"dist/index.cjs","type":"module","types":"dist/index.d.ts","module":"dist/index.js","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"},"./package.json":"./package.json"},"gitHead":"130ad6f0555c01965b57c948d02f4fecb8bd60c6","scripts":{"fmt":"biome format . --write","lint":"biome lint .","test":"npm run typecheck && vitest run","build":"npm run build:package && npm run example:build","fmt:check":"biome format .","typecheck":"tsc --noEmit && tsc --project examples/browser-basic/tsconfig.json --noEmit","test:watch":"vitest","example:dev":"vite --config examples/browser-basic/vite.config.ts","build:package":"tsup","example:build":"tsc --project examples/browser-basic/tsconfig.json --noEmit && vite build --config examples/browser-basic/vite.config.ts","test:coverage":"vitest run --coverage","prepublishOnly":"npm run build"},"_npmUser":{"name":"shinshin86","email":"shinshin86npm@gmail.com"},"repository":{"url":"git+https://github.com/shinshin86/aituber-onair.git","type":"git","directory":"packages/transcription"},"_npmVersion":"10.8.2","description":"Provider-neutral realtime transcription for AITuber OnAir","directories":{},"sideEffects":false,"_nodeVersion":"20.20.2","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.0.1","vite":"^5.4.21","jsdom":"^22.1.0","vitest":"^1.3.1","typescript":"^5.0.0","@types/node":"^18.15.0","@biomejs/biome":"1.9.4","@vitest/coverage-v8":"^1.3.1","@huggingface/transformers":"4.2.0"},"_npmOperationalInternal":{"tmp":"tmp/transcription_0.0.2_1786882127633_0.9725888164170271","host":"s3://npm-registry-packages-npm-production"}},"0.0.3":{"name":"@aituber-onair/transcription","version":"0.0.3","description":"Provider-neutral realtime transcription for AITuber OnAir","type":"module","main":"dist/index.cjs","module":"dist/index.js","types":"dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"},"./package.json":"./package.json"},"sideEffects":false,"scripts":{"build":"npm run build:package && npm run example:build","build:package":"tsup","example:build":"tsc --project examples/browser-basic/tsconfig.json --noEmit && vite build --config examples/browser-basic/vite.config.ts","example:dev":"vite --config examples/browser-basic/vite.config.ts","typecheck":"tsc --noEmit && tsc --project examples/browser-basic/tsconfig.json --noEmit","test":"npm run typecheck && vitest run","test:watch":"vitest","test:coverage":"vitest run --coverage","fmt":"biome format . --write","fmt:check":"biome format .","lint":"biome lint .","prepublishOnly":"npm run build"},"keywords":["aituber","transcription","speech-to-text","webrtc"],"author":{"name":"shinshin86","url":"https://github.com/shinshin86"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/shinshin86/aituber-onair.git","directory":"packages/transcription"},"publishConfig":{"access":"public"},"devDependencies":{"@biomejs/biome":"1.9.4","@huggingface/transformers":"4.2.0","@types/node":"^18.15.0","@vitest/coverage-v8":"^1.3.1","jsdom":"^22.1.0","tsup":"^8.0.1","typescript":"^5.0.0","vite":"^5.4.21","vitest":"^1.3.1"},"_id":"@aituber-onair/transcription@0.0.3","gitHead":"0dd3d208408ae1683acc662835ddca4f99fac31b","bugs":{"url":"https://github.com/shinshin86/aituber-onair/issues"},"homepage":"https://github.com/shinshin86/aituber-onair#readme","_nodeVersion":"20.20.2","_npmVersion":"10.8.2","dist":{"integrity":"sha512-pXlmF+VsbRV3+1wngOf2YgMtUkuPd0EZxb7wTBa+Y5ORg1ZnFx9fGYjQ/py2wOhhQpSGDh5Dqgm21/D7IESzsw==","shasum":"44b8cb0517fd6645bea917ccde6bd4951db79b72","tarball":"https://registry.npmjs.org/@aituber-onair/transcription/-/transcription-0.0.3.tgz","fileCount":10,"unpackedSize":1087272,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIFnLIMZLotckgdDgQm9n7xaVgWiZO+nY9dwKSh1CMrBhAiAc7SGG9POrUqrNleZzDm06G9SsSTXHenU/4HXi6OqmWg=="}]},"_npmUser":{"name":"shinshin86","email":"shinshin86npm@gmail.com"},"directories":{},"maintainers":[{"name":"shinshin86","email":"shinshin86npm@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/transcription_0.0.3_1787980117249_0.22069567390848022"},"_hasShrinkwrap":false}},"time":{"created":"2026-08-14T08:24:30.760Z","modified":"2026-08-29T05:08:37.577Z","0.0.1":"2026-08-14T08:24:31.087Z","0.0.2":"2026-08-16T12:08:47.760Z","0.0.3":"2026-08-29T05:08:37.413Z"},"bugs":{"url":"https://github.com/shinshin86/aituber-onair/issues"},"author":{"name":"shinshin86","url":"https://github.com/shinshin86"},"license":"MIT","homepage":"https://github.com/shinshin86/aituber-onair#readme","keywords":["aituber","transcription","speech-to-text","webrtc"],"repository":{"type":"git","url":"git+https://github.com/shinshin86/aituber-onair.git","directory":"packages/transcription"},"description":"Provider-neutral realtime transcription for AITuber OnAir","maintainers":[{"name":"shinshin86","email":"shinshin86npm@gmail.com"}],"readme":"# @aituber-onair/transcription\n\n![@aituber-onair/transcription logo](https://raw.githubusercontent.com/shinshin86/aituber-onair/main/packages/transcription/images/aituber-onair-transcription.png)\n\n[日本語版はこちら](./README.ja.md)\n\nProvider-neutral realtime microphone transcription for AITuber OnAir.\n\n> This package is an alpha release. Its public API may change before a stable\n> release.\n\nThe package supports Web Speech, OpenAI Realtime transcription over browser\nWebRTC, Gemini Live transcription over browser WebSocket, and local Whisper\nTiny, Base, and Small inference through WebGPU. All providers emit the same\nper-utterance snapshot events. File transcription, server WebSocket input,\nautomatic chat submission, and provider fallback are intentionally out of\nscope.\n\n## Browser example\n\nThe package includes a framework-free browser example that exercises all four\nproviders without depending on AITuber OnAir Core:\n\n```sh\ncd packages/transcription/examples/browser-basic\nnpm run dev\n```\n\nAfter installing dependencies from the repository root, you can also start the\nexample with a workspace command:\n\n```sh\nnpm -w @aituber-onair/transcription run example:dev\n```\n\nOpen the displayed localhost URL and grant microphone permission when starting\na session. Web Speech and Local Whisper need no key. For OpenAI or Gemini,\nenter an end-user-owned API key in the page. The sample connects to the selected\nservice directly from the browser, so avoid using it on a shared device. Local\nWhisper requires WebGPU; its first start downloads model/runtime assets and\ncaches them in the browser. The interface supports English and Japanese and\nselects the initial display from the browser language. See the\n[example README](https://github.com/shinshin86/aituber-onair/blob/main/packages/transcription/examples/browser-basic/README.md)\nfor details.\n\nBuild the example without starting a server:\n\n```sh\nnpm -w @aituber-onair/transcription run example:build\n```\n\n## Usage\n\n```ts\nimport { createRealtimeTranscriptionSession } from '@aituber-onair/transcription';\n\nconst session = createRealtimeTranscriptionSession({\n  provider: 'web-speech',\n  language: 'ja-JP',\n});\n\nsession.onTranscript(({ utteranceId, text, isFinal }) => {\n  console.log({ utteranceId, text, isFinal });\n});\n\nawait session.start();\n// Later:\nawait session.stop();\nawait session.dispose();\n```\n\nOnly providers with potentially long initialization emit `onProgress` events.\nCurrently, only `local-whisper` emits them; Web Speech, OpenAI Realtime, and\nGemini Live do not.\n\n### Local Whisper\n\nLocal Whisper runs the selected Whisper model in a module worker and emits\nfinal transcripts only:\n\n```ts\nimport { createRealtimeTranscriptionSession } from '@aituber-onair/transcription';\n\nconst session = createRealtimeTranscriptionSession({\n  provider: 'local-whisper',\n  model: 'tiny',\n  language: 'ja-JP',\n  silenceDurationMs: 500,\n});\n\nsession.onTranscript(({ text, isFinal }) => {\n  if (isFinal) {\n    console.log(text);\n  }\n});\n\nsession.onProgress(({ phase, progress }) => {\n  updateLoadingIndicator(phase, progress);\n});\n\nsession.onError((error) => {\n  console.error(error.code, error.message);\n});\n\nawait session.start();\n\n// Later:\nawait session.stop();\nawait session.dispose();\n```\n\nLocal Whisper is less accurate than Web Speech or OpenAI Realtime. It is\nintended for use cases that prioritize requiring no API key and not sending\nmicrophone audio to a remote service. Choose `small` when recognition quality\nis important.\n\n| Model | First download reported by progress | Quality guide | Inference (Japanese / English) |\n| --- | ---: | --- | ---: |\n| `tiny` (default) | About 122 MB | Lower | 237.3 ms / 203.0 ms |\n| `base` | About 209 MB | Middle | 255.9 ms / 311.2 ms |\n| `small` | About 589 MB | Practical | 574.7 ms / 551.6 ms |\n\nThese measurements were taken in Chrome with WebGPU using the same short\nJapanese and English microphone clips. Inference excludes capture/VAD time and\nvaries by GPU. First-use download time depends on network speed and can take\nseveral minutes for hundreds of MB. After caching, initialization measured\nabout 0.9 s for Tiny, 1.2 s for Base, and 2.5 s for Small. Download sizes are\nthe sum of the latest `totalBytes` reported for each model file and do not\ninclude assets that do not report progress.\n\nRequirements and behavior:\n\n- A secure browser context (HTTPS or localhost), microphone access, Web Audio,\n  AudioWorklet, module workers, and WebGPU are required.\n- No API key is required. There is no automatic fallback to a remote provider\n  or WASM inference when WebGPU initialization fails.\n- On first use, the selected model assets are downloaded from the Hugging Face\n  Hub and ONNX Runtime WebAssembly files are downloaded from jsDelivr. These\n  assets are cached by the browser. Larger models take longer to download and\n  infer.\n- Microphone audio is processed in the browser and never leaves the browser.\n  The package does not persist audio or transcripts.\n- `language` accepts a BCP 47-style hint and is optional. The default 500 ms\n  `silenceDurationMs` can be reduced to a minimum of 150 ms for faster turn\n  completion.\n- `model` accepts `tiny`, `base`, or `small` and defaults to `tiny`. Model dtype\n  is fixed to an fp32 encoder and q4 merged decoder for every size.\n- Download progress can include `file`, `loadedBytes`, `totalBytes`, and a\n  normalized `progress` value from 0 to 1. Initialization and ready phases do\n  not require byte totals.\n\nThe package normally resolves `dist/local-whisper.worker.js` relative to its\nESM entry. If a bundler pre-bundles the package and cannot resolve that asset,\nset the advanced `workerUrl` option to the same module worker asset or an\nequivalent build. This browser example uses Vite's `?worker&url` import for that\nreason.\n\n### OpenAI Realtime\n\nOpenAI Realtime uses `gpt-live-transcribe` and browser WebRTC. Because this\ntranscription model does not accept server turn detection, the package detects\nsustained silence through the browser Web Audio API and explicitly commits each\naudio turn. The recommended authentication mode obtains a short-lived client\nsecret from an application backend:\n\n```ts\nconst session = createRealtimeTranscriptionSession({\n  provider: 'openai-realtime',\n  auth: {\n    type: 'client-secret',\n    getClientSecret: async () => {\n      const response = await fetch('/api/openai/realtime/client-secret', {\n        method: 'POST',\n      });\n      const data = await response.json();\n      return data.value;\n    },\n  },\n  languages: ['ja', 'en'],\n  keywords: ['AITuber OnAir'],\n  prompt: 'An AITuber livestream.',\n  delay: 'low',\n});\n```\n\nFrontend-only, self-hosted applications may explicitly use an end-user-owned\nstandard API key to mint a client secret in the browser:\n\n```ts\nconst session = createRealtimeTranscriptionSession({\n  provider: 'openai-realtime',\n  auth: {\n    type: 'browser-api-key',\n    getApiKey: async () => readEndUserKeyAtRuntime(),\n    acknowledgeBrowserKeyRisk: true,\n  },\n  languages: ['ja'],\n});\n```\n\n### Gemini Live\n\nGemini Live uses `gemini-3.5-transcribe-live` and streams raw 16-bit PCM audio\nover a browser WebSocket. It emits low-latency interim snapshots and a final\nsnapshot for each detected utterance. The model supports automatic language\ndetection, language hints, custom vocabulary, and verbatim or smart\ntranscription modes.\n\nThe recommended browser authentication mode obtains a short-lived ephemeral\ntoken from an application backend:\n\n```ts\nconst session = createRealtimeTranscriptionSession({\n  provider: 'gemini-live',\n  auth: {\n    type: 'ephemeral-token',\n    getEphemeralToken: async () => {\n      const response = await fetch('/api/gemini/ephemeral-token', {\n        method: 'POST',\n      });\n      const data = await response.json();\n      return data.name;\n    },\n  },\n  languages: ['ja-JP', 'en-US'],\n  keywords: ['AITuber OnAir'],\n  mode: 'smart',\n});\n```\n\nFrontend-only, self-hosted applications may explicitly connect with an\nend-user-owned API key:\n\n```ts\nconst session = createRealtimeTranscriptionSession({\n  provider: 'gemini-live',\n  auth: {\n    type: 'browser-api-key',\n    getApiKey: async () => readEndUserKeyAtRuntime(),\n    acknowledgeBrowserKeyRisk: true,\n  },\n  languages: [], // Automatic language detection\n  mode: 'verbatim',\n});\n```\n\nGemini Live Transcribe currently has a 10-minute connection limit. This\nprovider reports an unexpected server disconnect as a typed connection error;\napplications that need longer listening periods should start a new session.\nLive streaming does not provide speaker diarization or word-level timestamps.\nSee the\n[Gemini Live transcription documentation](https://ai.google.dev/gemini-api/docs/live-api/live-transcribe)\nfor current preview status, supported languages, limits, and pricing links.\n\n## Security\n\nOpenAI and Google recommend keeping standard API keys on a server and minting\nshort-lived browser credentials. The browser-BYOK modes exist for trusted\nfrontend-only or self-hosted use. They must use a key owned and supplied by the\nend user; never bundle an application-owner key in source code or built assets.\n\nThe package requests credentials through `getApiKey()`, `getClientSecret()`, or\n`getEphemeralToken()` for each `start()` and does not persist, cache, return, or\nlog them. A consuming application still controls its own storage. Browser\npersistence can expose a key to XSS, extensions, local device access, or\ncompromised dependencies. Credential failures are returned as typed errors and\nnever trigger an authentication fallback.\n\n## Provider differences\n\n| Capability | Web Speech | OpenAI Realtime | Gemini Live | Local Whisper |\n| --- | --- | --- | --- | --- |\n| Interim snapshots | Yes | Yes | Yes | No |\n| Multiple expected languages | No | Yes | Yes | No |\n| Keywords / custom vocabulary | No | Yes | Yes | No |\n| Smart transcription | No | No | Yes | No |\n| Configurable delay | No | Yes | No | Yes |\n| Utterance boundary | Browser implementation | Browser audio-level detection | Gemini server VAD | Browser PCM/VAD |\n\nAll providers require a supported browser and microphone permission. OpenAI\nWebRTC, Gemini Live, and Local Whisper also require the Web Audio API and HTTPS\nor localhost; Gemini Live additionally requires WebSocket and Local Whisper\nrequires WebGPU. Web Speech availability and behavior vary by browser. Remote\nproviders can incur usage charges while listening, so applications should\nexpose state clearly and stop sessions when unused.\n","readmeFilename":"README.md"}