@copilotkit/aimock 1.13.0 → 1.14.1

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 (94) hide show
  1. package/.claude-plugin/marketplace.json +2 -2
  2. package/.claude-plugin/plugin.json +4 -4
  3. package/README.md +12 -7
  4. package/dist/cli.cjs +13 -2
  5. package/dist/cli.cjs.map +1 -1
  6. package/dist/cli.js +13 -2
  7. package/dist/cli.js.map +1 -1
  8. package/dist/config-loader.d.cts.map +1 -1
  9. package/dist/fixture-loader.cjs +131 -29
  10. package/dist/fixture-loader.cjs.map +1 -1
  11. package/dist/fixture-loader.d.cts +9 -2
  12. package/dist/fixture-loader.d.cts.map +1 -1
  13. package/dist/fixture-loader.d.ts +9 -2
  14. package/dist/fixture-loader.d.ts.map +1 -1
  15. package/dist/fixture-loader.js +132 -31
  16. package/dist/fixture-loader.js.map +1 -1
  17. package/dist/gemini.cjs +76 -55
  18. package/dist/gemini.cjs.map +1 -1
  19. package/dist/gemini.d.cts.map +1 -1
  20. package/dist/gemini.d.ts.map +1 -1
  21. package/dist/gemini.js +77 -56
  22. package/dist/gemini.js.map +1 -1
  23. package/dist/helpers.cjs +142 -76
  24. package/dist/helpers.cjs.map +1 -1
  25. package/dist/helpers.d.cts +14 -4
  26. package/dist/helpers.d.cts.map +1 -1
  27. package/dist/helpers.d.ts +14 -4
  28. package/dist/helpers.d.ts.map +1 -1
  29. package/dist/helpers.js +142 -77
  30. package/dist/helpers.js.map +1 -1
  31. package/dist/index.cjs +10 -0
  32. package/dist/index.d.cts +4 -4
  33. package/dist/index.d.ts +4 -4
  34. package/dist/index.js +3 -3
  35. package/dist/journal.cjs +6 -0
  36. package/dist/journal.cjs.map +1 -1
  37. package/dist/journal.d.cts +15 -1
  38. package/dist/journal.d.cts.map +1 -1
  39. package/dist/journal.d.ts +15 -1
  40. package/dist/journal.d.ts.map +1 -1
  41. package/dist/journal.js +6 -0
  42. package/dist/journal.js.map +1 -1
  43. package/dist/llmock.cjs +1 -1
  44. package/dist/llmock.cjs.map +1 -1
  45. package/dist/llmock.d.cts +6 -6
  46. package/dist/llmock.d.cts.map +1 -1
  47. package/dist/llmock.d.ts +6 -6
  48. package/dist/llmock.d.ts.map +1 -1
  49. package/dist/llmock.js +2 -2
  50. package/dist/llmock.js.map +1 -1
  51. package/dist/messages.cjs +69 -63
  52. package/dist/messages.cjs.map +1 -1
  53. package/dist/messages.d.cts.map +1 -1
  54. package/dist/messages.d.ts.map +1 -1
  55. package/dist/messages.js +70 -64
  56. package/dist/messages.js.map +1 -1
  57. package/dist/recorder.cjs +1 -1
  58. package/dist/recorder.cjs.map +1 -1
  59. package/dist/recorder.js +1 -1
  60. package/dist/recorder.js.map +1 -1
  61. package/dist/responses.cjs +66 -57
  62. package/dist/responses.cjs.map +1 -1
  63. package/dist/responses.d.cts +3 -3
  64. package/dist/responses.d.cts.map +1 -1
  65. package/dist/responses.d.ts +3 -3
  66. package/dist/responses.d.ts.map +1 -1
  67. package/dist/responses.js +67 -58
  68. package/dist/responses.js.map +1 -1
  69. package/dist/server.cjs +58 -31
  70. package/dist/server.cjs.map +1 -1
  71. package/dist/server.d.cts.map +1 -1
  72. package/dist/server.d.ts.map +1 -1
  73. package/dist/server.js +59 -32
  74. package/dist/server.js.map +1 -1
  75. package/dist/stream-collapse.cjs.map +1 -1
  76. package/dist/stream-collapse.d.cts.map +1 -1
  77. package/dist/stream-collapse.d.ts.map +1 -1
  78. package/dist/stream-collapse.js.map +1 -1
  79. package/dist/types.d.cts +74 -11
  80. package/dist/types.d.cts.map +1 -1
  81. package/dist/types.d.ts +74 -11
  82. package/dist/types.d.ts.map +1 -1
  83. package/dist/vector-types.d.ts.map +1 -1
  84. package/fixtures/example-multi-turn.json +1 -1
  85. package/fixtures/example-tool-call.json +1 -1
  86. package/fixtures/examples/adk/gemini-agent.json +47 -0
  87. package/fixtures/examples/crewai/multi-agent-crew.json +16 -0
  88. package/fixtures/examples/langchain/agent-loop.json +27 -0
  89. package/fixtures/examples/llamaindex/aimock-config.json +62 -0
  90. package/fixtures/examples/llamaindex/rag-pipeline.json +34 -0
  91. package/fixtures/examples/mastra/agent-workflow.json +32 -0
  92. package/fixtures/examples/pydanticai/structured-output.json +15 -0
  93. package/package.json +2 -1
  94. package/skills/write-fixtures/SKILL.md +148 -22
@@ -1 +1 @@
1
- {"version":3,"file":"server.js","names":[],"sources":["../src/server.ts"],"sourcesContent":["import * as http from \"node:http\";\nimport type {\n Fixture,\n FixtureFileEntry,\n ChatCompletionRequest,\n HandlerDefaults,\n MockServerOptions,\n Mountable,\n RecordProviderKey,\n} from \"./types.js\";\nimport { Journal } from \"./journal.js\";\nimport { matchFixture } from \"./router.js\";\nimport { validateFixtures, entryToFixture } from \"./fixture-loader.js\";\nimport { writeSSEStream, writeErrorResponse } from \"./sse-writer.js\";\nimport { createInterruptionSignal } from \"./interruption.js\";\nimport {\n buildTextChunks,\n buildToolCallChunks,\n buildTextCompletion,\n buildToolCallCompletion,\n buildContentWithToolCallsChunks,\n buildContentWithToolCallsCompletion,\n isTextResponse,\n isToolCallResponse,\n isContentWithToolCallsResponse,\n isErrorResponse,\n flattenHeaders,\n getTestId,\n} from \"./helpers.js\";\nimport { handleResponses } from \"./responses.js\";\nimport { handleMessages } from \"./messages.js\";\nimport { handleGemini } from \"./gemini.js\";\nimport { handleBedrock, handleBedrockStream } from \"./bedrock.js\";\nimport { handleConverse, handleConverseStream } from \"./bedrock-converse.js\";\nimport { handleEmbeddings } from \"./embeddings.js\";\nimport { handleImages } from \"./images.js\";\nimport { handleSpeech } from \"./speech.js\";\nimport { handleTranscription } from \"./transcription.js\";\nimport { handleVideoCreate, handleVideoStatus, type VideoStateMap } from \"./video.js\";\nimport { handleOllama, handleOllamaGenerate } from \"./ollama.js\";\nimport { handleCohere } from \"./cohere.js\";\nimport { handleSearch, type SearchFixture } from \"./search.js\";\nimport { handleRerank, type RerankFixture } from \"./rerank.js\";\nimport { handleModeration, type ModerationFixture } from \"./moderation.js\";\nimport { upgradeToWebSocket, type WebSocketConnection } from \"./ws-framing.js\";\nimport { handleWebSocketResponses } from \"./ws-responses.js\";\nimport { handleWebSocketRealtime } from \"./ws-realtime.js\";\nimport { handleWebSocketGeminiLive } from \"./ws-gemini-live.js\";\nimport { Logger } from \"./logger.js\";\nimport { applyChaos } from \"./chaos.js\";\nimport { createMetricsRegistry, normalizePathLabel } from \"./metrics.js\";\nimport { proxyAndRecord } from \"./recorder.js\";\n\nexport interface ServerInstance {\n server: http.Server;\n journal: Journal;\n url: string;\n defaults: HandlerDefaults;\n videoStates: VideoStateMap;\n}\n\nconst COMPLETIONS_PATH = \"/v1/chat/completions\";\nconst RESPONSES_PATH = \"/v1/responses\";\nconst REALTIME_PATH = \"/v1/realtime\";\nconst GEMINI_LIVE_PATH =\n \"/ws/google.ai.generativelanguage.v1beta.GenerativeService.BidiGenerateContent\";\nconst MESSAGES_PATH = \"/v1/messages\";\nconst EMBEDDINGS_PATH = \"/v1/embeddings\";\nconst COHERE_CHAT_PATH = \"/v2/chat\";\nconst SEARCH_PATH = \"/search\";\nconst RERANK_PATH = \"/v2/rerank\";\nconst MODERATIONS_PATH = \"/v1/moderations\";\nconst IMAGES_PATH = \"/v1/images/generations\";\nconst SPEECH_PATH = \"/v1/audio/speech\";\nconst TRANSCRIPTIONS_PATH = \"/v1/audio/transcriptions\";\nconst VIDEOS_PATH = \"/v1/videos\";\nconst VIDEOS_STATUS_RE = /^\\/v1\\/videos\\/([^/]+)$/;\nconst GEMINI_PREDICT_RE = /^\\/v1beta\\/models\\/([^:]+):predict$/;\nconst DEFAULT_CHUNK_SIZE = 20;\n\nconst GEMINI_PATH_RE = /^\\/v1beta\\/models\\/([^:]+):(generateContent|streamGenerateContent)$/;\nconst AZURE_DEPLOYMENT_RE = /^\\/openai\\/deployments\\/([^/]+)\\/(chat\\/completions|embeddings)$/;\nconst BEDROCK_INVOKE_RE = /^\\/model\\/([^/]+)\\/invoke$/;\nconst BEDROCK_STREAM_RE = /^\\/model\\/([^/]+)\\/invoke-with-response-stream$/;\nconst BEDROCK_CONVERSE_RE = /^\\/model\\/([^/]+)\\/converse$/;\nconst BEDROCK_CONVERSE_STREAM_RE = /^\\/model\\/([^/]+)\\/converse-stream$/;\nconst VERTEX_AI_RE =\n /^\\/v1\\/projects\\/[^/]+\\/locations\\/[^/]+\\/publishers\\/google\\/models\\/([^/:]+):(generateContent|streamGenerateContent)$/;\n\nconst OLLAMA_CHAT_PATH = \"/api/chat\";\nconst OLLAMA_GENERATE_PATH = \"/api/generate\";\nconst OLLAMA_TAGS_PATH = \"/api/tags\";\n\nconst HEALTH_PATH = \"/health\";\nconst READY_PATH = \"/ready\";\nconst MODELS_PATH = \"/v1/models\";\nconst REQUESTS_PATH = \"/v1/_requests\";\n\nconst DEFAULT_MODELS = [\n \"gpt-4\",\n \"gpt-4o\",\n \"claude-3-5-sonnet-20241022\",\n \"gemini-2.0-flash\",\n \"text-embedding-3-small\",\n];\n\nconst CORS_HEADERS: Record<string, string> = {\n \"Access-Control-Allow-Origin\": \"*\",\n \"Access-Control-Allow-Methods\": \"GET, POST, DELETE, OPTIONS\",\n \"Access-Control-Allow-Headers\": \"Content-Type, Authorization\",\n};\n\nfunction setCorsHeaders(res: http.ServerResponse): void {\n for (const [key, value] of Object.entries(CORS_HEADERS)) {\n res.setHeader(key, value);\n }\n}\n\nasync function readBody(req: http.IncomingMessage): Promise<string> {\n const buffers: Buffer[] = [];\n for await (const chunk of req) buffers.push(chunk as Buffer);\n return Buffer.concat(buffers).toString();\n}\n\nfunction handleOptions(res: http.ServerResponse): void {\n setCorsHeaders(res);\n res.writeHead(204);\n res.end();\n}\n\nfunction handleNotFound(res: http.ServerResponse, message: string): void {\n setCorsHeaders(res);\n writeErrorResponse(res, 404, JSON.stringify({ error: { message, type: \"not_found\" } }));\n}\n\n// ---------------------------------------------------------------------------\n// /__aimock/* control API — used by aimock-pytest and other test harnesses\n// to manage fixtures, journal, and error injection without restarting the\n// server.\n// ---------------------------------------------------------------------------\n\nconst CONTROL_PREFIX = \"/__aimock\";\n\n/**\n * Handle requests under `/__aimock/`. Returns `true` if the request was\n * handled, `false` if the path doesn't match the control prefix.\n */\nasync function handleControlAPI(\n req: http.IncomingMessage,\n res: http.ServerResponse,\n pathname: string,\n fixtures: Fixture[],\n journal: Journal,\n videoStates: VideoStateMap,\n): Promise<boolean> {\n if (!pathname.startsWith(CONTROL_PREFIX)) return false;\n\n const subPath = pathname.slice(CONTROL_PREFIX.length);\n setCorsHeaders(res);\n\n // GET /__aimock/health\n if (subPath === \"/health\" && req.method === \"GET\") {\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ status: \"ok\" }));\n return true;\n }\n\n // GET /__aimock/journal\n if (subPath === \"/journal\" && req.method === \"GET\") {\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify(journal.getAll()));\n return true;\n }\n\n // POST /__aimock/fixtures — add fixtures dynamically\n if (subPath === \"/fixtures\" && req.method === \"POST\") {\n let raw: string;\n try {\n raw = await readBody(req);\n } catch {\n res.writeHead(400, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"Failed to read request body\" }));\n return true;\n }\n\n let parsed: { fixtures?: FixtureFileEntry[] };\n try {\n parsed = JSON.parse(raw) as { fixtures?: FixtureFileEntry[] };\n } catch {\n res.writeHead(400, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"Invalid JSON\" }));\n return true;\n }\n\n if (!Array.isArray(parsed.fixtures)) {\n res.writeHead(400, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: 'Missing or invalid \"fixtures\" array' }));\n return true;\n }\n\n const converted = parsed.fixtures.map(entryToFixture);\n const issues = validateFixtures(converted);\n const errors = issues.filter((i) => i.severity === \"error\");\n if (errors.length > 0) {\n res.writeHead(400, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"Validation failed\", details: errors }));\n return true;\n }\n\n fixtures.push(...converted);\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ added: converted.length }));\n return true;\n }\n\n // DELETE /__aimock/fixtures — clear all fixtures\n if (subPath === \"/fixtures\" && req.method === \"DELETE\") {\n fixtures.length = 0;\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ cleared: true }));\n return true;\n }\n\n // POST /__aimock/reset — clear fixtures + journal + match counts\n if (subPath === \"/reset\" && req.method === \"POST\") {\n fixtures.length = 0;\n journal.clear();\n videoStates.clear();\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ reset: true }));\n return true;\n }\n\n // POST /__aimock/error — queue a one-shot error\n if (subPath === \"/error\" && req.method === \"POST\") {\n let raw: string;\n try {\n raw = await readBody(req);\n } catch {\n res.writeHead(400, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"Failed to read request body\" }));\n return true;\n }\n\n let parsed: { status?: number; body?: { message?: string; type?: string; code?: string } };\n try {\n parsed = JSON.parse(raw) as typeof parsed;\n } catch {\n res.writeHead(400, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"Invalid JSON\" }));\n return true;\n }\n\n const status = parsed.status ?? 500;\n const errorBody = parsed.body;\n const errorFixture: Fixture = {\n match: { predicate: () => true },\n response: {\n error: {\n message: errorBody?.message ?? \"Injected error\",\n type: errorBody?.type ?? \"server_error\",\n code: errorBody?.code,\n },\n status,\n },\n };\n // Insert at front so it matches before everything else\n fixtures.unshift(errorFixture);\n // Remove after first match\n const original = errorFixture.match.predicate!;\n errorFixture.match.predicate = (req) => {\n const result = original(req);\n if (result) {\n queueMicrotask(() => {\n const idx = fixtures.indexOf(errorFixture);\n if (idx !== -1) fixtures.splice(idx, 1);\n });\n }\n return result;\n };\n\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ queued: true }));\n return true;\n }\n\n // Unknown control path\n handleNotFound(res, `Unknown control endpoint: ${pathname}`);\n return true;\n}\n\nasync function handleCompletions(\n req: http.IncomingMessage,\n res: http.ServerResponse,\n fixtures: Fixture[],\n journal: Journal,\n defaults: HandlerDefaults,\n modelFallback?: string,\n providerKey?: RecordProviderKey,\n): Promise<void> {\n setCorsHeaders(res);\n\n // Read request body\n let raw: string;\n try {\n raw = await readBody(req);\n } catch (err) {\n const msg = err instanceof Error ? err.message : \"Failed to read request body\";\n journal.add({\n method: req.method ?? \"POST\",\n path: req.url ?? COMPLETIONS_PATH,\n headers: flattenHeaders(req.headers),\n body: null,\n response: { status: 500, fixture: null },\n });\n writeErrorResponse(\n res,\n 500,\n JSON.stringify({\n error: {\n message: `Request body read failed: ${msg}`,\n type: \"server_error\",\n },\n }),\n );\n return;\n }\n\n // Parse JSON body\n let body: ChatCompletionRequest;\n try {\n body = JSON.parse(raw) as ChatCompletionRequest;\n // Azure deployments may omit model from body — use deployment ID as fallback\n if (modelFallback && !body.model) {\n body.model = modelFallback;\n }\n } catch {\n journal.add({\n method: req.method ?? \"POST\",\n path: req.url ?? COMPLETIONS_PATH,\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 code: \"invalid_json\",\n },\n }),\n );\n return;\n }\n\n // Match fixture\n body._endpointType = \"chat\";\n const testId = getTestId(req);\n const fixture = matchFixture(\n fixtures,\n body,\n journal.getFixtureMatchCountsForTest(testId),\n defaults.requestTransform,\n );\n\n if (fixture) {\n journal.incrementFixtureMatchCount(fixture, fixtures, testId);\n }\n\n const method = req.method ?? \"POST\";\n const path = req.url ?? COMPLETIONS_PATH;\n const flatHeaders = flattenHeaders(req.headers);\n\n // Apply chaos before normal response handling\n if (\n applyChaos(\n res,\n fixture,\n defaults.chaos,\n req.headers,\n journal,\n {\n method,\n path,\n headers: flatHeaders,\n body,\n },\n defaults.registry,\n defaults.logger,\n )\n )\n return;\n\n if (!fixture) {\n // Try record-and-replay proxy if configured\n if (defaults.record && providerKey) {\n const proxied = await proxyAndRecord(\n req,\n res,\n body,\n providerKey,\n req.url ?? COMPLETIONS_PATH,\n fixtures,\n defaults,\n raw,\n );\n if (proxied) {\n journal.add({\n method: req.method ?? \"POST\",\n path: req.url ?? COMPLETIONS_PATH,\n headers: flattenHeaders(req.headers),\n body,\n response: { status: res.statusCode ?? 200, fixture: null },\n });\n return;\n }\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 defaults.logger.error(\n `STRICT: No fixture matched for ${req.method ?? \"POST\"} ${req.url ?? COMPLETIONS_PATH}`,\n );\n }\n\n journal.add({\n method: req.method ?? \"POST\",\n path: req.url ?? COMPLETIONS_PATH,\n headers: flattenHeaders(req.headers),\n body,\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 code: \"no_fixture_match\",\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: req.url ?? COMPLETIONS_PATH,\n headers: flattenHeaders(req.headers),\n body,\n response: { status, fixture },\n });\n writeErrorResponse(res, status, JSON.stringify(response));\n return;\n }\n\n // Content + tool calls response\n if (isContentWithToolCallsResponse(response)) {\n const journalEntry = journal.add({\n method: req.method ?? \"POST\",\n path: req.url ?? COMPLETIONS_PATH,\n headers: flattenHeaders(req.headers),\n body,\n response: { status: 200, fixture },\n });\n if (body.stream !== true) {\n const completion = buildContentWithToolCallsCompletion(\n response.content,\n response.toolCalls,\n body.model,\n );\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify(completion));\n } else {\n const chunks = buildContentWithToolCallsChunks(\n response.content,\n response.toolCalls,\n body.model,\n chunkSize,\n );\n const interruption = createInterruptionSignal(fixture);\n const completed = await writeSSEStream(res, chunks, {\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 }\n return;\n }\n\n // Text response\n if (isTextResponse(response)) {\n const journalEntry = journal.add({\n method: req.method ?? \"POST\",\n path: req.url ?? COMPLETIONS_PATH,\n headers: flattenHeaders(req.headers),\n body,\n response: { status: 200, fixture },\n });\n if (body.stream !== true) {\n const completion = buildTextCompletion(response.content, body.model, response.reasoning);\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify(completion));\n } else {\n const chunks = buildTextChunks(response.content, body.model, chunkSize, response.reasoning);\n const interruption = createInterruptionSignal(fixture);\n const completed = await writeSSEStream(res, chunks, {\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 }\n return;\n }\n\n // Tool call response\n if (isToolCallResponse(response)) {\n const journalEntry = journal.add({\n method: req.method ?? \"POST\",\n path: req.url ?? COMPLETIONS_PATH,\n headers: flattenHeaders(req.headers),\n body,\n response: { status: 200, fixture },\n });\n if (body.stream !== true) {\n const completion = buildToolCallCompletion(response.toolCalls, body.model);\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify(completion));\n } else {\n const chunks = buildToolCallChunks(response.toolCalls, body.model, chunkSize);\n const interruption = createInterruptionSignal(fixture);\n const completed = await writeSSEStream(res, chunks, {\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 }\n return;\n }\n\n // Fixture response matched no known type — guard against silent hang\n journal.add({\n method: req.method ?? \"POST\",\n path: req.url ?? COMPLETIONS_PATH,\n headers: flattenHeaders(req.headers),\n body,\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 interface ServiceFixtures {\n search: SearchFixture[];\n rerank: RerankFixture[];\n moderation: ModerationFixture[];\n}\n\n// NOTE: The fixtures array is read by reference on each request. Callers\n// (e.g. LLMock) may mutate it after the server starts and changes will\n// be visible immediately. This is intentional — do not copy the array.\nexport async function createServer(\n fixtures: Fixture[],\n options?: MockServerOptions,\n mounts?: Array<{ path: string; handler: Mountable }>,\n serviceFixtures?: ServiceFixtures,\n): Promise<ServerInstance> {\n const host = options?.host ?? \"127.0.0.1\";\n const port = options?.port ?? 0;\n const logger = new Logger(options?.logLevel ?? \"silent\");\n const registry = options?.metrics ? createMetricsRegistry() : undefined;\n const serverOptions = options ?? {};\n const defaults = {\n latency: serverOptions.latency ?? 0,\n chunkSize: Math.max(1, serverOptions.chunkSize ?? DEFAULT_CHUNK_SIZE),\n logger,\n get chaos() {\n return serverOptions.chaos;\n },\n registry,\n get record() {\n return serverOptions.record;\n },\n get strict() {\n return serverOptions.strict;\n },\n get requestTransform() {\n return serverOptions.requestTransform;\n },\n };\n\n // Validate chaos config rates\n if (options?.chaos) {\n const chaosRates = [\n { name: \"dropRate\", value: options.chaos.dropRate },\n { name: \"malformedRate\", value: options.chaos.malformedRate },\n { name: \"disconnectRate\", value: options.chaos.disconnectRate },\n ];\n for (const { name, value } of chaosRates) {\n if (value !== undefined && (value < 0 || value > 1)) {\n logger.warn(`Chaos ${name} (${value}) is outside 0-1 range — will be clamped at runtime`);\n }\n }\n }\n\n const journal = new Journal();\n const videoStates: VideoStateMap = new Map();\n\n // Share journal and metrics registry with mounted services\n if (mounts) {\n for (const { handler } of mounts) {\n if (handler.setJournal) handler.setJournal(journal);\n if (registry && handler.setRegistry) handler.setRegistry(registry);\n }\n }\n\n // Set initial fixtures-loaded gauge\n if (registry) {\n registry.setGauge(\"aimock_fixtures_loaded\", {}, fixtures.length);\n }\n\n const server = http.createServer((req: http.IncomingMessage, res: http.ServerResponse) => {\n // Delegate to async handler — catch unhandled rejections to prevent Node.js crashes\n handleHttpRequest(req, res).catch((err: unknown) => {\n const msg = err instanceof Error ? err.message : \"Internal error\";\n defaults.logger.warn(`Unhandled request error: ${msg}`);\n if (!res.headersSent) {\n res.writeHead(500, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: { message: msg, type: \"server_error\" } }));\n }\n });\n });\n\n async function handleHttpRequest(\n req: http.IncomingMessage,\n res: http.ServerResponse,\n ): Promise<void> {\n // OPTIONS preflight\n if (req.method === \"OPTIONS\") {\n handleOptions(res);\n return;\n }\n\n // Record start time for metrics\n const startTime = registry ? process.hrtime.bigint() : 0n;\n\n // Parse the URL pathname (strip query string)\n const parsedUrl = new URL(req.url ?? \"/\", `http://${req.headers.host ?? \"localhost\"}`);\n let pathname = parsedUrl.pathname;\n\n // Instrument response completion for metrics\n if (registry) {\n const rawPathname = pathname;\n res.on(\"finish\", () => {\n try {\n const normalizedPath = normalizePathLabel(rawPathname);\n const method = req.method ?? \"UNKNOWN\";\n const status = String(res.statusCode);\n registry.incrementCounter(\"aimock_requests_total\", {\n method,\n path: normalizedPath,\n status,\n });\n const elapsed = Number(process.hrtime.bigint() - startTime) / 1e9;\n registry.observeHistogram(\n \"aimock_request_duration_seconds\",\n { method, path: normalizedPath },\n elapsed,\n );\n } catch (err) {\n defaults.logger.warn(\"metrics instrumentation error\", err);\n }\n });\n }\n\n // Control API — must be checked before mounts and path rewrites\n if (pathname.startsWith(CONTROL_PREFIX)) {\n await handleControlAPI(req, res, pathname, fixtures, journal, videoStates);\n return;\n }\n\n // Dispatch to mounted services before any path rewrites\n if (mounts) {\n for (const { path: mountPath, handler } of mounts) {\n if (pathname === mountPath || pathname.startsWith(mountPath + \"/\")) {\n const subPath = pathname.slice(mountPath.length) || \"/\";\n const handled = await handler.handleRequest(req, res, subPath);\n if (handled) return;\n }\n }\n }\n\n // Azure OpenAI: /openai/deployments/{id}/{operation} → /v1/{operation} (chat/completions, embeddings)\n // Must be checked BEFORE the generic /openai/ prefix strip\n let azureDeploymentId: string | undefined;\n const azureMatch = pathname.match(AZURE_DEPLOYMENT_RE);\n if (azureMatch && req.method === \"POST\") {\n azureDeploymentId = azureMatch[1];\n const operation = azureMatch[2];\n pathname = `/v1/${operation}`;\n }\n\n // Groq/OpenAI-compatible alias: strip /openai prefix so that\n // /openai/v1/chat/completions → /v1/chat/completions, etc.\n if (!azureDeploymentId && pathname.startsWith(\"/openai/\")) {\n pathname = pathname.slice(7); // remove \"/openai\" prefix, keep the rest\n }\n\n // Health / readiness probes\n if (pathname === HEALTH_PATH && req.method === \"GET\") {\n setCorsHeaders(res);\n if (mounts && mounts.length > 0) {\n const services: Record<string, unknown> = {\n llm: { status: \"ok\", fixtures: fixtures.length },\n };\n for (const { path: mountPath, handler } of mounts) {\n if (handler.health) {\n const name = mountPath.replace(/^\\//, \"\");\n services[name] = handler.health();\n }\n }\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ status: \"ok\", services }));\n } else {\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ status: \"ok\" }));\n }\n return;\n }\n\n if (pathname === READY_PATH && req.method === \"GET\") {\n setCorsHeaders(res);\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ status: \"ready\" }));\n return;\n }\n\n // Prometheus metrics\n if (pathname === \"/metrics\" && req.method === \"GET\") {\n if (!registry) {\n handleNotFound(res, \"Not found\");\n return;\n }\n setCorsHeaders(res);\n res.writeHead(200, { \"Content-Type\": \"text/plain; version=0.0.4; charset=utf-8\" });\n res.end(registry.serialize());\n return;\n }\n\n // Models listing\n if (pathname === MODELS_PATH && req.method === \"GET\") {\n setCorsHeaders(res);\n const modelIds = new Set<string>();\n for (const f of fixtures) {\n if (f.match.model && typeof f.match.model === \"string\") {\n modelIds.add(f.match.model);\n }\n }\n const ids = modelIds.size > 0 ? [...modelIds] : DEFAULT_MODELS;\n const data = ids.map((id) => ({\n id,\n object: \"model\" as const,\n created: 1686935002,\n owned_by: \"aimock\",\n }));\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ object: \"list\", data }));\n return;\n }\n\n // Journal inspection endpoints\n if (pathname === REQUESTS_PATH) {\n setCorsHeaders(res);\n if (req.method === \"GET\") {\n const limitParam = parsedUrl.searchParams.get(\"limit\");\n let opts: { limit: number } | undefined;\n if (limitParam) {\n const limit = parseInt(limitParam, 10);\n if (Number.isNaN(limit) || limit <= 0) {\n writeErrorResponse(\n res,\n 400,\n JSON.stringify({\n error: {\n message: `Invalid limit parameter: \"${limitParam}\"`,\n type: \"invalid_request_error\",\n },\n }),\n );\n return;\n }\n opts = { limit };\n }\n const entries = journal.getAll(opts);\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify(entries));\n return;\n }\n if (req.method === \"DELETE\") {\n journal.clear();\n res.writeHead(204);\n res.end();\n return;\n }\n handleNotFound(res, \"Not found\");\n return;\n }\n\n // POST /v1/responses — OpenAI Responses API\n if (pathname === RESPONSES_PATH && req.method === \"POST\") {\n readBody(req)\n .then((raw) => handleResponses(req, res, raw, fixtures, journal, defaults, setCorsHeaders))\n .catch((err: unknown) => {\n const msg = err instanceof Error ? err.message : \"Internal error\";\n if (!res.headersSent) {\n writeErrorResponse(\n res,\n 500,\n JSON.stringify({ error: { message: msg, type: \"server_error\" } }),\n );\n } else if (!res.writableEnded) {\n try {\n res.write(`event: error\\ndata: ${JSON.stringify({ error: { message: msg } })}\\n\\n`);\n } catch (writeErr) {\n logger.debug(\"Failed to write error recovery response:\", writeErr);\n }\n res.end();\n }\n });\n return;\n }\n\n // POST /v1/messages — Anthropic Claude Messages API\n if (pathname === MESSAGES_PATH && req.method === \"POST\") {\n readBody(req)\n .then((raw) => handleMessages(req, res, raw, fixtures, journal, defaults, setCorsHeaders))\n .catch((err: unknown) => {\n const msg = err instanceof Error ? err.message : \"Internal error\";\n if (!res.headersSent) {\n writeErrorResponse(\n res,\n 500,\n JSON.stringify({ error: { message: msg, type: \"server_error\" } }),\n );\n } else if (!res.writableEnded) {\n try {\n res.write(`event: error\\ndata: ${JSON.stringify({ error: { message: msg } })}\\n\\n`);\n } catch (writeErr) {\n logger.debug(\"Failed to write error recovery response:\", writeErr);\n }\n res.end();\n }\n });\n return;\n }\n\n // POST /v2/chat — Cohere v2 Chat API\n if (pathname === COHERE_CHAT_PATH && req.method === \"POST\") {\n readBody(req)\n .then((raw) => handleCohere(req, res, raw, fixtures, journal, defaults, setCorsHeaders))\n .catch((err: unknown) => {\n const msg = err instanceof Error ? err.message : \"Internal error\";\n if (!res.headersSent) {\n writeErrorResponse(\n res,\n 500,\n JSON.stringify({ error: { message: msg, type: \"server_error\" } }),\n );\n } else if (!res.writableEnded) {\n try {\n res.write(`event: error\\ndata: ${JSON.stringify({ error: { message: msg } })}\\n\\n`);\n } catch (writeErr) {\n logger.debug(\"Failed to write error recovery response:\", writeErr);\n }\n res.end();\n }\n });\n return;\n }\n\n // POST /v1/embeddings — OpenAI Embeddings API\n if (pathname === EMBEDDINGS_PATH && req.method === \"POST\") {\n const deploymentId = azureDeploymentId;\n readBody(req)\n .then((raw) => {\n // Azure deployments may omit model from body — use deployment ID as fallback\n if (deploymentId) {\n try {\n const parsed = JSON.parse(raw) as Record<string, unknown>;\n if (!parsed.model) {\n parsed.model = deploymentId;\n return handleEmbeddings(\n req,\n res,\n JSON.stringify(parsed),\n fixtures,\n journal,\n defaults,\n setCorsHeaders,\n );\n }\n } catch {\n // Fall through — let handleEmbeddings report the parse error\n }\n }\n return handleEmbeddings(req, res, raw, fixtures, journal, defaults, setCorsHeaders);\n })\n .catch((err: unknown) => {\n const msg = err instanceof Error ? err.message : \"Internal error\";\n if (!res.headersSent) {\n writeErrorResponse(\n res,\n 500,\n JSON.stringify({ error: { message: msg, type: \"server_error\" } }),\n );\n } else if (!res.writableEnded) {\n res.destroy();\n }\n });\n return;\n }\n\n // POST /v1/images/generations — OpenAI Image Generation API\n if (pathname === IMAGES_PATH && req.method === \"POST\") {\n readBody(req)\n .then((raw) => handleImages(req, res, raw, fixtures, journal, defaults, setCorsHeaders))\n .catch((err: unknown) => {\n const msg = err instanceof Error ? err.message : \"Internal error\";\n if (!res.headersSent) {\n writeErrorResponse(\n res,\n 500,\n JSON.stringify({ error: { message: msg, type: \"server_error\" } }),\n );\n } else if (!res.writableEnded) {\n res.destroy();\n }\n });\n return;\n }\n\n // POST /v1/audio/speech — OpenAI TTS API\n if (pathname === SPEECH_PATH && req.method === \"POST\") {\n readBody(req)\n .then((raw) => handleSpeech(req, res, raw, fixtures, journal, defaults, setCorsHeaders))\n .catch((err: unknown) => {\n const msg = err instanceof Error ? err.message : \"Internal error\";\n if (!res.headersSent) {\n writeErrorResponse(\n res,\n 500,\n JSON.stringify({ error: { message: msg, type: \"server_error\" } }),\n );\n } else if (!res.writableEnded) {\n res.destroy();\n }\n });\n return;\n }\n\n // POST /v1/audio/transcriptions — OpenAI Transcription API\n if (pathname === TRANSCRIPTIONS_PATH && req.method === \"POST\") {\n readBody(req)\n .then((raw) =>\n handleTranscription(req, res, raw, fixtures, journal, defaults, setCorsHeaders),\n )\n .catch((err: unknown) => {\n const msg = err instanceof Error ? err.message : \"Internal error\";\n if (!res.headersSent) {\n writeErrorResponse(\n res,\n 500,\n JSON.stringify({ error: { message: msg, type: \"server_error\" } }),\n );\n } else if (!res.writableEnded) {\n res.destroy();\n }\n });\n return;\n }\n\n // POST /v1/videos — Video Generation API\n if (pathname === VIDEOS_PATH && req.method === \"POST\") {\n readBody(req)\n .then((raw) =>\n handleVideoCreate(\n req,\n res,\n raw,\n fixtures,\n journal,\n defaults,\n setCorsHeaders,\n videoStates,\n ),\n )\n .catch((err: unknown) => {\n const msg = err instanceof Error ? err.message : \"Internal error\";\n if (!res.headersSent) {\n writeErrorResponse(\n res,\n 500,\n JSON.stringify({ error: { message: msg, type: \"server_error\" } }),\n );\n } else if (!res.writableEnded) {\n res.destroy();\n }\n });\n return;\n }\n\n // GET /v1/videos/{id} — Video Status Check\n const videoStatusMatch = pathname.match(VIDEOS_STATUS_RE);\n if (videoStatusMatch && req.method === \"GET\") {\n const videoId = videoStatusMatch[1];\n handleVideoStatus(req, res, videoId, journal, setCorsHeaders, videoStates);\n return;\n }\n\n // POST /v1beta/models/{model}:predict — Gemini Imagen API\n const geminiPredictMatch = pathname.match(GEMINI_PREDICT_RE);\n if (geminiPredictMatch && req.method === \"POST\") {\n const predictModel = geminiPredictMatch[1];\n readBody(req)\n .then((raw) =>\n handleImages(\n req,\n res,\n raw,\n fixtures,\n journal,\n defaults,\n setCorsHeaders,\n \"gemini\",\n predictModel,\n ),\n )\n .catch((err: unknown) => {\n const msg = err instanceof Error ? err.message : \"Internal error\";\n if (!res.headersSent) {\n writeErrorResponse(\n res,\n 500,\n JSON.stringify({ error: { message: msg, type: \"server_error\" } }),\n );\n } else if (!res.writableEnded) {\n res.destroy();\n }\n });\n return;\n }\n\n // POST /v1beta/models/{model}:(generateContent|streamGenerateContent) — Google Gemini\n const geminiMatch = pathname.match(GEMINI_PATH_RE);\n if (geminiMatch && req.method === \"POST\") {\n const geminiModel = geminiMatch[1];\n const streaming = geminiMatch[2] === \"streamGenerateContent\";\n readBody(req)\n .then((raw) =>\n handleGemini(\n req,\n res,\n raw,\n geminiModel,\n streaming,\n fixtures,\n journal,\n defaults,\n setCorsHeaders,\n ),\n )\n .catch((err: unknown) => {\n const msg = err instanceof Error ? err.message : \"Internal error\";\n if (!res.headersSent) {\n writeErrorResponse(\n res,\n 500,\n JSON.stringify({ error: { message: msg, type: \"server_error\" } }),\n );\n } else if (!res.writableEnded) {\n try {\n res.write(`data: ${JSON.stringify({ error: { message: msg } })}\\n\\n`);\n } catch (writeErr) {\n logger.debug(\"Failed to write error recovery response:\", writeErr);\n }\n res.end();\n }\n });\n return;\n }\n\n // POST /v1/projects/{project}/locations/{location}/publishers/google/models/{model}:(generateContent|streamGenerateContent) — Vertex AI\n const vertexMatch = pathname.match(VERTEX_AI_RE);\n if (vertexMatch && req.method === \"POST\") {\n const vertexModel = vertexMatch[1];\n const streaming = vertexMatch[2] === \"streamGenerateContent\";\n readBody(req)\n .then((raw) =>\n handleGemini(\n req,\n res,\n raw,\n vertexModel,\n streaming,\n fixtures,\n journal,\n defaults,\n setCorsHeaders,\n \"vertexai\",\n ),\n )\n .catch((err: unknown) => {\n const msg = err instanceof Error ? err.message : \"Internal error\";\n if (!res.headersSent) {\n writeErrorResponse(\n res,\n 500,\n JSON.stringify({ error: { message: msg, type: \"server_error\" } }),\n );\n } else if (!res.writableEnded) {\n try {\n res.write(`data: ${JSON.stringify({ error: { message: msg } })}\\n\\n`);\n } catch (writeErr) {\n logger.debug(\"Failed to write error recovery response:\", writeErr);\n }\n res.end();\n }\n });\n return;\n }\n\n // POST /model/{modelId}/invoke — AWS Bedrock Claude API\n const bedrockMatch = pathname.match(BEDROCK_INVOKE_RE);\n if (bedrockMatch && req.method === \"POST\") {\n const bedrockModelId = bedrockMatch[1];\n readBody(req)\n .then((raw) =>\n handleBedrock(req, res, raw, bedrockModelId, fixtures, journal, defaults, setCorsHeaders),\n )\n .catch((err: unknown) => {\n const msg = err instanceof Error ? err.message : \"Internal error\";\n if (!res.headersSent) {\n writeErrorResponse(\n res,\n 500,\n JSON.stringify({ error: { message: msg, type: \"server_error\" } }),\n );\n } else if (!res.writableEnded) {\n res.destroy();\n }\n });\n return;\n }\n\n // POST /model/{modelId}/invoke-with-response-stream — AWS Bedrock Claude streaming\n const bedrockStreamMatch = pathname.match(BEDROCK_STREAM_RE);\n if (bedrockStreamMatch && req.method === \"POST\") {\n const bedrockModelId = bedrockStreamMatch[1];\n readBody(req)\n .then((raw) =>\n handleBedrockStream(\n req,\n res,\n raw,\n bedrockModelId,\n fixtures,\n journal,\n defaults,\n setCorsHeaders,\n ),\n )\n .catch((err: unknown) => {\n const msg = err instanceof Error ? err.message : \"Internal error\";\n if (!res.headersSent) {\n writeErrorResponse(\n res,\n 500,\n JSON.stringify({ error: { message: msg, type: \"server_error\" } }),\n );\n } else if (!res.writableEnded) {\n res.destroy();\n }\n });\n return;\n }\n\n // POST /model/{modelId}/converse — AWS Bedrock Converse API\n const converseMatch = pathname.match(BEDROCK_CONVERSE_RE);\n if (converseMatch && req.method === \"POST\") {\n const converseModelId = converseMatch[1];\n readBody(req)\n .then((raw) =>\n handleConverse(\n req,\n res,\n raw,\n converseModelId,\n fixtures,\n journal,\n defaults,\n setCorsHeaders,\n ),\n )\n .catch((err: unknown) => {\n const msg = err instanceof Error ? err.message : \"Internal error\";\n if (!res.headersSent) {\n writeErrorResponse(\n res,\n 500,\n JSON.stringify({ error: { message: msg, type: \"server_error\" } }),\n );\n } else if (!res.writableEnded) {\n res.destroy();\n }\n });\n return;\n }\n\n // POST /model/{modelId}/converse-stream — AWS Bedrock Converse streaming API\n const converseStreamMatch = pathname.match(BEDROCK_CONVERSE_STREAM_RE);\n if (converseStreamMatch && req.method === \"POST\") {\n const converseStreamModelId = converseStreamMatch[1];\n readBody(req)\n .then((raw) =>\n handleConverseStream(\n req,\n res,\n raw,\n converseStreamModelId,\n fixtures,\n journal,\n defaults,\n setCorsHeaders,\n ),\n )\n .catch((err: unknown) => {\n const msg = err instanceof Error ? err.message : \"Internal error\";\n if (!res.headersSent) {\n writeErrorResponse(\n res,\n 500,\n JSON.stringify({ error: { message: msg, type: \"server_error\" } }),\n );\n } else if (!res.writableEnded) {\n res.destroy();\n }\n });\n return;\n }\n\n // POST /api/chat — Ollama Chat API\n if (pathname === OLLAMA_CHAT_PATH && req.method === \"POST\") {\n readBody(req)\n .then((raw) => handleOllama(req, res, raw, fixtures, journal, defaults, setCorsHeaders))\n .catch((err: unknown) => {\n const msg = err instanceof Error ? err.message : \"Internal error\";\n if (!res.headersSent) {\n writeErrorResponse(\n res,\n 500,\n JSON.stringify({ error: { message: msg, type: \"server_error\" } }),\n );\n } else if (!res.writableEnded) {\n res.destroy();\n }\n });\n return;\n }\n\n // POST /api/generate — Ollama Generate API\n if (pathname === OLLAMA_GENERATE_PATH && req.method === \"POST\") {\n readBody(req)\n .then((raw) =>\n handleOllamaGenerate(req, res, raw, fixtures, journal, defaults, setCorsHeaders),\n )\n .catch((err: unknown) => {\n const msg = err instanceof Error ? err.message : \"Internal error\";\n if (!res.headersSent) {\n writeErrorResponse(\n res,\n 500,\n JSON.stringify({ error: { message: msg, type: \"server_error\" } }),\n );\n } else if (!res.writableEnded) {\n res.destroy();\n }\n });\n return;\n }\n\n // GET /api/tags — Ollama Models listing\n if (pathname === OLLAMA_TAGS_PATH && req.method === \"GET\") {\n setCorsHeaders(res);\n const modelIds = new Set<string>();\n for (const f of fixtures) {\n if (f.match.model && typeof f.match.model === \"string\") {\n modelIds.add(f.match.model);\n }\n }\n const ids = modelIds.size > 0 ? [...modelIds] : DEFAULT_MODELS;\n const models = ids.map((name) => ({\n name,\n model: name,\n modified_at: new Date().toISOString(),\n size: 0,\n digest: \"\",\n details: {},\n }));\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ models }));\n return;\n }\n\n // POST /search — Web Search API (Tavily-compatible)\n if (pathname === SEARCH_PATH && req.method === \"POST\") {\n readBody(req)\n .then((raw) =>\n handleSearch(\n req,\n res,\n raw,\n serviceFixtures?.search ?? [],\n journal,\n defaults,\n setCorsHeaders,\n ),\n )\n .catch((err: unknown) => {\n const msg = err instanceof Error ? err.message : \"Internal error\";\n if (!res.headersSent) {\n writeErrorResponse(\n res,\n 500,\n JSON.stringify({ error: { message: msg, type: \"server_error\" } }),\n );\n } else if (!res.writableEnded) {\n res.destroy();\n }\n });\n return;\n }\n\n // POST /v2/rerank — Reranking API (Cohere rerank-compatible)\n if (pathname === RERANK_PATH && req.method === \"POST\") {\n readBody(req)\n .then((raw) =>\n handleRerank(\n req,\n res,\n raw,\n serviceFixtures?.rerank ?? [],\n journal,\n defaults,\n setCorsHeaders,\n ),\n )\n .catch((err: unknown) => {\n const msg = err instanceof Error ? err.message : \"Internal error\";\n if (!res.headersSent) {\n writeErrorResponse(\n res,\n 500,\n JSON.stringify({ error: { message: msg, type: \"server_error\" } }),\n );\n } else if (!res.writableEnded) {\n res.destroy();\n }\n });\n return;\n }\n\n // POST /v1/moderations — Moderation API (OpenAI-compatible)\n if (pathname === MODERATIONS_PATH && req.method === \"POST\") {\n readBody(req)\n .then((raw) =>\n handleModeration(\n req,\n res,\n raw,\n serviceFixtures?.moderation ?? [],\n journal,\n defaults,\n setCorsHeaders,\n ),\n )\n .catch((err: unknown) => {\n const msg = err instanceof Error ? err.message : \"Internal error\";\n if (!res.headersSent) {\n writeErrorResponse(\n res,\n 500,\n JSON.stringify({ error: { message: msg, type: \"server_error\" } }),\n );\n } else if (!res.writableEnded) {\n res.destroy();\n }\n });\n return;\n }\n\n // POST /v1/chat/completions — Chat Completions API\n if (pathname !== COMPLETIONS_PATH) {\n handleNotFound(res, \"Not found\");\n return;\n }\n if (req.method !== \"POST\") {\n handleNotFound(res, \"Not found\");\n return;\n }\n\n const completionsProvider: RecordProviderKey = azureDeploymentId ? \"azure\" : \"openai\";\n handleCompletions(\n req,\n res,\n fixtures,\n journal,\n defaults,\n azureDeploymentId,\n completionsProvider,\n ).catch((err: unknown) => {\n const msg = err instanceof Error ? err.message : \"Internal error\";\n if (!res.headersSent) {\n writeErrorResponse(\n res,\n 500,\n JSON.stringify({\n error: {\n message: msg,\n type: \"server_error\",\n },\n }),\n );\n } else if (!res.writableEnded) {\n // Headers already sent (SSE stream in progress) — write error event then close\n try {\n res.write(\n `data: ${JSON.stringify({ error: { message: msg, type: \"server_error\" } })}\\n\\n`,\n );\n } catch (writeErr) {\n logger.debug(\"Failed to write error recovery response:\", writeErr);\n }\n res.end();\n }\n });\n }\n\n // ─── WebSocket upgrade handling ──────────────────────────────────────────\n\n const activeConnections = new Set<WebSocketConnection>();\n\n server.on(\n \"upgrade\",\n (req: http.IncomingMessage, socket: import(\"node:net\").Socket, head: Buffer) => {\n handleUpgradeRequest(req, socket, head).catch((err: unknown) => {\n const msg = err instanceof Error ? err.message : \"Internal error\";\n defaults.logger.warn(`Unhandled upgrade error: ${msg}`);\n if (!socket.destroyed) socket.destroy();\n });\n },\n );\n\n async function handleUpgradeRequest(\n req: http.IncomingMessage,\n socket: import(\"node:net\").Socket,\n head: Buffer,\n ): Promise<void> {\n const parsedUrl = new URL(req.url ?? \"/\", `http://${req.headers.host ?? \"localhost\"}`);\n const pathname = parsedUrl.pathname;\n\n // Dispatch to mounted services\n if (mounts) {\n for (const { path: mountPath, handler } of mounts) {\n if (\n (pathname === mountPath || pathname.startsWith(mountPath + \"/\")) &&\n handler.handleUpgrade\n ) {\n const subPath = pathname.slice(mountPath.length) || \"/\";\n if (await handler.handleUpgrade(socket, head, subPath)) return;\n }\n }\n }\n\n if (\n pathname !== RESPONSES_PATH &&\n pathname !== REALTIME_PATH &&\n pathname !== GEMINI_LIVE_PATH\n ) {\n socket.write(\"HTTP/1.1 404 Not Found\\r\\n\\r\\n\");\n socket.destroy();\n return;\n }\n\n // Push any buffered data back before upgrading\n if (head.length > 0) {\n socket.unshift(head);\n }\n\n let ws: WebSocketConnection;\n try {\n ws = upgradeToWebSocket(req, socket);\n } catch (err: unknown) {\n const msg = err instanceof Error ? err.message : \"WebSocket upgrade failed\";\n logger.error(`WebSocket upgrade error: ${msg}`);\n if (!socket.destroyed) socket.destroy();\n return;\n }\n\n activeConnections.add(ws);\n\n ws.on(\"error\", (err: Error) => {\n logger.error(`WebSocket error: ${err.message}`);\n activeConnections.delete(ws);\n });\n\n ws.on(\"close\", () => {\n activeConnections.delete(ws);\n });\n\n // Route to handler\n const wsTestId = getTestId(req);\n if (pathname === RESPONSES_PATH) {\n handleWebSocketResponses(ws, fixtures, journal, {\n ...defaults,\n model: \"gpt-4\",\n testId: wsTestId,\n });\n } else if (pathname === REALTIME_PATH) {\n const model = parsedUrl.searchParams.get(\"model\") ?? \"gpt-4o-realtime\";\n handleWebSocketRealtime(ws, fixtures, journal, {\n ...defaults,\n model,\n testId: wsTestId,\n });\n } else if (pathname === GEMINI_LIVE_PATH) {\n handleWebSocketGeminiLive(ws, fixtures, journal, {\n ...defaults,\n model: \"gemini-2.0-flash\",\n testId: wsTestId,\n });\n }\n }\n\n // Close active WS connections when server shuts down\n const originalClose = server.close.bind(server);\n server.close = function (this: http.Server, callback?: (err?: Error) => void) {\n for (const ws of activeConnections) {\n ws.close(1001, \"Server shutting down\");\n }\n activeConnections.clear();\n originalClose(callback);\n return this;\n } as typeof server.close;\n\n return new Promise<ServerInstance>((resolve, reject) => {\n server.on(\"error\", reject);\n server.listen(port, host, () => {\n const addr = server.address();\n if (!addr || typeof addr === \"string\") {\n reject(new Error(\"Unexpected address format\"));\n return;\n }\n const url = `http://${addr.address}:${addr.port}`;\n\n // Set base URL on mounted services that support it\n if (mounts) {\n for (const { path: mountPath, handler } of mounts) {\n if (handler.setBaseUrl) handler.setBaseUrl(url + mountPath);\n }\n }\n\n resolve({ server, journal, url, defaults, videoStates });\n });\n });\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"server.js","names":[],"sources":["../src/server.ts"],"sourcesContent":["import * as http from \"node:http\";\nimport type {\n Fixture,\n FixtureFileEntry,\n ChatCompletionRequest,\n HandlerDefaults,\n MockServerOptions,\n Mountable,\n RecordProviderKey,\n} from \"./types.js\";\nimport { Journal } from \"./journal.js\";\nimport { matchFixture } from \"./router.js\";\nimport { validateFixtures, entryToFixture } from \"./fixture-loader.js\";\nimport { writeSSEStream, writeErrorResponse } from \"./sse-writer.js\";\nimport { createInterruptionSignal } from \"./interruption.js\";\nimport {\n buildTextChunks,\n buildToolCallChunks,\n buildTextCompletion,\n buildToolCallCompletion,\n buildContentWithToolCallsChunks,\n buildContentWithToolCallsCompletion,\n extractOverrides,\n isTextResponse,\n isToolCallResponse,\n isContentWithToolCallsResponse,\n isErrorResponse,\n flattenHeaders,\n getTestId,\n} from \"./helpers.js\";\nimport { handleResponses } from \"./responses.js\";\nimport { handleMessages } from \"./messages.js\";\nimport { handleGemini } from \"./gemini.js\";\nimport { handleBedrock, handleBedrockStream } from \"./bedrock.js\";\nimport { handleConverse, handleConverseStream } from \"./bedrock-converse.js\";\nimport { handleEmbeddings } from \"./embeddings.js\";\nimport { handleImages } from \"./images.js\";\nimport { handleSpeech } from \"./speech.js\";\nimport { handleTranscription } from \"./transcription.js\";\nimport { handleVideoCreate, handleVideoStatus, type VideoStateMap } from \"./video.js\";\nimport { handleOllama, handleOllamaGenerate } from \"./ollama.js\";\nimport { handleCohere } from \"./cohere.js\";\nimport { handleSearch, type SearchFixture } from \"./search.js\";\nimport { handleRerank, type RerankFixture } from \"./rerank.js\";\nimport { handleModeration, type ModerationFixture } from \"./moderation.js\";\nimport { upgradeToWebSocket, type WebSocketConnection } from \"./ws-framing.js\";\nimport { handleWebSocketResponses } from \"./ws-responses.js\";\nimport { handleWebSocketRealtime } from \"./ws-realtime.js\";\nimport { handleWebSocketGeminiLive } from \"./ws-gemini-live.js\";\nimport { Logger } from \"./logger.js\";\nimport { applyChaos } from \"./chaos.js\";\nimport { createMetricsRegistry, normalizePathLabel } from \"./metrics.js\";\nimport { proxyAndRecord } from \"./recorder.js\";\n\nexport interface ServerInstance {\n server: http.Server;\n journal: Journal;\n url: string;\n defaults: HandlerDefaults;\n videoStates: VideoStateMap;\n}\n\nconst COMPLETIONS_PATH = \"/v1/chat/completions\";\nconst RESPONSES_PATH = \"/v1/responses\";\nconst REALTIME_PATH = \"/v1/realtime\";\nconst GEMINI_LIVE_PATH =\n \"/ws/google.ai.generativelanguage.v1beta.GenerativeService.BidiGenerateContent\";\nconst MESSAGES_PATH = \"/v1/messages\";\nconst EMBEDDINGS_PATH = \"/v1/embeddings\";\nconst COHERE_CHAT_PATH = \"/v2/chat\";\nconst SEARCH_PATH = \"/search\";\nconst RERANK_PATH = \"/v2/rerank\";\nconst MODERATIONS_PATH = \"/v1/moderations\";\nconst IMAGES_PATH = \"/v1/images/generations\";\nconst SPEECH_PATH = \"/v1/audio/speech\";\nconst TRANSCRIPTIONS_PATH = \"/v1/audio/transcriptions\";\nconst VIDEOS_PATH = \"/v1/videos\";\nconst VIDEOS_STATUS_RE = /^\\/v1\\/videos\\/([^/]+)$/;\nconst GEMINI_PREDICT_RE = /^\\/v1beta\\/models\\/([^:]+):predict$/;\nconst DEFAULT_CHUNK_SIZE = 20;\n\n// OpenAI-compatible endpoint suffixes for path prefix normalization.\n// Providers like BigModel (/v4/) use non-standard base URL prefixes.\n// Only includes endpoints that third-party OpenAI-compatible providers are\n// likely to serve — excludes provider-specific paths (/messages, /realtime)\n// and endpoints unlikely to appear behind non-standard prefixes\n// (/moderations, /videos, /models).\nconst COMPAT_SUFFIXES = [\n \"/chat/completions\",\n \"/embeddings\",\n \"/responses\",\n \"/audio/speech\",\n \"/audio/transcriptions\",\n \"/images/generations\",\n];\n\n/**\n * Normalize OpenAI-compatible paths with arbitrary prefixes.\n * Strips /openai/ prefix and rewrites paths ending in known suffixes to /v1/<suffix>.\n * Skips /v1/ (already standard) and /v2/ (Cohere convention).\n */\nfunction normalizeCompatPath(pathname: string, logger?: Logger): string {\n // Strip /openai/ prefix (Groq/OpenAI-compat alias)\n if (pathname.startsWith(\"/openai/\")) {\n pathname = pathname.slice(7);\n }\n\n // Normalize arbitrary prefixes to /v1/\n if (!pathname.startsWith(\"/v1/\") && !pathname.startsWith(\"/v2/\")) {\n for (const suffix of COMPAT_SUFFIXES) {\n if (pathname.endsWith(suffix)) {\n if (logger) logger.debug(`Path normalized: ${pathname} → /v1${suffix}`);\n pathname = \"/v1\" + suffix;\n break;\n }\n }\n }\n\n return pathname;\n}\n\nconst GEMINI_PATH_RE = /^\\/v1beta\\/models\\/([^:]+):(generateContent|streamGenerateContent)$/;\nconst AZURE_DEPLOYMENT_RE = /^\\/openai\\/deployments\\/([^/]+)\\/(chat\\/completions|embeddings)$/;\nconst BEDROCK_INVOKE_RE = /^\\/model\\/([^/]+)\\/invoke$/;\nconst BEDROCK_STREAM_RE = /^\\/model\\/([^/]+)\\/invoke-with-response-stream$/;\nconst BEDROCK_CONVERSE_RE = /^\\/model\\/([^/]+)\\/converse$/;\nconst BEDROCK_CONVERSE_STREAM_RE = /^\\/model\\/([^/]+)\\/converse-stream$/;\nconst VERTEX_AI_RE =\n /^\\/v1\\/projects\\/[^/]+\\/locations\\/[^/]+\\/publishers\\/google\\/models\\/([^/:]+):(generateContent|streamGenerateContent)$/;\n\nconst OLLAMA_CHAT_PATH = \"/api/chat\";\nconst OLLAMA_GENERATE_PATH = \"/api/generate\";\nconst OLLAMA_TAGS_PATH = \"/api/tags\";\n\nconst HEALTH_PATH = \"/health\";\nconst READY_PATH = \"/ready\";\nconst MODELS_PATH = \"/v1/models\";\nconst REQUESTS_PATH = \"/v1/_requests\";\n\nconst DEFAULT_MODELS = [\n \"gpt-4\",\n \"gpt-4o\",\n \"claude-3-5-sonnet-20241022\",\n \"gemini-2.0-flash\",\n \"text-embedding-3-small\",\n];\n\nconst CORS_HEADERS: Record<string, string> = {\n \"Access-Control-Allow-Origin\": \"*\",\n \"Access-Control-Allow-Methods\": \"GET, POST, DELETE, OPTIONS\",\n \"Access-Control-Allow-Headers\": \"Content-Type, Authorization\",\n};\n\nfunction setCorsHeaders(res: http.ServerResponse): void {\n for (const [key, value] of Object.entries(CORS_HEADERS)) {\n res.setHeader(key, value);\n }\n}\n\nasync function readBody(req: http.IncomingMessage): Promise<string> {\n const buffers: Buffer[] = [];\n for await (const chunk of req) buffers.push(chunk as Buffer);\n return Buffer.concat(buffers).toString();\n}\n\nfunction handleOptions(res: http.ServerResponse): void {\n setCorsHeaders(res);\n res.writeHead(204);\n res.end();\n}\n\nfunction handleNotFound(res: http.ServerResponse, message: string): void {\n setCorsHeaders(res);\n writeErrorResponse(res, 404, JSON.stringify({ error: { message, type: \"not_found\" } }));\n}\n\n// ---------------------------------------------------------------------------\n// /__aimock/* control API — used by aimock-pytest and other test harnesses\n// to manage fixtures, journal, and error injection without restarting the\n// server.\n// ---------------------------------------------------------------------------\n\nconst CONTROL_PREFIX = \"/__aimock\";\n\n/**\n * Handle requests under `/__aimock/`. Returns `true` if the request was\n * handled, `false` if the path doesn't match the control prefix.\n */\nasync function handleControlAPI(\n req: http.IncomingMessage,\n res: http.ServerResponse,\n pathname: string,\n fixtures: Fixture[],\n journal: Journal,\n videoStates: VideoStateMap,\n): Promise<boolean> {\n if (!pathname.startsWith(CONTROL_PREFIX)) return false;\n\n const subPath = pathname.slice(CONTROL_PREFIX.length);\n setCorsHeaders(res);\n\n // GET /__aimock/health\n if (subPath === \"/health\" && req.method === \"GET\") {\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ status: \"ok\" }));\n return true;\n }\n\n // GET /__aimock/journal\n if (subPath === \"/journal\" && req.method === \"GET\") {\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify(journal.getAll()));\n return true;\n }\n\n // POST /__aimock/fixtures — add fixtures dynamically\n if (subPath === \"/fixtures\" && req.method === \"POST\") {\n let raw: string;\n try {\n raw = await readBody(req);\n } catch {\n res.writeHead(400, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"Failed to read request body\" }));\n return true;\n }\n\n let parsed: { fixtures?: FixtureFileEntry[] };\n try {\n parsed = JSON.parse(raw) as { fixtures?: FixtureFileEntry[] };\n } catch {\n res.writeHead(400, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"Invalid JSON\" }));\n return true;\n }\n\n if (!Array.isArray(parsed.fixtures)) {\n res.writeHead(400, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: 'Missing or invalid \"fixtures\" array' }));\n return true;\n }\n\n const converted = parsed.fixtures.map(entryToFixture);\n const issues = validateFixtures(converted);\n const errors = issues.filter((i) => i.severity === \"error\");\n if (errors.length > 0) {\n res.writeHead(400, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"Validation failed\", details: errors }));\n return true;\n }\n\n fixtures.push(...converted);\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ added: converted.length }));\n return true;\n }\n\n // DELETE /__aimock/fixtures — clear all fixtures\n if (subPath === \"/fixtures\" && req.method === \"DELETE\") {\n fixtures.length = 0;\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ cleared: true }));\n return true;\n }\n\n // POST /__aimock/reset — clear fixtures + journal + match counts\n if (subPath === \"/reset\" && req.method === \"POST\") {\n fixtures.length = 0;\n journal.clear();\n videoStates.clear();\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ reset: true }));\n return true;\n }\n\n // POST /__aimock/error — queue a one-shot error\n if (subPath === \"/error\" && req.method === \"POST\") {\n let raw: string;\n try {\n raw = await readBody(req);\n } catch {\n res.writeHead(400, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"Failed to read request body\" }));\n return true;\n }\n\n let parsed: { status?: number; body?: { message?: string; type?: string; code?: string } };\n try {\n parsed = JSON.parse(raw) as typeof parsed;\n } catch {\n res.writeHead(400, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"Invalid JSON\" }));\n return true;\n }\n\n const status = parsed.status ?? 500;\n const errorBody = parsed.body;\n const errorFixture: Fixture = {\n match: { predicate: () => true },\n response: {\n error: {\n message: errorBody?.message ?? \"Injected error\",\n type: errorBody?.type ?? \"server_error\",\n code: errorBody?.code,\n },\n status,\n },\n };\n // Insert at front so it matches before everything else\n fixtures.unshift(errorFixture);\n // Remove after first match\n const original = errorFixture.match.predicate!;\n errorFixture.match.predicate = (req) => {\n const result = original(req);\n if (result) {\n queueMicrotask(() => {\n const idx = fixtures.indexOf(errorFixture);\n if (idx !== -1) fixtures.splice(idx, 1);\n });\n }\n return result;\n };\n\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ queued: true }));\n return true;\n }\n\n // Unknown control path\n handleNotFound(res, `Unknown control endpoint: ${pathname}`);\n return true;\n}\n\nasync function handleCompletions(\n req: http.IncomingMessage,\n res: http.ServerResponse,\n fixtures: Fixture[],\n journal: Journal,\n defaults: HandlerDefaults,\n modelFallback?: string,\n providerKey?: RecordProviderKey,\n): Promise<void> {\n setCorsHeaders(res);\n\n // Read request body\n let raw: string;\n try {\n raw = await readBody(req);\n } catch (err) {\n const msg = err instanceof Error ? err.message : \"Failed to read request body\";\n journal.add({\n method: req.method ?? \"POST\",\n path: req.url ?? COMPLETIONS_PATH,\n headers: flattenHeaders(req.headers),\n body: null,\n response: { status: 500, fixture: null },\n });\n writeErrorResponse(\n res,\n 500,\n JSON.stringify({\n error: {\n message: `Request body read failed: ${msg}`,\n type: \"server_error\",\n },\n }),\n );\n return;\n }\n\n // Parse JSON body\n let body: ChatCompletionRequest;\n try {\n body = JSON.parse(raw) as ChatCompletionRequest;\n // Azure deployments may omit model from body — use deployment ID as fallback\n if (modelFallback && !body.model) {\n body.model = modelFallback;\n }\n } catch {\n journal.add({\n method: req.method ?? \"POST\",\n path: req.url ?? COMPLETIONS_PATH,\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 code: \"invalid_json\",\n },\n }),\n );\n return;\n }\n\n // Match fixture\n body._endpointType = \"chat\";\n const testId = getTestId(req);\n const fixture = matchFixture(\n fixtures,\n body,\n journal.getFixtureMatchCountsForTest(testId),\n defaults.requestTransform,\n );\n\n if (fixture) {\n journal.incrementFixtureMatchCount(fixture, fixtures, testId);\n }\n\n const method = req.method ?? \"POST\";\n const path = req.url ?? COMPLETIONS_PATH;\n const flatHeaders = flattenHeaders(req.headers);\n\n // Apply chaos before normal response handling\n if (\n applyChaos(\n res,\n fixture,\n defaults.chaos,\n req.headers,\n journal,\n {\n method,\n path,\n headers: flatHeaders,\n body,\n },\n defaults.registry,\n defaults.logger,\n )\n )\n return;\n\n if (!fixture) {\n // Try record-and-replay proxy if configured\n if (defaults.record && providerKey) {\n const proxied = await proxyAndRecord(\n req,\n res,\n body,\n providerKey,\n req.url ?? COMPLETIONS_PATH,\n fixtures,\n defaults,\n raw,\n );\n if (proxied) {\n journal.add({\n method: req.method ?? \"POST\",\n path: req.url ?? COMPLETIONS_PATH,\n headers: flattenHeaders(req.headers),\n body,\n response: { status: res.statusCode ?? 200, fixture: null },\n });\n return;\n }\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 defaults.logger.error(\n `STRICT: No fixture matched for ${req.method ?? \"POST\"} ${req.url ?? COMPLETIONS_PATH}`,\n );\n }\n\n journal.add({\n method: req.method ?? \"POST\",\n path: req.url ?? COMPLETIONS_PATH,\n headers: flattenHeaders(req.headers),\n body,\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 code: \"no_fixture_match\",\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: req.url ?? COMPLETIONS_PATH,\n headers: flattenHeaders(req.headers),\n body,\n response: { status, fixture },\n });\n writeErrorResponse(res, status, JSON.stringify(response));\n return;\n }\n\n // Content + tool calls response\n if (isContentWithToolCallsResponse(response)) {\n if (response.webSearches?.length) {\n defaults.logger.warn(\n \"webSearches in fixture response are not supported for Chat Completions API — ignoring\",\n );\n }\n const overrides = extractOverrides(response);\n const journalEntry = journal.add({\n method: req.method ?? \"POST\",\n path: req.url ?? COMPLETIONS_PATH,\n headers: flattenHeaders(req.headers),\n body,\n response: { status: 200, fixture },\n });\n if (body.stream !== true) {\n const completion = buildContentWithToolCallsCompletion(\n response.content,\n response.toolCalls,\n body.model,\n response.reasoning,\n overrides,\n );\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify(completion));\n } else {\n const chunks = buildContentWithToolCallsChunks(\n response.content,\n response.toolCalls,\n body.model,\n chunkSize,\n response.reasoning,\n overrides,\n );\n const interruption = createInterruptionSignal(fixture);\n const completed = await writeSSEStream(res, chunks, {\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 }\n return;\n }\n\n // Text response\n if (isTextResponse(response)) {\n if (response.webSearches?.length) {\n defaults.logger.warn(\n \"webSearches in fixture response are not supported for Chat Completions API — ignoring\",\n );\n }\n const overrides = extractOverrides(response);\n const journalEntry = journal.add({\n method: req.method ?? \"POST\",\n path: req.url ?? COMPLETIONS_PATH,\n headers: flattenHeaders(req.headers),\n body,\n response: { status: 200, fixture },\n });\n if (body.stream !== true) {\n const completion = buildTextCompletion(\n response.content,\n body.model,\n response.reasoning,\n overrides,\n );\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify(completion));\n } else {\n const chunks = buildTextChunks(\n response.content,\n body.model,\n chunkSize,\n response.reasoning,\n overrides,\n );\n const interruption = createInterruptionSignal(fixture);\n const completed = await writeSSEStream(res, chunks, {\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 }\n return;\n }\n\n // Tool call response\n if (isToolCallResponse(response)) {\n const overrides = extractOverrides(response);\n const journalEntry = journal.add({\n method: req.method ?? \"POST\",\n path: req.url ?? COMPLETIONS_PATH,\n headers: flattenHeaders(req.headers),\n body,\n response: { status: 200, fixture },\n });\n if (body.stream !== true) {\n const completion = buildToolCallCompletion(response.toolCalls, body.model, overrides);\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify(completion));\n } else {\n const chunks = buildToolCallChunks(response.toolCalls, body.model, chunkSize, overrides);\n const interruption = createInterruptionSignal(fixture);\n const completed = await writeSSEStream(res, chunks, {\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 }\n return;\n }\n\n // Fixture response matched no known type — guard against silent hang\n journal.add({\n method: req.method ?? \"POST\",\n path: req.url ?? COMPLETIONS_PATH,\n headers: flattenHeaders(req.headers),\n body,\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 interface ServiceFixtures {\n search: SearchFixture[];\n rerank: RerankFixture[];\n moderation: ModerationFixture[];\n}\n\n// NOTE: The fixtures array is read by reference on each request. Callers\n// (e.g. LLMock) may mutate it after the server starts and changes will\n// be visible immediately. This is intentional — do not copy the array.\nexport async function createServer(\n fixtures: Fixture[],\n options?: MockServerOptions,\n mounts?: Array<{ path: string; handler: Mountable }>,\n serviceFixtures?: ServiceFixtures,\n): Promise<ServerInstance> {\n const host = options?.host ?? \"127.0.0.1\";\n const port = options?.port ?? 0;\n const logger = new Logger(options?.logLevel ?? \"silent\");\n const registry = options?.metrics ? createMetricsRegistry() : undefined;\n const serverOptions = options ?? {};\n const defaults = {\n latency: serverOptions.latency ?? 0,\n chunkSize: Math.max(1, serverOptions.chunkSize ?? DEFAULT_CHUNK_SIZE),\n logger,\n get chaos() {\n return serverOptions.chaos;\n },\n registry,\n get record() {\n return serverOptions.record;\n },\n get strict() {\n return serverOptions.strict;\n },\n get requestTransform() {\n return serverOptions.requestTransform;\n },\n };\n\n // Validate chaos config rates\n if (options?.chaos) {\n const chaosRates = [\n { name: \"dropRate\", value: options.chaos.dropRate },\n { name: \"malformedRate\", value: options.chaos.malformedRate },\n { name: \"disconnectRate\", value: options.chaos.disconnectRate },\n ];\n for (const { name, value } of chaosRates) {\n if (value !== undefined && (value < 0 || value > 1)) {\n logger.warn(`Chaos ${name} (${value}) is outside 0-1 range — will be clamped at runtime`);\n }\n }\n }\n\n const journal = new Journal({ maxEntries: options?.journalMaxEntries });\n const videoStates: VideoStateMap = new Map();\n\n // Share journal and metrics registry with mounted services\n if (mounts) {\n for (const { handler } of mounts) {\n if (handler.setJournal) handler.setJournal(journal);\n if (registry && handler.setRegistry) handler.setRegistry(registry);\n }\n }\n\n // Set initial fixtures-loaded gauge\n if (registry) {\n registry.setGauge(\"aimock_fixtures_loaded\", {}, fixtures.length);\n }\n\n const server = http.createServer((req: http.IncomingMessage, res: http.ServerResponse) => {\n // Delegate to async handler — catch unhandled rejections to prevent Node.js crashes\n handleHttpRequest(req, res).catch((err: unknown) => {\n const msg = err instanceof Error ? err.message : \"Internal error\";\n defaults.logger.warn(`Unhandled request error: ${msg}`);\n if (!res.headersSent) {\n res.writeHead(500, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: { message: msg, type: \"server_error\" } }));\n }\n });\n });\n\n async function handleHttpRequest(\n req: http.IncomingMessage,\n res: http.ServerResponse,\n ): Promise<void> {\n // OPTIONS preflight\n if (req.method === \"OPTIONS\") {\n handleOptions(res);\n return;\n }\n\n // Record start time for metrics\n const startTime = registry ? process.hrtime.bigint() : 0n;\n\n // Parse the URL pathname (strip query string)\n const parsedUrl = new URL(req.url ?? \"/\", `http://${req.headers.host ?? \"localhost\"}`);\n let pathname = parsedUrl.pathname;\n\n // Instrument response completion for metrics. The finish callback reads\n // pathname via closure after normalizeCompatPath has rewritten it, so\n // metrics record the canonical /v1/... path.\n if (registry) {\n res.on(\"finish\", () => {\n try {\n const normalizedPath = normalizePathLabel(pathname);\n const method = req.method ?? \"UNKNOWN\";\n const status = String(res.statusCode);\n registry.incrementCounter(\"aimock_requests_total\", {\n method,\n path: normalizedPath,\n status,\n });\n const elapsed = Number(process.hrtime.bigint() - startTime) / 1e9;\n registry.observeHistogram(\n \"aimock_request_duration_seconds\",\n { method, path: normalizedPath },\n elapsed,\n );\n } catch (err) {\n defaults.logger.warn(\"metrics instrumentation error\", err);\n }\n });\n }\n\n // Control API — must be checked before mounts and path rewrites\n if (pathname.startsWith(CONTROL_PREFIX)) {\n await handleControlAPI(req, res, pathname, fixtures, journal, videoStates);\n return;\n }\n\n // Dispatch to mounted services before any path rewrites\n if (mounts) {\n for (const { path: mountPath, handler } of mounts) {\n if (pathname === mountPath || pathname.startsWith(mountPath + \"/\")) {\n const subPath = pathname.slice(mountPath.length) || \"/\";\n const handled = await handler.handleRequest(req, res, subPath);\n if (handled) return;\n }\n }\n }\n\n // Azure OpenAI: /openai/deployments/{id}/{operation} → /v1/{operation} (chat/completions, embeddings)\n // Must be checked BEFORE the generic /openai/ prefix strip\n let azureDeploymentId: string | undefined;\n const azureMatch = pathname.match(AZURE_DEPLOYMENT_RE);\n if (azureMatch && req.method === \"POST\") {\n azureDeploymentId = azureMatch[1];\n const operation = azureMatch[2];\n pathname = `/v1/${operation}`;\n }\n\n // Normalize OpenAI-compatible paths (strip /openai/ prefix + rewrite arbitrary prefixes)\n if (!azureDeploymentId) {\n pathname = normalizeCompatPath(pathname, logger);\n }\n\n // Health / readiness probes\n if (pathname === HEALTH_PATH && req.method === \"GET\") {\n setCorsHeaders(res);\n if (mounts && mounts.length > 0) {\n const services: Record<string, unknown> = {\n llm: { status: \"ok\", fixtures: fixtures.length },\n };\n for (const { path: mountPath, handler } of mounts) {\n if (handler.health) {\n const name = mountPath.replace(/^\\//, \"\");\n services[name] = handler.health();\n }\n }\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ status: \"ok\", services }));\n } else {\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ status: \"ok\" }));\n }\n return;\n }\n\n if (pathname === READY_PATH && req.method === \"GET\") {\n setCorsHeaders(res);\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ status: \"ready\" }));\n return;\n }\n\n // Prometheus metrics\n if (pathname === \"/metrics\" && req.method === \"GET\") {\n if (!registry) {\n handleNotFound(res, \"Not found\");\n return;\n }\n setCorsHeaders(res);\n res.writeHead(200, { \"Content-Type\": \"text/plain; version=0.0.4; charset=utf-8\" });\n res.end(registry.serialize());\n return;\n }\n\n // Models listing\n if (pathname === MODELS_PATH && req.method === \"GET\") {\n setCorsHeaders(res);\n const modelIds = new Set<string>();\n for (const f of fixtures) {\n if (f.match.model && typeof f.match.model === \"string\") {\n modelIds.add(f.match.model);\n }\n }\n const ids = modelIds.size > 0 ? [...modelIds] : DEFAULT_MODELS;\n const data = ids.map((id) => ({\n id,\n object: \"model\" as const,\n created: 1686935002,\n owned_by: \"aimock\",\n }));\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ object: \"list\", data }));\n return;\n }\n\n // Journal inspection endpoints\n if (pathname === REQUESTS_PATH) {\n setCorsHeaders(res);\n if (req.method === \"GET\") {\n const limitParam = parsedUrl.searchParams.get(\"limit\");\n let opts: { limit: number } | undefined;\n if (limitParam) {\n const limit = parseInt(limitParam, 10);\n if (Number.isNaN(limit) || limit <= 0) {\n writeErrorResponse(\n res,\n 400,\n JSON.stringify({\n error: {\n message: `Invalid limit parameter: \"${limitParam}\"`,\n type: \"invalid_request_error\",\n },\n }),\n );\n return;\n }\n opts = { limit };\n }\n const entries = journal.getAll(opts);\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify(entries));\n return;\n }\n if (req.method === \"DELETE\") {\n journal.clear();\n res.writeHead(204);\n res.end();\n return;\n }\n handleNotFound(res, \"Not found\");\n return;\n }\n\n // POST /v1/responses — OpenAI Responses API\n if (pathname === RESPONSES_PATH && req.method === \"POST\") {\n readBody(req)\n .then((raw) => handleResponses(req, res, raw, fixtures, journal, defaults, setCorsHeaders))\n .catch((err: unknown) => {\n const msg = err instanceof Error ? err.message : \"Internal error\";\n if (!res.headersSent) {\n writeErrorResponse(\n res,\n 500,\n JSON.stringify({ error: { message: msg, type: \"server_error\" } }),\n );\n } else if (!res.writableEnded) {\n try {\n res.write(`event: error\\ndata: ${JSON.stringify({ error: { message: msg } })}\\n\\n`);\n } catch (writeErr) {\n logger.debug(\"Failed to write error recovery response:\", writeErr);\n }\n res.end();\n }\n });\n return;\n }\n\n // POST /v1/messages — Anthropic Claude Messages API\n if (pathname === MESSAGES_PATH && req.method === \"POST\") {\n readBody(req)\n .then((raw) => handleMessages(req, res, raw, fixtures, journal, defaults, setCorsHeaders))\n .catch((err: unknown) => {\n const msg = err instanceof Error ? err.message : \"Internal error\";\n if (!res.headersSent) {\n writeErrorResponse(\n res,\n 500,\n JSON.stringify({ error: { message: msg, type: \"server_error\" } }),\n );\n } else if (!res.writableEnded) {\n try {\n res.write(`event: error\\ndata: ${JSON.stringify({ error: { message: msg } })}\\n\\n`);\n } catch (writeErr) {\n logger.debug(\"Failed to write error recovery response:\", writeErr);\n }\n res.end();\n }\n });\n return;\n }\n\n // POST /v2/chat — Cohere v2 Chat API\n if (pathname === COHERE_CHAT_PATH && req.method === \"POST\") {\n readBody(req)\n .then((raw) => handleCohere(req, res, raw, fixtures, journal, defaults, setCorsHeaders))\n .catch((err: unknown) => {\n const msg = err instanceof Error ? err.message : \"Internal error\";\n if (!res.headersSent) {\n writeErrorResponse(\n res,\n 500,\n JSON.stringify({ error: { message: msg, type: \"server_error\" } }),\n );\n } else if (!res.writableEnded) {\n try {\n res.write(`event: error\\ndata: ${JSON.stringify({ error: { message: msg } })}\\n\\n`);\n } catch (writeErr) {\n logger.debug(\"Failed to write error recovery response:\", writeErr);\n }\n res.end();\n }\n });\n return;\n }\n\n // POST /v1/embeddings — OpenAI Embeddings API\n if (pathname === EMBEDDINGS_PATH && req.method === \"POST\") {\n const deploymentId = azureDeploymentId;\n readBody(req)\n .then((raw) => {\n // Azure deployments may omit model from body — use deployment ID as fallback\n if (deploymentId) {\n try {\n const parsed = JSON.parse(raw) as Record<string, unknown>;\n if (!parsed.model) {\n parsed.model = deploymentId;\n return handleEmbeddings(\n req,\n res,\n JSON.stringify(parsed),\n fixtures,\n journal,\n defaults,\n setCorsHeaders,\n );\n }\n } catch {\n // Fall through — let handleEmbeddings report the parse error\n }\n }\n return handleEmbeddings(req, res, raw, fixtures, journal, defaults, setCorsHeaders);\n })\n .catch((err: unknown) => {\n const msg = err instanceof Error ? err.message : \"Internal error\";\n if (!res.headersSent) {\n writeErrorResponse(\n res,\n 500,\n JSON.stringify({ error: { message: msg, type: \"server_error\" } }),\n );\n } else if (!res.writableEnded) {\n res.destroy();\n }\n });\n return;\n }\n\n // POST /v1/images/generations — OpenAI Image Generation API\n if (pathname === IMAGES_PATH && req.method === \"POST\") {\n readBody(req)\n .then((raw) => handleImages(req, res, raw, fixtures, journal, defaults, setCorsHeaders))\n .catch((err: unknown) => {\n const msg = err instanceof Error ? err.message : \"Internal error\";\n if (!res.headersSent) {\n writeErrorResponse(\n res,\n 500,\n JSON.stringify({ error: { message: msg, type: \"server_error\" } }),\n );\n } else if (!res.writableEnded) {\n res.destroy();\n }\n });\n return;\n }\n\n // POST /v1/audio/speech — OpenAI TTS API\n if (pathname === SPEECH_PATH && req.method === \"POST\") {\n readBody(req)\n .then((raw) => handleSpeech(req, res, raw, fixtures, journal, defaults, setCorsHeaders))\n .catch((err: unknown) => {\n const msg = err instanceof Error ? err.message : \"Internal error\";\n if (!res.headersSent) {\n writeErrorResponse(\n res,\n 500,\n JSON.stringify({ error: { message: msg, type: \"server_error\" } }),\n );\n } else if (!res.writableEnded) {\n res.destroy();\n }\n });\n return;\n }\n\n // POST /v1/audio/transcriptions — OpenAI Transcription API\n if (pathname === TRANSCRIPTIONS_PATH && req.method === \"POST\") {\n readBody(req)\n .then((raw) =>\n handleTranscription(req, res, raw, fixtures, journal, defaults, setCorsHeaders),\n )\n .catch((err: unknown) => {\n const msg = err instanceof Error ? err.message : \"Internal error\";\n if (!res.headersSent) {\n writeErrorResponse(\n res,\n 500,\n JSON.stringify({ error: { message: msg, type: \"server_error\" } }),\n );\n } else if (!res.writableEnded) {\n res.destroy();\n }\n });\n return;\n }\n\n // POST /v1/videos — Video Generation API\n if (pathname === VIDEOS_PATH && req.method === \"POST\") {\n readBody(req)\n .then((raw) =>\n handleVideoCreate(\n req,\n res,\n raw,\n fixtures,\n journal,\n defaults,\n setCorsHeaders,\n videoStates,\n ),\n )\n .catch((err: unknown) => {\n const msg = err instanceof Error ? err.message : \"Internal error\";\n if (!res.headersSent) {\n writeErrorResponse(\n res,\n 500,\n JSON.stringify({ error: { message: msg, type: \"server_error\" } }),\n );\n } else if (!res.writableEnded) {\n res.destroy();\n }\n });\n return;\n }\n\n // GET /v1/videos/{id} — Video Status Check\n const videoStatusMatch = pathname.match(VIDEOS_STATUS_RE);\n if (videoStatusMatch && req.method === \"GET\") {\n const videoId = videoStatusMatch[1];\n handleVideoStatus(req, res, videoId, journal, setCorsHeaders, videoStates);\n return;\n }\n\n // POST /v1beta/models/{model}:predict — Gemini Imagen API\n const geminiPredictMatch = pathname.match(GEMINI_PREDICT_RE);\n if (geminiPredictMatch && req.method === \"POST\") {\n const predictModel = geminiPredictMatch[1];\n readBody(req)\n .then((raw) =>\n handleImages(\n req,\n res,\n raw,\n fixtures,\n journal,\n defaults,\n setCorsHeaders,\n \"gemini\",\n predictModel,\n ),\n )\n .catch((err: unknown) => {\n const msg = err instanceof Error ? err.message : \"Internal error\";\n if (!res.headersSent) {\n writeErrorResponse(\n res,\n 500,\n JSON.stringify({ error: { message: msg, type: \"server_error\" } }),\n );\n } else if (!res.writableEnded) {\n res.destroy();\n }\n });\n return;\n }\n\n // POST /v1beta/models/{model}:(generateContent|streamGenerateContent) — Google Gemini\n const geminiMatch = pathname.match(GEMINI_PATH_RE);\n if (geminiMatch && req.method === \"POST\") {\n const geminiModel = geminiMatch[1];\n const streaming = geminiMatch[2] === \"streamGenerateContent\";\n readBody(req)\n .then((raw) =>\n handleGemini(\n req,\n res,\n raw,\n geminiModel,\n streaming,\n fixtures,\n journal,\n defaults,\n setCorsHeaders,\n ),\n )\n .catch((err: unknown) => {\n const msg = err instanceof Error ? err.message : \"Internal error\";\n if (!res.headersSent) {\n writeErrorResponse(\n res,\n 500,\n JSON.stringify({ error: { message: msg, type: \"server_error\" } }),\n );\n } else if (!res.writableEnded) {\n try {\n res.write(`data: ${JSON.stringify({ error: { message: msg } })}\\n\\n`);\n } catch (writeErr) {\n logger.debug(\"Failed to write error recovery response:\", writeErr);\n }\n res.end();\n }\n });\n return;\n }\n\n // POST /v1/projects/{project}/locations/{location}/publishers/google/models/{model}:(generateContent|streamGenerateContent) — Vertex AI\n const vertexMatch = pathname.match(VERTEX_AI_RE);\n if (vertexMatch && req.method === \"POST\") {\n const vertexModel = vertexMatch[1];\n const streaming = vertexMatch[2] === \"streamGenerateContent\";\n readBody(req)\n .then((raw) =>\n handleGemini(\n req,\n res,\n raw,\n vertexModel,\n streaming,\n fixtures,\n journal,\n defaults,\n setCorsHeaders,\n \"vertexai\",\n ),\n )\n .catch((err: unknown) => {\n const msg = err instanceof Error ? err.message : \"Internal error\";\n if (!res.headersSent) {\n writeErrorResponse(\n res,\n 500,\n JSON.stringify({ error: { message: msg, type: \"server_error\" } }),\n );\n } else if (!res.writableEnded) {\n try {\n res.write(`data: ${JSON.stringify({ error: { message: msg } })}\\n\\n`);\n } catch (writeErr) {\n logger.debug(\"Failed to write error recovery response:\", writeErr);\n }\n res.end();\n }\n });\n return;\n }\n\n // POST /model/{modelId}/invoke — AWS Bedrock Claude API\n const bedrockMatch = pathname.match(BEDROCK_INVOKE_RE);\n if (bedrockMatch && req.method === \"POST\") {\n const bedrockModelId = bedrockMatch[1];\n readBody(req)\n .then((raw) =>\n handleBedrock(req, res, raw, bedrockModelId, fixtures, journal, defaults, setCorsHeaders),\n )\n .catch((err: unknown) => {\n const msg = err instanceof Error ? err.message : \"Internal error\";\n if (!res.headersSent) {\n writeErrorResponse(\n res,\n 500,\n JSON.stringify({ error: { message: msg, type: \"server_error\" } }),\n );\n } else if (!res.writableEnded) {\n res.destroy();\n }\n });\n return;\n }\n\n // POST /model/{modelId}/invoke-with-response-stream — AWS Bedrock Claude streaming\n const bedrockStreamMatch = pathname.match(BEDROCK_STREAM_RE);\n if (bedrockStreamMatch && req.method === \"POST\") {\n const bedrockModelId = bedrockStreamMatch[1];\n readBody(req)\n .then((raw) =>\n handleBedrockStream(\n req,\n res,\n raw,\n bedrockModelId,\n fixtures,\n journal,\n defaults,\n setCorsHeaders,\n ),\n )\n .catch((err: unknown) => {\n const msg = err instanceof Error ? err.message : \"Internal error\";\n if (!res.headersSent) {\n writeErrorResponse(\n res,\n 500,\n JSON.stringify({ error: { message: msg, type: \"server_error\" } }),\n );\n } else if (!res.writableEnded) {\n res.destroy();\n }\n });\n return;\n }\n\n // POST /model/{modelId}/converse — AWS Bedrock Converse API\n const converseMatch = pathname.match(BEDROCK_CONVERSE_RE);\n if (converseMatch && req.method === \"POST\") {\n const converseModelId = converseMatch[1];\n readBody(req)\n .then((raw) =>\n handleConverse(\n req,\n res,\n raw,\n converseModelId,\n fixtures,\n journal,\n defaults,\n setCorsHeaders,\n ),\n )\n .catch((err: unknown) => {\n const msg = err instanceof Error ? err.message : \"Internal error\";\n if (!res.headersSent) {\n writeErrorResponse(\n res,\n 500,\n JSON.stringify({ error: { message: msg, type: \"server_error\" } }),\n );\n } else if (!res.writableEnded) {\n res.destroy();\n }\n });\n return;\n }\n\n // POST /model/{modelId}/converse-stream — AWS Bedrock Converse streaming API\n const converseStreamMatch = pathname.match(BEDROCK_CONVERSE_STREAM_RE);\n if (converseStreamMatch && req.method === \"POST\") {\n const converseStreamModelId = converseStreamMatch[1];\n readBody(req)\n .then((raw) =>\n handleConverseStream(\n req,\n res,\n raw,\n converseStreamModelId,\n fixtures,\n journal,\n defaults,\n setCorsHeaders,\n ),\n )\n .catch((err: unknown) => {\n const msg = err instanceof Error ? err.message : \"Internal error\";\n if (!res.headersSent) {\n writeErrorResponse(\n res,\n 500,\n JSON.stringify({ error: { message: msg, type: \"server_error\" } }),\n );\n } else if (!res.writableEnded) {\n res.destroy();\n }\n });\n return;\n }\n\n // POST /api/chat — Ollama Chat API\n if (pathname === OLLAMA_CHAT_PATH && req.method === \"POST\") {\n readBody(req)\n .then((raw) => handleOllama(req, res, raw, fixtures, journal, defaults, setCorsHeaders))\n .catch((err: unknown) => {\n const msg = err instanceof Error ? err.message : \"Internal error\";\n if (!res.headersSent) {\n writeErrorResponse(\n res,\n 500,\n JSON.stringify({ error: { message: msg, type: \"server_error\" } }),\n );\n } else if (!res.writableEnded) {\n res.destroy();\n }\n });\n return;\n }\n\n // POST /api/generate — Ollama Generate API\n if (pathname === OLLAMA_GENERATE_PATH && req.method === \"POST\") {\n readBody(req)\n .then((raw) =>\n handleOllamaGenerate(req, res, raw, fixtures, journal, defaults, setCorsHeaders),\n )\n .catch((err: unknown) => {\n const msg = err instanceof Error ? err.message : \"Internal error\";\n if (!res.headersSent) {\n writeErrorResponse(\n res,\n 500,\n JSON.stringify({ error: { message: msg, type: \"server_error\" } }),\n );\n } else if (!res.writableEnded) {\n res.destroy();\n }\n });\n return;\n }\n\n // GET /api/tags — Ollama Models listing\n if (pathname === OLLAMA_TAGS_PATH && req.method === \"GET\") {\n setCorsHeaders(res);\n const modelIds = new Set<string>();\n for (const f of fixtures) {\n if (f.match.model && typeof f.match.model === \"string\") {\n modelIds.add(f.match.model);\n }\n }\n const ids = modelIds.size > 0 ? [...modelIds] : DEFAULT_MODELS;\n const models = ids.map((name) => ({\n name,\n model: name,\n modified_at: new Date().toISOString(),\n size: 0,\n digest: \"\",\n details: {},\n }));\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ models }));\n return;\n }\n\n // POST /search — Web Search API (Tavily-compatible)\n if (pathname === SEARCH_PATH && req.method === \"POST\") {\n readBody(req)\n .then((raw) =>\n handleSearch(\n req,\n res,\n raw,\n serviceFixtures?.search ?? [],\n journal,\n defaults,\n setCorsHeaders,\n ),\n )\n .catch((err: unknown) => {\n const msg = err instanceof Error ? err.message : \"Internal error\";\n if (!res.headersSent) {\n writeErrorResponse(\n res,\n 500,\n JSON.stringify({ error: { message: msg, type: \"server_error\" } }),\n );\n } else if (!res.writableEnded) {\n res.destroy();\n }\n });\n return;\n }\n\n // POST /v2/rerank — Reranking API (Cohere rerank-compatible)\n if (pathname === RERANK_PATH && req.method === \"POST\") {\n readBody(req)\n .then((raw) =>\n handleRerank(\n req,\n res,\n raw,\n serviceFixtures?.rerank ?? [],\n journal,\n defaults,\n setCorsHeaders,\n ),\n )\n .catch((err: unknown) => {\n const msg = err instanceof Error ? err.message : \"Internal error\";\n if (!res.headersSent) {\n writeErrorResponse(\n res,\n 500,\n JSON.stringify({ error: { message: msg, type: \"server_error\" } }),\n );\n } else if (!res.writableEnded) {\n res.destroy();\n }\n });\n return;\n }\n\n // POST /v1/moderations — Moderation API (OpenAI-compatible)\n if (pathname === MODERATIONS_PATH && req.method === \"POST\") {\n readBody(req)\n .then((raw) =>\n handleModeration(\n req,\n res,\n raw,\n serviceFixtures?.moderation ?? [],\n journal,\n defaults,\n setCorsHeaders,\n ),\n )\n .catch((err: unknown) => {\n const msg = err instanceof Error ? err.message : \"Internal error\";\n if (!res.headersSent) {\n writeErrorResponse(\n res,\n 500,\n JSON.stringify({ error: { message: msg, type: \"server_error\" } }),\n );\n } else if (!res.writableEnded) {\n res.destroy();\n }\n });\n return;\n }\n\n // POST /v1/chat/completions — Chat Completions API\n if (pathname !== COMPLETIONS_PATH) {\n handleNotFound(res, \"Not found\");\n return;\n }\n if (req.method !== \"POST\") {\n handleNotFound(res, \"Not found\");\n return;\n }\n\n const completionsProvider: RecordProviderKey = azureDeploymentId ? \"azure\" : \"openai\";\n handleCompletions(\n req,\n res,\n fixtures,\n journal,\n defaults,\n azureDeploymentId,\n completionsProvider,\n ).catch((err: unknown) => {\n const msg = err instanceof Error ? err.message : \"Internal error\";\n if (!res.headersSent) {\n writeErrorResponse(\n res,\n 500,\n JSON.stringify({\n error: {\n message: msg,\n type: \"server_error\",\n },\n }),\n );\n } else if (!res.writableEnded) {\n // Headers already sent (SSE stream in progress) — write error event then close\n try {\n res.write(\n `data: ${JSON.stringify({ error: { message: msg, type: \"server_error\" } })}\\n\\n`,\n );\n } catch (writeErr) {\n logger.debug(\"Failed to write error recovery response:\", writeErr);\n }\n res.end();\n }\n });\n }\n\n // ─── WebSocket upgrade handling ──────────────────────────────────────────\n\n const activeConnections = new Set<WebSocketConnection>();\n\n server.on(\n \"upgrade\",\n (req: http.IncomingMessage, socket: import(\"node:net\").Socket, head: Buffer) => {\n handleUpgradeRequest(req, socket, head).catch((err: unknown) => {\n const msg = err instanceof Error ? err.message : \"Internal error\";\n defaults.logger.warn(`Unhandled upgrade error: ${msg}`);\n if (!socket.destroyed) socket.destroy();\n });\n },\n );\n\n async function handleUpgradeRequest(\n req: http.IncomingMessage,\n socket: import(\"node:net\").Socket,\n head: Buffer,\n ): Promise<void> {\n const parsedUrl = new URL(req.url ?? \"/\", `http://${req.headers.host ?? \"localhost\"}`);\n let pathname = parsedUrl.pathname;\n\n // Dispatch to mounted services before any path rewrites\n if (mounts) {\n for (const { path: mountPath, handler } of mounts) {\n if (\n (pathname === mountPath || pathname.startsWith(mountPath + \"/\")) &&\n handler.handleUpgrade\n ) {\n const subPath = pathname.slice(mountPath.length) || \"/\";\n if (await handler.handleUpgrade(socket, head, subPath)) return;\n }\n }\n }\n\n // Normalize OpenAI-compatible paths (strip /openai/ prefix + rewrite arbitrary prefixes)\n // Skip Azure deployment paths — they have their own rewrite in the HTTP handler\n if (!pathname.match(AZURE_DEPLOYMENT_RE)) {\n pathname = normalizeCompatPath(pathname, logger);\n }\n\n if (\n pathname !== RESPONSES_PATH &&\n pathname !== REALTIME_PATH &&\n pathname !== GEMINI_LIVE_PATH\n ) {\n socket.write(\"HTTP/1.1 404 Not Found\\r\\n\\r\\n\");\n socket.destroy();\n return;\n }\n\n // Push any buffered data back before upgrading\n if (head.length > 0) {\n socket.unshift(head);\n }\n\n let ws: WebSocketConnection;\n try {\n ws = upgradeToWebSocket(req, socket);\n } catch (err: unknown) {\n const msg = err instanceof Error ? err.message : \"WebSocket upgrade failed\";\n logger.error(`WebSocket upgrade error: ${msg}`);\n if (!socket.destroyed) socket.destroy();\n return;\n }\n\n activeConnections.add(ws);\n\n ws.on(\"error\", (err: Error) => {\n logger.error(`WebSocket error: ${err.message}`);\n activeConnections.delete(ws);\n });\n\n ws.on(\"close\", () => {\n activeConnections.delete(ws);\n });\n\n // Route to handler\n const wsTestId = getTestId(req);\n if (pathname === RESPONSES_PATH) {\n handleWebSocketResponses(ws, fixtures, journal, {\n ...defaults,\n model: \"gpt-4\",\n testId: wsTestId,\n });\n } else if (pathname === REALTIME_PATH) {\n const model = parsedUrl.searchParams.get(\"model\") ?? \"gpt-4o-realtime\";\n handleWebSocketRealtime(ws, fixtures, journal, {\n ...defaults,\n model,\n testId: wsTestId,\n });\n } else if (pathname === GEMINI_LIVE_PATH) {\n handleWebSocketGeminiLive(ws, fixtures, journal, {\n ...defaults,\n model: \"gemini-2.0-flash\",\n testId: wsTestId,\n });\n }\n }\n\n // Close active WS connections when server shuts down\n const originalClose = server.close.bind(server);\n server.close = function (this: http.Server, callback?: (err?: Error) => void) {\n for (const ws of activeConnections) {\n ws.close(1001, \"Server shutting down\");\n }\n activeConnections.clear();\n originalClose(callback);\n return this;\n } as typeof server.close;\n\n return new Promise<ServerInstance>((resolve, reject) => {\n server.on(\"error\", reject);\n server.listen(port, host, () => {\n const addr = server.address();\n if (!addr || typeof addr === \"string\") {\n reject(new Error(\"Unexpected address format\"));\n return;\n }\n const url = `http://${addr.address}:${addr.port}`;\n\n // Set base URL on mounted services that support it\n if (mounts) {\n for (const { path: mountPath, handler } of mounts) {\n if (handler.setBaseUrl) handler.setBaseUrl(url + mountPath);\n }\n }\n\n resolve({ server, journal, url, defaults, videoStates });\n });\n });\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"stream-collapse.cjs","names":[],"sources":["../src/stream-collapse.ts"],"sourcesContent":["/**\n * Stream collapsing functions for record-and-replay.\n *\n * Each function takes a raw streaming response body (SSE, NDJSON, or binary\n * EventStream) and collapses it into a non-streaming fixture response\n * containing `{ content }`, `{ toolCalls }`, or both when the stream includes\n * text followed by tool calls.\n */\n\nimport { crc32 } from \"node:zlib\";\nimport type { RecordProviderKey, ToolCall } from \"./types.js\";\nimport type { Logger } from \"./logger.js\";\n\n// ---------------------------------------------------------------------------\n// Result type shared by all collapse functions\n// ---------------------------------------------------------------------------\n\n// TODO: Consider making this a discriminated union ({ type: \"text\"; content: string }\n// | { type: \"toolCalls\"; toolCalls: ToolCall[] } | { type: \"empty\" }) to prevent\n// ambiguous results and simplify downstream consumers.\nexport interface CollapseResult {\n content?: string;\n reasoning?: string;\n webSearches?: string[];\n toolCalls?: ToolCall[];\n droppedChunks?: number;\n truncated?: boolean;\n}\n\n// ---------------------------------------------------------------------------\n// 1. OpenAI SSE\n// ---------------------------------------------------------------------------\n\n/**\n * Collapse OpenAI Chat Completions SSE stream into a single response.\n *\n * Format:\n * data: {\"id\":\"chatcmpl-123\",\"choices\":[{\"delta\":{\"content\":\"Hello\"}}]}\\n\\n\n * data: [DONE]\\n\\n\n */\nexport function collapseOpenAISSE(body: string): CollapseResult {\n const lines = body.split(\"\\n\\n\").filter((l) => l.trim().length > 0);\n let content = \"\";\n let reasoning = \"\";\n const webSearchQueries: string[] = [];\n let droppedChunks = 0;\n const toolCallMap = new Map<number, { id: string; name: string; arguments: string }>();\n\n for (const line of lines) {\n const dataLine = line.split(\"\\n\").find((l) => l.startsWith(\"data:\"));\n if (!dataLine) continue;\n\n const payload = dataLine.slice(5).trim();\n if (payload === \"[DONE]\") continue;\n\n let parsed: Record<string, unknown>;\n try {\n parsed = JSON.parse(payload) as Record<string, unknown>;\n } catch {\n droppedChunks++;\n continue;\n }\n\n // Responses API reasoning events\n if (\n parsed.type === \"response.reasoning_summary_text.delta\" &&\n typeof parsed.delta === \"string\"\n ) {\n reasoning += parsed.delta;\n continue;\n }\n\n // Responses API web search events\n if (parsed.type === \"response.output_item.done\") {\n const item = parsed.item as Record<string, unknown> | undefined;\n if (item?.type === \"web_search_call\") {\n const action = item.action as Record<string, unknown> | undefined;\n if (action && typeof action.query === \"string\") {\n webSearchQueries.push(action.query);\n continue;\n }\n }\n }\n\n // Responses API text content events\n if (parsed.type === \"response.output_text.delta\" && typeof parsed.delta === \"string\") {\n content += parsed.delta;\n continue;\n }\n\n // Skip other Responses API structural events\n if (typeof parsed.type === \"string\" && parsed.type.startsWith(\"response.\")) {\n continue;\n }\n\n const choices = parsed.choices as Array<Record<string, unknown>> | undefined;\n if (!choices || choices.length === 0) continue;\n\n const delta = choices[0].delta as Record<string, unknown> | undefined;\n if (!delta) continue;\n\n // Reasoning content (OpenRouter / chat completions format)\n if (typeof delta.reasoning_content === \"string\") {\n reasoning += delta.reasoning_content;\n }\n\n // Text content\n if (typeof delta.content === \"string\") {\n content += delta.content;\n }\n\n // Tool calls\n const toolCalls = delta.tool_calls as Array<Record<string, unknown>> | undefined;\n if (toolCalls) {\n for (const tc of toolCalls) {\n const index = tc.index as number;\n const fn = tc.function as Record<string, unknown> | undefined;\n\n if (!toolCallMap.has(index)) {\n toolCallMap.set(index, {\n id: (tc.id as string) ?? \"\",\n name: (fn?.name as string) ?? \"\",\n arguments: \"\",\n });\n }\n\n const entry = toolCallMap.get(index)!;\n if (fn?.name && typeof fn.name === \"string\" && !entry.name) {\n entry.name = fn.name;\n }\n if (tc.id && typeof tc.id === \"string\" && !entry.id) {\n entry.id = tc.id;\n }\n if (fn?.arguments && typeof fn.arguments === \"string\") {\n entry.arguments += fn.arguments;\n }\n }\n }\n }\n\n if (toolCallMap.size > 0) {\n const sorted = Array.from(toolCallMap.entries()).sort(([a], [b]) => a - b);\n return {\n ...(content ? { content } : {}),\n toolCalls: sorted.map(([, tc]) => ({\n name: tc.name,\n arguments: tc.arguments,\n ...(tc.id ? { id: tc.id } : {}),\n })),\n ...(droppedChunks > 0 ? { droppedChunks } : {}),\n };\n }\n\n return {\n content,\n ...(reasoning ? { reasoning } : {}),\n ...(webSearchQueries.length > 0 ? { webSearches: webSearchQueries } : {}),\n ...(droppedChunks > 0 ? { droppedChunks } : {}),\n };\n}\n\n// ---------------------------------------------------------------------------\n// 2. Anthropic SSE\n// ---------------------------------------------------------------------------\n\n/**\n * Collapse Anthropic Claude Messages SSE stream into a single response.\n *\n * Format:\n * event: message_start\\ndata: {...}\\n\\n\n * event: content_block_delta\\ndata: {\"delta\":{\"type\":\"text_delta\",\"text\":\"Hello\"}}\\n\\n\n */\nexport function collapseAnthropicSSE(body: string): CollapseResult {\n const blocks = body.split(\"\\n\\n\").filter((b) => b.trim().length > 0);\n let content = \"\";\n let reasoning = \"\";\n let droppedChunks = 0;\n const toolCallMap = new Map<number, { id: string; name: string; arguments: string }>();\n\n for (const block of blocks) {\n const lines = block.split(\"\\n\");\n const eventLine = lines.find((l) => l.startsWith(\"event:\"));\n const dataLine = lines.find((l) => l.startsWith(\"data:\"));\n if (!dataLine) continue;\n\n const eventType = eventLine ? eventLine.slice(6).trim() : \"\";\n const payload = dataLine.slice(5).trim();\n\n let parsed: Record<string, unknown>;\n try {\n parsed = JSON.parse(payload) as Record<string, unknown>;\n } catch {\n droppedChunks++;\n continue;\n }\n\n if (eventType === \"content_block_start\") {\n const index = parsed.index as number;\n const contentBlock = parsed.content_block as Record<string, unknown> | undefined;\n if (contentBlock?.type === \"tool_use\") {\n toolCallMap.set(index, {\n id: (contentBlock.id as string) ?? \"\",\n name: (contentBlock.name as string) ?? \"\",\n arguments: \"\",\n });\n }\n }\n\n if (eventType === \"content_block_delta\") {\n const index = parsed.index as number;\n const delta = parsed.delta as Record<string, unknown> | undefined;\n if (!delta) continue;\n\n if (delta.type === \"text_delta\" && typeof delta.text === \"string\") {\n content += delta.text;\n }\n\n if (delta.type === \"thinking_delta\" && typeof delta.thinking === \"string\") {\n reasoning += delta.thinking;\n }\n\n if (delta.type === \"input_json_delta\" && typeof delta.partial_json === \"string\") {\n const entry = toolCallMap.get(index);\n if (entry) {\n entry.arguments += delta.partial_json;\n }\n }\n }\n }\n\n if (toolCallMap.size > 0) {\n const sorted = Array.from(toolCallMap.entries()).sort(([a], [b]) => a - b);\n return {\n ...(content ? { content } : {}),\n toolCalls: sorted.map(([, tc]) => ({\n name: tc.name,\n arguments: tc.arguments,\n ...(tc.id ? { id: tc.id } : {}),\n })),\n ...(reasoning ? { reasoning } : {}),\n ...(droppedChunks > 0 ? { droppedChunks } : {}),\n };\n }\n\n return {\n content,\n ...(reasoning ? { reasoning } : {}),\n ...(droppedChunks > 0 ? { droppedChunks } : {}),\n };\n}\n\n// ---------------------------------------------------------------------------\n// 3. Gemini SSE\n// ---------------------------------------------------------------------------\n\n/**\n * Collapse Gemini SSE stream into a single response.\n *\n * Format (data-only, no event prefix, no [DONE]):\n * data: {\"candidates\":[{\"content\":{\"parts\":[{\"text\":\"Hello\"}]}}]}\\n\\n\n */\nexport function collapseGeminiSSE(body: string): CollapseResult {\n const lines = body.split(\"\\n\\n\").filter((l) => l.trim().length > 0);\n let content = \"\";\n let droppedChunks = 0;\n const toolCalls: ToolCall[] = [];\n\n for (const line of lines) {\n const dataLine = line.split(\"\\n\").find((l) => l.startsWith(\"data:\"));\n if (!dataLine) continue;\n\n const payload = dataLine.slice(5).trim();\n\n let parsed: Record<string, unknown>;\n try {\n parsed = JSON.parse(payload) as Record<string, unknown>;\n } catch {\n droppedChunks++;\n continue;\n }\n\n const candidates = parsed.candidates as Array<Record<string, unknown>> | undefined;\n if (!candidates || candidates.length === 0) continue;\n\n const candidateContent = candidates[0].content as Record<string, unknown> | undefined;\n if (!candidateContent) continue;\n\n const parts = candidateContent.parts as Array<Record<string, unknown>> | undefined;\n if (!parts || parts.length === 0) continue;\n\n for (const part of parts) {\n if (part.functionCall) {\n const fc = part.functionCall as Record<string, unknown>;\n toolCalls.push({\n name: String(fc.name ?? \"\"),\n arguments: typeof fc.args === \"string\" ? (fc.args as string) : JSON.stringify(fc.args),\n });\n } else if (typeof part.text === \"string\") {\n content += part.text;\n }\n }\n }\n\n if (toolCalls.length > 0) {\n return {\n ...(content ? { content } : {}),\n toolCalls,\n ...(droppedChunks > 0 ? { droppedChunks } : {}),\n };\n }\n\n return { content, ...(droppedChunks > 0 ? { droppedChunks } : {}) };\n}\n\n// ---------------------------------------------------------------------------\n// 4. Ollama NDJSON\n// ---------------------------------------------------------------------------\n\n/**\n * Collapse Ollama NDJSON stream into a single response.\n *\n * /api/chat format:\n * {\"model\":\"llama3\",\"message\":{\"role\":\"assistant\",\"content\":\"Hello\"},\"done\":false}\\n\n *\n * /api/generate format:\n * {\"model\":\"llama3\",\"response\":\"Hello\",\"done\":false}\\n\n */\nexport function collapseOllamaNDJSON(body: string): CollapseResult {\n const lines = body.split(\"\\n\").filter((l) => l.trim().length > 0);\n let content = \"\";\n let droppedChunks = 0;\n const toolCalls: ToolCall[] = [];\n\n for (const line of lines) {\n let parsed: Record<string, unknown>;\n try {\n parsed = JSON.parse(line.trim()) as Record<string, unknown>;\n } catch {\n droppedChunks++;\n continue;\n }\n\n // /api/chat format\n const message = parsed.message as Record<string, unknown> | undefined;\n if (message) {\n if (typeof message.content === \"string\") {\n content += message.content;\n }\n\n // Tool calls\n if (Array.isArray(message.tool_calls)) {\n for (const tc of message.tool_calls as Array<Record<string, unknown>>) {\n const fn = tc.function as Record<string, unknown> | undefined;\n if (fn) {\n toolCalls.push({\n name: String(fn.name ?? \"\"),\n arguments:\n typeof fn.arguments === \"string\" ? fn.arguments : JSON.stringify(fn.arguments),\n });\n }\n }\n }\n }\n\n // /api/generate format\n else if (typeof parsed.response === \"string\") {\n content += parsed.response;\n }\n }\n\n if (toolCalls.length > 0) {\n return {\n ...(content ? { content } : {}),\n toolCalls,\n ...(droppedChunks > 0 ? { droppedChunks } : {}),\n };\n }\n\n return { content, ...(droppedChunks > 0 ? { droppedChunks } : {}) };\n}\n\n// ---------------------------------------------------------------------------\n// 5. Cohere SSE\n// ---------------------------------------------------------------------------\n\n/**\n * Collapse Cohere SSE stream into a single response.\n *\n * Format:\n * event: content-delta\\ndata: {\"type\":\"content-delta\",\"delta\":{\"message\":{\"content\":{\"text\":\"Hello\"}}}}\\n\\n\n */\nexport function collapseCohereSSE(body: string): CollapseResult {\n const blocks = body.split(\"\\n\\n\").filter((b) => b.trim().length > 0);\n let content = \"\";\n let droppedChunks = 0;\n const toolCallMap = new Map<number, { id: string; name: string; arguments: string }>();\n\n for (const block of blocks) {\n const lines = block.split(\"\\n\");\n const eventLine = lines.find((l) => l.startsWith(\"event:\"));\n const dataLine = lines.find((l) => l.startsWith(\"data:\"));\n if (!dataLine) continue;\n\n const eventType = eventLine ? eventLine.slice(6).trim() : \"\";\n const payload = dataLine.slice(5).trim();\n\n let parsed: Record<string, unknown>;\n try {\n parsed = JSON.parse(payload) as Record<string, unknown>;\n } catch {\n droppedChunks++;\n continue;\n }\n\n if (eventType === \"content-delta\") {\n const delta = parsed.delta as Record<string, unknown> | undefined;\n const message = delta?.message as Record<string, unknown> | undefined;\n const contentObj = message?.content as Record<string, unknown> | undefined;\n if (contentObj && typeof contentObj.text === \"string\") {\n content += contentObj.text;\n }\n }\n\n if (eventType === \"tool-call-start\") {\n const index = parsed.index as number;\n const delta = parsed.delta as Record<string, unknown> | undefined;\n const message = delta?.message as Record<string, unknown> | undefined;\n const toolCalls = message?.tool_calls as Record<string, unknown> | undefined;\n if (toolCalls) {\n const fn = toolCalls.function as Record<string, unknown> | undefined;\n toolCallMap.set(index, {\n id: (toolCalls.id as string) ?? \"\",\n name: (fn?.name as string) ?? \"\",\n arguments: \"\",\n });\n }\n }\n\n if (eventType === \"tool-call-delta\") {\n const index = parsed.index as number;\n const delta = parsed.delta as Record<string, unknown> | undefined;\n const message = delta?.message as Record<string, unknown> | undefined;\n const toolCalls = message?.tool_calls as Record<string, unknown> | undefined;\n if (toolCalls) {\n const fn = toolCalls.function as Record<string, unknown> | undefined;\n if (fn && typeof fn.arguments === \"string\") {\n const entry = toolCallMap.get(index);\n if (entry) {\n entry.arguments += fn.arguments;\n }\n }\n }\n }\n }\n\n if (toolCallMap.size > 0) {\n const sorted = Array.from(toolCallMap.entries()).sort(([a], [b]) => a - b);\n return {\n toolCalls: sorted.map(([, tc]) => ({\n name: tc.name,\n arguments: tc.arguments,\n ...(tc.id ? { id: tc.id } : {}),\n })),\n ...(droppedChunks > 0 ? { droppedChunks } : {}),\n };\n }\n\n return { content, ...(droppedChunks > 0 ? { droppedChunks } : {}) };\n}\n\n// ---------------------------------------------------------------------------\n// 6. Bedrock EventStream (binary)\n// ---------------------------------------------------------------------------\n\n/**\n * Decode AWS Event Stream binary frames and extract JSON payloads.\n *\n * Binary frame layout:\n * [total_length: 4B uint32-BE]\n * [headers_length: 4B uint32-BE]\n * [prelude_crc32: 4B]\n * [headers: variable]\n * [payload: variable]\n * [message_crc32: 4B]\n */\nfunction decodeEventStreamFrames(buf: Buffer): {\n frames: Array<{ headers: Record<string, string>; payload: Buffer }>;\n truncated: boolean;\n} {\n const frames: Array<{ headers: Record<string, string>; payload: Buffer }> = [];\n let offset = 0;\n\n while (offset < buf.length) {\n if (offset + 12 > buf.length) break;\n\n const totalLength = buf.readUInt32BE(offset);\n const headersLength = buf.readUInt32BE(offset + 4);\n\n // Validate bounds: ensure the full frame is within the buffer\n if (totalLength < 12 || offset + totalLength > buf.length) {\n return { frames, truncated: true };\n }\n\n // Validate prelude CRC\n const preludeCrc = buf.readUInt32BE(offset + 8);\n const computedPreludeCrc = crc32(buf.subarray(offset, offset + 8));\n if (preludeCrc >>> 0 !== computedPreludeCrc >>> 0) {\n return { frames, truncated: true }; // Prelude CRC mismatch — stop parsing\n }\n\n // Parse headers\n const headersStart = offset + 12;\n const headersEnd = headersStart + headersLength;\n const headers: Record<string, string> = {};\n let hOffset = headersStart;\n\n while (hOffset < headersEnd) {\n const nameLen = buf.readUInt8(hOffset);\n hOffset += 1;\n const name = buf.subarray(hOffset, hOffset + nameLen).toString(\"utf8\");\n hOffset += nameLen;\n // Skip header type byte (type 7 = STRING)\n hOffset += 1;\n const valueLen = buf.readUInt16BE(hOffset);\n hOffset += 2;\n const value = buf.subarray(hOffset, hOffset + valueLen).toString(\"utf8\");\n hOffset += valueLen;\n headers[name] = value;\n }\n\n // Extract payload\n const payloadStart = headersEnd;\n const payloadEnd = offset + totalLength - 4; // minus message CRC\n const payload = buf.subarray(payloadStart, payloadEnd);\n\n // Validate message CRC (covers entire frame minus last 4 bytes)\n const messageCrc = buf.readUInt32BE(offset + totalLength - 4);\n const computedMessageCrc = crc32(buf.subarray(offset, offset + totalLength - 4));\n if (messageCrc >>> 0 !== computedMessageCrc >>> 0) {\n return { frames, truncated: true }; // Message CRC mismatch — stop parsing\n }\n\n frames.push({ headers, payload });\n offset += totalLength;\n }\n\n return { frames, truncated: false };\n}\n\n/**\n * Collapse Bedrock binary Event Stream into a single response.\n *\n * Each frame contains a JSON payload with event types like:\n * contentBlockDelta, contentBlockStart, etc.\n */\nexport function collapseBedrockEventStream(body: Buffer): CollapseResult {\n const { frames, truncated } = decodeEventStreamFrames(body);\n let content = \"\";\n let droppedChunks = 0;\n const toolCallMap = new Map<number, { id: string; name: string; arguments: string }>();\n\n for (const frame of frames) {\n let parsed: Record<string, unknown>;\n try {\n parsed = JSON.parse(frame.payload.toString(\"utf8\")) as Record<string, unknown>;\n } catch {\n droppedChunks++;\n continue;\n }\n\n // Anthropic Messages format (invoke-with-response-stream): flat payload with \"type\" field\n if (parsed.type === \"content_block_delta\") {\n const delta = parsed.delta as Record<string, unknown> | undefined;\n if (delta?.type === \"text_delta\" && typeof delta.text === \"string\") {\n content += delta.text;\n }\n if (delta?.type === \"input_json_delta\" && typeof delta.partial_json === \"string\") {\n const index = parsed.index as number | undefined;\n if (index !== undefined) {\n const entry = toolCallMap.get(index);\n if (entry) entry.arguments += delta.partial_json;\n }\n }\n continue;\n }\n if (parsed.type === \"content_block_start\") {\n const block = parsed.content_block as Record<string, unknown> | undefined;\n const index = parsed.index as number | undefined;\n if (block?.type === \"tool_use\" && index !== undefined) {\n toolCallMap.set(index, {\n id: (block.id as string) ?? \"\",\n name: (block.name as string) ?? \"\",\n arguments: \"\",\n });\n }\n continue;\n }\n\n // Converse format (converse-stream): camelCase wrapper keys\n // contentBlockStart — may initiate a tool_use block\n if (parsed.contentBlockStart) {\n const blockStart = parsed.contentBlockStart as Record<string, unknown>;\n const index = (parsed.contentBlockIndex ?? blockStart.contentBlockIndex) as\n | number\n | undefined;\n const start = blockStart.start as Record<string, unknown> | undefined;\n if (start?.toolUse && index !== undefined) {\n const toolUse = start.toolUse as Record<string, unknown>;\n toolCallMap.set(index, {\n id: (toolUse.toolUseId as string) ?? \"\",\n name: (toolUse.name as string) ?? \"\",\n arguments: \"\",\n });\n }\n }\n\n // contentBlockDelta\n if (parsed.contentBlockDelta) {\n const blockDelta = parsed.contentBlockDelta as Record<string, unknown>;\n const index = (parsed.contentBlockIndex ?? blockDelta.contentBlockIndex) as\n | number\n | undefined;\n const delta = blockDelta.delta as Record<string, unknown> | undefined;\n if (!delta) continue;\n\n // Text delta\n if (typeof delta.text === \"string\") {\n content += delta.text;\n }\n\n // Tool use input JSON delta\n if (typeof delta.toolUse === \"object\" && delta.toolUse !== null) {\n const toolUseDelta = delta.toolUse as Record<string, unknown>;\n if (typeof toolUseDelta.input === \"string\" && index !== undefined) {\n const entry = toolCallMap.get(index);\n if (entry) {\n entry.arguments += toolUseDelta.input;\n }\n }\n }\n }\n }\n\n if (toolCallMap.size > 0) {\n const sorted = Array.from(toolCallMap.entries()).sort(([a], [b]) => a - b);\n return {\n toolCalls: sorted.map(([, tc]) => ({\n name: tc.name,\n arguments: tc.arguments,\n ...(tc.id ? { id: tc.id } : {}),\n })),\n ...(droppedChunks > 0 ? { droppedChunks } : {}),\n ...(truncated ? { truncated } : {}),\n };\n }\n\n return {\n content,\n ...(droppedChunks > 0 ? { droppedChunks } : {}),\n ...(truncated ? { truncated } : {}),\n };\n}\n\n// ---------------------------------------------------------------------------\n// Dispatch helper — pick the right collapse function by provider\n// ---------------------------------------------------------------------------\n\n/**\n * Collapse a streaming response body into a non-streaming fixture response.\n * Returns null if the content type is not a known streaming format.\n * Falls back to OpenAI SSE parsing for unrecognized provider keys with text/event-stream.\n */\nexport function collapseStreamingResponse(\n contentType: string,\n providerKey: RecordProviderKey,\n body: string | Buffer,\n logger?: Logger,\n): CollapseResult | null {\n const ct = contentType.toLowerCase();\n\n if (ct.includes(\"application/vnd.amazon.eventstream\")) {\n const buf = typeof body === \"string\" ? Buffer.from(body, \"binary\") : body;\n return collapseBedrockEventStream(buf);\n }\n\n if (ct.includes(\"application/x-ndjson\")) {\n const str = typeof body === \"string\" ? body : body.toString(\"utf8\");\n return collapseOllamaNDJSON(str);\n }\n\n if (ct.includes(\"text/event-stream\")) {\n const str = typeof body === \"string\" ? body : body.toString(\"utf8\");\n switch (providerKey) {\n case \"openai\":\n case \"azure\":\n return collapseOpenAISSE(str);\n case \"anthropic\":\n return collapseAnthropicSSE(str);\n case \"gemini\":\n case \"vertexai\":\n return collapseGeminiSSE(str);\n case \"cohere\":\n return collapseCohereSSE(str);\n case \"bedrock\":\n return collapseAnthropicSSE(str);\n default:\n logger?.warn(\n `[stream-collapse] unknown SSE provider \"${providerKey}\", falling back to OpenAI SSE format`,\n );\n return collapseOpenAISSE(str);\n }\n }\n\n return null;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"stream-collapse.cjs","names":[],"sources":["../src/stream-collapse.ts"],"sourcesContent":["/**\n * Stream collapsing functions for record-and-replay.\n *\n * Each function takes a raw streaming response body (SSE, NDJSON, or binary\n * EventStream) and collapses it into a non-streaming fixture response\n * containing `{ content }`, `{ toolCalls }`, or both when the stream includes\n * text followed by tool calls.\n */\n\nimport { crc32 } from \"node:zlib\";\nimport type { RecordProviderKey, ToolCall } from \"./types.js\";\nimport type { Logger } from \"./logger.js\";\n\n// ---------------------------------------------------------------------------\n// Result type shared by all collapse functions\n// ---------------------------------------------------------------------------\n\nexport interface CollapseResult {\n content?: string;\n reasoning?: string;\n webSearches?: string[];\n toolCalls?: ToolCall[];\n droppedChunks?: number;\n truncated?: boolean;\n}\n\n// ---------------------------------------------------------------------------\n// 1. OpenAI SSE\n// ---------------------------------------------------------------------------\n\n/**\n * Collapse OpenAI Chat Completions SSE stream into a single response.\n *\n * Format:\n * data: {\"id\":\"chatcmpl-123\",\"choices\":[{\"delta\":{\"content\":\"Hello\"}}]}\\n\\n\n * data: [DONE]\\n\\n\n */\nexport function collapseOpenAISSE(body: string): CollapseResult {\n const lines = body.split(\"\\n\\n\").filter((l) => l.trim().length > 0);\n let content = \"\";\n let reasoning = \"\";\n const webSearchQueries: string[] = [];\n let droppedChunks = 0;\n const toolCallMap = new Map<number, { id: string; name: string; arguments: string }>();\n\n for (const line of lines) {\n const dataLine = line.split(\"\\n\").find((l) => l.startsWith(\"data:\"));\n if (!dataLine) continue;\n\n const payload = dataLine.slice(5).trim();\n if (payload === \"[DONE]\") continue;\n\n let parsed: Record<string, unknown>;\n try {\n parsed = JSON.parse(payload) as Record<string, unknown>;\n } catch {\n droppedChunks++;\n continue;\n }\n\n // Responses API reasoning events\n if (\n parsed.type === \"response.reasoning_summary_text.delta\" &&\n typeof parsed.delta === \"string\"\n ) {\n reasoning += parsed.delta;\n continue;\n }\n\n // Responses API web search events\n if (parsed.type === \"response.output_item.done\") {\n const item = parsed.item as Record<string, unknown> | undefined;\n if (item?.type === \"web_search_call\") {\n const action = item.action as Record<string, unknown> | undefined;\n if (action && typeof action.query === \"string\") {\n webSearchQueries.push(action.query);\n continue;\n }\n }\n }\n\n // Responses API text content events\n if (parsed.type === \"response.output_text.delta\" && typeof parsed.delta === \"string\") {\n content += parsed.delta;\n continue;\n }\n\n // Skip other Responses API structural events\n if (typeof parsed.type === \"string\" && parsed.type.startsWith(\"response.\")) {\n continue;\n }\n\n const choices = parsed.choices as Array<Record<string, unknown>> | undefined;\n if (!choices || choices.length === 0) continue;\n\n const delta = choices[0].delta as Record<string, unknown> | undefined;\n if (!delta) continue;\n\n // Reasoning content (OpenRouter / chat completions format)\n if (typeof delta.reasoning_content === \"string\") {\n reasoning += delta.reasoning_content;\n }\n\n // Text content\n if (typeof delta.content === \"string\") {\n content += delta.content;\n }\n\n // Tool calls\n const toolCalls = delta.tool_calls as Array<Record<string, unknown>> | undefined;\n if (toolCalls) {\n for (const tc of toolCalls) {\n const index = tc.index as number;\n const fn = tc.function as Record<string, unknown> | undefined;\n\n if (!toolCallMap.has(index)) {\n toolCallMap.set(index, {\n id: (tc.id as string) ?? \"\",\n name: (fn?.name as string) ?? \"\",\n arguments: \"\",\n });\n }\n\n const entry = toolCallMap.get(index)!;\n if (fn?.name && typeof fn.name === \"string\" && !entry.name) {\n entry.name = fn.name;\n }\n if (tc.id && typeof tc.id === \"string\" && !entry.id) {\n entry.id = tc.id;\n }\n if (fn?.arguments && typeof fn.arguments === \"string\") {\n entry.arguments += fn.arguments;\n }\n }\n }\n }\n\n if (toolCallMap.size > 0) {\n const sorted = Array.from(toolCallMap.entries()).sort(([a], [b]) => a - b);\n return {\n ...(content ? { content } : {}),\n toolCalls: sorted.map(([, tc]) => ({\n name: tc.name,\n arguments: tc.arguments,\n ...(tc.id ? { id: tc.id } : {}),\n })),\n ...(droppedChunks > 0 ? { droppedChunks } : {}),\n };\n }\n\n return {\n content,\n ...(reasoning ? { reasoning } : {}),\n ...(webSearchQueries.length > 0 ? { webSearches: webSearchQueries } : {}),\n ...(droppedChunks > 0 ? { droppedChunks } : {}),\n };\n}\n\n// ---------------------------------------------------------------------------\n// 2. Anthropic SSE\n// ---------------------------------------------------------------------------\n\n/**\n * Collapse Anthropic Claude Messages SSE stream into a single response.\n *\n * Format:\n * event: message_start\\ndata: {...}\\n\\n\n * event: content_block_delta\\ndata: {\"delta\":{\"type\":\"text_delta\",\"text\":\"Hello\"}}\\n\\n\n */\nexport function collapseAnthropicSSE(body: string): CollapseResult {\n const blocks = body.split(\"\\n\\n\").filter((b) => b.trim().length > 0);\n let content = \"\";\n let reasoning = \"\";\n let droppedChunks = 0;\n const toolCallMap = new Map<number, { id: string; name: string; arguments: string }>();\n\n for (const block of blocks) {\n const lines = block.split(\"\\n\");\n const eventLine = lines.find((l) => l.startsWith(\"event:\"));\n const dataLine = lines.find((l) => l.startsWith(\"data:\"));\n if (!dataLine) continue;\n\n const eventType = eventLine ? eventLine.slice(6).trim() : \"\";\n const payload = dataLine.slice(5).trim();\n\n let parsed: Record<string, unknown>;\n try {\n parsed = JSON.parse(payload) as Record<string, unknown>;\n } catch {\n droppedChunks++;\n continue;\n }\n\n if (eventType === \"content_block_start\") {\n const index = parsed.index as number;\n const contentBlock = parsed.content_block as Record<string, unknown> | undefined;\n if (contentBlock?.type === \"tool_use\") {\n toolCallMap.set(index, {\n id: (contentBlock.id as string) ?? \"\",\n name: (contentBlock.name as string) ?? \"\",\n arguments: \"\",\n });\n }\n }\n\n if (eventType === \"content_block_delta\") {\n const index = parsed.index as number;\n const delta = parsed.delta as Record<string, unknown> | undefined;\n if (!delta) continue;\n\n if (delta.type === \"text_delta\" && typeof delta.text === \"string\") {\n content += delta.text;\n }\n\n if (delta.type === \"thinking_delta\" && typeof delta.thinking === \"string\") {\n reasoning += delta.thinking;\n }\n\n if (delta.type === \"input_json_delta\" && typeof delta.partial_json === \"string\") {\n const entry = toolCallMap.get(index);\n if (entry) {\n entry.arguments += delta.partial_json;\n }\n }\n }\n }\n\n if (toolCallMap.size > 0) {\n const sorted = Array.from(toolCallMap.entries()).sort(([a], [b]) => a - b);\n return {\n ...(content ? { content } : {}),\n toolCalls: sorted.map(([, tc]) => ({\n name: tc.name,\n arguments: tc.arguments,\n ...(tc.id ? { id: tc.id } : {}),\n })),\n ...(reasoning ? { reasoning } : {}),\n ...(droppedChunks > 0 ? { droppedChunks } : {}),\n };\n }\n\n return {\n content,\n ...(reasoning ? { reasoning } : {}),\n ...(droppedChunks > 0 ? { droppedChunks } : {}),\n };\n}\n\n// ---------------------------------------------------------------------------\n// 3. Gemini SSE\n// ---------------------------------------------------------------------------\n\n/**\n * Collapse Gemini SSE stream into a single response.\n *\n * Format (data-only, no event prefix, no [DONE]):\n * data: {\"candidates\":[{\"content\":{\"parts\":[{\"text\":\"Hello\"}]}}]}\\n\\n\n */\nexport function collapseGeminiSSE(body: string): CollapseResult {\n const lines = body.split(\"\\n\\n\").filter((l) => l.trim().length > 0);\n let content = \"\";\n let droppedChunks = 0;\n const toolCalls: ToolCall[] = [];\n\n for (const line of lines) {\n const dataLine = line.split(\"\\n\").find((l) => l.startsWith(\"data:\"));\n if (!dataLine) continue;\n\n const payload = dataLine.slice(5).trim();\n\n let parsed: Record<string, unknown>;\n try {\n parsed = JSON.parse(payload) as Record<string, unknown>;\n } catch {\n droppedChunks++;\n continue;\n }\n\n const candidates = parsed.candidates as Array<Record<string, unknown>> | undefined;\n if (!candidates || candidates.length === 0) continue;\n\n const candidateContent = candidates[0].content as Record<string, unknown> | undefined;\n if (!candidateContent) continue;\n\n const parts = candidateContent.parts as Array<Record<string, unknown>> | undefined;\n if (!parts || parts.length === 0) continue;\n\n for (const part of parts) {\n if (part.functionCall) {\n const fc = part.functionCall as Record<string, unknown>;\n toolCalls.push({\n name: String(fc.name ?? \"\"),\n arguments: typeof fc.args === \"string\" ? (fc.args as string) : JSON.stringify(fc.args),\n });\n } else if (typeof part.text === \"string\") {\n content += part.text;\n }\n }\n }\n\n if (toolCalls.length > 0) {\n return {\n ...(content ? { content } : {}),\n toolCalls,\n ...(droppedChunks > 0 ? { droppedChunks } : {}),\n };\n }\n\n return { content, ...(droppedChunks > 0 ? { droppedChunks } : {}) };\n}\n\n// ---------------------------------------------------------------------------\n// 4. Ollama NDJSON\n// ---------------------------------------------------------------------------\n\n/**\n * Collapse Ollama NDJSON stream into a single response.\n *\n * /api/chat format:\n * {\"model\":\"llama3\",\"message\":{\"role\":\"assistant\",\"content\":\"Hello\"},\"done\":false}\\n\n *\n * /api/generate format:\n * {\"model\":\"llama3\",\"response\":\"Hello\",\"done\":false}\\n\n */\nexport function collapseOllamaNDJSON(body: string): CollapseResult {\n const lines = body.split(\"\\n\").filter((l) => l.trim().length > 0);\n let content = \"\";\n let droppedChunks = 0;\n const toolCalls: ToolCall[] = [];\n\n for (const line of lines) {\n let parsed: Record<string, unknown>;\n try {\n parsed = JSON.parse(line.trim()) as Record<string, unknown>;\n } catch {\n droppedChunks++;\n continue;\n }\n\n // /api/chat format\n const message = parsed.message as Record<string, unknown> | undefined;\n if (message) {\n if (typeof message.content === \"string\") {\n content += message.content;\n }\n\n // Tool calls\n if (Array.isArray(message.tool_calls)) {\n for (const tc of message.tool_calls as Array<Record<string, unknown>>) {\n const fn = tc.function as Record<string, unknown> | undefined;\n if (fn) {\n toolCalls.push({\n name: String(fn.name ?? \"\"),\n arguments:\n typeof fn.arguments === \"string\" ? fn.arguments : JSON.stringify(fn.arguments),\n });\n }\n }\n }\n }\n\n // /api/generate format\n else if (typeof parsed.response === \"string\") {\n content += parsed.response;\n }\n }\n\n if (toolCalls.length > 0) {\n return {\n ...(content ? { content } : {}),\n toolCalls,\n ...(droppedChunks > 0 ? { droppedChunks } : {}),\n };\n }\n\n return { content, ...(droppedChunks > 0 ? { droppedChunks } : {}) };\n}\n\n// ---------------------------------------------------------------------------\n// 5. Cohere SSE\n// ---------------------------------------------------------------------------\n\n/**\n * Collapse Cohere SSE stream into a single response.\n *\n * Format:\n * event: content-delta\\ndata: {\"type\":\"content-delta\",\"delta\":{\"message\":{\"content\":{\"text\":\"Hello\"}}}}\\n\\n\n */\nexport function collapseCohereSSE(body: string): CollapseResult {\n const blocks = body.split(\"\\n\\n\").filter((b) => b.trim().length > 0);\n let content = \"\";\n let droppedChunks = 0;\n const toolCallMap = new Map<number, { id: string; name: string; arguments: string }>();\n\n for (const block of blocks) {\n const lines = block.split(\"\\n\");\n const eventLine = lines.find((l) => l.startsWith(\"event:\"));\n const dataLine = lines.find((l) => l.startsWith(\"data:\"));\n if (!dataLine) continue;\n\n const eventType = eventLine ? eventLine.slice(6).trim() : \"\";\n const payload = dataLine.slice(5).trim();\n\n let parsed: Record<string, unknown>;\n try {\n parsed = JSON.parse(payload) as Record<string, unknown>;\n } catch {\n droppedChunks++;\n continue;\n }\n\n if (eventType === \"content-delta\") {\n const delta = parsed.delta as Record<string, unknown> | undefined;\n const message = delta?.message as Record<string, unknown> | undefined;\n const contentObj = message?.content as Record<string, unknown> | undefined;\n if (contentObj && typeof contentObj.text === \"string\") {\n content += contentObj.text;\n }\n }\n\n if (eventType === \"tool-call-start\") {\n const index = parsed.index as number;\n const delta = parsed.delta as Record<string, unknown> | undefined;\n const message = delta?.message as Record<string, unknown> | undefined;\n const toolCalls = message?.tool_calls as Record<string, unknown> | undefined;\n if (toolCalls) {\n const fn = toolCalls.function as Record<string, unknown> | undefined;\n toolCallMap.set(index, {\n id: (toolCalls.id as string) ?? \"\",\n name: (fn?.name as string) ?? \"\",\n arguments: \"\",\n });\n }\n }\n\n if (eventType === \"tool-call-delta\") {\n const index = parsed.index as number;\n const delta = parsed.delta as Record<string, unknown> | undefined;\n const message = delta?.message as Record<string, unknown> | undefined;\n const toolCalls = message?.tool_calls as Record<string, unknown> | undefined;\n if (toolCalls) {\n const fn = toolCalls.function as Record<string, unknown> | undefined;\n if (fn && typeof fn.arguments === \"string\") {\n const entry = toolCallMap.get(index);\n if (entry) {\n entry.arguments += fn.arguments;\n }\n }\n }\n }\n }\n\n if (toolCallMap.size > 0) {\n const sorted = Array.from(toolCallMap.entries()).sort(([a], [b]) => a - b);\n return {\n toolCalls: sorted.map(([, tc]) => ({\n name: tc.name,\n arguments: tc.arguments,\n ...(tc.id ? { id: tc.id } : {}),\n })),\n ...(droppedChunks > 0 ? { droppedChunks } : {}),\n };\n }\n\n return { content, ...(droppedChunks > 0 ? { droppedChunks } : {}) };\n}\n\n// ---------------------------------------------------------------------------\n// 6. Bedrock EventStream (binary)\n// ---------------------------------------------------------------------------\n\n/**\n * Decode AWS Event Stream binary frames and extract JSON payloads.\n *\n * Binary frame layout:\n * [total_length: 4B uint32-BE]\n * [headers_length: 4B uint32-BE]\n * [prelude_crc32: 4B]\n * [headers: variable]\n * [payload: variable]\n * [message_crc32: 4B]\n */\nfunction decodeEventStreamFrames(buf: Buffer): {\n frames: Array<{ headers: Record<string, string>; payload: Buffer }>;\n truncated: boolean;\n} {\n const frames: Array<{ headers: Record<string, string>; payload: Buffer }> = [];\n let offset = 0;\n\n while (offset < buf.length) {\n if (offset + 12 > buf.length) break;\n\n const totalLength = buf.readUInt32BE(offset);\n const headersLength = buf.readUInt32BE(offset + 4);\n\n // Validate bounds: ensure the full frame is within the buffer\n if (totalLength < 12 || offset + totalLength > buf.length) {\n return { frames, truncated: true };\n }\n\n // Validate prelude CRC\n const preludeCrc = buf.readUInt32BE(offset + 8);\n const computedPreludeCrc = crc32(buf.subarray(offset, offset + 8));\n if (preludeCrc >>> 0 !== computedPreludeCrc >>> 0) {\n return { frames, truncated: true }; // Prelude CRC mismatch — stop parsing\n }\n\n // Parse headers\n const headersStart = offset + 12;\n const headersEnd = headersStart + headersLength;\n const headers: Record<string, string> = {};\n let hOffset = headersStart;\n\n while (hOffset < headersEnd) {\n const nameLen = buf.readUInt8(hOffset);\n hOffset += 1;\n const name = buf.subarray(hOffset, hOffset + nameLen).toString(\"utf8\");\n hOffset += nameLen;\n // Skip header type byte (type 7 = STRING)\n hOffset += 1;\n const valueLen = buf.readUInt16BE(hOffset);\n hOffset += 2;\n const value = buf.subarray(hOffset, hOffset + valueLen).toString(\"utf8\");\n hOffset += valueLen;\n headers[name] = value;\n }\n\n // Extract payload\n const payloadStart = headersEnd;\n const payloadEnd = offset + totalLength - 4; // minus message CRC\n const payload = buf.subarray(payloadStart, payloadEnd);\n\n // Validate message CRC (covers entire frame minus last 4 bytes)\n const messageCrc = buf.readUInt32BE(offset + totalLength - 4);\n const computedMessageCrc = crc32(buf.subarray(offset, offset + totalLength - 4));\n if (messageCrc >>> 0 !== computedMessageCrc >>> 0) {\n return { frames, truncated: true }; // Message CRC mismatch — stop parsing\n }\n\n frames.push({ headers, payload });\n offset += totalLength;\n }\n\n return { frames, truncated: false };\n}\n\n/**\n * Collapse Bedrock binary Event Stream into a single response.\n *\n * Each frame contains a JSON payload with event types like:\n * contentBlockDelta, contentBlockStart, etc.\n */\nexport function collapseBedrockEventStream(body: Buffer): CollapseResult {\n const { frames, truncated } = decodeEventStreamFrames(body);\n let content = \"\";\n let droppedChunks = 0;\n const toolCallMap = new Map<number, { id: string; name: string; arguments: string }>();\n\n for (const frame of frames) {\n let parsed: Record<string, unknown>;\n try {\n parsed = JSON.parse(frame.payload.toString(\"utf8\")) as Record<string, unknown>;\n } catch {\n droppedChunks++;\n continue;\n }\n\n // Anthropic Messages format (invoke-with-response-stream): flat payload with \"type\" field\n if (parsed.type === \"content_block_delta\") {\n const delta = parsed.delta as Record<string, unknown> | undefined;\n if (delta?.type === \"text_delta\" && typeof delta.text === \"string\") {\n content += delta.text;\n }\n if (delta?.type === \"input_json_delta\" && typeof delta.partial_json === \"string\") {\n const index = parsed.index as number | undefined;\n if (index !== undefined) {\n const entry = toolCallMap.get(index);\n if (entry) entry.arguments += delta.partial_json;\n }\n }\n continue;\n }\n if (parsed.type === \"content_block_start\") {\n const block = parsed.content_block as Record<string, unknown> | undefined;\n const index = parsed.index as number | undefined;\n if (block?.type === \"tool_use\" && index !== undefined) {\n toolCallMap.set(index, {\n id: (block.id as string) ?? \"\",\n name: (block.name as string) ?? \"\",\n arguments: \"\",\n });\n }\n continue;\n }\n\n // Converse format (converse-stream): camelCase wrapper keys\n // contentBlockStart — may initiate a tool_use block\n if (parsed.contentBlockStart) {\n const blockStart = parsed.contentBlockStart as Record<string, unknown>;\n const index = (parsed.contentBlockIndex ?? blockStart.contentBlockIndex) as\n | number\n | undefined;\n const start = blockStart.start as Record<string, unknown> | undefined;\n if (start?.toolUse && index !== undefined) {\n const toolUse = start.toolUse as Record<string, unknown>;\n toolCallMap.set(index, {\n id: (toolUse.toolUseId as string) ?? \"\",\n name: (toolUse.name as string) ?? \"\",\n arguments: \"\",\n });\n }\n }\n\n // contentBlockDelta\n if (parsed.contentBlockDelta) {\n const blockDelta = parsed.contentBlockDelta as Record<string, unknown>;\n const index = (parsed.contentBlockIndex ?? blockDelta.contentBlockIndex) as\n | number\n | undefined;\n const delta = blockDelta.delta as Record<string, unknown> | undefined;\n if (!delta) continue;\n\n // Text delta\n if (typeof delta.text === \"string\") {\n content += delta.text;\n }\n\n // Tool use input JSON delta\n if (typeof delta.toolUse === \"object\" && delta.toolUse !== null) {\n const toolUseDelta = delta.toolUse as Record<string, unknown>;\n if (typeof toolUseDelta.input === \"string\" && index !== undefined) {\n const entry = toolCallMap.get(index);\n if (entry) {\n entry.arguments += toolUseDelta.input;\n }\n }\n }\n }\n }\n\n if (toolCallMap.size > 0) {\n const sorted = Array.from(toolCallMap.entries()).sort(([a], [b]) => a - b);\n return {\n toolCalls: sorted.map(([, tc]) => ({\n name: tc.name,\n arguments: tc.arguments,\n ...(tc.id ? { id: tc.id } : {}),\n })),\n ...(droppedChunks > 0 ? { droppedChunks } : {}),\n ...(truncated ? { truncated } : {}),\n };\n }\n\n return {\n content,\n ...(droppedChunks > 0 ? { droppedChunks } : {}),\n ...(truncated ? { truncated } : {}),\n };\n}\n\n// ---------------------------------------------------------------------------\n// Dispatch helper — pick the right collapse function by provider\n// ---------------------------------------------------------------------------\n\n/**\n * Collapse a streaming response body into a non-streaming fixture response.\n * Returns null if the content type is not a known streaming format.\n * Falls back to OpenAI SSE parsing for unrecognized provider keys with text/event-stream.\n */\nexport function collapseStreamingResponse(\n contentType: string,\n providerKey: RecordProviderKey,\n body: string | Buffer,\n logger?: Logger,\n): CollapseResult | null {\n const ct = contentType.toLowerCase();\n\n if (ct.includes(\"application/vnd.amazon.eventstream\")) {\n const buf = typeof body === \"string\" ? Buffer.from(body, \"binary\") : body;\n return collapseBedrockEventStream(buf);\n }\n\n if (ct.includes(\"application/x-ndjson\")) {\n const str = typeof body === \"string\" ? body : body.toString(\"utf8\");\n return collapseOllamaNDJSON(str);\n }\n\n if (ct.includes(\"text/event-stream\")) {\n const str = typeof body === \"string\" ? body : body.toString(\"utf8\");\n switch (providerKey) {\n case \"openai\":\n case \"azure\":\n return collapseOpenAISSE(str);\n case \"anthropic\":\n return collapseAnthropicSSE(str);\n case \"gemini\":\n case \"vertexai\":\n return collapseGeminiSSE(str);\n case \"cohere\":\n return collapseCohereSSE(str);\n case \"bedrock\":\n return collapseAnthropicSSE(str);\n default:\n logger?.warn(\n `[stream-collapse] unknown SSE provider \"${providerKey}\", falling back to OpenAI SSE format`,\n );\n return collapseOpenAISSE(str);\n }\n }\n\n return null;\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"stream-collapse.d.cts","names":[],"sources":["../src/stream-collapse.ts"],"sourcesContent":[],"mappings":";;;;;AA2iBgB,UAvhBC,cAAA,CAuhBD;EAA0B,OAAA,CAAA,EAAA,MAAA;WAAO,CAAA,EAAA,MAAA;aAAS,CAAA,EAAA,MAAA,EAAA;EAAc,SAAA,CAAA,EAnhB1D,QAmhB0D,EAAA;EAqHxD,aAAA,CAAA,EAAA,MAAA;EAAyB,SAAA,CAAA,EAAA,OAAA;;;;;;;;;iBAxnBzB,iBAAA,gBAAiC;;;;;;;;iBAoIjC,oBAAA,gBAAoC;;;;;;;iBAyFpC,iBAAA,gBAAiC;;;;;;;;;;iBAkEjC,oBAAA,gBAAoC;;;;;;;iBAgEpC,iBAAA,gBAAiC;;;;;;;iBAoKjC,0BAAA,OAAiC,SAAS;;;;;;iBAqH1C,yBAAA,mCAED,kCACE,iBACN,SACR"}
1
+ {"version":3,"file":"stream-collapse.d.cts","names":[],"sources":["../src/stream-collapse.ts"],"sourcesContent":[],"mappings":";;;;;AAwiBgB,UAvhBC,cAAA,CAuhBD;EAA0B,OAAA,CAAA,EAAA,MAAA;WAAO,CAAA,EAAA,MAAA;aAAS,CAAA,EAAA,MAAA,EAAA;EAAc,SAAA,CAAA,EAnhB1D,QAmhB0D,EAAA;EAqHxD,aAAA,CAAA,EAAA,MAAA;EAAyB,SAAA,CAAA,EAAA,OAAA;;;;;;;;;iBAxnBzB,iBAAA,gBAAiC;;;;;;;;iBAoIjC,oBAAA,gBAAoC;;;;;;;iBAyFpC,iBAAA,gBAAiC;;;;;;;;;;iBAkEjC,oBAAA,gBAAoC;;;;;;;iBAgEpC,iBAAA,gBAAiC;;;;;;;iBAoKjC,0BAAA,OAAiC,SAAS;;;;;;iBAqH1C,yBAAA,mCAED,kCACE,iBACN,SACR"}
@@ -1 +1 @@
1
- {"version":3,"file":"stream-collapse.d.ts","names":[],"sources":["../src/stream-collapse.ts"],"sourcesContent":[],"mappings":";;;;;AA2iBgB,UAvhBC,cAAA,CAuhBD;EAA0B,OAAA,CAAA,EAAA,MAAA;WAAO,CAAA,EAAA,MAAA;aAAS,CAAA,EAAA,MAAA,EAAA;EAAc,SAAA,CAAA,EAnhB1D,QAmhB0D,EAAA;EAqHxD,aAAA,CAAA,EAAA,MAAA;EAAyB,SAAA,CAAA,EAAA,OAAA;;;;;;;;;iBAxnBzB,iBAAA,gBAAiC;;;;;;;;iBAoIjC,oBAAA,gBAAoC;;;;;;;iBAyFpC,iBAAA,gBAAiC;;;;;;;;;;iBAkEjC,oBAAA,gBAAoC;;;;;;;iBAgEpC,iBAAA,gBAAiC;;;;;;;iBAoKjC,0BAAA,OAAiC,SAAS;;;;;;iBAqH1C,yBAAA,mCAED,kCACE,iBACN,SACR"}
1
+ {"version":3,"file":"stream-collapse.d.ts","names":[],"sources":["../src/stream-collapse.ts"],"sourcesContent":[],"mappings":";;;;;AAwiBgB,UAvhBC,cAAA,CAuhBD;EAA0B,OAAA,CAAA,EAAA,MAAA;WAAO,CAAA,EAAA,MAAA;aAAS,CAAA,EAAA,MAAA,EAAA;EAAc,SAAA,CAAA,EAnhB1D,QAmhB0D,EAAA;EAqHxD,aAAA,CAAA,EAAA,MAAA;EAAyB,SAAA,CAAA,EAAA,OAAA;;;;;;;;;iBAxnBzB,iBAAA,gBAAiC;;;;;;;;iBAoIjC,oBAAA,gBAAoC;;;;;;;iBAyFpC,iBAAA,gBAAiC;;;;;;;;;;iBAkEjC,oBAAA,gBAAoC;;;;;;;iBAgEpC,iBAAA,gBAAiC;;;;;;;iBAoKjC,0BAAA,OAAiC,SAAS;;;;;;iBAqH1C,yBAAA,mCAED,kCACE,iBACN,SACR"}