@crvouga/mockingbird-service-aws-speech

中文文档
3.11.0发布于 2026/11/11MITTypeScriptES Module构建来源已验证近 30 天下载 1.6万

Stateful emulator of AWS Polly (SynthesizeSpeech, StartSpeechSynthesisStream) and Transcribe (streaming over h2c, batch jobs), with exact event-stream framing and scripted transcripts.

原始使用文档
文档目录9 节
  1. Install
  2. Usage
  3. Protocols
  4. Routes
  5. Transcripts ( PUT /__admin/transcripts )
  6. Admin (beyond the standard contract)
  7. Namespaces
  8. Deliberately not modelled
  9. API

@crvouga/mockingbird-service-aws-speech

Local emulators. Real API contracts. Part of Mockingbird.

Stateful emulator of Amazon Polly and Amazon Transcribe for test suites: Polly SynthesizeSpeech and StartSpeechSynthesisStream (HTTP/2 duplex event stream), Transcribe Streaming StartStreamTranscription (HTTP/2 duplex) and Transcribe batch StartTranscriptionJob / GetTranscriptionJob. Chat voice turns run end to end with no audio leaving the machine: Transcribe "hears" what a test scripts, Polly answers with deterministic synthetic audio whose length follows the text.

  • Operation coverage: SUPPORT.md
  • Proven with the official clients our consumer pins: @aws-sdk/client-polly, @aws-sdk/client-transcribe-streaming, @aws-sdk/client-transcribe (3.1132.0), and the MP3 decoder our backend uses ([email protected]).

Install

npm install -D @crvouga/mockingbird-service-aws-speech

ESM only. Node >= 22 or Bun >= 1.2. No native dependencies. Serve it with npx mockingbird-aws-speech serve (h2c + HTTP/1.1 on one port), createServer from ./server, or createRuntime with any Fetch server (HTTP/1.1 only).

Usage

npx mockingbird-aws-speech serve --port 8797
export AWS_ENDPOINT_URL_POLLY=http://127.0.0.1:8797
export AWS_ENDPOINT_URL_TRANSCRIBE_STREAMING=http://127.0.0.1:8797
export AWS_ENDPOINT_URL_TRANSCRIBE=http://127.0.0.1:8797
import { PollyClient, SynthesizeSpeechCommand } from "@aws-sdk/client-polly"
import {
  StartStreamTranscriptionCommand,
  TranscribeStreamingClient,
} from "@aws-sdk/client-transcribe-streaming"
import { createServer } from "@crvouga/mockingbird-service-aws-speech/server"

// Test setup: start the emulator and script what the next voice session "hears":
// partials while audio streams in, then the final.
const emulator = await createServer()
await fetch(`${emulator.url}/__admin/transcripts`, {
  method: "PUT",
  headers: { "content-type": "application/json" },
  body: JSON.stringify({ match: { any: true }, partials: ["I have", "I have a headache"], final: "I have a headache." }),
})

// The AWS SDK clients your application uses; only the endpoint changes.
const config = {
  endpoint: emulator.url,
  region: "us-east-1",
  credentials: { accessKeyId: "fixture", secretAccessKey: "fixture" },
}
const transcribe = new TranscribeStreamingClient(config)
const polly = new PollyClient(config)

// Application code: a voice turn transcribes the member's audio, then speaks the reply.
const { TranscriptResultStream } = await transcribe.send(
  new StartStreamTranscriptionCommand({
    LanguageCode: "en-US",
    MediaEncoding: "pcm",
    MediaSampleRateHertz: 16_000,
    AudioStream: (async function* () {
      yield { AudioEvent: { AudioChunk: new Uint8Array(3_200) } }
    })(),
  }),
)
const heard: string[] = []
for await (const event of TranscriptResultStream ?? []) {
  for (const result of event.TranscriptEvent?.Transcript?.Results ?? [])
    heard.push(result.Alternatives?.[0]?.Transcript ?? "")
}
// heard: ["I have", "I have a headache", "I have a headache."]
const { AudioStream } = await polly.send(
  new SynthesizeSpeechCommand({ VoiceId: "Matthew", OutputFormat: "mp3", Text: "Rest and drink water." }),
)
const audio = await AudioStream?.transformToByteArray() // deterministic MP3 frames

await emulator.close() // after the test

Protocols

Polly and Transcribe Streaming clients default to NodeHttp2Handler: against an http:// endpoint they speak h2c (cleartext HTTP/2, prior knowledge), and their bidirectional operations need HTTP/2 duplex. Transcribe batch uses HTTP/1.1. mockingbird-aws-speech serve and createServer sniff each connection and serve both on one port (serve --config from another service's CLI, and createRuntime behind a plain Fetch server, speak HTTP/1.1 only).

Routes

RouteBehaviour
POST /v1/speechPolly SynthesizeSpeech. OutputFormat: pcm → raw PCM s16le mono 440 Hz tone (audio/pcm, even byte count, SampleRate 8000/16000, default 16000); mp3 → valid MPEG-2 Layer III frames (audio/mpeg, 8000/16000/22050/24000). 60 ms of audio per character, byte-identical on every run; x-amzn-RequestCharacters. Errors: ValidationException (unknown voice or engine), InvalidSampleRateException, TextLengthExceededException (> 3000 characters).
POST /v1/synthesisStreamPolly StartSpeechSynthesisStream (HTTP/2 duplex, parameters in x-amzn-Engine / x-amzn-VoiceId / x-amzn-OutputFormat / x-amzn-SampleRate; generative engine only). Each TextEvent answers AudioEvents (24 kHz MP3 frames mpg123 decodes); CloseStreamEvent (or the end of input) answers StreamClosedEvent {RequestCharacters}.
POST /stream-transcriptionTranscribe StartStreamTranscription (HTTP/2 duplex). Validates the x-amzn-transcribe-* parameters (400 BadRequestException), echoes them in the response headers with x-amzn-transcribe-session-id. Sends one scripted partial (IsPartial: true) per AudioEvent received, the rest when the audio ends, then the final result, then closes. Audio bytes are counted, never kept.
POST /Transcribe batch (AWS JSON 1.1, X-Amz-Target). StartTranscriptionJob → IN_PROGRESS (ConflictException for a repeated name); GetTranscriptionJob → COMPLETED once jobDurationMs (2 s) have passed on the emulator clock, with Transcript.TranscriptFileUri = https://s3.<region>.amazonaws.com/<OutputBucketName>/<OutputKey>; BadRequestException for an unknown job.

Transcripts (PUT /__admin/transcripts)

{match?, partials?, final, times?} (or {"transcripts": [...]}). match is {sessionIndex} (0-based count of streaming sessions in the namespace), {jobName} (a batch job), or {any: true}; an exact match wins over any. Unscripted sessions and jobs hear defaultTranscript ("Hello.", PUT /__admin/settings) and count as unscripted (GET /__admin/transcripts → stats).

Admin (beyond the standard contract)

RouteEffect
PUT / POST / GET / DELETE /__admin/transcriptsReplace, add, list (with stats), remove (?id=).
GET /__admin/jobsBatch jobs (advanced on the emulator clock).
POST /__admin/jobs/:name/completeComplete now, optionally with {transcript}.
POST /__admin/jobs/:name/failFail with {reason} (FailureReason).
GET /__admin/jobs/:name/transcriptThe transcript JSON Transcribe would write to S3.
GET /__admin/speechMetadata of every synthesis / transcription (voice, engine, format, characters, bytes, script id — never text).
GET/PUT /__admin/settingsdefaultTranscript, jobDurationMs.

With --s3-endpoint <url> (transcriptStore in code) a completed job's transcript JSON is also written to that S3 (the stack's s3rver) at OutputBucketName/OutputKey, so the app's own GetObject on TranscriptFileUri finds it.

Fault presets (POST /__admin/faults {"preset": "<name>", "count"?: n}): polly_throttling (429 ThrottlingException), polly_service_failure (500), polly_stream_throttling / polly_stream_validation / polly_stream_quota (an exception event before any audio: our adapter falls back to SynthesizeSpeech), polly_stream_failure (ServiceFailureException after audio), polly_stream_no_close (no StreamClosedEvent), transcribe_bad_request (400), transcribe_limit_exceeded (429), transcribe_service_unavailable (503), transcribe_mid_stream_failure (InternalFailureException after the first partial), transcribe_job_limit (LimitExceededException), transcribe_job_failed (next job FAILED).

Namespaces

x-mockingbird-namespace, a /__admin/ns/<name> prefix on the endpoint URL, or the SigV4 access key id: PUT /__admin/credentials {"credentials": {"<AWS_ACCESS_KEY_ID>": "<namespace>"}}. The journal records voice, engine, format, character counts and script ids — never text.

Deliberately not modelled

  • Real speech: Polly audio is a tone (PCM) or silent-but-valid MP3 frames (a tone needs a real MP3 encoder); Transcribe never listens — words come only from scripts.
  • ogg_vorbis, ogg_opus, mulaw, alaw and speech-mark (json) output, SSML semantics (tags are stripped for length), lexicons, per-voice engine availability.
  • Transcribe language identification, speaker labels, custom vocabularies (accepted and echoed, not applied), Call Analytics and Medical variants, batch jobs reading their media from S3.
  • SigV4 signatures and event signatures are not verified; the access key id only selects a namespace.

API

ExportKindDescription
SpeechAPIclassThe in-process emulator: fetch, reset, jobs(), complete(name, transcript?), fail(name, reason), transcriptDocument(job), speechLog(), stats(). Options: sqlite, now, namespace, settings, transcripts, transcriptStore.
createRuntimefunctionThe emulator with the full service contract. Options: settings, transcripts, transcriptStore, clock, seed, adminKey, onLog, sqlite.
SPEECH_PRESETSobjectEvery named fault preset.
SPEECH_NAMESPACEstringThe service name, "aws-speech".
POLLY_VOICESarrayEvery Polly voice id the emulator accepts.
speechErrorfunctionA restJson1 error response (x-amzn-ErrorType + {message}).
accessKeyCredentialfunctionThe SigV4 access key id of a request (how credentials map to namespaces).
pcmTone, mp3Audio, mp3Frame, durationFor, MS_PER_CHARACTER, MP3_SAMPLE_RATES, MP3_SAMPLES_PER_FRAMEaudioThe deterministic synthetic audio.
DEFAULT_SETTINGSvalueDefault settings.
encodeMessage, decodeMessage, FrameReader, readFrames, eventFrame, exceptionFrame, unwrapSigned, crc32, EventStreamErrorcodecThe event-stream codec (exact prelude, headers and CRC32s), in both directions.
document, operationIds, supportedOperationIdsvaluesThe vendored OpenAPI contract and its operation ids.
createServer, serveTarget, DEFAULT_PORT, listenH2c (./server)NodeServe h2c + HTTP/1.1 on one port; the serve CLI target; port 8797; the dual-protocol listener.

Part of mockingbird.

版本与发布记录

@crvouga/mockingbird-service-aws-speech 共发布 42 个版本,首次发布于 2026/09/23;过去 12 个月发布了 42 个版本。最新版本附带 npm provenance 构建来源证明,可追溯到公开的 CI 构建记录。

版本发布日期距今
3.11.0latest2026/11/11今天
3.10.92026/11/038 天前
3.10.82026/11/038 天前
3.10.72026/11/038 天前
3.10.62026/11/038 天前
3.10.52026/11/038 天前

关于 @crvouga/mockingbird-service-aws-speech 的常见问题

@crvouga/mockingbird-service-aws-speech 是什么?

Stateful emulator of AWS Polly (SynthesizeSpeech, StartSpeechSynthesisStream) and Transcribe (streaming over h2c, batch jobs), with exact event-stream framing and scripted transcripts.

如何安装 @crvouga/mockingbird-service-aws-speech?

在项目目录运行 npm install @crvouga/mockingbird-service-aws-speech,或使用 pnpm add / yarn add / bun add。它还提供命令行工具 mockingbird-aws-speech,可以用 npx mockingbird-aws-speech 直接运行。

@crvouga/mockingbird-service-aws-speech 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@crvouga/mockingbird-service-aws-speech 支持 ESM 还是 CommonJS?

以 ES Module 形式发布,推荐使用 import 引入。在 CommonJS 项目中,Node.js 20.19+ / 22.12+ 可以直接 require() 不含顶层 await 的 ES Module,更早的版本需要改用动态 import()。

@crvouga/mockingbird-service-aws-speech 可以商用吗?

@crvouga/mockingbird-service-aws-speech 使用 MIT 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@crvouga/mockingbird-service-aws-speech 的下载量有多少?

根据 npm 官方下载统计,@crvouga/mockingbird-service-aws-speech 在 2026/10/11 – 2026/11/09 这 30 天内被下载 16,240 次,日均约 541 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@crvouga/mockingbird-service-aws-speech 还在维护吗?

最新版本 3.11.0 发布于 2026/11/11(今天);至今共发布 42 个版本,其中过去 12 个月发布了 42 个;首次发布于 2026/09/23。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。