@copilotkit/aimock 1.7.0 → 1.9.0

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 (134) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/README.md +3 -1
  4. package/dist/bedrock-converse.cjs +13 -8
  5. package/dist/bedrock-converse.cjs.map +1 -1
  6. package/dist/bedrock-converse.d.cts.map +1 -1
  7. package/dist/bedrock-converse.d.ts.map +1 -1
  8. package/dist/bedrock-converse.js +14 -9
  9. package/dist/bedrock-converse.js.map +1 -1
  10. package/dist/bedrock.cjs +51 -15
  11. package/dist/bedrock.cjs.map +1 -1
  12. package/dist/bedrock.d.cts.map +1 -1
  13. package/dist/bedrock.d.ts.map +1 -1
  14. package/dist/bedrock.js +52 -16
  15. package/dist/bedrock.js.map +1 -1
  16. package/dist/cohere.cjs +3 -2
  17. package/dist/cohere.cjs.map +1 -1
  18. package/dist/cohere.d.cts.map +1 -1
  19. package/dist/cohere.d.ts.map +1 -1
  20. package/dist/cohere.js +4 -3
  21. package/dist/cohere.js.map +1 -1
  22. package/dist/embeddings.cjs +3 -2
  23. package/dist/embeddings.cjs.map +1 -1
  24. package/dist/embeddings.d.cts.map +1 -1
  25. package/dist/embeddings.d.ts.map +1 -1
  26. package/dist/embeddings.js +4 -3
  27. package/dist/embeddings.js.map +1 -1
  28. package/dist/gemini.cjs +129 -35
  29. package/dist/gemini.cjs.map +1 -1
  30. package/dist/gemini.d.cts.map +1 -1
  31. package/dist/gemini.d.ts.map +1 -1
  32. package/dist/gemini.js +130 -36
  33. package/dist/gemini.js.map +1 -1
  34. package/dist/helpers.cjs +154 -3
  35. package/dist/helpers.cjs.map +1 -1
  36. package/dist/helpers.d.cts +1 -1
  37. package/dist/helpers.d.cts.map +1 -1
  38. package/dist/helpers.d.ts +1 -1
  39. package/dist/helpers.d.ts.map +1 -1
  40. package/dist/helpers.js +151 -4
  41. package/dist/helpers.js.map +1 -1
  42. package/dist/index.cjs +1 -0
  43. package/dist/index.d.cts +2 -2
  44. package/dist/index.d.ts +2 -2
  45. package/dist/index.js +2 -2
  46. package/dist/journal.cjs +26 -9
  47. package/dist/journal.cjs.map +1 -1
  48. package/dist/journal.d.cts +10 -5
  49. package/dist/journal.d.cts.map +1 -1
  50. package/dist/journal.d.ts +10 -5
  51. package/dist/journal.d.ts.map +1 -1
  52. package/dist/journal.js +26 -10
  53. package/dist/journal.js.map +1 -1
  54. package/dist/llmock.cjs +2 -2
  55. package/dist/llmock.cjs.map +1 -1
  56. package/dist/llmock.d.cts +1 -1
  57. package/dist/llmock.d.ts +1 -1
  58. package/dist/llmock.js +2 -2
  59. package/dist/llmock.js.map +1 -1
  60. package/dist/messages.cjs +192 -2
  61. package/dist/messages.cjs.map +1 -1
  62. package/dist/messages.d.cts.map +1 -1
  63. package/dist/messages.d.ts.map +1 -1
  64. package/dist/messages.js +193 -3
  65. package/dist/messages.js.map +1 -1
  66. package/dist/ollama.cjs +39 -13
  67. package/dist/ollama.cjs.map +1 -1
  68. package/dist/ollama.d.cts.map +1 -1
  69. package/dist/ollama.d.ts.map +1 -1
  70. package/dist/ollama.js +40 -14
  71. package/dist/ollama.js.map +1 -1
  72. package/dist/recorder.cjs +1 -1
  73. package/dist/recorder.cjs.map +1 -1
  74. package/dist/recorder.d.cts +1 -0
  75. package/dist/recorder.d.cts.map +1 -1
  76. package/dist/recorder.d.ts +1 -0
  77. package/dist/recorder.d.ts.map +1 -1
  78. package/dist/recorder.js +1 -1
  79. package/dist/recorder.js.map +1 -1
  80. package/dist/responses.cjs +250 -126
  81. package/dist/responses.cjs.map +1 -1
  82. package/dist/responses.d.cts.map +1 -1
  83. package/dist/responses.d.ts.map +1 -1
  84. package/dist/responses.js +251 -127
  85. package/dist/responses.js.map +1 -1
  86. package/dist/router.cjs +16 -10
  87. package/dist/router.cjs.map +1 -1
  88. package/dist/router.d.cts +1 -1
  89. package/dist/router.d.cts.map +1 -1
  90. package/dist/router.d.ts +1 -1
  91. package/dist/router.d.ts.map +1 -1
  92. package/dist/router.js +16 -10
  93. package/dist/router.js.map +1 -1
  94. package/dist/server.cjs +47 -7
  95. package/dist/server.cjs.map +1 -1
  96. package/dist/server.d.cts.map +1 -1
  97. package/dist/server.d.ts.map +1 -1
  98. package/dist/server.js +48 -8
  99. package/dist/server.js.map +1 -1
  100. package/dist/stream-collapse.cjs +48 -40
  101. package/dist/stream-collapse.cjs.map +1 -1
  102. package/dist/stream-collapse.d.cts.map +1 -1
  103. package/dist/stream-collapse.d.ts.map +1 -1
  104. package/dist/stream-collapse.js +48 -40
  105. package/dist/stream-collapse.js.map +1 -1
  106. package/dist/types.d.cts +22 -1
  107. package/dist/types.d.cts.map +1 -1
  108. package/dist/types.d.ts +22 -1
  109. package/dist/types.d.ts.map +1 -1
  110. package/dist/ws-gemini-live.cjs +4 -2
  111. package/dist/ws-gemini-live.cjs.map +1 -1
  112. package/dist/ws-gemini-live.d.cts +3 -1
  113. package/dist/ws-gemini-live.d.cts.map +1 -1
  114. package/dist/ws-gemini-live.d.ts +3 -1
  115. package/dist/ws-gemini-live.d.ts.map +1 -1
  116. package/dist/ws-gemini-live.js +4 -2
  117. package/dist/ws-gemini-live.js.map +1 -1
  118. package/dist/ws-realtime.cjs +4 -2
  119. package/dist/ws-realtime.cjs.map +1 -1
  120. package/dist/ws-realtime.d.cts +3 -1
  121. package/dist/ws-realtime.d.cts.map +1 -1
  122. package/dist/ws-realtime.d.ts +3 -1
  123. package/dist/ws-realtime.d.ts.map +1 -1
  124. package/dist/ws-realtime.js +4 -2
  125. package/dist/ws-realtime.js.map +1 -1
  126. package/dist/ws-responses.cjs +4 -2
  127. package/dist/ws-responses.cjs.map +1 -1
  128. package/dist/ws-responses.d.cts +3 -1
  129. package/dist/ws-responses.d.cts.map +1 -1
  130. package/dist/ws-responses.d.ts +3 -1
  131. package/dist/ws-responses.d.ts.map +1 -1
  132. package/dist/ws-responses.js +4 -2
  133. package/dist/ws-responses.js.map +1 -1
  134. package/package.json +5 -1
@@ -9,7 +9,7 @@
9
9
  "source": {
10
10
  "source": "npm",
11
11
  "package": "@copilotkit/aimock",
12
- "version": "^1.7.0"
12
+ "version": "^1.9.0"
13
13
  },
14
14
  "description": "Fixture authoring skill for @copilotkit/aimock — match fields, response types, embeddings, structured output, sequential responses, streaming physics, agent loop patterns, gotchas, and debugging"
15
15
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "llmock",
3
- "version": "1.7.0",
3
+ "version": "1.9.0",
4
4
  "description": "Fixture authoring guidance for @copilotkit/aimock",
5
5
  "author": {
6
6
  "name": "CopilotKit"
package/README.md CHANGED
@@ -1,4 +1,6 @@
1
- # aimock [![Unit Tests](https://github.com/CopilotKit/llmock/actions/workflows/test-unit.yml/badge.svg)](https://github.com/CopilotKit/llmock/actions/workflows/test-unit.yml) [![Drift Tests](https://github.com/CopilotKit/llmock/actions/workflows/test-drift.yml/badge.svg)](https://github.com/CopilotKit/llmock/actions/workflows/test-drift.yml) [![npm version](https://img.shields.io/npm/v/@copilotkit/aimock)](https://www.npmjs.com/package/@copilotkit/aimock)
1
+ # aimock [![Unit Tests](https://github.com/CopilotKit/aimock/actions/workflows/test-unit.yml/badge.svg)](https://github.com/CopilotKit/aimock/actions/workflows/test-unit.yml) [![Drift Tests](https://github.com/CopilotKit/aimock/actions/workflows/test-drift.yml/badge.svg)](https://github.com/CopilotKit/aimock/actions/workflows/test-drift.yml) [![npm version](https://img.shields.io/npm/v/@copilotkit/aimock)](https://www.npmjs.com/package/@copilotkit/aimock)
2
+
3
+ https://github.com/user-attachments/assets/646bf106-0320-41f2-a9b1-5090454830f3
2
4
 
3
5
  Mock infrastructure for AI application testing — LLM APIs, MCP tools, A2A agents, vector databases, search, rerank, and moderation. One package, one port, zero dependencies.
4
6
 
@@ -78,11 +78,14 @@ function converseToCompletionRequest(req, modelId) {
78
78
  tools
79
79
  };
80
80
  }
81
- function buildConverseTextResponse(content) {
81
+ function buildConverseTextResponse(content, reasoning) {
82
+ const contentBlocks = [];
83
+ if (reasoning) contentBlocks.push({ reasoningContent: { reasoningText: { text: reasoning } } });
84
+ contentBlocks.push({ text: content });
82
85
  return {
83
86
  output: { message: {
84
87
  role: "assistant",
85
- content: [{ text: content }]
88
+ content: contentBlocks
86
89
  } },
87
90
  stopReason: "end_turn",
88
91
  usage: {
@@ -161,8 +164,9 @@ async function handleConverse(req, res, raw, modelId, fixtures, journal, default
161
164
  return;
162
165
  }
163
166
  const completionReq = converseToCompletionRequest(converseReq, modelId);
164
- const fixture = require_router.matchFixture(fixtures, completionReq, journal.fixtureMatchCounts);
165
- if (fixture) journal.incrementFixtureMatchCount(fixture, fixtures);
167
+ const testId = require_helpers.getTestId(req);
168
+ const fixture = require_router.matchFixture(fixtures, completionReq, journal.getFixtureMatchCountsForTest(testId), defaults.requestTransform);
169
+ if (fixture) journal.incrementFixtureMatchCount(fixture, fixtures, testId);
166
170
  if (require_chaos.applyChaos(res, fixture, defaults.chaos, req.headers, journal, {
167
171
  method: req.method ?? "POST",
168
172
  path: urlPath,
@@ -231,7 +235,7 @@ async function handleConverse(req, res, raw, modelId, fixtures, journal, default
231
235
  fixture
232
236
  }
233
237
  });
234
- const body = buildConverseTextResponse(response.content);
238
+ const body = buildConverseTextResponse(response.content, response.reasoning);
235
239
  res.writeHead(200, { "Content-Type": "application/json" });
236
240
  res.end(JSON.stringify(body));
237
241
  return;
@@ -309,8 +313,9 @@ async function handleConverseStream(req, res, raw, modelId, fixtures, journal, d
309
313
  return;
310
314
  }
311
315
  const completionReq = converseToCompletionRequest(converseReq, modelId);
312
- const fixture = require_router.matchFixture(fixtures, completionReq, journal.fixtureMatchCounts);
313
- if (fixture) journal.incrementFixtureMatchCount(fixture, fixtures);
316
+ const testId = require_helpers.getTestId(req);
317
+ const fixture = require_router.matchFixture(fixtures, completionReq, journal.getFixtureMatchCountsForTest(testId), defaults.requestTransform);
318
+ if (fixture) journal.incrementFixtureMatchCount(fixture, fixtures, testId);
314
319
  if (require_chaos.applyChaos(res, fixture, defaults.chaos, req.headers, journal, {
315
320
  method: req.method ?? "POST",
316
321
  path: urlPath,
@@ -381,7 +386,7 @@ async function handleConverseStream(req, res, raw, modelId, fixtures, journal, d
381
386
  fixture
382
387
  }
383
388
  });
384
- const events = require_bedrock.buildBedrockStreamTextEvents(response.content, chunkSize);
389
+ const events = require_bedrock.buildBedrockStreamTextEvents(response.content, chunkSize, response.reasoning);
385
390
  const interruption = require_interruption.createInterruptionSignal(fixture);
386
391
  if (!await require_aws_event_stream.writeEventStream(res, events, {
387
392
  latency,
@@ -1 +1 @@
1
- {"version":3,"file":"bedrock-converse.cjs","names":["generateToolUseId","flattenHeaders","matchFixture","applyChaos","proxyAndRecord","isErrorResponse","isTextResponse","isToolCallResponse","buildBedrockStreamTextEvents","createInterruptionSignal","writeEventStream","buildBedrockStreamToolCallEvents"],"sources":["../src/bedrock-converse.ts"],"sourcesContent":["/**\n * AWS Bedrock Converse API support.\n *\n * Translates incoming Converse and Converse-stream requests (Bedrock Converse\n * format) into the ChatCompletionRequest format used by the fixture router,\n * and converts fixture responses back into Converse API format — either a\n * single JSON response or an Event Stream binary stream.\n */\n\nimport type * as http from \"node:http\";\nimport type {\n ChatCompletionRequest,\n ChatMessage,\n Fixture,\n HandlerDefaults,\n ToolCall,\n ToolDefinition,\n} from \"./types.js\";\nimport {\n generateToolUseId,\n isTextResponse,\n isToolCallResponse,\n isErrorResponse,\n flattenHeaders,\n} from \"./helpers.js\";\nimport { matchFixture } from \"./router.js\";\nimport { writeErrorResponse } from \"./sse-writer.js\";\nimport { writeEventStream } from \"./aws-event-stream.js\";\nimport { createInterruptionSignal } from \"./interruption.js\";\nimport type { Journal } from \"./journal.js\";\nimport type { Logger } from \"./logger.js\";\nimport { applyChaos } from \"./chaos.js\";\nimport { proxyAndRecord } from \"./recorder.js\";\nimport { buildBedrockStreamTextEvents, buildBedrockStreamToolCallEvents } from \"./bedrock.js\";\n\n// ─── Converse request types ─────────────────────────────────────────────────\n\ninterface ConverseContentBlock {\n text?: string;\n toolUse?: { toolUseId: string; name: string; input: object };\n toolResult?: { toolUseId: string; content: { text?: string }[] };\n}\n\ninterface ConverseMessage {\n role: \"user\" | \"assistant\";\n content: ConverseContentBlock[];\n}\n\ninterface ConverseToolSpec {\n name: string;\n description?: string;\n inputSchema?: object;\n}\n\ninterface ConverseRequest {\n messages: ConverseMessage[];\n system?: { text: string }[];\n inferenceConfig?: { maxTokens?: number; temperature?: number };\n toolConfig?: { tools: { toolSpec: ConverseToolSpec }[] };\n}\n\n// ─── Input conversion: Converse → ChatCompletionRequest ─────────────────────\n\nexport function converseToCompletionRequest(\n req: ConverseRequest,\n modelId: string,\n): ChatCompletionRequest {\n const messages: ChatMessage[] = [];\n\n // system field → system message\n if (req.system && req.system.length > 0) {\n const systemText = req.system.map((s) => s.text).join(\"\");\n if (systemText) {\n messages.push({ role: \"system\", content: systemText });\n }\n }\n\n for (const msg of req.messages) {\n if (msg.role === \"user\") {\n // Check for toolResult blocks\n const toolResults = msg.content.filter((b) => b.toolResult);\n const textBlocks = msg.content.filter((b) => b.text !== undefined && !b.toolResult);\n\n if (toolResults.length > 0) {\n for (const block of toolResults) {\n const tr = block.toolResult!;\n const resultContent = tr.content.map((c) => c.text ?? \"\").join(\"\");\n messages.push({\n role: \"tool\",\n content: resultContent,\n tool_call_id: tr.toolUseId,\n });\n }\n if (textBlocks.length > 0) {\n messages.push({\n role: \"user\",\n content: textBlocks.map((b) => b.text ?? \"\").join(\"\"),\n });\n }\n continue;\n }\n\n // Plain user message\n const text = msg.content\n .filter((b) => b.text !== undefined)\n .map((b) => b.text ?? \"\")\n .join(\"\");\n messages.push({ role: \"user\", content: text });\n } else if (msg.role === \"assistant\") {\n const toolUseBlocks = msg.content.filter((b) => b.toolUse);\n const textContent = msg.content\n .filter((b) => b.text !== undefined)\n .map((b) => b.text ?? \"\")\n .join(\"\");\n\n if (toolUseBlocks.length > 0) {\n messages.push({\n role: \"assistant\",\n content: textContent || null,\n tool_calls: toolUseBlocks.map((b) => ({\n id: b.toolUse!.toolUseId,\n type: \"function\" as const,\n function: {\n name: b.toolUse!.name,\n arguments: JSON.stringify(b.toolUse!.input),\n },\n })),\n });\n } else {\n messages.push({ role: \"assistant\", content: textContent || null });\n }\n }\n }\n\n // Convert tools\n let tools: ToolDefinition[] | undefined;\n if (req.toolConfig?.tools && req.toolConfig.tools.length > 0) {\n tools = req.toolConfig.tools.map((t) => ({\n type: \"function\" as const,\n function: {\n name: t.toolSpec.name,\n description: t.toolSpec.description,\n parameters: t.toolSpec.inputSchema,\n },\n }));\n }\n\n return {\n model: modelId,\n messages,\n stream: false,\n temperature: req.inferenceConfig?.temperature,\n tools,\n };\n}\n\n// ─── Response builders ──────────────────────────────────────────────────────\n\nfunction buildConverseTextResponse(content: string): object {\n return {\n output: {\n message: {\n role: \"assistant\",\n content: [{ text: content }],\n },\n },\n stopReason: \"end_turn\",\n usage: { inputTokens: 0, outputTokens: 0, totalTokens: 0 },\n };\n}\n\nfunction buildConverseToolCallResponse(toolCalls: ToolCall[], logger: Logger): object {\n return {\n output: {\n message: {\n role: \"assistant\",\n content: toolCalls.map((tc) => {\n let argsObj: unknown;\n try {\n argsObj = JSON.parse(tc.arguments || \"{}\");\n } catch {\n logger.warn(\n `Malformed JSON in fixture tool call arguments for \"${tc.name}\": ${tc.arguments}`,\n );\n argsObj = {};\n }\n return {\n toolUse: {\n toolUseId: tc.id || generateToolUseId(),\n name: tc.name,\n input: argsObj,\n },\n };\n }),\n },\n },\n stopReason: \"tool_use\",\n usage: { inputTokens: 0, outputTokens: 0, totalTokens: 0 },\n };\n}\n\n// ─── Request handlers ───────────────────────────────────────────────────────\n\nexport async function handleConverse(\n req: http.IncomingMessage,\n res: http.ServerResponse,\n raw: string,\n modelId: string,\n fixtures: Fixture[],\n journal: Journal,\n defaults: HandlerDefaults,\n setCorsHeaders: (res: http.ServerResponse) => void,\n): Promise<void> {\n const { logger } = defaults;\n setCorsHeaders(res);\n\n const urlPath = req.url ?? `/model/${modelId}/converse`;\n\n let converseReq: ConverseRequest;\n try {\n converseReq = JSON.parse(raw) as ConverseRequest;\n } catch {\n journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: null,\n response: { status: 400, fixture: null },\n });\n writeErrorResponse(\n res,\n 400,\n JSON.stringify({\n error: {\n message: \"Malformed JSON\",\n type: \"invalid_request_error\",\n },\n }),\n );\n return;\n }\n\n if (!converseReq.messages || !Array.isArray(converseReq.messages)) {\n journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: null,\n response: { status: 400, fixture: null },\n });\n writeErrorResponse(\n res,\n 400,\n JSON.stringify({\n error: {\n message: \"Invalid request: messages array is required\",\n type: \"invalid_request_error\",\n },\n }),\n );\n return;\n }\n\n const completionReq = converseToCompletionRequest(converseReq, modelId);\n\n const fixture = matchFixture(fixtures, completionReq, journal.fixtureMatchCounts);\n\n if (fixture) {\n journal.incrementFixtureMatchCount(fixture, fixtures);\n }\n\n if (\n applyChaos(\n res,\n fixture,\n defaults.chaos,\n req.headers,\n journal,\n {\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n },\n defaults.registry,\n defaults.logger,\n )\n )\n return;\n\n if (!fixture) {\n if (defaults.record) {\n const proxied = await proxyAndRecord(\n req,\n res,\n completionReq,\n \"bedrock\",\n urlPath,\n fixtures,\n defaults,\n raw,\n );\n if (proxied) {\n journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: res.statusCode ?? 200, fixture: null },\n });\n return;\n }\n }\n const strictStatus = defaults.strict ? 503 : 404;\n const strictMessage = defaults.strict\n ? \"Strict mode: no fixture matched\"\n : \"No fixture matched\";\n if (defaults.strict) {\n logger.error(`STRICT: No fixture matched for ${req.method ?? \"POST\"} ${urlPath}`);\n }\n journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: strictStatus, fixture: null },\n });\n writeErrorResponse(\n res,\n strictStatus,\n JSON.stringify({\n error: {\n message: strictMessage,\n type: \"invalid_request_error\",\n },\n }),\n );\n return;\n }\n\n const response = fixture.response;\n\n // Error response\n if (isErrorResponse(response)) {\n const status = response.status ?? 500;\n journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status, fixture },\n });\n writeErrorResponse(res, status, JSON.stringify(response));\n return;\n }\n\n // Text response\n if (isTextResponse(response)) {\n journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: 200, fixture },\n });\n const body = buildConverseTextResponse(response.content);\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify(body));\n return;\n }\n\n // Tool call response\n if (isToolCallResponse(response)) {\n journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: 200, fixture },\n });\n const body = buildConverseToolCallResponse(response.toolCalls, logger);\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify(body));\n return;\n }\n\n // Unknown response type\n journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: 500, fixture },\n });\n writeErrorResponse(\n res,\n 500,\n JSON.stringify({\n error: {\n message: \"Fixture response did not match any known type\",\n type: \"server_error\",\n },\n }),\n );\n}\n\nexport async function handleConverseStream(\n req: http.IncomingMessage,\n res: http.ServerResponse,\n raw: string,\n modelId: string,\n fixtures: Fixture[],\n journal: Journal,\n defaults: HandlerDefaults,\n setCorsHeaders: (res: http.ServerResponse) => void,\n): Promise<void> {\n const { logger } = defaults;\n setCorsHeaders(res);\n\n const urlPath = req.url ?? `/model/${modelId}/converse-stream`;\n\n let converseReq: ConverseRequest;\n try {\n converseReq = JSON.parse(raw) as ConverseRequest;\n } catch {\n journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: null,\n response: { status: 400, fixture: null },\n });\n writeErrorResponse(\n res,\n 400,\n JSON.stringify({\n error: {\n message: \"Malformed JSON\",\n type: \"invalid_request_error\",\n },\n }),\n );\n return;\n }\n\n if (!converseReq.messages || !Array.isArray(converseReq.messages)) {\n journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: null,\n response: { status: 400, fixture: null },\n });\n writeErrorResponse(\n res,\n 400,\n JSON.stringify({\n error: {\n message: \"Invalid request: messages array is required\",\n type: \"invalid_request_error\",\n },\n }),\n );\n return;\n }\n\n const completionReq = converseToCompletionRequest(converseReq, modelId);\n\n const fixture = matchFixture(fixtures, completionReq, journal.fixtureMatchCounts);\n\n if (fixture) {\n journal.incrementFixtureMatchCount(fixture, fixtures);\n }\n\n if (\n applyChaos(\n res,\n fixture,\n defaults.chaos,\n req.headers,\n journal,\n {\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n },\n defaults.registry,\n defaults.logger,\n )\n )\n return;\n\n if (!fixture) {\n if (defaults.record) {\n const proxied = await proxyAndRecord(\n req,\n res,\n completionReq,\n \"bedrock\",\n urlPath,\n fixtures,\n defaults,\n raw,\n );\n if (proxied) {\n journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: res.statusCode ?? 200, fixture: null },\n });\n return;\n }\n }\n const strictStatus = defaults.strict ? 503 : 404;\n const strictMessage = defaults.strict\n ? \"Strict mode: no fixture matched\"\n : \"No fixture matched\";\n if (defaults.strict) {\n logger.error(`STRICT: No fixture matched for ${req.method ?? \"POST\"} ${urlPath}`);\n }\n journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: strictStatus, fixture: null },\n });\n writeErrorResponse(\n res,\n strictStatus,\n JSON.stringify({\n error: {\n message: strictMessage,\n type: \"invalid_request_error\",\n },\n }),\n );\n return;\n }\n\n const response = fixture.response;\n const latency = fixture.latency ?? defaults.latency;\n const chunkSize = Math.max(1, fixture.chunkSize ?? defaults.chunkSize);\n\n // Error response\n if (isErrorResponse(response)) {\n const status = response.status ?? 500;\n journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status, fixture },\n });\n writeErrorResponse(res, status, JSON.stringify(response));\n return;\n }\n\n // Text response — stream as Event Stream\n if (isTextResponse(response)) {\n const journalEntry = journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: 200, fixture },\n });\n const events = buildBedrockStreamTextEvents(response.content, chunkSize);\n const interruption = createInterruptionSignal(fixture);\n const completed = await writeEventStream(res, events, {\n latency,\n streamingProfile: fixture.streamingProfile,\n signal: interruption?.signal,\n onChunkSent: interruption?.tick,\n });\n if (!completed) {\n if (!res.writableEnded) res.destroy();\n journalEntry.response.interrupted = true;\n journalEntry.response.interruptReason = interruption?.reason();\n }\n interruption?.cleanup();\n return;\n }\n\n // Tool call response — stream as Event Stream\n if (isToolCallResponse(response)) {\n const journalEntry = journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: 200, fixture },\n });\n const events = buildBedrockStreamToolCallEvents(response.toolCalls, chunkSize, logger);\n const interruption = createInterruptionSignal(fixture);\n const completed = await writeEventStream(res, events, {\n latency,\n streamingProfile: fixture.streamingProfile,\n signal: interruption?.signal,\n onChunkSent: interruption?.tick,\n });\n if (!completed) {\n if (!res.writableEnded) res.destroy();\n journalEntry.response.interrupted = true;\n journalEntry.response.interruptReason = interruption?.reason();\n }\n interruption?.cleanup();\n return;\n }\n\n // Unknown response type\n journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: 500, fixture },\n });\n writeErrorResponse(\n res,\n 500,\n JSON.stringify({\n error: {\n message: \"Fixture response did not match any known type\",\n type: \"server_error\",\n },\n }),\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"bedrock-converse.cjs","names":["generateToolUseId","flattenHeaders","getTestId","matchFixture","applyChaos","proxyAndRecord","isErrorResponse","isTextResponse","isToolCallResponse","buildBedrockStreamTextEvents","createInterruptionSignal","writeEventStream","buildBedrockStreamToolCallEvents"],"sources":["../src/bedrock-converse.ts"],"sourcesContent":["/**\n * AWS Bedrock Converse API support.\n *\n * Translates incoming Converse and Converse-stream requests (Bedrock Converse\n * format) into the ChatCompletionRequest format used by the fixture router,\n * and converts fixture responses back into Converse API format — either a\n * single JSON response or an Event Stream binary stream.\n */\n\nimport type * as http from \"node:http\";\nimport type {\n ChatCompletionRequest,\n ChatMessage,\n Fixture,\n HandlerDefaults,\n ToolCall,\n ToolDefinition,\n} from \"./types.js\";\nimport {\n generateToolUseId,\n isTextResponse,\n isToolCallResponse,\n isErrorResponse,\n flattenHeaders,\n getTestId,\n} from \"./helpers.js\";\nimport { matchFixture } from \"./router.js\";\nimport { writeErrorResponse } from \"./sse-writer.js\";\nimport { writeEventStream } from \"./aws-event-stream.js\";\nimport { createInterruptionSignal } from \"./interruption.js\";\nimport type { Journal } from \"./journal.js\";\nimport type { Logger } from \"./logger.js\";\nimport { applyChaos } from \"./chaos.js\";\nimport { proxyAndRecord } from \"./recorder.js\";\nimport { buildBedrockStreamTextEvents, buildBedrockStreamToolCallEvents } from \"./bedrock.js\";\n\n// ─── Converse request types ─────────────────────────────────────────────────\n\ninterface ConverseContentBlock {\n text?: string;\n toolUse?: { toolUseId: string; name: string; input: object };\n toolResult?: { toolUseId: string; content: { text?: string }[] };\n}\n\ninterface ConverseMessage {\n role: \"user\" | \"assistant\";\n content: ConverseContentBlock[];\n}\n\ninterface ConverseToolSpec {\n name: string;\n description?: string;\n inputSchema?: object;\n}\n\ninterface ConverseRequest {\n messages: ConverseMessage[];\n system?: { text: string }[];\n inferenceConfig?: { maxTokens?: number; temperature?: number };\n toolConfig?: { tools: { toolSpec: ConverseToolSpec }[] };\n}\n\n// ─── Input conversion: Converse → ChatCompletionRequest ─────────────────────\n\nexport function converseToCompletionRequest(\n req: ConverseRequest,\n modelId: string,\n): ChatCompletionRequest {\n const messages: ChatMessage[] = [];\n\n // system field → system message\n if (req.system && req.system.length > 0) {\n const systemText = req.system.map((s) => s.text).join(\"\");\n if (systemText) {\n messages.push({ role: \"system\", content: systemText });\n }\n }\n\n for (const msg of req.messages) {\n if (msg.role === \"user\") {\n // Check for toolResult blocks\n const toolResults = msg.content.filter((b) => b.toolResult);\n const textBlocks = msg.content.filter((b) => b.text !== undefined && !b.toolResult);\n\n if (toolResults.length > 0) {\n for (const block of toolResults) {\n const tr = block.toolResult!;\n const resultContent = tr.content.map((c) => c.text ?? \"\").join(\"\");\n messages.push({\n role: \"tool\",\n content: resultContent,\n tool_call_id: tr.toolUseId,\n });\n }\n if (textBlocks.length > 0) {\n messages.push({\n role: \"user\",\n content: textBlocks.map((b) => b.text ?? \"\").join(\"\"),\n });\n }\n continue;\n }\n\n // Plain user message\n const text = msg.content\n .filter((b) => b.text !== undefined)\n .map((b) => b.text ?? \"\")\n .join(\"\");\n messages.push({ role: \"user\", content: text });\n } else if (msg.role === \"assistant\") {\n const toolUseBlocks = msg.content.filter((b) => b.toolUse);\n const textContent = msg.content\n .filter((b) => b.text !== undefined)\n .map((b) => b.text ?? \"\")\n .join(\"\");\n\n if (toolUseBlocks.length > 0) {\n messages.push({\n role: \"assistant\",\n content: textContent || null,\n tool_calls: toolUseBlocks.map((b) => ({\n id: b.toolUse!.toolUseId,\n type: \"function\" as const,\n function: {\n name: b.toolUse!.name,\n arguments: JSON.stringify(b.toolUse!.input),\n },\n })),\n });\n } else {\n messages.push({ role: \"assistant\", content: textContent || null });\n }\n }\n }\n\n // Convert tools\n let tools: ToolDefinition[] | undefined;\n if (req.toolConfig?.tools && req.toolConfig.tools.length > 0) {\n tools = req.toolConfig.tools.map((t) => ({\n type: \"function\" as const,\n function: {\n name: t.toolSpec.name,\n description: t.toolSpec.description,\n parameters: t.toolSpec.inputSchema,\n },\n }));\n }\n\n return {\n model: modelId,\n messages,\n stream: false,\n temperature: req.inferenceConfig?.temperature,\n tools,\n };\n}\n\n// ─── Response builders ──────────────────────────────────────────────────────\n\nfunction buildConverseTextResponse(content: string, reasoning?: string): object {\n const contentBlocks: object[] = [];\n if (reasoning) {\n contentBlocks.push({\n reasoningContent: { reasoningText: { text: reasoning } },\n });\n }\n contentBlocks.push({ text: content });\n\n return {\n output: {\n message: {\n role: \"assistant\",\n content: contentBlocks,\n },\n },\n stopReason: \"end_turn\",\n usage: { inputTokens: 0, outputTokens: 0, totalTokens: 0 },\n };\n}\n\nfunction buildConverseToolCallResponse(toolCalls: ToolCall[], logger: Logger): object {\n return {\n output: {\n message: {\n role: \"assistant\",\n content: toolCalls.map((tc) => {\n let argsObj: unknown;\n try {\n argsObj = JSON.parse(tc.arguments || \"{}\");\n } catch {\n logger.warn(\n `Malformed JSON in fixture tool call arguments for \"${tc.name}\": ${tc.arguments}`,\n );\n argsObj = {};\n }\n return {\n toolUse: {\n toolUseId: tc.id || generateToolUseId(),\n name: tc.name,\n input: argsObj,\n },\n };\n }),\n },\n },\n stopReason: \"tool_use\",\n usage: { inputTokens: 0, outputTokens: 0, totalTokens: 0 },\n };\n}\n\n// ─── Request handlers ───────────────────────────────────────────────────────\n\nexport async function handleConverse(\n req: http.IncomingMessage,\n res: http.ServerResponse,\n raw: string,\n modelId: string,\n fixtures: Fixture[],\n journal: Journal,\n defaults: HandlerDefaults,\n setCorsHeaders: (res: http.ServerResponse) => void,\n): Promise<void> {\n const { logger } = defaults;\n setCorsHeaders(res);\n\n const urlPath = req.url ?? `/model/${modelId}/converse`;\n\n let converseReq: ConverseRequest;\n try {\n converseReq = JSON.parse(raw) as ConverseRequest;\n } catch {\n journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: null,\n response: { status: 400, fixture: null },\n });\n writeErrorResponse(\n res,\n 400,\n JSON.stringify({\n error: {\n message: \"Malformed JSON\",\n type: \"invalid_request_error\",\n },\n }),\n );\n return;\n }\n\n if (!converseReq.messages || !Array.isArray(converseReq.messages)) {\n journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: null,\n response: { status: 400, fixture: null },\n });\n writeErrorResponse(\n res,\n 400,\n JSON.stringify({\n error: {\n message: \"Invalid request: messages array is required\",\n type: \"invalid_request_error\",\n },\n }),\n );\n return;\n }\n\n const completionReq = converseToCompletionRequest(converseReq, modelId);\n\n const testId = getTestId(req);\n const fixture = matchFixture(\n fixtures,\n completionReq,\n journal.getFixtureMatchCountsForTest(testId),\n defaults.requestTransform,\n );\n\n if (fixture) {\n journal.incrementFixtureMatchCount(fixture, fixtures, testId);\n }\n\n if (\n applyChaos(\n res,\n fixture,\n defaults.chaos,\n req.headers,\n journal,\n {\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n },\n defaults.registry,\n defaults.logger,\n )\n )\n return;\n\n if (!fixture) {\n if (defaults.record) {\n const proxied = await proxyAndRecord(\n req,\n res,\n completionReq,\n \"bedrock\",\n urlPath,\n fixtures,\n defaults,\n raw,\n );\n if (proxied) {\n journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: res.statusCode ?? 200, fixture: null },\n });\n return;\n }\n }\n const strictStatus = defaults.strict ? 503 : 404;\n const strictMessage = defaults.strict\n ? \"Strict mode: no fixture matched\"\n : \"No fixture matched\";\n if (defaults.strict) {\n logger.error(`STRICT: No fixture matched for ${req.method ?? \"POST\"} ${urlPath}`);\n }\n journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: strictStatus, fixture: null },\n });\n writeErrorResponse(\n res,\n strictStatus,\n JSON.stringify({\n error: {\n message: strictMessage,\n type: \"invalid_request_error\",\n },\n }),\n );\n return;\n }\n\n const response = fixture.response;\n\n // Error response\n if (isErrorResponse(response)) {\n const status = response.status ?? 500;\n journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status, fixture },\n });\n writeErrorResponse(res, status, JSON.stringify(response));\n return;\n }\n\n // Text response\n if (isTextResponse(response)) {\n journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: 200, fixture },\n });\n const body = buildConverseTextResponse(response.content, response.reasoning);\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify(body));\n return;\n }\n\n // Tool call response\n if (isToolCallResponse(response)) {\n journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: 200, fixture },\n });\n const body = buildConverseToolCallResponse(response.toolCalls, logger);\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify(body));\n return;\n }\n\n // Unknown response type\n journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: 500, fixture },\n });\n writeErrorResponse(\n res,\n 500,\n JSON.stringify({\n error: {\n message: \"Fixture response did not match any known type\",\n type: \"server_error\",\n },\n }),\n );\n}\n\nexport async function handleConverseStream(\n req: http.IncomingMessage,\n res: http.ServerResponse,\n raw: string,\n modelId: string,\n fixtures: Fixture[],\n journal: Journal,\n defaults: HandlerDefaults,\n setCorsHeaders: (res: http.ServerResponse) => void,\n): Promise<void> {\n const { logger } = defaults;\n setCorsHeaders(res);\n\n const urlPath = req.url ?? `/model/${modelId}/converse-stream`;\n\n let converseReq: ConverseRequest;\n try {\n converseReq = JSON.parse(raw) as ConverseRequest;\n } catch {\n journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: null,\n response: { status: 400, fixture: null },\n });\n writeErrorResponse(\n res,\n 400,\n JSON.stringify({\n error: {\n message: \"Malformed JSON\",\n type: \"invalid_request_error\",\n },\n }),\n );\n return;\n }\n\n if (!converseReq.messages || !Array.isArray(converseReq.messages)) {\n journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: null,\n response: { status: 400, fixture: null },\n });\n writeErrorResponse(\n res,\n 400,\n JSON.stringify({\n error: {\n message: \"Invalid request: messages array is required\",\n type: \"invalid_request_error\",\n },\n }),\n );\n return;\n }\n\n const completionReq = converseToCompletionRequest(converseReq, modelId);\n\n const testId = getTestId(req);\n const fixture = matchFixture(\n fixtures,\n completionReq,\n journal.getFixtureMatchCountsForTest(testId),\n defaults.requestTransform,\n );\n\n if (fixture) {\n journal.incrementFixtureMatchCount(fixture, fixtures, testId);\n }\n\n if (\n applyChaos(\n res,\n fixture,\n defaults.chaos,\n req.headers,\n journal,\n {\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n },\n defaults.registry,\n defaults.logger,\n )\n )\n return;\n\n if (!fixture) {\n if (defaults.record) {\n const proxied = await proxyAndRecord(\n req,\n res,\n completionReq,\n \"bedrock\",\n urlPath,\n fixtures,\n defaults,\n raw,\n );\n if (proxied) {\n journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: res.statusCode ?? 200, fixture: null },\n });\n return;\n }\n }\n const strictStatus = defaults.strict ? 503 : 404;\n const strictMessage = defaults.strict\n ? \"Strict mode: no fixture matched\"\n : \"No fixture matched\";\n if (defaults.strict) {\n logger.error(`STRICT: No fixture matched for ${req.method ?? \"POST\"} ${urlPath}`);\n }\n journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: strictStatus, fixture: null },\n });\n writeErrorResponse(\n res,\n strictStatus,\n JSON.stringify({\n error: {\n message: strictMessage,\n type: \"invalid_request_error\",\n },\n }),\n );\n return;\n }\n\n const response = fixture.response;\n const latency = fixture.latency ?? defaults.latency;\n const chunkSize = Math.max(1, fixture.chunkSize ?? defaults.chunkSize);\n\n // Error response\n if (isErrorResponse(response)) {\n const status = response.status ?? 500;\n journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status, fixture },\n });\n writeErrorResponse(res, status, JSON.stringify(response));\n return;\n }\n\n // Text response — stream as Event Stream\n if (isTextResponse(response)) {\n const journalEntry = journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: 200, fixture },\n });\n const events = buildBedrockStreamTextEvents(response.content, chunkSize, response.reasoning);\n const interruption = createInterruptionSignal(fixture);\n const completed = await writeEventStream(res, events, {\n latency,\n streamingProfile: fixture.streamingProfile,\n signal: interruption?.signal,\n onChunkSent: interruption?.tick,\n });\n if (!completed) {\n if (!res.writableEnded) res.destroy();\n journalEntry.response.interrupted = true;\n journalEntry.response.interruptReason = interruption?.reason();\n }\n interruption?.cleanup();\n return;\n }\n\n // Tool call response — stream as Event Stream\n if (isToolCallResponse(response)) {\n const journalEntry = journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: 200, fixture },\n });\n const events = buildBedrockStreamToolCallEvents(response.toolCalls, chunkSize, logger);\n const interruption = createInterruptionSignal(fixture);\n const completed = await writeEventStream(res, events, {\n latency,\n streamingProfile: fixture.streamingProfile,\n signal: interruption?.signal,\n onChunkSent: interruption?.tick,\n });\n if (!completed) {\n if (!res.writableEnded) res.destroy();\n journalEntry.response.interrupted = true;\n journalEntry.response.interruptReason = interruption?.reason();\n }\n interruption?.cleanup();\n return;\n }\n\n // Unknown response type\n journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: 500, fixture },\n });\n writeErrorResponse(\n res,\n 500,\n JSON.stringify({\n error: {\n message: \"Fixture response did not match any known type\",\n type: \"server_error\",\n },\n }),\n );\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"bedrock-converse.d.cts","names":[],"sources":["../src/bedrock-converse.ts"],"sourcesContent":[],"mappings":";;;;;;UAqCU,oBAAA,CAqB0B;EAAgB,IAAA,CAAA,EAAA,MAAA;EAKpC,OAAA,CAAA,EAAA;IAA2B,SAAA,EAAA,MAAA;IACpC,IAAA,EAAA,MAAA;IAEJ,KAAA,EAAA,MAAA;EAAqB,CAAA;EAyIF,UAAA,CAAA,EAAA;IAAc,SAAA,EAAA,MAAA;IAC7B,OAAK,EAAA;MACA,IAAA,CAAA,EAAA,MAAA;IAGA,CAAA,EAAA;;;UArKF,eAAA,CAwKmB;MAC1B,EAAA,MAAA,GAAA,WAAA;EAAO,OAAA,EAvKC,oBAuKD,EAAA;AAkMV;UAtWU,gBAAA,CAsWgC;MACnC,EAAA,MAAK;aACA,CAAA,EAAA,MAAA;aAGA,CAAA,EAAA,MAAA;;UArWF,eAAA,CAuWE;UACiB,EAvWjB,eAuWiB,EAAA;QAC1B,CAAA,EAAA;IAAO,IAAA,EAAA,MAAA;;;;;;;;gBArW0B;;;;iBAKpB,2BAAA,MACT,mCAEJ;iBAyImB,cAAA,MACf,IAAA,CAAK,sBACL,IAAA,CAAK,wDAGA,oBACD,mBACC,uCACY,IAAA,CAAK,0BAC1B;iBAkMmB,oBAAA,MACf,IAAA,CAAK,sBACL,IAAA,CAAK,wDAGA,oBACD,mBACC,uCACY,IAAA,CAAK,0BAC1B"}
1
+ {"version":3,"file":"bedrock-converse.d.cts","names":[],"sources":["../src/bedrock-converse.ts"],"sourcesContent":[],"mappings":";;;;;;UAsCU,oBAAA,CAqB0B;EAAgB,IAAA,CAAA,EAAA,MAAA;EAKpC,OAAA,CAAA,EAAA;IAA2B,SAAA,EAAA,MAAA;IACpC,IAAA,EAAA,MAAA;IAEJ,KAAA,EAAA,MAAA;EAAqB,CAAA;EAiJF,UAAA,CAAA,EAAA;IAAc,SAAA,EAAA,MAAA;IAC7B,OAAK,EAAA;MACA,IAAA,CAAA,EAAA,MAAA;IAGA,CAAA,EAAA;;;UA7KF,eAAA,CAgLmB;MAC1B,EAAA,MAAA,GAAA,WAAA;EAAO,OAAA,EA/KC,oBA+KD,EAAA;AAwMV;UApXU,gBAAA,CAoXgC;MACnC,EAAA,MAAK;aACA,CAAA,EAAA,MAAA;aAGA,CAAA,EAAA,MAAA;;UAnXF,eAAA,CAqXE;UACiB,EArXjB,eAqXiB,EAAA;QAC1B,CAAA,EAAA;IAAO,IAAA,EAAA,MAAA;;;;;;;;gBAnX0B;;;;iBAKpB,2BAAA,MACT,mCAEJ;iBAiJmB,cAAA,MACf,IAAA,CAAK,sBACL,IAAA,CAAK,wDAGA,oBACD,mBACC,uCACY,IAAA,CAAK,0BAC1B;iBAwMmB,oBAAA,MACf,IAAA,CAAK,sBACL,IAAA,CAAK,wDAGA,oBACD,mBACC,uCACY,IAAA,CAAK,0BAC1B"}
@@ -1 +1 @@
1
- {"version":3,"file":"bedrock-converse.d.ts","names":[],"sources":["../src/bedrock-converse.ts"],"sourcesContent":[],"mappings":";;;;;;UAqCU,oBAAA,CAqB0B;EAAgB,IAAA,CAAA,EAAA,MAAA;EAKpC,OAAA,CAAA,EAAA;IAA2B,SAAA,EAAA,MAAA;IACpC,IAAA,EAAA,MAAA;IAEJ,KAAA,EAAA,MAAA;EAAqB,CAAA;EAyIF,UAAA,CAAA,EAAA;IAAc,SAAA,EAAA,MAAA;IAC7B,OAAK,EAAA;MACA,IAAA,CAAA,EAAA,MAAA;IAGA,CAAA,EAAA;;;UArKF,eAAA,CAwKmB;MAC1B,EAAA,MAAA,GAAA,WAAA;EAAO,OAAA,EAvKC,oBAuKD,EAAA;AAkMV;UAtWU,gBAAA,CAsWgC;MACnC,EAAA,MAAK;aACA,CAAA,EAAA,MAAA;aAGA,CAAA,EAAA,MAAA;;UArWF,eAAA,CAuWE;UACiB,EAvWjB,eAuWiB,EAAA;QAC1B,CAAA,EAAA;IAAO,IAAA,EAAA,MAAA;;;;;;;;gBArW0B;;;;iBAKpB,2BAAA,MACT,mCAEJ;iBAyImB,cAAA,MACf,IAAA,CAAK,sBACL,IAAA,CAAK,wDAGA,oBACD,mBACC,uCACY,IAAA,CAAK,0BAC1B;iBAkMmB,oBAAA,MACf,IAAA,CAAK,sBACL,IAAA,CAAK,wDAGA,oBACD,mBACC,uCACY,IAAA,CAAK,0BAC1B"}
1
+ {"version":3,"file":"bedrock-converse.d.ts","names":[],"sources":["../src/bedrock-converse.ts"],"sourcesContent":[],"mappings":";;;;;;UAsCU,oBAAA,CAqB0B;EAAgB,IAAA,CAAA,EAAA,MAAA;EAKpC,OAAA,CAAA,EAAA;IAA2B,SAAA,EAAA,MAAA;IACpC,IAAA,EAAA,MAAA;IAEJ,KAAA,EAAA,MAAA;EAAqB,CAAA;EAiJF,UAAA,CAAA,EAAA;IAAc,SAAA,EAAA,MAAA;IAC7B,OAAK,EAAA;MACA,IAAA,CAAA,EAAA,MAAA;IAGA,CAAA,EAAA;;;UA7KF,eAAA,CAgLmB;MAC1B,EAAA,MAAA,GAAA,WAAA;EAAO,OAAA,EA/KC,oBA+KD,EAAA;AAwMV;UApXU,gBAAA,CAoXgC;MACnC,EAAA,MAAK;aACA,CAAA,EAAA,MAAA;aAGA,CAAA,EAAA,MAAA;;UAnXF,eAAA,CAqXE;UACiB,EArXjB,eAqXiB,EAAA;QAC1B,CAAA,EAAA;IAAO,IAAA,EAAA,MAAA;;;;;;;;gBAnX0B;;;;iBAKpB,2BAAA,MACT,mCAEJ;iBAiJmB,cAAA,MACf,IAAA,CAAK,sBACL,IAAA,CAAK,wDAGA,oBACD,mBACC,uCACY,IAAA,CAAK,0BAC1B;iBAwMmB,oBAAA,MACf,IAAA,CAAK,sBACL,IAAA,CAAK,wDAGA,oBACD,mBACC,uCACY,IAAA,CAAK,0BAC1B"}
@@ -1,4 +1,4 @@
1
- import { flattenHeaders, generateToolUseId, isErrorResponse, isTextResponse, isToolCallResponse } from "./helpers.js";
1
+ import { flattenHeaders, generateToolUseId, getTestId, isErrorResponse, isTextResponse, isToolCallResponse } from "./helpers.js";
2
2
  import { matchFixture } from "./router.js";
3
3
  import { writeErrorResponse } from "./sse-writer.js";
4
4
  import { createInterruptionSignal } from "./interruption.js";
@@ -78,11 +78,14 @@ function converseToCompletionRequest(req, modelId) {
78
78
  tools
79
79
  };
80
80
  }
81
- function buildConverseTextResponse(content) {
81
+ function buildConverseTextResponse(content, reasoning) {
82
+ const contentBlocks = [];
83
+ if (reasoning) contentBlocks.push({ reasoningContent: { reasoningText: { text: reasoning } } });
84
+ contentBlocks.push({ text: content });
82
85
  return {
83
86
  output: { message: {
84
87
  role: "assistant",
85
- content: [{ text: content }]
88
+ content: contentBlocks
86
89
  } },
87
90
  stopReason: "end_turn",
88
91
  usage: {
@@ -161,8 +164,9 @@ async function handleConverse(req, res, raw, modelId, fixtures, journal, default
161
164
  return;
162
165
  }
163
166
  const completionReq = converseToCompletionRequest(converseReq, modelId);
164
- const fixture = matchFixture(fixtures, completionReq, journal.fixtureMatchCounts);
165
- if (fixture) journal.incrementFixtureMatchCount(fixture, fixtures);
167
+ const testId = getTestId(req);
168
+ const fixture = matchFixture(fixtures, completionReq, journal.getFixtureMatchCountsForTest(testId), defaults.requestTransform);
169
+ if (fixture) journal.incrementFixtureMatchCount(fixture, fixtures, testId);
166
170
  if (applyChaos(res, fixture, defaults.chaos, req.headers, journal, {
167
171
  method: req.method ?? "POST",
168
172
  path: urlPath,
@@ -231,7 +235,7 @@ async function handleConverse(req, res, raw, modelId, fixtures, journal, default
231
235
  fixture
232
236
  }
233
237
  });
234
- const body = buildConverseTextResponse(response.content);
238
+ const body = buildConverseTextResponse(response.content, response.reasoning);
235
239
  res.writeHead(200, { "Content-Type": "application/json" });
236
240
  res.end(JSON.stringify(body));
237
241
  return;
@@ -309,8 +313,9 @@ async function handleConverseStream(req, res, raw, modelId, fixtures, journal, d
309
313
  return;
310
314
  }
311
315
  const completionReq = converseToCompletionRequest(converseReq, modelId);
312
- const fixture = matchFixture(fixtures, completionReq, journal.fixtureMatchCounts);
313
- if (fixture) journal.incrementFixtureMatchCount(fixture, fixtures);
316
+ const testId = getTestId(req);
317
+ const fixture = matchFixture(fixtures, completionReq, journal.getFixtureMatchCountsForTest(testId), defaults.requestTransform);
318
+ if (fixture) journal.incrementFixtureMatchCount(fixture, fixtures, testId);
314
319
  if (applyChaos(res, fixture, defaults.chaos, req.headers, journal, {
315
320
  method: req.method ?? "POST",
316
321
  path: urlPath,
@@ -381,7 +386,7 @@ async function handleConverseStream(req, res, raw, modelId, fixtures, journal, d
381
386
  fixture
382
387
  }
383
388
  });
384
- const events = buildBedrockStreamTextEvents(response.content, chunkSize);
389
+ const events = buildBedrockStreamTextEvents(response.content, chunkSize, response.reasoning);
385
390
  const interruption = createInterruptionSignal(fixture);
386
391
  if (!await writeEventStream(res, events, {
387
392
  latency,
@@ -1 +1 @@
1
- {"version":3,"file":"bedrock-converse.js","names":[],"sources":["../src/bedrock-converse.ts"],"sourcesContent":["/**\n * AWS Bedrock Converse API support.\n *\n * Translates incoming Converse and Converse-stream requests (Bedrock Converse\n * format) into the ChatCompletionRequest format used by the fixture router,\n * and converts fixture responses back into Converse API format — either a\n * single JSON response or an Event Stream binary stream.\n */\n\nimport type * as http from \"node:http\";\nimport type {\n ChatCompletionRequest,\n ChatMessage,\n Fixture,\n HandlerDefaults,\n ToolCall,\n ToolDefinition,\n} from \"./types.js\";\nimport {\n generateToolUseId,\n isTextResponse,\n isToolCallResponse,\n isErrorResponse,\n flattenHeaders,\n} from \"./helpers.js\";\nimport { matchFixture } from \"./router.js\";\nimport { writeErrorResponse } from \"./sse-writer.js\";\nimport { writeEventStream } from \"./aws-event-stream.js\";\nimport { createInterruptionSignal } from \"./interruption.js\";\nimport type { Journal } from \"./journal.js\";\nimport type { Logger } from \"./logger.js\";\nimport { applyChaos } from \"./chaos.js\";\nimport { proxyAndRecord } from \"./recorder.js\";\nimport { buildBedrockStreamTextEvents, buildBedrockStreamToolCallEvents } from \"./bedrock.js\";\n\n// ─── Converse request types ─────────────────────────────────────────────────\n\ninterface ConverseContentBlock {\n text?: string;\n toolUse?: { toolUseId: string; name: string; input: object };\n toolResult?: { toolUseId: string; content: { text?: string }[] };\n}\n\ninterface ConverseMessage {\n role: \"user\" | \"assistant\";\n content: ConverseContentBlock[];\n}\n\ninterface ConverseToolSpec {\n name: string;\n description?: string;\n inputSchema?: object;\n}\n\ninterface ConverseRequest {\n messages: ConverseMessage[];\n system?: { text: string }[];\n inferenceConfig?: { maxTokens?: number; temperature?: number };\n toolConfig?: { tools: { toolSpec: ConverseToolSpec }[] };\n}\n\n// ─── Input conversion: Converse → ChatCompletionRequest ─────────────────────\n\nexport function converseToCompletionRequest(\n req: ConverseRequest,\n modelId: string,\n): ChatCompletionRequest {\n const messages: ChatMessage[] = [];\n\n // system field → system message\n if (req.system && req.system.length > 0) {\n const systemText = req.system.map((s) => s.text).join(\"\");\n if (systemText) {\n messages.push({ role: \"system\", content: systemText });\n }\n }\n\n for (const msg of req.messages) {\n if (msg.role === \"user\") {\n // Check for toolResult blocks\n const toolResults = msg.content.filter((b) => b.toolResult);\n const textBlocks = msg.content.filter((b) => b.text !== undefined && !b.toolResult);\n\n if (toolResults.length > 0) {\n for (const block of toolResults) {\n const tr = block.toolResult!;\n const resultContent = tr.content.map((c) => c.text ?? \"\").join(\"\");\n messages.push({\n role: \"tool\",\n content: resultContent,\n tool_call_id: tr.toolUseId,\n });\n }\n if (textBlocks.length > 0) {\n messages.push({\n role: \"user\",\n content: textBlocks.map((b) => b.text ?? \"\").join(\"\"),\n });\n }\n continue;\n }\n\n // Plain user message\n const text = msg.content\n .filter((b) => b.text !== undefined)\n .map((b) => b.text ?? \"\")\n .join(\"\");\n messages.push({ role: \"user\", content: text });\n } else if (msg.role === \"assistant\") {\n const toolUseBlocks = msg.content.filter((b) => b.toolUse);\n const textContent = msg.content\n .filter((b) => b.text !== undefined)\n .map((b) => b.text ?? \"\")\n .join(\"\");\n\n if (toolUseBlocks.length > 0) {\n messages.push({\n role: \"assistant\",\n content: textContent || null,\n tool_calls: toolUseBlocks.map((b) => ({\n id: b.toolUse!.toolUseId,\n type: \"function\" as const,\n function: {\n name: b.toolUse!.name,\n arguments: JSON.stringify(b.toolUse!.input),\n },\n })),\n });\n } else {\n messages.push({ role: \"assistant\", content: textContent || null });\n }\n }\n }\n\n // Convert tools\n let tools: ToolDefinition[] | undefined;\n if (req.toolConfig?.tools && req.toolConfig.tools.length > 0) {\n tools = req.toolConfig.tools.map((t) => ({\n type: \"function\" as const,\n function: {\n name: t.toolSpec.name,\n description: t.toolSpec.description,\n parameters: t.toolSpec.inputSchema,\n },\n }));\n }\n\n return {\n model: modelId,\n messages,\n stream: false,\n temperature: req.inferenceConfig?.temperature,\n tools,\n };\n}\n\n// ─── Response builders ──────────────────────────────────────────────────────\n\nfunction buildConverseTextResponse(content: string): object {\n return {\n output: {\n message: {\n role: \"assistant\",\n content: [{ text: content }],\n },\n },\n stopReason: \"end_turn\",\n usage: { inputTokens: 0, outputTokens: 0, totalTokens: 0 },\n };\n}\n\nfunction buildConverseToolCallResponse(toolCalls: ToolCall[], logger: Logger): object {\n return {\n output: {\n message: {\n role: \"assistant\",\n content: toolCalls.map((tc) => {\n let argsObj: unknown;\n try {\n argsObj = JSON.parse(tc.arguments || \"{}\");\n } catch {\n logger.warn(\n `Malformed JSON in fixture tool call arguments for \"${tc.name}\": ${tc.arguments}`,\n );\n argsObj = {};\n }\n return {\n toolUse: {\n toolUseId: tc.id || generateToolUseId(),\n name: tc.name,\n input: argsObj,\n },\n };\n }),\n },\n },\n stopReason: \"tool_use\",\n usage: { inputTokens: 0, outputTokens: 0, totalTokens: 0 },\n };\n}\n\n// ─── Request handlers ───────────────────────────────────────────────────────\n\nexport async function handleConverse(\n req: http.IncomingMessage,\n res: http.ServerResponse,\n raw: string,\n modelId: string,\n fixtures: Fixture[],\n journal: Journal,\n defaults: HandlerDefaults,\n setCorsHeaders: (res: http.ServerResponse) => void,\n): Promise<void> {\n const { logger } = defaults;\n setCorsHeaders(res);\n\n const urlPath = req.url ?? `/model/${modelId}/converse`;\n\n let converseReq: ConverseRequest;\n try {\n converseReq = JSON.parse(raw) as ConverseRequest;\n } catch {\n journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: null,\n response: { status: 400, fixture: null },\n });\n writeErrorResponse(\n res,\n 400,\n JSON.stringify({\n error: {\n message: \"Malformed JSON\",\n type: \"invalid_request_error\",\n },\n }),\n );\n return;\n }\n\n if (!converseReq.messages || !Array.isArray(converseReq.messages)) {\n journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: null,\n response: { status: 400, fixture: null },\n });\n writeErrorResponse(\n res,\n 400,\n JSON.stringify({\n error: {\n message: \"Invalid request: messages array is required\",\n type: \"invalid_request_error\",\n },\n }),\n );\n return;\n }\n\n const completionReq = converseToCompletionRequest(converseReq, modelId);\n\n const fixture = matchFixture(fixtures, completionReq, journal.fixtureMatchCounts);\n\n if (fixture) {\n journal.incrementFixtureMatchCount(fixture, fixtures);\n }\n\n if (\n applyChaos(\n res,\n fixture,\n defaults.chaos,\n req.headers,\n journal,\n {\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n },\n defaults.registry,\n defaults.logger,\n )\n )\n return;\n\n if (!fixture) {\n if (defaults.record) {\n const proxied = await proxyAndRecord(\n req,\n res,\n completionReq,\n \"bedrock\",\n urlPath,\n fixtures,\n defaults,\n raw,\n );\n if (proxied) {\n journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: res.statusCode ?? 200, fixture: null },\n });\n return;\n }\n }\n const strictStatus = defaults.strict ? 503 : 404;\n const strictMessage = defaults.strict\n ? \"Strict mode: no fixture matched\"\n : \"No fixture matched\";\n if (defaults.strict) {\n logger.error(`STRICT: No fixture matched for ${req.method ?? \"POST\"} ${urlPath}`);\n }\n journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: strictStatus, fixture: null },\n });\n writeErrorResponse(\n res,\n strictStatus,\n JSON.stringify({\n error: {\n message: strictMessage,\n type: \"invalid_request_error\",\n },\n }),\n );\n return;\n }\n\n const response = fixture.response;\n\n // Error response\n if (isErrorResponse(response)) {\n const status = response.status ?? 500;\n journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status, fixture },\n });\n writeErrorResponse(res, status, JSON.stringify(response));\n return;\n }\n\n // Text response\n if (isTextResponse(response)) {\n journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: 200, fixture },\n });\n const body = buildConverseTextResponse(response.content);\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify(body));\n return;\n }\n\n // Tool call response\n if (isToolCallResponse(response)) {\n journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: 200, fixture },\n });\n const body = buildConverseToolCallResponse(response.toolCalls, logger);\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify(body));\n return;\n }\n\n // Unknown response type\n journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: 500, fixture },\n });\n writeErrorResponse(\n res,\n 500,\n JSON.stringify({\n error: {\n message: \"Fixture response did not match any known type\",\n type: \"server_error\",\n },\n }),\n );\n}\n\nexport async function handleConverseStream(\n req: http.IncomingMessage,\n res: http.ServerResponse,\n raw: string,\n modelId: string,\n fixtures: Fixture[],\n journal: Journal,\n defaults: HandlerDefaults,\n setCorsHeaders: (res: http.ServerResponse) => void,\n): Promise<void> {\n const { logger } = defaults;\n setCorsHeaders(res);\n\n const urlPath = req.url ?? `/model/${modelId}/converse-stream`;\n\n let converseReq: ConverseRequest;\n try {\n converseReq = JSON.parse(raw) as ConverseRequest;\n } catch {\n journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: null,\n response: { status: 400, fixture: null },\n });\n writeErrorResponse(\n res,\n 400,\n JSON.stringify({\n error: {\n message: \"Malformed JSON\",\n type: \"invalid_request_error\",\n },\n }),\n );\n return;\n }\n\n if (!converseReq.messages || !Array.isArray(converseReq.messages)) {\n journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: null,\n response: { status: 400, fixture: null },\n });\n writeErrorResponse(\n res,\n 400,\n JSON.stringify({\n error: {\n message: \"Invalid request: messages array is required\",\n type: \"invalid_request_error\",\n },\n }),\n );\n return;\n }\n\n const completionReq = converseToCompletionRequest(converseReq, modelId);\n\n const fixture = matchFixture(fixtures, completionReq, journal.fixtureMatchCounts);\n\n if (fixture) {\n journal.incrementFixtureMatchCount(fixture, fixtures);\n }\n\n if (\n applyChaos(\n res,\n fixture,\n defaults.chaos,\n req.headers,\n journal,\n {\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n },\n defaults.registry,\n defaults.logger,\n )\n )\n return;\n\n if (!fixture) {\n if (defaults.record) {\n const proxied = await proxyAndRecord(\n req,\n res,\n completionReq,\n \"bedrock\",\n urlPath,\n fixtures,\n defaults,\n raw,\n );\n if (proxied) {\n journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: res.statusCode ?? 200, fixture: null },\n });\n return;\n }\n }\n const strictStatus = defaults.strict ? 503 : 404;\n const strictMessage = defaults.strict\n ? \"Strict mode: no fixture matched\"\n : \"No fixture matched\";\n if (defaults.strict) {\n logger.error(`STRICT: No fixture matched for ${req.method ?? \"POST\"} ${urlPath}`);\n }\n journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: strictStatus, fixture: null },\n });\n writeErrorResponse(\n res,\n strictStatus,\n JSON.stringify({\n error: {\n message: strictMessage,\n type: \"invalid_request_error\",\n },\n }),\n );\n return;\n }\n\n const response = fixture.response;\n const latency = fixture.latency ?? defaults.latency;\n const chunkSize = Math.max(1, fixture.chunkSize ?? defaults.chunkSize);\n\n // Error response\n if (isErrorResponse(response)) {\n const status = response.status ?? 500;\n journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status, fixture },\n });\n writeErrorResponse(res, status, JSON.stringify(response));\n return;\n }\n\n // Text response — stream as Event Stream\n if (isTextResponse(response)) {\n const journalEntry = journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: 200, fixture },\n });\n const events = buildBedrockStreamTextEvents(response.content, chunkSize);\n const interruption = createInterruptionSignal(fixture);\n const completed = await writeEventStream(res, events, {\n latency,\n streamingProfile: fixture.streamingProfile,\n signal: interruption?.signal,\n onChunkSent: interruption?.tick,\n });\n if (!completed) {\n if (!res.writableEnded) res.destroy();\n journalEntry.response.interrupted = true;\n journalEntry.response.interruptReason = interruption?.reason();\n }\n interruption?.cleanup();\n return;\n }\n\n // Tool call response — stream as Event Stream\n if (isToolCallResponse(response)) {\n const journalEntry = journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: 200, fixture },\n });\n const events = buildBedrockStreamToolCallEvents(response.toolCalls, chunkSize, logger);\n const interruption = createInterruptionSignal(fixture);\n const completed = await writeEventStream(res, events, {\n latency,\n streamingProfile: fixture.streamingProfile,\n signal: interruption?.signal,\n onChunkSent: interruption?.tick,\n });\n if (!completed) {\n if (!res.writableEnded) res.destroy();\n journalEntry.response.interrupted = true;\n journalEntry.response.interruptReason = interruption?.reason();\n }\n interruption?.cleanup();\n return;\n }\n\n // Unknown response type\n journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: 500, fixture },\n });\n writeErrorResponse(\n res,\n 500,\n JSON.stringify({\n error: {\n message: \"Fixture response did not match any known type\",\n type: \"server_error\",\n },\n }),\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"bedrock-converse.js","names":[],"sources":["../src/bedrock-converse.ts"],"sourcesContent":["/**\n * AWS Bedrock Converse API support.\n *\n * Translates incoming Converse and Converse-stream requests (Bedrock Converse\n * format) into the ChatCompletionRequest format used by the fixture router,\n * and converts fixture responses back into Converse API format — either a\n * single JSON response or an Event Stream binary stream.\n */\n\nimport type * as http from \"node:http\";\nimport type {\n ChatCompletionRequest,\n ChatMessage,\n Fixture,\n HandlerDefaults,\n ToolCall,\n ToolDefinition,\n} from \"./types.js\";\nimport {\n generateToolUseId,\n isTextResponse,\n isToolCallResponse,\n isErrorResponse,\n flattenHeaders,\n getTestId,\n} from \"./helpers.js\";\nimport { matchFixture } from \"./router.js\";\nimport { writeErrorResponse } from \"./sse-writer.js\";\nimport { writeEventStream } from \"./aws-event-stream.js\";\nimport { createInterruptionSignal } from \"./interruption.js\";\nimport type { Journal } from \"./journal.js\";\nimport type { Logger } from \"./logger.js\";\nimport { applyChaos } from \"./chaos.js\";\nimport { proxyAndRecord } from \"./recorder.js\";\nimport { buildBedrockStreamTextEvents, buildBedrockStreamToolCallEvents } from \"./bedrock.js\";\n\n// ─── Converse request types ─────────────────────────────────────────────────\n\ninterface ConverseContentBlock {\n text?: string;\n toolUse?: { toolUseId: string; name: string; input: object };\n toolResult?: { toolUseId: string; content: { text?: string }[] };\n}\n\ninterface ConverseMessage {\n role: \"user\" | \"assistant\";\n content: ConverseContentBlock[];\n}\n\ninterface ConverseToolSpec {\n name: string;\n description?: string;\n inputSchema?: object;\n}\n\ninterface ConverseRequest {\n messages: ConverseMessage[];\n system?: { text: string }[];\n inferenceConfig?: { maxTokens?: number; temperature?: number };\n toolConfig?: { tools: { toolSpec: ConverseToolSpec }[] };\n}\n\n// ─── Input conversion: Converse → ChatCompletionRequest ─────────────────────\n\nexport function converseToCompletionRequest(\n req: ConverseRequest,\n modelId: string,\n): ChatCompletionRequest {\n const messages: ChatMessage[] = [];\n\n // system field → system message\n if (req.system && req.system.length > 0) {\n const systemText = req.system.map((s) => s.text).join(\"\");\n if (systemText) {\n messages.push({ role: \"system\", content: systemText });\n }\n }\n\n for (const msg of req.messages) {\n if (msg.role === \"user\") {\n // Check for toolResult blocks\n const toolResults = msg.content.filter((b) => b.toolResult);\n const textBlocks = msg.content.filter((b) => b.text !== undefined && !b.toolResult);\n\n if (toolResults.length > 0) {\n for (const block of toolResults) {\n const tr = block.toolResult!;\n const resultContent = tr.content.map((c) => c.text ?? \"\").join(\"\");\n messages.push({\n role: \"tool\",\n content: resultContent,\n tool_call_id: tr.toolUseId,\n });\n }\n if (textBlocks.length > 0) {\n messages.push({\n role: \"user\",\n content: textBlocks.map((b) => b.text ?? \"\").join(\"\"),\n });\n }\n continue;\n }\n\n // Plain user message\n const text = msg.content\n .filter((b) => b.text !== undefined)\n .map((b) => b.text ?? \"\")\n .join(\"\");\n messages.push({ role: \"user\", content: text });\n } else if (msg.role === \"assistant\") {\n const toolUseBlocks = msg.content.filter((b) => b.toolUse);\n const textContent = msg.content\n .filter((b) => b.text !== undefined)\n .map((b) => b.text ?? \"\")\n .join(\"\");\n\n if (toolUseBlocks.length > 0) {\n messages.push({\n role: \"assistant\",\n content: textContent || null,\n tool_calls: toolUseBlocks.map((b) => ({\n id: b.toolUse!.toolUseId,\n type: \"function\" as const,\n function: {\n name: b.toolUse!.name,\n arguments: JSON.stringify(b.toolUse!.input),\n },\n })),\n });\n } else {\n messages.push({ role: \"assistant\", content: textContent || null });\n }\n }\n }\n\n // Convert tools\n let tools: ToolDefinition[] | undefined;\n if (req.toolConfig?.tools && req.toolConfig.tools.length > 0) {\n tools = req.toolConfig.tools.map((t) => ({\n type: \"function\" as const,\n function: {\n name: t.toolSpec.name,\n description: t.toolSpec.description,\n parameters: t.toolSpec.inputSchema,\n },\n }));\n }\n\n return {\n model: modelId,\n messages,\n stream: false,\n temperature: req.inferenceConfig?.temperature,\n tools,\n };\n}\n\n// ─── Response builders ──────────────────────────────────────────────────────\n\nfunction buildConverseTextResponse(content: string, reasoning?: string): object {\n const contentBlocks: object[] = [];\n if (reasoning) {\n contentBlocks.push({\n reasoningContent: { reasoningText: { text: reasoning } },\n });\n }\n contentBlocks.push({ text: content });\n\n return {\n output: {\n message: {\n role: \"assistant\",\n content: contentBlocks,\n },\n },\n stopReason: \"end_turn\",\n usage: { inputTokens: 0, outputTokens: 0, totalTokens: 0 },\n };\n}\n\nfunction buildConverseToolCallResponse(toolCalls: ToolCall[], logger: Logger): object {\n return {\n output: {\n message: {\n role: \"assistant\",\n content: toolCalls.map((tc) => {\n let argsObj: unknown;\n try {\n argsObj = JSON.parse(tc.arguments || \"{}\");\n } catch {\n logger.warn(\n `Malformed JSON in fixture tool call arguments for \"${tc.name}\": ${tc.arguments}`,\n );\n argsObj = {};\n }\n return {\n toolUse: {\n toolUseId: tc.id || generateToolUseId(),\n name: tc.name,\n input: argsObj,\n },\n };\n }),\n },\n },\n stopReason: \"tool_use\",\n usage: { inputTokens: 0, outputTokens: 0, totalTokens: 0 },\n };\n}\n\n// ─── Request handlers ───────────────────────────────────────────────────────\n\nexport async function handleConverse(\n req: http.IncomingMessage,\n res: http.ServerResponse,\n raw: string,\n modelId: string,\n fixtures: Fixture[],\n journal: Journal,\n defaults: HandlerDefaults,\n setCorsHeaders: (res: http.ServerResponse) => void,\n): Promise<void> {\n const { logger } = defaults;\n setCorsHeaders(res);\n\n const urlPath = req.url ?? `/model/${modelId}/converse`;\n\n let converseReq: ConverseRequest;\n try {\n converseReq = JSON.parse(raw) as ConverseRequest;\n } catch {\n journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: null,\n response: { status: 400, fixture: null },\n });\n writeErrorResponse(\n res,\n 400,\n JSON.stringify({\n error: {\n message: \"Malformed JSON\",\n type: \"invalid_request_error\",\n },\n }),\n );\n return;\n }\n\n if (!converseReq.messages || !Array.isArray(converseReq.messages)) {\n journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: null,\n response: { status: 400, fixture: null },\n });\n writeErrorResponse(\n res,\n 400,\n JSON.stringify({\n error: {\n message: \"Invalid request: messages array is required\",\n type: \"invalid_request_error\",\n },\n }),\n );\n return;\n }\n\n const completionReq = converseToCompletionRequest(converseReq, modelId);\n\n const testId = getTestId(req);\n const fixture = matchFixture(\n fixtures,\n completionReq,\n journal.getFixtureMatchCountsForTest(testId),\n defaults.requestTransform,\n );\n\n if (fixture) {\n journal.incrementFixtureMatchCount(fixture, fixtures, testId);\n }\n\n if (\n applyChaos(\n res,\n fixture,\n defaults.chaos,\n req.headers,\n journal,\n {\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n },\n defaults.registry,\n defaults.logger,\n )\n )\n return;\n\n if (!fixture) {\n if (defaults.record) {\n const proxied = await proxyAndRecord(\n req,\n res,\n completionReq,\n \"bedrock\",\n urlPath,\n fixtures,\n defaults,\n raw,\n );\n if (proxied) {\n journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: res.statusCode ?? 200, fixture: null },\n });\n return;\n }\n }\n const strictStatus = defaults.strict ? 503 : 404;\n const strictMessage = defaults.strict\n ? \"Strict mode: no fixture matched\"\n : \"No fixture matched\";\n if (defaults.strict) {\n logger.error(`STRICT: No fixture matched for ${req.method ?? \"POST\"} ${urlPath}`);\n }\n journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: strictStatus, fixture: null },\n });\n writeErrorResponse(\n res,\n strictStatus,\n JSON.stringify({\n error: {\n message: strictMessage,\n type: \"invalid_request_error\",\n },\n }),\n );\n return;\n }\n\n const response = fixture.response;\n\n // Error response\n if (isErrorResponse(response)) {\n const status = response.status ?? 500;\n journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status, fixture },\n });\n writeErrorResponse(res, status, JSON.stringify(response));\n return;\n }\n\n // Text response\n if (isTextResponse(response)) {\n journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: 200, fixture },\n });\n const body = buildConverseTextResponse(response.content, response.reasoning);\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify(body));\n return;\n }\n\n // Tool call response\n if (isToolCallResponse(response)) {\n journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: 200, fixture },\n });\n const body = buildConverseToolCallResponse(response.toolCalls, logger);\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify(body));\n return;\n }\n\n // Unknown response type\n journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: 500, fixture },\n });\n writeErrorResponse(\n res,\n 500,\n JSON.stringify({\n error: {\n message: \"Fixture response did not match any known type\",\n type: \"server_error\",\n },\n }),\n );\n}\n\nexport async function handleConverseStream(\n req: http.IncomingMessage,\n res: http.ServerResponse,\n raw: string,\n modelId: string,\n fixtures: Fixture[],\n journal: Journal,\n defaults: HandlerDefaults,\n setCorsHeaders: (res: http.ServerResponse) => void,\n): Promise<void> {\n const { logger } = defaults;\n setCorsHeaders(res);\n\n const urlPath = req.url ?? `/model/${modelId}/converse-stream`;\n\n let converseReq: ConverseRequest;\n try {\n converseReq = JSON.parse(raw) as ConverseRequest;\n } catch {\n journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: null,\n response: { status: 400, fixture: null },\n });\n writeErrorResponse(\n res,\n 400,\n JSON.stringify({\n error: {\n message: \"Malformed JSON\",\n type: \"invalid_request_error\",\n },\n }),\n );\n return;\n }\n\n if (!converseReq.messages || !Array.isArray(converseReq.messages)) {\n journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: null,\n response: { status: 400, fixture: null },\n });\n writeErrorResponse(\n res,\n 400,\n JSON.stringify({\n error: {\n message: \"Invalid request: messages array is required\",\n type: \"invalid_request_error\",\n },\n }),\n );\n return;\n }\n\n const completionReq = converseToCompletionRequest(converseReq, modelId);\n\n const testId = getTestId(req);\n const fixture = matchFixture(\n fixtures,\n completionReq,\n journal.getFixtureMatchCountsForTest(testId),\n defaults.requestTransform,\n );\n\n if (fixture) {\n journal.incrementFixtureMatchCount(fixture, fixtures, testId);\n }\n\n if (\n applyChaos(\n res,\n fixture,\n defaults.chaos,\n req.headers,\n journal,\n {\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n },\n defaults.registry,\n defaults.logger,\n )\n )\n return;\n\n if (!fixture) {\n if (defaults.record) {\n const proxied = await proxyAndRecord(\n req,\n res,\n completionReq,\n \"bedrock\",\n urlPath,\n fixtures,\n defaults,\n raw,\n );\n if (proxied) {\n journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: res.statusCode ?? 200, fixture: null },\n });\n return;\n }\n }\n const strictStatus = defaults.strict ? 503 : 404;\n const strictMessage = defaults.strict\n ? \"Strict mode: no fixture matched\"\n : \"No fixture matched\";\n if (defaults.strict) {\n logger.error(`STRICT: No fixture matched for ${req.method ?? \"POST\"} ${urlPath}`);\n }\n journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: strictStatus, fixture: null },\n });\n writeErrorResponse(\n res,\n strictStatus,\n JSON.stringify({\n error: {\n message: strictMessage,\n type: \"invalid_request_error\",\n },\n }),\n );\n return;\n }\n\n const response = fixture.response;\n const latency = fixture.latency ?? defaults.latency;\n const chunkSize = Math.max(1, fixture.chunkSize ?? defaults.chunkSize);\n\n // Error response\n if (isErrorResponse(response)) {\n const status = response.status ?? 500;\n journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status, fixture },\n });\n writeErrorResponse(res, status, JSON.stringify(response));\n return;\n }\n\n // Text response — stream as Event Stream\n if (isTextResponse(response)) {\n const journalEntry = journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: 200, fixture },\n });\n const events = buildBedrockStreamTextEvents(response.content, chunkSize, response.reasoning);\n const interruption = createInterruptionSignal(fixture);\n const completed = await writeEventStream(res, events, {\n latency,\n streamingProfile: fixture.streamingProfile,\n signal: interruption?.signal,\n onChunkSent: interruption?.tick,\n });\n if (!completed) {\n if (!res.writableEnded) res.destroy();\n journalEntry.response.interrupted = true;\n journalEntry.response.interruptReason = interruption?.reason();\n }\n interruption?.cleanup();\n return;\n }\n\n // Tool call response — stream as Event Stream\n if (isToolCallResponse(response)) {\n const journalEntry = journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: 200, fixture },\n });\n const events = buildBedrockStreamToolCallEvents(response.toolCalls, chunkSize, logger);\n const interruption = createInterruptionSignal(fixture);\n const completed = await writeEventStream(res, events, {\n latency,\n streamingProfile: fixture.streamingProfile,\n signal: interruption?.signal,\n onChunkSent: interruption?.tick,\n });\n if (!completed) {\n if (!res.writableEnded) res.destroy();\n journalEntry.response.interrupted = true;\n journalEntry.response.interruptReason = interruption?.reason();\n }\n interruption?.cleanup();\n return;\n }\n\n // Unknown response type\n journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: 500, fixture },\n });\n writeErrorResponse(\n res,\n 500,\n JSON.stringify({\n error: {\n message: \"Fixture response did not match any known type\",\n type: \"server_error\",\n },\n }),\n );\n}\n"],"mappings":"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"}
package/dist/bedrock.cjs CHANGED
@@ -88,15 +88,21 @@ function bedrockToCompletionRequest(req, modelId) {
88
88
  tools
89
89
  };
90
90
  }
91
- function buildBedrockTextResponse(content, model) {
91
+ function buildBedrockTextResponse(content, model, reasoning) {
92
+ const contentBlocks = [];
93
+ if (reasoning) contentBlocks.push({
94
+ type: "thinking",
95
+ thinking: reasoning
96
+ });
97
+ contentBlocks.push({
98
+ type: "text",
99
+ text: content
100
+ });
92
101
  return {
93
102
  id: require_helpers.generateMessageId(),
94
103
  type: "message",
95
104
  role: "assistant",
96
- content: [{
97
- type: "text",
98
- text: content
99
- }],
105
+ content: contentBlocks,
100
106
  model,
101
107
  stop_reason: "end_turn",
102
108
  stop_sequence: null,
@@ -177,8 +183,9 @@ async function handleBedrock(req, res, raw, modelId, fixtures, journal, defaults
177
183
  return;
178
184
  }
179
185
  const completionReq = bedrockToCompletionRequest(bedrockReq, modelId);
180
- const fixture = require_router.matchFixture(fixtures, completionReq, journal.fixtureMatchCounts);
181
- if (fixture) journal.incrementFixtureMatchCount(fixture, fixtures);
186
+ const testId = require_helpers.getTestId(req);
187
+ const fixture = require_router.matchFixture(fixtures, completionReq, journal.getFixtureMatchCountsForTest(testId), defaults.requestTransform);
188
+ if (fixture) journal.incrementFixtureMatchCount(fixture, fixtures, testId);
182
189
  if (require_chaos.applyChaos(res, fixture, defaults.chaos, req.headers, journal, {
183
190
  method: req.method ?? "POST",
184
191
  path: urlPath,
@@ -254,7 +261,7 @@ async function handleBedrock(req, res, raw, modelId, fixtures, journal, defaults
254
261
  fixture
255
262
  }
256
263
  });
257
- const body = buildBedrockTextResponse(response.content, completionReq.model);
264
+ const body = buildBedrockTextResponse(response.content, completionReq.model, response.reasoning);
258
265
  res.writeHead(200, { "Content-Type": "application/json" });
259
266
  res.end(JSON.stringify(body));
260
267
  return;
@@ -290,16 +297,44 @@ async function handleBedrock(req, res, raw, modelId, fixtures, journal, defaults
290
297
  type: "server_error"
291
298
  } }));
292
299
  }
293
- function buildBedrockStreamTextEvents(content, chunkSize) {
300
+ function buildBedrockStreamTextEvents(content, chunkSize, reasoning) {
294
301
  const events = [];
295
302
  events.push({
296
303
  eventType: "messageStart",
297
304
  payload: { role: "assistant" }
298
305
  });
306
+ if (reasoning) {
307
+ const blockIndex = 0;
308
+ events.push({
309
+ eventType: "contentBlockStart",
310
+ payload: {
311
+ contentBlockIndex: blockIndex,
312
+ start: { type: "thinking" }
313
+ }
314
+ });
315
+ for (let i = 0; i < reasoning.length; i += chunkSize) {
316
+ const slice = reasoning.slice(i, i + chunkSize);
317
+ events.push({
318
+ eventType: "contentBlockDelta",
319
+ payload: {
320
+ contentBlockIndex: blockIndex,
321
+ delta: {
322
+ type: "thinking_delta",
323
+ thinking: slice
324
+ }
325
+ }
326
+ });
327
+ }
328
+ events.push({
329
+ eventType: "contentBlockStop",
330
+ payload: { contentBlockIndex: blockIndex }
331
+ });
332
+ }
333
+ const textBlockIndex = reasoning ? 1 : 0;
299
334
  events.push({
300
335
  eventType: "contentBlockStart",
301
336
  payload: {
302
- contentBlockIndex: 0,
337
+ contentBlockIndex: textBlockIndex,
303
338
  start: {}
304
339
  }
305
340
  });
@@ -308,7 +343,7 @@ function buildBedrockStreamTextEvents(content, chunkSize) {
308
343
  events.push({
309
344
  eventType: "contentBlockDelta",
310
345
  payload: {
311
- contentBlockIndex: 0,
346
+ contentBlockIndex: textBlockIndex,
312
347
  delta: {
313
348
  type: "text_delta",
314
349
  text: slice
@@ -318,7 +353,7 @@ function buildBedrockStreamTextEvents(content, chunkSize) {
318
353
  }
319
354
  events.push({
320
355
  eventType: "contentBlockStop",
321
- payload: { contentBlockIndex: 0 }
356
+ payload: { contentBlockIndex: textBlockIndex }
322
357
  });
323
358
  events.push({
324
359
  eventType: "messageStop",
@@ -419,8 +454,9 @@ async function handleBedrockStream(req, res, raw, modelId, fixtures, journal, de
419
454
  return;
420
455
  }
421
456
  const completionReq = bedrockToCompletionRequest(bedrockReq, modelId);
422
- const fixture = require_router.matchFixture(fixtures, completionReq, journal.fixtureMatchCounts);
423
- if (fixture) journal.incrementFixtureMatchCount(fixture, fixtures);
457
+ const testId = require_helpers.getTestId(req);
458
+ const fixture = require_router.matchFixture(fixtures, completionReq, journal.getFixtureMatchCountsForTest(testId), defaults.requestTransform);
459
+ if (fixture) journal.incrementFixtureMatchCount(fixture, fixtures, testId);
424
460
  if (require_chaos.applyChaos(res, fixture, defaults.chaos, req.headers, journal, {
425
461
  method: req.method ?? "POST",
426
462
  path: urlPath,
@@ -491,7 +527,7 @@ async function handleBedrockStream(req, res, raw, modelId, fixtures, journal, de
491
527
  fixture
492
528
  }
493
529
  });
494
- const events = buildBedrockStreamTextEvents(response.content, chunkSize);
530
+ const events = buildBedrockStreamTextEvents(response.content, chunkSize, response.reasoning);
495
531
  const interruption = require_interruption.createInterruptionSignal(fixture);
496
532
  if (!await require_aws_event_stream.writeEventStream(res, events, {
497
533
  latency,