@crvouga/mockingbird-service-bedrock

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

Stateful, scriptable emulator of AWS Bedrock Runtime (Converse, ConverseStream, InvokeModel incl. Titan embeddings, Nova Sonic bidirectional streams over h2c) and AgentCore InvokeHarness, with exact event-stream framing.

原始使用文档
文档目录11 节
  1. Install
  2. Usage
  3. Protocols
  4. Routes
  5. Scripts ( PUT /__admin/scripts )
  6. Faults
  7. Admin (beyond the standard contract)
  8. Best guesses, not oracle-verified
  9. Namespaces
  10. Deliberately not modelled
  11. API

@crvouga/mockingbird-service-bedrock

Local emulators. Real API contracts. Part of Mockingbird.

Stateful, scriptable emulator of Amazon Bedrock Runtime for test suites: Converse, ConverseStream (byte-exact application/vnd.amazon.eventstream frames), InvokeModel (Anthropic Messages bodies and Titan text embeddings), InvokeModelWithBidirectionalStream (Nova Sonic over HTTP/2 duplex), and the AgentCore InvokeHarness event stream.

The emulator never generates language. It replays scripts: a test says "when the chat model sees dizzy with report_rx_symptom available, emit this toolUse; after the tool result comes back, say this". Chat turns, approval cards, guardrail blocks, structured output and throttles become deterministic and take milliseconds.

Install

npm install -D @crvouga/mockingbird-service-bedrock

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

Usage

Point the app at it. No code change: every client honours the endpoint variables.

npx mockingbird-bedrock serve --port 8796
export AWS_ENDPOINT_URL_BEDROCK_RUNTIME=http://127.0.0.1:8796    # SDK v3, AI SDK, botocore
export AWS_ENDPOINT_URL_BEDROCK_AGENTCORE=http://127.0.0.1:8796  # AgentCore InvokeHarness
import { BedrockRuntimeClient, ConverseCommand } from "@aws-sdk/client-bedrock-runtime"
import { createServer } from "@crvouga/mockingbird-service-bedrock/server"

// Test setup: start the emulator and script the model's turns.
const emulator = await createServer()
await fetch(`${emulator.url}/__admin/scripts`, {
  method: "PUT",
  headers: { "content-type": "application/json" },
  body: JSON.stringify({
    scripts: [
      {
        id: "rx-symptom-approval",
        match: { modelId: "*sonnet*", lastUserText: { contains: "dizzy" }, toolsInclude: ["report_rx_symptom"] },
        turns: [
          {
            toolUse: { name: "report_rx_symptom", input: { symptoms: [{ symptomDefinitionId: 7, severity: 3 }] } },
            stopReason: "tool_use",
          },
          {
            expectToolResult: { name: "report_rx_symptom" },
            text: "I've flagged that for your care team.",
            chunkSize: 12,
            usage: { inputTokens: 1200, outputTokens: 40, cacheReadInputTokens: 900 },
          },
        ],
      },
    ],
  }),
})

// The AWS SDK client your application uses; only the endpoint changes.
const bedrock = new BedrockRuntimeClient({
  endpoint: emulator.url,
  region: "us-east-1",
  credentials: { accessKeyId: "fixture", secretAccessKey: "fixture" },
})

// Application code: the chat turn now asks for the tool, which the app shows as an approval card.
const turn = await bedrock.send(
  new ConverseCommand({
    modelId: "global.anthropic.claude-sonnet-4-6",
    messages: [{ role: "user", content: [{ text: "I feel dizzy" }] }],
    toolConfig: {
      tools: [{ toolSpec: { name: "report_rx_symptom", inputSchema: { json: { type: "object" } } } }],
    },
  }),
)
// turn.stopReason === "tool_use"
// turn.output?.message?.content?.[0]?.toolUse?.name === "report_rx_symptom"
// Sending the tool result back resumes the turn: "I've flagged that for your care team."

await emulator.close() // after the test

Protocols

The AWS SDK v3 clients for Bedrock Runtime default to NodeHttp2Handler: against an http:// endpoint they speak h2c (cleartext HTTP/2 with prior knowledge), and Nova Sonic needs HTTP/2 duplex. The AI SDK and AgentCore use HTTP/1.1. mockingbird-bedrock serve and createServer sniff each connection's first bytes 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 — fine for the AI SDK, not for the SDK v3 Bedrock client.)

Routes

RouteBehaviour
POST /model/{modelId}/converseConverse JSON: output.message.content[] (text, toolUse, reasoningContent), stopReason, usage (with cacheRead/WriteInputTokens when a cachePoint is present), metrics, trace.guardrail (with guardrailConfig.trace: "enabled"), x-amzn-RequestId.
POST /model/{modelId}/converse-streamThe same answer as event frames: messageStart, contentBlockStart (tool use), contentBlockDelta (text, toolUse.input partial JSON, reasoningContent), contentBlockStop, messageStop, metadata; exception frames mid-stream.
POST /model/{modelId}/invokeamazon.titan-embed-text-*: {embedding, inputTextTokenCount} — a deterministic unit vector from SHA-256(inputText) (1024-d by default; dimensions 256/512/1024), or a lexical one with embeddingMode: "lexical" (see Settings). Claude models: an Anthropic Messages body in, a Messages response out.
POST /model/{modelId}/invoke-with-bidirectional-streamNova Sonic (*sonic*), HTTP/2 duplex: reads chunk events as they arrive (SigV4 envelopes unwrapped), answers each user turn with textOutput + 24 kHz PCM tone audioOutput + toolUse, then usageEvent; completionEnd after sessionEnd.
POST /harnesses/invoke?harnessArn=AgentCore harness stream: messageStart, contentBlockDelta (text or toolResult[]), contentBlockStop, messageStop, metadata; validationException / internalServerException / runtimeClientError frames.

modelId is any model id, inference-profile id (global. / us.) or URL-encoded ARN. SigV4 is accepted without verification. Request checks Bedrock makes and our code branches on are enforced: role alternation, first/last message is the user (assistant prefill is rejected for Claude 4.5+), tool-use/tool-result pairing, toolConfig required with tool blocks, a document needs a sibling text block, temperature + top_p together on Claude 4.5+, plus the best-guess checks listed under "Best guesses, not oracle-verified". Errors are x-amzn-ErrorType: <Name>:http://internal.amazon.com/coral/com.amazon.bedrock/ + {"message"}. Output longer than maxTokens (≈4 chars/token) is cut with stopReason: "max_tokens".

Scripts (PUT /__admin/scripts)

A script is {id, match?, turns, times?}. The first script (in insertion order) whose match accepts a call and has a turn for that point in the conversation answers it; times caps how many calls it answers.

  • Match keys: modelId (glob), operation (Converse, ConverseStream, InvokeModel, InvokeModelWithBidirectionalStream, InvokeHarness), lastUserText (string = contains, or {contains, regex, flags}), systemHash (SHA-256 hex of the system text blocks joined with \n), systemText (the same joined system text, cachePoint blocks skipped; string = case-insensitive contains, or {contains, regex, flags}; a call with no system prompt never matches; for InvokeModel the Claude system string or text blocks, never stored), jsonSchemaName (outputConfig.textFormat.structure.jsonSchema.name; string = case-insensitive equality, or {contains, regex, flags}; a call without a named outputConfig schema never matches), toolsInclude, toolChoice (auto / any / a tool name), hasDocument, hasImage, callIndex (0-based index of the call in the namespace). match is a closed set: an unknown or misspelled key (lastUserTxt) or a malformed criterion makes PUT/POST /__admin/scripts answer 400 (scripts[0].match.lastUserTxt: unknown match field (one of ...)) instead of silently matching every call; the same goes for an unknown key inside a text match.
  • Turn selection reads the conversation, not server state: turn n answers the call that comes after n assistant messages since the member last said something. So turn 0 is the first call of a user turn, turn 1 is the call that resumes after a tool result, and every new conversation starts over. expectToolResult: {name} makes a turn answer only when the last user message carries that tool's result. Nova Sonic counts answers within the session.
  • A turn is any of: text (streamed in chunkSize-character deltas, delayMsPerChunk emulator-clock ms apart), reasoning, toolUse ({name, input, toolUseId?} or a list), json (structured output, rendered in the form the request asked for — see below), guardrail (true or {text, trace}: guardrail_intervened with Bedrock's refusal text and a trace), toolResult (harness), userTranscript (Nova Sonic), stopReason, usage, fault.
  • Structured output (json) goes out as text JSON for outputConfig.textFormat.structure.jsonSchema (Python boto3 clients) and additionalModelRequestFields.output_config.format (AI SDK native), and as a toolUse of the forced tool for toolChoice: {tool} or {any} (the AI SDK's synthetic json tool, insight reports' record_chat_*).
  • Unscripted defaults, each counted as unscripted (GET /__admin/scripts → stats): chat → "OK." (PUT /__admin/settings {"defaultText"}); structured output → the minimal object valid against the request's schema; our intent classifier (its system prompt asks for {"category","confidence"}) → {"category":"general","confidence":0.9}; InvokeModel with a Claude body (the EMR scribe) → a 4-section SOAP JSON; Titan → the SHA-256 vector; InvokeHarness → an eligible_for_clinician_review prescreen summary; Nova Sonic → "OK." spoken.

Faults

A turn's fault (or a preset, POST /__admin/faults {"preset": "<name>", "count"?: n}, which applies to every model and harness call in the calling namespace):

Preset / faultEffect
throttling429 ThrottlingException before the first chunk
mid_stream_exception (afterChunks, exceptionType)content chunks, then a modelStreamErrorException frame (harness: internalServerException)
mid_stream_throttlingthe same with a throttlingException frame
validation_exception / validation400 ValidationException
max_tokensoutput cut in half, stopReason: "max_tokens"
latency (latencyMs)the response starts after 2 s on the emulator clock
truncated_framethe stream stops half-way through a frame (both decoders throw)
model_timeout, service_unavailable, access_denied, internal_server408 / 503 / 403 / 500 with the matching x-amzn-ErrorType
resource_not_found404 ResourceNotFoundException ("The specified resource ARN was not found. Check the ARN and try again.", per the Converse API reference), before any frame, on Converse, ConverseStream and InvokeModel

Chunk pacing and latency wait on the emulator clock: freeze it (POST /__admin/clock {"freeze": true}) and advance it to release each chunk, so time-to-first-token tests are exact.

Admin (beyond the standard contract)

RouteEffect
PUT /__admin/scriptsReplace the namespace's scripts ({"scripts": [...]}); validated.
POST /__admin/scriptsAdd scripts (same ids overwrite).
GET /__admin/scriptsScripts plus stats (calls, scripted, unscripted, byScript, byFallback, byOperation).
DELETE /__admin/scripts[?id=]Remove one or all.
GET /__admin/model-metricsJust the stats.
GET/PUT /__admin/settingsdefaultText, chunkSize (16), delayMsPerChunk (0), audioTurnChunks (0: a Nova Sonic spoken turn ends at the audio contentEnd; n: after n audio frames), embeddingMode (sha256 default, or lexical), validatePromptCaching (true default; false disables the model check on cachePoint).

embeddingMode: "lexical" (opt-in; the test-control request in #522, not a Bedrock behaviour) keeps Titan vectors deterministic and unit length but makes them similarity-preserving: each word (lowercased, accents and punctuation dropped) adds to a SHA-256-chosen signed dimension, so texts differing only in case, punctuation or spacing have cosine similarity 1, texts sharing most words score high, and texts with no word in common score near 0. It is bag-of-words, not a language model: synonyms ("evening" / "night") stay apart. Scripted per-text vectors are not implemented. normalize: false is ignored in both modes (vectors are always unit length).

The request journal (GET /__admin/requests) records per call only modelId, script (or unscripted:<default>), tool names, flags (cachePoint, guardrail, document, image, structured:<form>), stopReason and token counts — never prompt or message text.

Best guesses, not oracle-verified

These request checks come from the AWS documentation and what consumers reported about live Bedrock, not from observing the service; the exact message text of the ones marked "ours" is an emulator choice. All answer 400 ValidationException.

  • Structured-output schemas (outputConfig.textFormat.structure.jsonSchema.schema, Converse and ConverseStream, docs): schema that is not a JSON object (ours); oneOf (Schema type 'oneOf' is not supported); additionalProperties set to anything other than false on any node, true or a sub-schema (For 'object' type, 'additionalProperties: <X>' is not supported. Please set 'additionalProperties' to false); a string-only enum combined with a union type such as ["string","null"] (ours); minimum, maximum, multipleOf, minLength, maxLength, minItems above 1, an external $ref, and a recursive $ref cycle (ours). An omitted additionalProperties and anyOf / allOf / internal $ref / $defs pass. Only the outputConfig form is checked, not additionalModelRequestFields.output_config nor InvokeModel bodies.
  • Document names (document.name, API reference): 1 to 200 characters of ASCII letters, digits, whitespace, hyphens, parentheses and square brackets, with no run of two whitespace characters. The message is the one a consumer recorded from live Bedrock.
  • cachePoint (docs): a cachePoint content block directly after a document block (consumer report; message ours); and, on Converse / ConverseStream, a cachePoint in system, messages or toolConfig.tools for a model outside the documented list (Claude 3.5 Haiku, 3.5 Sonnet v2, 3.7 Sonnet, Claude 4 and later Opus / Sonnet / Haiku, Fable and Mythos; Nova Micro / Lite / Pro / 2 Lite), matched on the family inside plain, us. / global. and ARN forms. The list grows: turn the model check off with PUT /__admin/settings {"validatePromptCaching": false}. The ttl field is not checked.
  • Unknown model ids still answer normally (modelId is any id). A knownModels setting that makes an unknown id fail was requested in #523 but not built: whether live Bedrock answers ResourceNotFoundException or ValidationException for it has not been observed.

Namespaces

x-mockingbird-namespace, a /__admin/ns/<name> prefix on the endpoint URL, or by credential: the SDKs cannot add headers, so map each worker's access key id: PUT /__admin/credentials {"credentials": {"<AWS_ACCESS_KEY_ID>": "<namespace>"}}.

Deliberately not modelled

  • Language: output only ever comes from scripts or the fixed defaults.
  • Real speech: Nova Sonic audio out is a 440 Hz PCM tone whose length follows the text; audio in is counted, never transcribed (a spoken turn matches with lastUserText: "").
  • InvokeModelWithResponseStream (no consumer calls it), guardrail evaluation itself (ApplyGuardrail; scripts decide when the guardrail intervenes), prompt caching arithmetic (cache token counts are 0 unless scripted), model-specific output token limits.
  • SigV4 signatures are not verified; the access key id only selects a namespace.

API

ExportKindDescription
BedrockAPIclassThe in-process emulator: fetch, reset, scripts(), putScripts(scripts, replace?), removeScripts(id?), stats(). Options: sqlite, now, namespace, settings, scripts, sleep.
createRuntimefunctionThe emulator with the full service contract (health, admin, namespaces, SigV4 credentials, presets, scripts). Options: settings, scripts, clock, seed, adminKey, onLog, sqlite.
BEDROCK_PRESETSobjectEvery named fault preset.
BEDROCK_NAMESPACEstringThe service name, "bedrock".
bedrockErrorfunctionA Bedrock error response (status, x-amzn-ErrorType, {message}).
accessKeyCredentialfunctionThe SigV4 access key id of a request (how credentials map to namespaces).
clockSleepfunctionA sleep that waits on a (possibly frozen) emulator clock.
titanEmbeddingfunctionThe deterministic unit vector Titan answers with.
lexicalEmbedding, EMBEDDING_MODESfunction, arrayThe opt-in similarity-preserving embedding and the embeddingMode values.
sampleSchemafunctionThe minimal instance of a JSON Schema (the unscripted structured output).
parseScriptfunctionValidate one script (what PUT /__admin/scripts runs).
MODEL_OPERATIONS, STOP_REASONS, TURN_FAULTSarraysThe values operation, stopReason and fault accept.
DEFAULT_SETTINGS, DEFAULT_CHAT_TEXT, DEFAULT_CLASSIFIER, DEFAULT_SOAP_NOTE, GUARDRAIL_BLOCKED_TEXTvaluesThe defaults.
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 8796; the dual-protocol listener for any Fetch handler.

Part of mockingbird.

版本与发布记录

@crvouga/mockingbird-service-bedrock 共发布 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-bedrock 的常见问题

@crvouga/mockingbird-service-bedrock 是什么?

Stateful, scriptable emulator of AWS Bedrock Runtime (Converse, ConverseStream, InvokeModel incl. Titan embeddings, Nova Sonic bidirectional streams over h2c) and AgentCore InvokeHarness, with exact event-stream framing.

如何安装 @crvouga/mockingbird-service-bedrock?

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

@crvouga/mockingbird-service-bedrock 支持 TypeScript 吗?

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

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

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

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

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

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

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

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

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