{"_id":"@acegalaxy/voice-gateway","_rev":"3-21db87db45a8aac21832b159e71f5b43","name":"@acegalaxy/voice-gateway","dist-tags":{"latest":"0.1.2"},"versions":{"0.1.0":{"name":"@acegalaxy/voice-gateway","version":"0.1.0","keywords":["voice","stt","whisper","openai","telegram","cost-cap","rate-limit"],"author":{"name":"ACE Galaxy","email":"hello@acegalaxy.co"},"license":"MIT","_id":"@acegalaxy/voice-gateway@0.1.0","maintainers":[{"name":"kanelr","email":"lanhnk@acegalaxy.co"}],"homepage":"https://github.com/acegalaxy-co/ace_commons-voice-gateway-nodejs#readme","bugs":{"url":"https://github.com/acegalaxy-co/ace_commons-voice-gateway-nodejs/issues"},"dist":{"shasum":"eb9fc5b6716b9b3c386fc837d0d6db1a660a358d","tarball":"https://registry.npmjs.org/@acegalaxy/voice-gateway/-/voice-gateway-0.1.0.tgz","fileCount":50,"integrity":"sha512-AzYYVj8wqjEeLLUFt41oqNz4AQdrkItaC/DoNabcjbj3oOCr6uRrmjM2WWViWfdCk0lzJ6ZwE8sTErermyGZ6w==","signatures":[{"sig":"MEUCIFvpd7gm6QjlUxvxn2BeHSmA7AuF4gCQ15bA2UOJguUiAiEA8fBshPQ+R1AUBAGcjDtmpom/f1RHdetujkDWNftxA9M=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":63556},"main":"dist/index.js","types":"dist/index.d.ts","engines":{"node":">=20"},"exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"./*":{"types":"./dist/*.d.ts","default":"./dist/*.js"},"./sources/*":{"types":"./dist/sources/*.d.ts","default":"./dist/sources/*.js"},"./sources/telegram":{"types":"./dist/sources/telegram.d.ts","default":"./dist/sources/telegram.js"}},"gitHead":"cac601b5ce8a2d553428f0f5ac3a511de8c21e5b","private":false,"scripts":{"test":"node --test test/*.test.js","build":"tsc","clean":"rm -rf dist","pretest":"npm run build","prepublishOnly":"npm run build"},"_npmUser":{"name":"kanelr","email":"lanhnk@acegalaxy.co"},"repository":{"url":"git+https://github.com/acegalaxy-co/ace_commons-voice-gateway-nodejs.git","type":"git"},"_npmVersion":"11.12.1","description":"OpenAI Whisper wrapper with cost cap, rate limit, default-deny authz, queue, and audit log. Production-ready STT for Telegram bots.","directories":{},"_nodeVersion":"25.9.0","dependencies":{"@acegalaxy/security-utils":"^0.1.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.7.0","@types/node":"^20.0.0"},"_npmOperationalInternal":{"tmp":"tmp/voice-gateway_0.1.0_1778086034679_0.23975218117451336","host":"s3://npm-registry-packages-npm-production"}},"0.1.1":{"name":"@acegalaxy/voice-gateway","version":"0.1.1","keywords":["voice","stt","whisper","openai","telegram","cost-cap","rate-limit"],"author":{"name":"ACE Galaxy","email":"hello@acegalaxy.co"},"license":"MIT","_id":"@acegalaxy/voice-gateway@0.1.1","maintainers":[{"name":"kanelr","email":"lanhnk@acegalaxy.co"}],"homepage":"https://github.com/acegalaxy-co/ace_commons-voice-gateway-nodejs#readme","bugs":{"url":"https://github.com/acegalaxy-co/ace_commons-voice-gateway-nodejs/issues"},"dist":{"shasum":"cb04e3c27ab91e6f65b43b5bd8af0fb7f8eabf29","tarball":"https://registry.npmjs.org/@acegalaxy/voice-gateway/-/voice-gateway-0.1.1.tgz","fileCount":50,"integrity":"sha512-eYsXJijvc07yHMXOfBdcNwfUt5aatGWOMRCgNMUdNeqtDZZJJ6pua7UT0dne2gw0OhwKwmTHNuKVzcat7BTVOw==","signatures":[{"sig":"MEUCIFpaORFvMmiyEzR8ANPIEynCR3pr19xIwZhoXqFJXkDUAiEAu0KwasY5V60aU31mODTh12bumlY3r3kOQZtIKFsfb8A=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":63509},"main":"dist/index.js","types":"dist/index.d.ts","engines":{"node":">=20"},"exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"./*":{"types":"./dist/*.d.ts","default":"./dist/*.js"},"./sources/*":{"types":"./dist/sources/*.d.ts","default":"./dist/sources/*.js"},"./sources/telegram":{"types":"./dist/sources/telegram.d.ts","default":"./dist/sources/telegram.js"}},"gitHead":"4944d288c0ec585f06edef8751351caf2179988e","private":false,"scripts":{"test":"node --test test/*.test.js","build":"tsc","clean":"rm -rf dist","pretest":"npm run build","prepublishOnly":"npm run build"},"_npmUser":{"name":"kanelr","email":"lanhnk@acegalaxy.co"},"repository":{"url":"git+https://github.com/acegalaxy-co/ace_commons-voice-gateway-nodejs.git","type":"git"},"_npmVersion":"11.12.1","description":"OpenAI Whisper wrapper with cost cap, rate limit, default-deny authz, queue, and audit log. Production-ready STT for Telegram bots.","directories":{},"_nodeVersion":"25.9.0","dependencies":{"@acegalaxy/security-utils":"^0.1.1"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.7.0","@types/node":"^20.0.0"},"_npmOperationalInternal":{"tmp":"tmp/voice-gateway_0.1.1_1778088203942_0.13089038547266152","host":"s3://npm-registry-packages-npm-production"}},"0.1.2":{"name":"@acegalaxy/voice-gateway","version":"0.1.2","description":"OpenAI Whisper wrapper with cost cap, rate limit, default-deny authz, queue, and audit log. Production-ready STT for Telegram bots.","main":"dist/index.js","scripts":{"test":"node --test test/*.test.js","build":"tsc","prepublishOnly":"npm run build","clean":"rm -rf dist","pretest":"npm run build"},"keywords":["voice","stt","whisper","openai","telegram","cost-cap","rate-limit"],"author":{"name":"ACE Galaxy","email":"hello@acegalaxy.co"},"license":"MIT","private":false,"engines":{"node":">=20"},"publishConfig":{"access":"public"},"repository":{"type":"git","url":"git+https://github.com/acegalaxy-co/ace_commons-voice-gateway-nodejs.git"},"bugs":{"url":"https://github.com/acegalaxy-co/ace_commons-voice-gateway-nodejs/issues"},"homepage":"https://github.com/acegalaxy-co/ace_commons-voice-gateway-nodejs#readme","dependencies":{"@acegalaxy/security-utils":"^0.1.2"},"types":"dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"./sources/telegram":{"types":"./dist/sources/telegram.d.ts","default":"./dist/sources/telegram.js"},"./sources/*":{"types":"./dist/sources/*.d.ts","default":"./dist/sources/*.js"},"./*":{"types":"./dist/*.d.ts","default":"./dist/*.js"},"./package.json":"./package.json"},"devDependencies":{"typescript":"^5.7.0","@types/node":"^20.0.0"},"gitHead":"4944d288c0ec585f06edef8751351caf2179988e","_id":"@acegalaxy/voice-gateway@0.1.2","_nodeVersion":"25.9.0","_npmVersion":"11.12.1","dist":{"integrity":"sha512-0pcp9B53mSdsCh9oDiB63ligZx1dBFgqucTGIylCgWFDkKnX5WqlUURgyjg1hr2pHVRe7FMNxIOqMBVPmDzwVw==","shasum":"5a798afa897e3fbad6eefb2ebc6011ebe0b2f512","tarball":"https://registry.npmjs.org/@acegalaxy/voice-gateway/-/voice-gateway-0.1.2.tgz","fileCount":50,"unpackedSize":63549,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIAsXVmsZNkTPU5I77N+6gizVzB6C4L84yXzsOlYy3ukwAiEAu2uXsi+8xezPjbitIoDuP8oFQNKw/PIfjmSsEjKvkI4="}]},"_npmUser":{"name":"kanelr","email":"lanhnk@acegalaxy.co"},"directories":{},"maintainers":[{"name":"kanelr","email":"lanhnk@acegalaxy.co"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/voice-gateway_0.1.2_1778088288164_0.5259482253592784"},"_hasShrinkwrap":false}},"time":{"created":"2026-05-06T16:47:14.584Z","modified":"2026-05-06T17:24:48.425Z","0.1.0":"2026-05-06T16:47:14.856Z","0.1.1":"2026-05-06T17:23:24.096Z","0.1.2":"2026-05-06T17:24:48.307Z"},"bugs":{"url":"https://github.com/acegalaxy-co/ace_commons-voice-gateway-nodejs/issues"},"author":{"name":"ACE Galaxy","email":"hello@acegalaxy.co"},"license":"MIT","homepage":"https://github.com/acegalaxy-co/ace_commons-voice-gateway-nodejs#readme","keywords":["voice","stt","whisper","openai","telegram","cost-cap","rate-limit"],"repository":{"type":"git","url":"git+https://github.com/acegalaxy-co/ace_commons-voice-gateway-nodejs.git"},"description":"OpenAI Whisper wrapper with cost cap, rate limit, default-deny authz, queue, and audit log. Production-ready STT for Telegram bots.","maintainers":[{"name":"kanelr","email":"lanhnk@acegalaxy.co"}],"readme":"# @acegalaxy/voice-gateway\n\n> **OpenAI Whisper wrapper với cost cap + rate limit + audit — production-ready STT cho Telegram bots.**\n\nMulti-source speech-to-text gateway với 5-layer security (caller identity, default-deny authz, rate limit, daily cost cap, bounded queue, append-only audit log). Built for Telegram voice/audio/video_note messages but works with any audio file path.\n\n[![npm](https://img.shields.io/npm/v/@acegalaxy/voice-gateway.svg)](https://www.npmjs.com/package/@acegalaxy/voice-gateway)\n[![license](https://img.shields.io/npm/l/@acegalaxy/voice-gateway.svg)](LICENSE)\n\n## Why\n\nCalling Whisper directly from a Telegram bot is one runaway loop away from a $200 OpenAI bill. `voice-gateway` funnels every transcription through:\n\n1. **Identity** — who is calling (service + scope)\n2. **Authz** — is this caller allowed to use this source × provider\n3. **Rate limit** — per-service QPS\n4. **Cost cap** — daily USD budget per service\n5. **Queue** — bounded concurrency, no unbounded fan-out\n6. **Audit log** — append-only record of every call\n\n`transcribe()` **never throws** — it returns `{ outcome: 'ok' | 'denied' | 'error', ... }`. Your bot stays up.\n\n## Install\n\n```bash\nnpm install @acegalaxy/voice-gateway\n```\n\nRequires Node `>=20`.\n\n## Quick start — Telegram bot voice message\n\n```js\nconst TelegramBot = require('node-telegram-bot-api');\nconst voiceGw = require('@acegalaxy/voice-gateway');\nconst { pickAudioSource } = require('@acegalaxy/voice-gateway/sources/telegram');\n\nconst bot = new TelegramBot(process.env.TELEGRAM_BOT_TOKEN, { polling: true });\n\nbot.on('message', async (msg) => {\n  const audio = pickAudioSource(msg); // { fileId, mimeType, isVoice, durationSec } or null\n  if (!audio) return;\n\n  const result = await voiceGw.transcribe(\n    {\n      source: 'telegram',\n      sourceData: { botToken: process.env.TELEGRAM_BOT_TOKEN, ...audio },\n      language: 'vi',\n    },\n    { service: 'my-bot', scope: 'public', userId: msg.from.id },\n  );\n\n  if (result.outcome === 'ok') {\n    bot.sendMessage(msg.chat.id, `📝 ${result.text}\\n\\n_$${result.costUsd.toFixed(4)}_`);\n  } else if (result.outcome === 'denied' && result.denyReason === 'L4_cost_cap') {\n    bot.sendMessage(msg.chat.id, '⚠️ Daily voice budget exhausted, try tomorrow.');\n  } else {\n    bot.sendMessage(msg.chat.id, `❌ ${result.denyReason}`);\n  }\n});\n```\n\nThat's it. Cost cap, rate limit, and audit log are automatic.\n\n## API\n\n```js\nconst result = await voiceGw.transcribe(request, caller);\n```\n\n**`request`**\n| field | type | notes |\n| --- | --- | --- |\n| `source` | `'telegram'` \\| `'file'` | source name |\n| `sourceData` | object | source-specific (see below) |\n| `language` | string | ISO 639-1, e.g. `'vi'`, `'en'` |\n| `provider` | string | default `'whisper'` |\n| `skipQueue` | boolean | default `false` |\n\n**`caller`** — `{ service: string, scope: string, userId?: string|number }`\n\n**`result`** — never throws:\n- `{ outcome: 'ok',     text, model, durationSec, bytes, costUsd, latencyMs }`\n- `{ outcome: 'denied', denyReason, latencyMs }`\n- `{ outcome: 'error',  denyReason, latencyMs }`\n\n### Admin\n\n```js\nvoiceGw.queueStats(); // { active, pending, maxConcurrent, maxQueued }\n```\n\n## Sources\n\n| source     | sourceData fields                                          |\n| ---------- | ---------------------------------------------------------- |\n| `telegram` | `botToken`, `fileId`, `mimeType`, `durationSec`, `isVoice` |\n| `file`     | `path` (local audio file)                                  |\n\nTelegram helper: `require('@acegalaxy/voice-gateway/sources/telegram').pickAudioSource(msg)` extracts voice / audio / video_note from any Telegram update.\n\n## Providers\n\n| provider  | env vars                                                                |\n| --------- | ----------------------------------------------------------------------- |\n| `whisper` | `OPENAI_API_KEY` (or `VOICE_GW_WHISPER_API_KEY_ENV`), `VOICE_GW_WHISPER_MODEL` |\n\n## Env config\n\n| Var                            | Default                     |\n| ------------------------------ | --------------------------- |\n| `VOICE_GW_MAX_CONCURRENT`      | `2`                         |\n| `VOICE_GW_MAX_QUEUED`          | `100`                       |\n| `VOICE_GW_QPS_PER_MIN`         | `30`                        |\n| `VOICE_GW_DAILY_COST_USD`      | `5`                         |\n| `VOICE_GW_WHISPER_MODEL`       | `gpt-4o-mini-transcribe`    |\n| `VOICE_GW_WHISPER_BASE_URL`    | `https://api.openai.com/v1` |\n| `VOICE_GW_WHISPER_API_KEY_ENV` | `OPENAI_API_KEY`            |\n| `VOICE_GW_WHISPER_USD_PER_MIN` | `0.006`                     |\n\n## Deny reasons\n\n| reason              | layer | meaning                                    |\n| ------------------- | ----- | ------------------------------------------ |\n| `L1_source`         | L1    | source name not registered                 |\n| `L1_adapter`        | L1    | provider name not registered               |\n| `L1_too_large`      | L1    | file exceeded `maxBytes`                   |\n| `L1_empty`          | L1    | source returned empty buffer               |\n| `L2_unknown_caller` | L2    | caller missing `service` or `scope`        |\n| `L3_authz`          | L3    | source/provider not allowed for caller     |\n| `L4_rate_limit`     | L4    | per-service QPS exceeded OR queue full     |\n| `L4_cost_cap`       | L4    | daily $ budget exceeded                    |\n\n## Hard rules\n\n- ❌ No direct OpenAI/Whisper SDK import outside `adapters/`.\n- ❌ No direct Telegram CDN fetch outside `sources/telegram.js`.\n- ❌ No bypass of identity / authz / audit log.\n- ❌ No persisting audio bytes — streams in-memory only.\n\n## Sanity check (pre-commit)\n\n```bash\ngrep -rn \"fetch.*api.openai.com/v1/audio\\|fetch.*api.telegram.org/file\" . \\\n  --include=\"*.js\" \\\n  --exclude-dir=adapters --exclude-dir=sources --exclude-dir=node_modules\n```\n\nMust be empty.\n\n## License\n\n[MIT](LICENSE) © 2026 ACE Galaxy\n\n## Related\n\n- [@acegalaxy/db-gateway](https://github.com/acegalaxy-co/ace_commons-db-gateway-nodejs) — same pattern for databases\n- [@acegalaxy/ott-gateway](https://github.com/acegalaxy-co/ace_commons-ott-gateway-nodejs) — same pattern for messaging\n","readmeFilename":"README.md"}