@copilotkit/aimock 1.37.4 → 1.38.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/CHANGELOG.md +22 -0
- package/README.md +27 -9
- package/dist/agui-recorder.cjs +6 -0
- package/dist/agui-recorder.cjs.map +1 -1
- package/dist/agui-recorder.d.cts.map +1 -1
- package/dist/agui-recorder.d.ts.map +1 -1
- package/dist/agui-recorder.js +6 -0
- package/dist/agui-recorder.js.map +1 -1
- package/dist/agui-types.d.ts.map +1 -1
- package/dist/api-key-auth.cjs +166 -0
- package/dist/api-key-auth.cjs.map +1 -0
- package/dist/api-key-auth.d.cts +17 -0
- package/dist/api-key-auth.d.cts.map +1 -0
- package/dist/api-key-auth.d.ts +17 -0
- package/dist/api-key-auth.d.ts.map +1 -0
- package/dist/api-key-auth.js +158 -0
- package/dist/api-key-auth.js.map +1 -0
- package/dist/cli.cjs +4 -1
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +4 -1
- package/dist/cli.js.map +1 -1
- package/dist/config-loader.cjs +4 -2
- package/dist/config-loader.cjs.map +1 -1
- package/dist/config-loader.d.cts +2 -1
- package/dist/config-loader.d.cts.map +1 -1
- package/dist/config-loader.d.ts +2 -1
- package/dist/config-loader.d.ts.map +1 -1
- package/dist/config-loader.js +5 -3
- package/dist/config-loader.js.map +1 -1
- package/dist/fal-audio.cjs +2 -0
- package/dist/fal-audio.cjs.map +1 -1
- package/dist/fal-audio.d.cts.map +1 -1
- package/dist/fal-audio.d.ts.map +1 -1
- package/dist/fal-audio.js +2 -0
- package/dist/fal-audio.js.map +1 -1
- package/dist/fal.cjs +4 -1
- package/dist/fal.cjs.map +1 -1
- package/dist/fal.d.cts.map +1 -1
- package/dist/fal.d.ts.map +1 -1
- package/dist/fal.js +4 -1
- package/dist/fal.js.map +1 -1
- package/dist/fixture-loader.cjs +0 -0
- package/dist/fixture-loader.cjs.map +1 -1
- package/dist/fixture-loader.d.cts +3 -1
- package/dist/fixture-loader.d.cts.map +1 -1
- package/dist/fixture-loader.d.ts +3 -1
- package/dist/fixture-loader.d.ts.map +1 -1
- package/dist/fixture-loader.js +0 -0
- package/dist/fixture-loader.js.map +1 -1
- package/dist/grok-video.cjs +9 -2
- package/dist/grok-video.cjs.map +1 -1
- package/dist/grok-video.js +10 -3
- package/dist/grok-video.js.map +1 -1
- package/dist/helpers.cjs +16 -22
- package/dist/helpers.cjs.map +1 -1
- package/dist/helpers.d.cts.map +1 -1
- package/dist/helpers.d.ts.map +1 -1
- package/dist/helpers.js +16 -21
- package/dist/helpers.js.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/llmock.cjs +14 -3
- package/dist/llmock.cjs.map +1 -1
- package/dist/llmock.d.cts +4 -2
- package/dist/llmock.d.cts.map +1 -1
- package/dist/llmock.d.ts +4 -2
- package/dist/llmock.d.ts.map +1 -1
- package/dist/llmock.js +15 -5
- package/dist/llmock.js.map +1 -1
- package/dist/openrouter-chat.cjs +333 -0
- package/dist/openrouter-chat.cjs.map +1 -0
- package/dist/openrouter-chat.js +325 -0
- package/dist/openrouter-chat.js.map +1 -0
- package/dist/openrouter-video.cjs +24 -7
- package/dist/openrouter-video.cjs.map +1 -1
- package/dist/openrouter-video.d.cts.map +1 -1
- package/dist/openrouter-video.d.ts.map +1 -1
- package/dist/openrouter-video.js +25 -8
- package/dist/openrouter-video.js.map +1 -1
- package/dist/provider-auth.cjs +20 -0
- package/dist/provider-auth.cjs.map +1 -1
- package/dist/provider-auth.js +20 -1
- package/dist/provider-auth.js.map +1 -1
- package/dist/recorder.cjs +50 -8
- package/dist/recorder.cjs.map +1 -1
- package/dist/recorder.d.cts.map +1 -1
- package/dist/recorder.d.ts.map +1 -1
- package/dist/recorder.js +50 -10
- package/dist/recorder.js.map +1 -1
- package/dist/responses.cjs +200 -89
- package/dist/responses.cjs.map +1 -1
- package/dist/responses.d.cts +18 -3
- package/dist/responses.d.cts.map +1 -1
- package/dist/responses.d.ts +18 -3
- package/dist/responses.d.ts.map +1 -1
- package/dist/responses.js +199 -90
- package/dist/responses.js.map +1 -1
- package/dist/server.cjs +156 -66
- package/dist/server.cjs.map +1 -1
- package/dist/server.d.cts +1 -1
- package/dist/server.d.cts.map +1 -1
- package/dist/server.d.ts +1 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +157 -68
- package/dist/server.js.map +1 -1
- package/dist/sse-writer.cjs +3 -0
- package/dist/sse-writer.cjs.map +1 -1
- package/dist/sse-writer.d.cts +8 -0
- package/dist/sse-writer.d.cts.map +1 -1
- package/dist/sse-writer.d.ts +8 -0
- package/dist/sse-writer.d.ts.map +1 -1
- package/dist/sse-writer.js +3 -0
- package/dist/sse-writer.js.map +1 -1
- package/dist/stream-collapse.cjs +26 -0
- package/dist/stream-collapse.cjs.map +1 -1
- package/dist/stream-collapse.d.cts +7 -0
- package/dist/stream-collapse.d.cts.map +1 -1
- package/dist/stream-collapse.d.ts +7 -0
- package/dist/stream-collapse.d.ts.map +1 -1
- package/dist/stream-collapse.js +26 -0
- package/dist/stream-collapse.js.map +1 -1
- package/dist/transcription.cjs +49 -2
- package/dist/transcription.cjs.map +1 -1
- package/dist/transcription.d.cts.map +1 -1
- package/dist/transcription.d.ts.map +1 -1
- package/dist/transcription.js +50 -3
- package/dist/transcription.js.map +1 -1
- package/dist/types.d.cts +187 -5
- package/dist/types.d.cts.map +1 -1
- package/dist/types.d.ts +187 -5
- package/dist/types.d.ts.map +1 -1
- package/dist/veo-video.cjs +9 -2
- package/dist/veo-video.cjs.map +1 -1
- package/dist/veo-video.js +10 -3
- package/dist/veo-video.js.map +1 -1
- package/dist/ws-realtime.cjs +306 -57
- package/dist/ws-realtime.cjs.map +1 -1
- package/dist/ws-realtime.d.cts +1 -0
- package/dist/ws-realtime.d.cts.map +1 -1
- package/dist/ws-realtime.d.ts +1 -0
- package/dist/ws-realtime.d.ts.map +1 -1
- package/dist/ws-realtime.js +307 -58
- package/dist/ws-realtime.js.map +1 -1
- package/dist/ws-responses.cjs +11 -6
- package/dist/ws-responses.cjs.map +1 -1
- package/dist/ws-responses.d.cts.map +1 -1
- package/dist/ws-responses.d.ts.map +1 -1
- package/dist/ws-responses.js +12 -7
- package/dist/ws-responses.js.map +1 -1
- package/package.json +1 -1
- package/skills/write-fixtures/SKILL.md +53 -19
package/dist/ws-realtime.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ws-realtime.cjs","names":["generateToolCallId","DEFAULT_TEST_ID","matchFixtureDiagnostic","resolveStrictMode","strictNoMatchMessage","strictNoMatchLogLine","flattenHeaders","strictOverrideField","resolveResponse","isErrorResponse","isContentWithToolCallsResponse","resolveFixtureBlocks","createInterruptionSignal","calculateDelay","delay","isTextResponse","isToolCallResponse"],"sources":["../src/ws-realtime.ts"],"sourcesContent":["/**\n * WebSocket handler for OpenAI Realtime API.\n *\n * Accepts Realtime API messages (session.update, conversation.item.create,\n * response.create) over WebSocket and sends back Realtime API events as\n * individual WebSocket text frames.\n */\n\nimport { randomBytes } from \"node:crypto\";\nimport type {\n ChatCompletionRequest,\n ChatMessage,\n Fixture,\n FixtureBlock,\n JournalEntry,\n} from \"./types.js\";\nimport { matchFixtureDiagnostic } from \"./router.js\";\nimport {\n generateToolCallId,\n flattenHeaders,\n isTextResponse,\n isToolCallResponse,\n isContentWithToolCallsResponse,\n isErrorResponse,\n resolveFixtureBlocks,\n resolveResponse,\n resolveStrictMode,\n strictOverrideField,\n strictNoMatchMessage,\n strictNoMatchLogLine,\n} from \"./helpers.js\";\nimport { createInterruptionSignal } from \"./interruption.js\";\nimport { delay, calculateDelay } from \"./sse-writer.js\";\nimport { DEFAULT_TEST_ID, type Journal } from \"./journal.js\";\nimport type { Logger } from \"./logger.js\";\nimport type { WebSocketConnection } from \"./ws-framing.js\";\n\n/** Generate a Realtime-API-style ID with underscore separator (e.g. event_xxx, item_xxx). */\nfunction realtimeId(prefix: string): string {\n return `${prefix}_${randomBytes(12).toString(\"base64url\")}`;\n}\n\n// ─── Realtime protocol types ────────────────────────────────────────────────\n\ninterface RealtimeItem {\n type: \"message\" | \"function_call\" | \"function_call_output\";\n id?: string;\n role?: \"user\" | \"assistant\" | \"system\";\n content?: Array<{ type: string; text?: string; url?: string; transcript?: string | null }>;\n name?: string;\n call_id?: string;\n arguments?: string;\n output?: string;\n}\n\ninterface SessionConfig {\n model: string;\n modalities: string[];\n instructions: string;\n tools: unknown[];\n voice: string | null;\n input_audio_format: string | null;\n output_audio_format: string | null;\n input_audio_noise_reduction: { type: string } | null;\n input_audio_transcription: { model: string } | null;\n turn_detection: unknown | null;\n temperature: number;\n type: \"conversation\" | \"transcription\" | \"translation\";\n reasoning: { effort: string } | null;\n}\n\ninterface RealtimeMessage {\n type: string;\n event_id?: string;\n session?: Partial<SessionConfig>;\n item?: RealtimeItem;\n response?: {\n modalities?: string[];\n instructions?: string;\n [key: string]: unknown;\n };\n}\n\n// ─── Conversion helpers ─────────────────────────────────────────────────────\n\nexport function realtimeItemsToMessages(\n items: RealtimeItem[],\n instructions?: string,\n logger?: Logger,\n): ChatMessage[] {\n const messages: ChatMessage[] = [];\n\n if (instructions) {\n messages.push({ role: \"system\", content: instructions });\n }\n\n for (const item of items) {\n if (item.type === \"message\") {\n const role =\n item.role === \"assistant\" ? \"assistant\" : item.role === \"system\" ? \"system\" : \"user\";\n\n // Check if content contains multimodal input types (input_text, input_image, input_audio)\n const hasMultimodal = item.content?.some(\n (p) => p.type === \"input_text\" || p.type === \"input_image\" || p.type === \"input_audio\",\n );\n\n if (hasMultimodal && item.content) {\n // Map realtime input content types to ChatMessage content parts\n const mappedContent = item.content.map((part) => {\n if (part.type === \"input_text\") {\n return { type: \"text\" as const, text: part.text ?? \"\" };\n }\n if (part.type === \"input_image\") {\n return {\n type: \"image_url\" as const,\n image_url: { url: part.url ?? \"\" },\n };\n }\n if (part.type === \"input_audio\") {\n return { type: \"text\" as const, text: \"[audio input]\" };\n }\n // Pass through unknown content types as-is\n return part;\n });\n messages.push({ role, content: mappedContent });\n } else {\n // Join all text content parts (not just the first)\n const text =\n item.content\n ?.map((c) => c.text)\n .filter(Boolean)\n .join(\"\") ?? \"\";\n messages.push({ role, content: text });\n }\n } else if (item.type === \"function_call\") {\n if (!item.name) {\n logger?.warn(\"Realtime function_call item missing 'name'\");\n }\n messages.push({\n role: \"assistant\",\n content: null,\n tool_calls: [\n {\n id: item.call_id ?? generateToolCallId(),\n type: \"function\",\n function: {\n name: item.name ?? \"\",\n arguments: item.arguments ?? \"\",\n },\n },\n ],\n });\n } else if (item.type === \"function_call_output\") {\n if (!item.output) {\n logger?.warn(\"Realtime function_call_output item missing 'output'\");\n }\n messages.push({\n role: \"tool\",\n content: item.output ?? \"\",\n tool_call_id: item.call_id,\n });\n }\n }\n\n return messages;\n}\n\n// ─── GA -> Beta translation ─────────────────────────────────────────────────\n\n/** GA -> Beta event name mapping */\nconst GA_TO_BETA_EVENT: Record<string, string> = {\n \"response.output_text.delta\": \"response.text.delta\",\n \"response.output_text.done\": \"response.text.done\",\n \"response.output_audio.delta\": \"response.audio.delta\",\n \"response.output_audio.done\": \"response.audio.done\",\n \"response.output_audio_transcript.delta\": \"response.audio_transcript.delta\",\n \"response.output_audio_transcript.done\": \"response.audio_transcript.done\",\n \"conversation.item.added\": \"conversation.item.created\",\n};\n\n/** GA -> Beta content type mapping */\nconst GA_TO_BETA_CONTENT_TYPE: Record<string, string> = {\n output_text: \"text\",\n output_audio: \"audio\",\n};\n\n/** Events suppressed in Beta mode (GA-only events) */\nconst BETA_SUPPRESSED_EVENTS = new Set([\"conversation.item.done\"]);\n\nfunction translateGAToBeta(event: Record<string, unknown>): Record<string, unknown> | null {\n const type = event.type as string;\n if (BETA_SUPPRESSED_EVENTS.has(type)) return null;\n\n const translated = { ...event };\n if (GA_TO_BETA_EVENT[type]) {\n translated.type = GA_TO_BETA_EVENT[type];\n }\n\n // Translate content types in nested structures\n if (translated.part && typeof translated.part === \"object\") {\n const part = { ...(translated.part as Record<string, unknown>) };\n if (typeof part.type === \"string\" && GA_TO_BETA_CONTENT_TYPE[part.type]) {\n part.type = GA_TO_BETA_CONTENT_TYPE[part.type];\n }\n translated.part = part;\n }\n if (translated.content_part && typeof translated.content_part === \"object\") {\n const cp = { ...(translated.content_part as Record<string, unknown>) };\n if (typeof cp.type === \"string\" && GA_TO_BETA_CONTENT_TYPE[cp.type]) {\n cp.type = GA_TO_BETA_CONTENT_TYPE[cp.type];\n }\n translated.content_part = cp;\n }\n // Translate content arrays\n if (Array.isArray(translated.content)) {\n translated.content = (translated.content as Record<string, unknown>[]).map((c) => {\n if (typeof c.type === \"string\" && GA_TO_BETA_CONTENT_TYPE[c.type]) {\n return { ...c, type: GA_TO_BETA_CONTENT_TYPE[c.type] };\n }\n return c;\n });\n }\n // Translate item.content arrays (response.output_item.added/done, conversation.item.added)\n if (translated.item && typeof translated.item === \"object\") {\n const item = { ...(translated.item as Record<string, unknown>) };\n delete item.phase; // GA-only field\n if (Array.isArray(item.content)) {\n item.content = (item.content as Record<string, unknown>[]).map((c) => {\n if (typeof c.type === \"string\" && GA_TO_BETA_CONTENT_TYPE[c.type]) {\n return { ...c, type: GA_TO_BETA_CONTENT_TYPE[c.type] };\n }\n return c;\n });\n }\n translated.item = item;\n }\n // Translate response.output[].content arrays (response.done)\n if (translated.response && typeof translated.response === \"object\") {\n const resp = { ...(translated.response as Record<string, unknown>) };\n if (Array.isArray(resp.output)) {\n resp.output = (resp.output as Record<string, unknown>[]).map((outItem) => {\n const o = { ...(outItem as Record<string, unknown>) };\n if (Array.isArray(o.content)) {\n o.content = (o.content as Record<string, unknown>[]).map((c) =>\n typeof c.type === \"string\" && GA_TO_BETA_CONTENT_TYPE[c.type]\n ? { ...c, type: GA_TO_BETA_CONTENT_TYPE[c.type] }\n : c,\n );\n }\n return o;\n });\n }\n translated.response = resp;\n }\n\n // Flatten GA session config for Beta (session.created / session.updated)\n if (type === \"session.created\" || type === \"session.updated\") {\n if (translated.session && typeof translated.session === \"object\") {\n const session = { ...(translated.session as Record<string, unknown>) };\n if (session.audio && typeof session.audio === \"object\") {\n const audio = session.audio as Record<string, unknown>;\n session.voice = audio.voice;\n session.input_audio_format = audio.input_audio_format;\n session.output_audio_format = audio.output_audio_format;\n session.input_audio_transcription = audio.input_audio_transcription;\n delete session.audio;\n }\n delete session.type;\n delete session.reasoning;\n translated.session = session;\n }\n }\n\n return translated;\n}\n\n// ─── Event sending ──────────────────────────────────────────────────────────\n\nfunction sendEvent(ws: WebSocketConnection, event: Record<string, unknown>, isBeta: boolean): void {\n const out = { ...event, event_id: event.event_id ?? realtimeId(\"event\") };\n if (isBeta) {\n const translated = translateGAToBeta(out);\n if (translated === null) return; // suppressed in Beta mode\n ws.send(JSON.stringify(translated));\n } else {\n ws.send(JSON.stringify(out));\n }\n}\n\nfunction buildErrorRealtimeEvent(\n ws: WebSocketConnection,\n message: string,\n isBeta: boolean,\n type = \"invalid_request_error\",\n code?: string,\n): void {\n sendEvent(ws, { type: \"error\", error: { message, type, code } }, isBeta);\n}\n\n// ─── Main handler ───────────────────────────────────────────────────────────\n\nexport function handleWebSocketRealtime(\n ws: WebSocketConnection,\n fixtures: Fixture[],\n journal: Journal,\n defaults: {\n latency: number;\n chunkSize: number;\n replaySpeed?: number;\n model: string;\n logger: Logger;\n strict?: boolean;\n requestTransform?: (req: ChatCompletionRequest) => ChatCompletionRequest;\n testId?: string;\n upgradeHeaders?: import(\"node:http\").IncomingHttpHeaders;\n },\n): void {\n const { logger } = defaults;\n const sessionId = realtimeId(\"sess\");\n\n const isBeta = defaults.upgradeHeaders?.[\"openai-beta\"]\n ? String(defaults.upgradeHeaders[\"openai-beta\"]).includes(\"realtime=v1\")\n : false;\n\n const session: SessionConfig = {\n model: defaults.model,\n modalities: [\"text\"],\n instructions: \"\",\n tools: [],\n voice: null,\n input_audio_format: null,\n output_audio_format: null,\n input_audio_noise_reduction: null,\n input_audio_transcription: null,\n turn_detection: null,\n temperature: 0.8,\n type: \"conversation\",\n reasoning: null,\n };\n\n const conversationItems: RealtimeItem[] = [];\n\n // Send session.created immediately on connect (GA format — shim flattens for Beta)\n sendEvent(\n ws,\n {\n type: \"session.created\",\n session: {\n id: sessionId,\n object: \"realtime.session\",\n model: session.model,\n expires_at: Math.floor(Date.now() / 1000) + 3600,\n modalities: session.modalities,\n instructions: session.instructions,\n tools: session.tools,\n tool_choice: \"auto\",\n temperature: session.temperature,\n max_response_output_tokens: \"inf\",\n audio: {\n voice: session.voice,\n input_audio_format: session.input_audio_format,\n output_audio_format: session.output_audio_format,\n input_audio_noise_reduction: session.input_audio_noise_reduction,\n input_audio_transcription: session.input_audio_transcription,\n },\n turn_detection: session.turn_detection,\n type: session.type,\n reasoning: session.reasoning,\n },\n },\n isBeta,\n );\n\n // Serialize message processing to prevent event interleaving\n let pending = Promise.resolve();\n ws.on(\"message\", (raw: string) => {\n pending = pending.then(() =>\n processMessage(\n raw,\n ws,\n fixtures,\n journal,\n defaults,\n session,\n conversationItems,\n isBeta,\n ).catch((err: unknown) => {\n const msg = err instanceof Error ? err.message : \"Internal error\";\n logger.error(`WebSocket realtime error: ${msg}`);\n try {\n buildErrorRealtimeEvent(ws, msg, isBeta, \"server_error\");\n } catch (sendErr) {\n defaults.logger.debug(\n `Failed to send error to client: ${sendErr instanceof Error ? sendErr.message : \"unknown\"}`,\n );\n }\n }),\n );\n });\n}\n\nasync function processMessage(\n raw: string,\n ws: WebSocketConnection,\n fixtures: Fixture[],\n journal: Journal,\n defaults: {\n latency: number;\n chunkSize: number;\n replaySpeed?: number;\n model: string;\n logger: Logger;\n strict?: boolean;\n requestTransform?: (req: ChatCompletionRequest) => ChatCompletionRequest;\n testId?: string;\n upgradeHeaders?: import(\"node:http\").IncomingHttpHeaders;\n },\n session: SessionConfig,\n conversationItems: RealtimeItem[],\n isBeta: boolean,\n): Promise<void> {\n let parsed: RealtimeMessage;\n try {\n parsed = JSON.parse(raw) as RealtimeMessage;\n } catch (parseErr) {\n const detail = parseErr instanceof Error ? parseErr.message : \"unknown\";\n buildErrorRealtimeEvent(\n ws,\n `Malformed JSON: ${detail}`,\n isBeta,\n \"invalid_request_error\",\n \"invalid_json\",\n );\n return;\n }\n\n const msgType = parsed.type;\n\n // ── session.update ────────────────────────────────────────────────────\n if (msgType === \"session.update\") {\n if (parsed.session) {\n const s = parsed.session;\n\n // Validate session.type value before applying any mutations\n const validTypes = new Set([\"conversation\", \"transcription\", \"translation\"]);\n if ((s as Record<string, unknown>).type !== undefined) {\n if (!validTypes.has((s as Record<string, unknown>).type as string)) {\n sendEvent(\n ws,\n {\n type: \"error\",\n error: {\n message: `Invalid session type: ${(s as Record<string, unknown>).type}`,\n type: \"invalid_request_error\",\n code: \"invalid_session_config\",\n },\n },\n isBeta,\n );\n return;\n }\n }\n\n // Capture full pre-mutation snapshot for rollback on validation failure\n const prevSession = { ...session };\n\n if (s.instructions !== undefined) session.instructions = s.instructions;\n if (s.tools !== undefined) session.tools = s.tools;\n if (s.modalities !== undefined) session.modalities = s.modalities;\n if (s.model !== undefined) session.model = s.model;\n if (s.temperature !== undefined) session.temperature = s.temperature;\n if ((s as Record<string, unknown>).type !== undefined)\n session.type = (s as Record<string, unknown>).type as SessionConfig[\"type\"];\n // GA nested audio config\n if ((s as Record<string, unknown>).audio) {\n const audio = (s as Record<string, unknown>).audio as Record<string, unknown>;\n if (audio.voice !== undefined) session.voice = audio.voice as string | null;\n if (audio.input_audio_format !== undefined)\n session.input_audio_format = audio.input_audio_format as string | null;\n if (audio.output_audio_format !== undefined)\n session.output_audio_format = audio.output_audio_format as string | null;\n if (audio.input_audio_noise_reduction !== undefined)\n session.input_audio_noise_reduction = audio.input_audio_noise_reduction as {\n type: string;\n } | null;\n if (audio.input_audio_transcription !== undefined)\n session.input_audio_transcription = audio.input_audio_transcription as {\n model: string;\n } | null;\n }\n // Beta flat fields (backward compat)\n if (s.voice !== undefined) session.voice = s.voice;\n if (s.input_audio_format !== undefined) session.input_audio_format = s.input_audio_format;\n if (s.output_audio_format !== undefined) session.output_audio_format = s.output_audio_format;\n if (s.input_audio_noise_reduction !== undefined)\n session.input_audio_noise_reduction = s.input_audio_noise_reduction;\n if (s.input_audio_transcription !== undefined)\n session.input_audio_transcription = s.input_audio_transcription;\n // turn_detection config\n if (s.turn_detection !== undefined) session.turn_detection = s.turn_detection;\n // reasoning config\n if ((s as Record<string, unknown>).reasoning !== undefined)\n session.reasoning = (s as Record<string, unknown>).reasoning as {\n effort: string;\n } | null;\n\n // Validate model+type combinations (rollback on failure)\n const transcriptionModels = new Set([\n \"gpt-4o-transcribe\",\n \"gpt-4o-mini-transcribe\",\n \"gpt-realtime-whisper\",\n \"whisper-1\",\n ]);\n const translationModels = new Set([\n \"gpt-4o-transcribe\",\n \"gpt-4o-mini-transcribe\",\n \"gpt-realtime-translate\",\n ]);\n\n if (session.type === \"transcription\" && !transcriptionModels.has(session.model)) {\n Object.assign(session, prevSession);\n sendEvent(\n ws,\n {\n type: \"error\",\n error: {\n message: `Model ${s.model ?? prevSession.model} does not support session type transcription`,\n type: \"invalid_request_error\",\n code: \"invalid_session_config\",\n },\n },\n isBeta,\n );\n return;\n }\n if (session.type === \"translation\" && !translationModels.has(session.model)) {\n Object.assign(session, prevSession);\n sendEvent(\n ws,\n {\n type: \"error\",\n error: {\n message: `Model ${s.model ?? prevSession.model} does not support session type translation`,\n type: \"invalid_request_error\",\n code: \"invalid_session_config\",\n },\n },\n isBeta,\n );\n return;\n }\n }\n\n sendEvent(\n ws,\n {\n type: \"session.updated\",\n session: {\n object: \"realtime.session\",\n model: session.model,\n expires_at: Math.floor(Date.now() / 1000) + 3600,\n modalities: session.modalities,\n instructions: session.instructions,\n tools: session.tools,\n tool_choice: \"auto\",\n temperature: session.temperature,\n max_response_output_tokens: \"inf\",\n audio: {\n voice: session.voice,\n input_audio_format: session.input_audio_format,\n output_audio_format: session.output_audio_format,\n input_audio_noise_reduction: session.input_audio_noise_reduction,\n input_audio_transcription: session.input_audio_transcription,\n },\n turn_detection: session.turn_detection,\n type: session.type,\n reasoning: session.reasoning,\n },\n },\n isBeta,\n );\n return;\n }\n\n // ── conversation.item.create ──────────────────────────────────────────\n if (msgType === \"conversation.item.create\") {\n if (!parsed.item) {\n buildErrorRealtimeEvent(\n ws,\n \"Missing 'item' in conversation.item.create\",\n isBeta,\n \"invalid_request_error\",\n );\n return;\n }\n const item = parsed.item;\n if (!item.id) {\n item.id = realtimeId(\"item\");\n }\n const previousId =\n conversationItems.length > 0\n ? (conversationItems[conversationItems.length - 1].id ?? null)\n : null;\n conversationItems.push(item);\n sendEvent(ws, { type: \"conversation.item.added\", previous_item_id: previousId, item }, isBeta);\n return;\n }\n\n // ── response.create ───────────────────────────────────────────────────\n if (msgType === \"response.create\") {\n await handleResponseCreate(\n ws,\n fixtures,\n journal,\n defaults,\n session,\n conversationItems,\n isBeta,\n parsed.response,\n );\n return;\n }\n\n // ── input_audio_buffer.append ────────────────────────────────────────\n if (msgType === \"input_audio_buffer.append\") {\n // Accept silently — aimock doesn't process actual audio\n return;\n }\n\n // ── input_audio_buffer.commit ──────────────────────────────────────\n if (msgType === \"input_audio_buffer.commit\") {\n sendEvent(ws, { type: \"input_audio_buffer.committed\" }, isBeta);\n // In transcription/translation mode, add a placeholder user item\n if (session.type === \"transcription\" || session.type === \"translation\") {\n const audioItem: RealtimeItem = {\n type: \"message\",\n id: realtimeId(\"item\"),\n role: \"user\",\n content: [{ type: \"input_audio\", transcript: null }],\n };\n conversationItems.push(audioItem);\n sendEvent(\n ws,\n {\n type: \"conversation.item.added\",\n item: audioItem,\n },\n isBeta,\n );\n }\n return;\n }\n\n // ── input_audio_buffer.clear ───────────────────────────────────────\n if (msgType === \"input_audio_buffer.clear\") {\n sendEvent(ws, { type: \"input_audio_buffer.cleared\" }, isBeta);\n return;\n }\n\n // ── response.cancel ────────────────────────────────────────────────\n if (msgType === \"response.cancel\") {\n sendEvent(ws, { type: \"response.cancelled\" }, isBeta);\n return;\n }\n\n // Unknown message type — ignore silently (matches OpenAI behavior)\n}\n\nasync function handleResponseCreate(\n ws: WebSocketConnection,\n fixtures: Fixture[],\n journal: Journal,\n defaults: {\n latency: number;\n chunkSize: number;\n replaySpeed?: number;\n model: string;\n logger: Logger;\n strict?: boolean;\n requestTransform?: (req: ChatCompletionRequest) => ChatCompletionRequest;\n testId?: string;\n upgradeHeaders?: import(\"node:http\").IncomingHttpHeaders;\n },\n session: SessionConfig,\n conversationItems: RealtimeItem[],\n isBeta: boolean,\n responseOverrides?: { instructions?: string; [key: string]: unknown },\n): Promise<void> {\n const instructions = (responseOverrides?.instructions ?? session.instructions) || undefined;\n const messages = realtimeItemsToMessages(conversationItems, instructions, defaults.logger);\n\n const endpointTypeMap: Record<string, string> = {\n conversation: \"realtime\",\n transcription: \"realtime-transcription\",\n translation: \"realtime-translation\",\n };\n const endpointType = endpointTypeMap[session.type] ?? \"realtime\";\n\n const realtimeContextHeader = defaults.upgradeHeaders?.[\"x-aimock-context\"];\n const realtimeContext =\n typeof realtimeContextHeader === \"string\"\n ? realtimeContextHeader\n : Array.isArray(realtimeContextHeader) && realtimeContextHeader.length > 0\n ? realtimeContextHeader[0]\n : undefined;\n\n const completionReq: ChatCompletionRequest = {\n model: session.model,\n messages,\n _endpointType: endpointType,\n _context: realtimeContext,\n };\n\n const testId = defaults.testId ?? DEFAULT_TEST_ID;\n const { fixture, skippedBySequenceOrTurn } = matchFixtureDiagnostic(\n fixtures,\n completionReq,\n journal.getFixtureMatchCountsForTest(testId),\n defaults.requestTransform,\n );\n const responseId = realtimeId(\"resp\");\n\n if (fixture) {\n journal.incrementFixtureMatchCount(fixture, fixtures, testId);\n }\n\n if (!fixture) {\n if (resolveStrictMode(defaults.strict, defaults.upgradeHeaders)) {\n const strictMessage = strictNoMatchMessage(skippedBySequenceOrTurn);\n defaults.logger.error(strictNoMatchLogLine(\"WS\", \"/v1/realtime\", skippedBySequenceOrTurn));\n journal.add({\n method: \"WS\",\n path: \"/v1/realtime\",\n headers: flattenHeaders(defaults.upgradeHeaders ?? {}),\n body: completionReq,\n response: {\n status: 503,\n fixture: null,\n ...strictOverrideField(defaults.strict, defaults.upgradeHeaders),\n },\n });\n ws.close(1008, strictMessage);\n return;\n }\n journal.add({\n method: \"WS\",\n path: \"/v1/realtime\",\n headers: flattenHeaders(defaults.upgradeHeaders ?? {}),\n body: completionReq,\n response: {\n status: 404,\n fixture: null,\n ...strictOverrideField(defaults.strict, defaults.upgradeHeaders),\n },\n });\n // Send response.created with failed status then response.done with error\n sendEvent(\n ws,\n {\n type: \"response.created\",\n response: {\n id: responseId,\n object: \"realtime.response\",\n status: \"failed\",\n status_details: null,\n output: [],\n usage: null,\n },\n },\n isBeta,\n );\n sendEvent(\n ws,\n {\n type: \"response.done\",\n response: {\n id: responseId,\n object: \"realtime.response\",\n status: \"failed\",\n output: [],\n status_details: {\n type: \"error\",\n error: {\n message: \"No fixture matched\",\n type: \"invalid_request_error\",\n code: \"no_fixture_match\",\n },\n },\n usage: { total_tokens: 0, input_tokens: 0, output_tokens: 0 },\n },\n },\n isBeta,\n );\n return;\n }\n\n const response = await resolveResponse(fixture, completionReq);\n const latency = fixture.latency ?? defaults.latency;\n const chunkSize = Math.max(1, fixture.chunkSize ?? defaults.chunkSize);\n\n // ── Error fixture ───────────────────────────────────────────────────\n if (isErrorResponse(response)) {\n const status = response.status ?? 500;\n journal.add({\n method: \"WS\",\n path: \"/v1/realtime\",\n headers: flattenHeaders(defaults.upgradeHeaders ?? {}),\n body: completionReq,\n response: { status, fixture },\n });\n sendEvent(\n ws,\n {\n type: \"response.created\",\n response: {\n id: responseId,\n object: \"realtime.response\",\n status: \"failed\",\n status_details: null,\n output: [],\n usage: null,\n },\n },\n isBeta,\n );\n sendEvent(\n ws,\n {\n type: \"response.done\",\n response: {\n id: responseId,\n object: \"realtime.response\",\n status: \"failed\",\n output: [],\n status_details: {\n type: \"error\",\n error: {\n message: response.error.message,\n type: response.error.type,\n code: response.error.code,\n },\n },\n usage: { total_tokens: 0, input_tokens: 0, output_tokens: 0 },\n },\n },\n isBeta,\n );\n return;\n }\n\n // ── Content + tool calls response ──────────────────────────────────\n if (isContentWithToolCallsResponse(response)) {\n const journalEntry = journal.add({\n method: \"WS\",\n path: \"/v1/realtime\",\n headers: flattenHeaders(defaults.upgradeHeaders ?? {}),\n body: completionReq,\n response: { status: 200, fixture },\n });\n\n // ── Ordered blocks path (#274) ──────────────────────────────────\n // When the fixture supplies an ordered `blocks` array, stream the items in\n // array order so a blocks-only fixture (no `content`/`toolCalls`) never\n // emits an empty payload, and a combined `{content,toolCalls,blocks}`\n // fixture preserves block ordering vs the HTTP path. Unlike OpenAI\n // chat-completions (separate, non-positional content/tool channels), the\n // Realtime WS protocol sequences output items on the wire with explicit\n // `output_index`, so block order — including tool-before-text — IS\n // observable to a client and is honored here.\n if (response.blocks && response.blocks.length > 0) {\n await streamRealtimeBlocks(\n ws,\n resolveFixtureBlocks(response.blocks),\n responseId,\n fixture,\n defaults,\n latency,\n chunkSize,\n isBeta,\n conversationItems,\n journalEntry,\n );\n return;\n }\n\n // response.created\n sendEvent(\n ws,\n {\n type: \"response.created\",\n response: {\n id: responseId,\n object: \"realtime.response\",\n status: \"in_progress\",\n status_details: null,\n output: [],\n usage: null,\n },\n },\n isBeta,\n );\n\n const interruption = createInterruptionSignal(fixture);\n let interrupted = false;\n const allOutputItems: unknown[] = [];\n\n // ── Text content part ──────────────────────────────────────────\n const textItemId = realtimeId(\"item\");\n const contentIndex = 0;\n const textOutputIndex = 0;\n\n const textOutputItem = {\n id: textItemId,\n type: \"message\",\n role: \"assistant\",\n status: \"completed\",\n content: [{ type: \"output_text\", text: response.content }],\n };\n\n // Determine phase: text is \"commentary\" when tool calls are also present\n const hasToolCalls = response.toolCalls && response.toolCalls.length > 0;\n const textPhase = hasToolCalls ? \"commentary\" : \"final_answer\";\n\n // response.output_item.added (text)\n sendEvent(\n ws,\n {\n type: \"response.output_item.added\",\n response_id: responseId,\n output_index: textOutputIndex,\n item: {\n id: textItemId,\n type: \"message\",\n role: \"assistant\",\n status: \"in_progress\",\n content: [],\n phase: textPhase,\n },\n },\n isBeta,\n );\n\n // response.content_part.added\n sendEvent(\n ws,\n {\n type: \"response.content_part.added\",\n response_id: responseId,\n item_id: textItemId,\n output_index: textOutputIndex,\n content_index: contentIndex,\n part: { type: \"output_text\", text: \"\" },\n },\n isBeta,\n );\n\n // response.output_text.delta (chunked) — GA name\n const content = response.content ?? \"\";\n const replaySpeed = fixture.replaySpeed ?? defaults.replaySpeed;\n const { recordedTimings } = fixture;\n let eventIndex = 0;\n for (let i = 0; i < content.length; i += chunkSize) {\n if (ws.isClosed) break;\n const chunkDelay = calculateDelay(\n eventIndex,\n undefined,\n latency,\n recordedTimings,\n replaySpeed,\n );\n if (chunkDelay > 0) await delay(chunkDelay, interruption?.signal);\n if (interruption?.signal.aborted) {\n interrupted = true;\n break;\n }\n if (ws.isClosed) break;\n const chunk = content.slice(i, i + chunkSize);\n try {\n sendEvent(\n ws,\n {\n type: \"response.output_text.delta\",\n response_id: responseId,\n item_id: textItemId,\n output_index: textOutputIndex,\n content_index: contentIndex,\n delta: chunk,\n },\n isBeta,\n );\n } catch (err) {\n defaults.logger.debug(\"[ws-realtime] send failed during text streaming, closing\", err);\n break;\n }\n eventIndex++;\n interruption?.tick();\n if (interruption?.signal.aborted) {\n interrupted = true;\n break;\n }\n }\n\n if (interrupted) {\n ws.destroy();\n journalEntry.response.interrupted = true;\n journalEntry.response.interruptReason = interruption?.reason();\n interruption?.cleanup();\n return;\n }\n\n if (ws.isClosed) {\n interruption?.cleanup();\n return;\n }\n\n // response.output_text.done\n sendEvent(\n ws,\n {\n type: \"response.output_text.done\",\n response_id: responseId,\n item_id: textItemId,\n output_index: textOutputIndex,\n content_index: contentIndex,\n text: content,\n },\n isBeta,\n );\n\n if (ws.isClosed) {\n interruption?.cleanup();\n return;\n }\n\n // response.content_part.done\n sendEvent(\n ws,\n {\n type: \"response.content_part.done\",\n response_id: responseId,\n item_id: textItemId,\n output_index: textOutputIndex,\n content_index: contentIndex,\n part: { type: \"output_text\", text: content },\n },\n isBeta,\n );\n\n if (ws.isClosed) {\n interruption?.cleanup();\n return;\n }\n\n // response.output_item.done (text)\n sendEvent(\n ws,\n {\n type: \"response.output_item.done\",\n response_id: responseId,\n output_index: textOutputIndex,\n item: { ...textOutputItem, phase: textPhase },\n },\n isBeta,\n );\n\n // conversation.item.done (text message)\n sendEvent(\n ws,\n {\n type: \"conversation.item.done\",\n item: {\n id: textItemId,\n object: \"realtime.item\",\n type: \"message\",\n role: \"assistant\",\n status: \"completed\",\n content: textOutputItem.content,\n },\n },\n isBeta,\n );\n\n if (ws.isClosed) {\n interruption?.cleanup();\n return;\n }\n\n allOutputItems.push(textOutputItem);\n\n // ── Tool call parts ────────────────────────────────────────────\n const toolCalls = response.toolCalls ?? [];\n for (let tcIdx = 0; tcIdx < toolCalls.length; tcIdx++) {\n const tc = toolCalls[tcIdx];\n const callId = tc.id ?? generateToolCallId();\n const itemId = realtimeId(\"item\");\n const outputIndex = tcIdx + 1; // offset by 1 for the text item\n\n const toolOutputItem = {\n id: itemId,\n type: \"function_call\",\n status: \"completed\",\n call_id: callId,\n name: tc.name,\n arguments: tc.arguments,\n };\n\n // response.output_item.added\n sendEvent(\n ws,\n {\n type: \"response.output_item.added\",\n response_id: responseId,\n output_index: outputIndex,\n item: {\n id: itemId,\n type: \"function_call\",\n status: \"in_progress\",\n call_id: callId,\n name: tc.name,\n arguments: \"\",\n phase: \"final_answer\",\n },\n },\n isBeta,\n );\n\n // response.function_call_arguments.delta (chunked)\n // Continue eventIndex from content chunks to avoid re-triggering TTFT\n const args = tc.arguments;\n for (let i = 0; i < args.length; i += chunkSize) {\n if (ws.isClosed) break;\n const chunkDelay = calculateDelay(\n eventIndex,\n undefined,\n latency,\n recordedTimings,\n replaySpeed,\n );\n if (chunkDelay > 0) await delay(chunkDelay, interruption?.signal);\n if (interruption?.signal.aborted) {\n interrupted = true;\n break;\n }\n if (ws.isClosed) break;\n const chunk = args.slice(i, i + chunkSize);\n try {\n sendEvent(\n ws,\n {\n type: \"response.function_call_arguments.delta\",\n response_id: responseId,\n item_id: itemId,\n output_index: outputIndex,\n call_id: callId,\n delta: chunk,\n },\n isBeta,\n );\n } catch (err) {\n defaults.logger.debug(\n \"[ws-realtime] send failed during tool call streaming, closing\",\n err,\n );\n break;\n }\n eventIndex++;\n interruption?.tick();\n if (interruption?.signal.aborted) {\n interrupted = true;\n break;\n }\n }\n\n if (interrupted) break;\n\n if (ws.isClosed) break;\n\n // response.function_call_arguments.done\n sendEvent(\n ws,\n {\n type: \"response.function_call_arguments.done\",\n response_id: responseId,\n item_id: itemId,\n output_index: outputIndex,\n call_id: callId,\n arguments: args,\n },\n isBeta,\n );\n\n if (ws.isClosed) break;\n\n // response.output_item.done\n sendEvent(\n ws,\n {\n type: \"response.output_item.done\",\n response_id: responseId,\n output_index: outputIndex,\n item: { ...toolOutputItem, phase: \"final_answer\" },\n },\n isBeta,\n );\n\n // conversation.item.done (tool call)\n sendEvent(\n ws,\n {\n type: \"conversation.item.done\",\n item: {\n id: itemId,\n object: \"realtime.item\",\n type: \"function_call\",\n status: \"completed\",\n call_id: callId,\n name: tc.name,\n arguments: args,\n },\n },\n isBeta,\n );\n\n if (ws.isClosed) break;\n\n allOutputItems.push(toolOutputItem);\n }\n\n if (interrupted) {\n ws.destroy();\n journalEntry.response.interrupted = true;\n journalEntry.response.interruptReason = interruption?.reason();\n interruption?.cleanup();\n return;\n }\n\n interruption?.cleanup();\n\n if (ws.isClosed) return;\n\n // response.done\n sendEvent(\n ws,\n {\n type: \"response.done\",\n response: {\n id: responseId,\n object: \"realtime.response\",\n status: \"completed\",\n output: allOutputItems,\n usage: { total_tokens: 0, input_tokens: 0, output_tokens: 0 },\n },\n },\n isBeta,\n );\n\n // Accumulate into conversation for multi-turn\n conversationItems.push({\n type: \"message\",\n id: textItemId,\n role: \"assistant\",\n content: [{ type: \"text\", text: content }],\n });\n for (const item of allOutputItems.slice(1)) {\n conversationItems.push(item as RealtimeItem);\n }\n return;\n }\n\n // ── Text response ───────────────────────────────────────────────────\n if (isTextResponse(response)) {\n const journalEntry = journal.add({\n method: \"WS\",\n path: \"/v1/realtime\",\n headers: flattenHeaders(defaults.upgradeHeaders ?? {}),\n body: completionReq,\n response: { status: 200, fixture },\n });\n\n const itemId = realtimeId(\"item\");\n const contentIndex = 0;\n const outputIndex = 0;\n\n const outputItem = {\n id: itemId,\n type: \"message\",\n role: \"assistant\",\n status: \"completed\",\n content: [{ type: \"output_text\", text: response.content }],\n };\n\n // response.created\n sendEvent(\n ws,\n {\n type: \"response.created\",\n response: {\n id: responseId,\n object: \"realtime.response\",\n status: \"in_progress\",\n status_details: null,\n output: [],\n usage: null,\n },\n },\n isBeta,\n );\n\n // response.output_item.added\n sendEvent(\n ws,\n {\n type: \"response.output_item.added\",\n response_id: responseId,\n output_index: outputIndex,\n item: {\n id: itemId,\n type: \"message\",\n role: \"assistant\",\n status: \"in_progress\",\n content: [],\n phase: \"final_answer\",\n },\n },\n isBeta,\n );\n\n // response.content_part.added\n sendEvent(\n ws,\n {\n type: \"response.content_part.added\",\n response_id: responseId,\n item_id: itemId,\n output_index: outputIndex,\n content_index: contentIndex,\n part: { type: \"output_text\", text: \"\" },\n },\n isBeta,\n );\n\n // response.output_text.delta (chunked) — GA name\n const content = response.content ?? \"\";\n const replaySpeed = fixture.replaySpeed ?? defaults.replaySpeed;\n const { recordedTimings } = fixture;\n const interruption = createInterruptionSignal(fixture);\n let interrupted = false;\n let eventIndex = 0;\n\n for (let i = 0; i < content.length; i += chunkSize) {\n if (ws.isClosed) break;\n const chunkDelay = calculateDelay(\n eventIndex,\n undefined,\n latency,\n recordedTimings,\n replaySpeed,\n );\n if (chunkDelay > 0) await delay(chunkDelay, interruption?.signal);\n if (interruption?.signal.aborted) {\n interrupted = true;\n break;\n }\n if (ws.isClosed) break;\n const chunk = content.slice(i, i + chunkSize);\n try {\n sendEvent(\n ws,\n {\n type: \"response.output_text.delta\",\n response_id: responseId,\n item_id: itemId,\n output_index: outputIndex,\n content_index: contentIndex,\n delta: chunk,\n },\n isBeta,\n );\n } catch (err) {\n defaults.logger.debug(\"[ws-realtime] send failed during text streaming, closing\", err);\n break;\n }\n eventIndex++;\n interruption?.tick();\n if (interruption?.signal.aborted) {\n interrupted = true;\n break;\n }\n }\n\n if (interrupted) {\n ws.destroy();\n journalEntry.response.interrupted = true;\n journalEntry.response.interruptReason = interruption?.reason();\n interruption?.cleanup();\n return;\n }\n\n interruption?.cleanup();\n\n if (ws.isClosed) return;\n\n // response.output_text.done\n sendEvent(\n ws,\n {\n type: \"response.output_text.done\",\n response_id: responseId,\n item_id: itemId,\n output_index: outputIndex,\n content_index: contentIndex,\n text: content,\n },\n isBeta,\n );\n\n // response.content_part.done\n sendEvent(\n ws,\n {\n type: \"response.content_part.done\",\n response_id: responseId,\n item_id: itemId,\n output_index: outputIndex,\n content_index: contentIndex,\n part: { type: \"output_text\", text: content },\n },\n isBeta,\n );\n\n // response.output_item.done\n sendEvent(\n ws,\n {\n type: \"response.output_item.done\",\n response_id: responseId,\n output_index: outputIndex,\n item: { ...outputItem, phase: \"final_answer\" },\n },\n isBeta,\n );\n\n // conversation.item.done (text message)\n sendEvent(\n ws,\n {\n type: \"conversation.item.done\",\n item: {\n id: itemId,\n object: \"realtime.item\",\n type: \"message\",\n role: \"assistant\",\n status: \"completed\",\n content: outputItem.content,\n },\n },\n isBeta,\n );\n\n // response.done\n sendEvent(\n ws,\n {\n type: \"response.done\",\n response: {\n id: responseId,\n object: \"realtime.response\",\n status: \"completed\",\n output: [outputItem],\n usage: { total_tokens: 0, input_tokens: 0, output_tokens: 0 },\n },\n },\n isBeta,\n );\n\n // Accumulate assistant response into conversation for multi-turn\n conversationItems.push({\n type: \"message\",\n id: itemId,\n role: \"assistant\",\n content: [{ type: \"text\", text: content }],\n });\n return;\n }\n\n // ── Tool call response ──────────────────────────────────────────────\n if (isToolCallResponse(response)) {\n const journalEntry = journal.add({\n method: \"WS\",\n path: \"/v1/realtime\",\n headers: flattenHeaders(defaults.upgradeHeaders ?? {}),\n body: completionReq,\n response: { status: 200, fixture },\n });\n\n // response.created\n sendEvent(\n ws,\n {\n type: \"response.created\",\n response: {\n id: responseId,\n object: \"realtime.response\",\n status: \"in_progress\",\n status_details: null,\n output: [],\n usage: null,\n },\n },\n isBeta,\n );\n\n const outputItems: unknown[] = [];\n const interruption = createInterruptionSignal(fixture);\n const replaySpeed = fixture.replaySpeed ?? defaults.replaySpeed;\n const { recordedTimings } = fixture;\n let interrupted = false;\n let eventIndex = 0;\n\n for (let tcIdx = 0; tcIdx < response.toolCalls.length; tcIdx++) {\n const tc = response.toolCalls[tcIdx];\n const callId = tc.id ?? generateToolCallId();\n const itemId = realtimeId(\"item\");\n\n const outputItem = {\n id: itemId,\n type: \"function_call\",\n status: \"completed\",\n call_id: callId,\n name: tc.name,\n arguments: tc.arguments,\n };\n\n // response.output_item.added\n sendEvent(\n ws,\n {\n type: \"response.output_item.added\",\n response_id: responseId,\n output_index: tcIdx,\n item: {\n id: itemId,\n type: \"function_call\",\n status: \"in_progress\",\n call_id: callId,\n name: tc.name,\n arguments: \"\",\n phase: \"final_answer\",\n },\n },\n isBeta,\n );\n\n // response.function_call_arguments.delta (chunked)\n // eventIndex is continuous across all tool calls to avoid re-triggering TTFT\n const args = tc.arguments;\n for (let i = 0; i < args.length; i += chunkSize) {\n if (ws.isClosed) break;\n const chunkDelay = calculateDelay(\n eventIndex,\n undefined,\n latency,\n recordedTimings,\n replaySpeed,\n );\n if (chunkDelay > 0) await delay(chunkDelay, interruption?.signal);\n if (interruption?.signal.aborted) {\n interrupted = true;\n break;\n }\n if (ws.isClosed) break;\n const chunk = args.slice(i, i + chunkSize);\n try {\n sendEvent(\n ws,\n {\n type: \"response.function_call_arguments.delta\",\n response_id: responseId,\n item_id: itemId,\n output_index: tcIdx,\n call_id: callId,\n delta: chunk,\n },\n isBeta,\n );\n } catch (err) {\n defaults.logger.debug(\n \"[ws-realtime] send failed during tool call streaming, closing\",\n err,\n );\n break;\n }\n eventIndex++;\n interruption?.tick();\n if (interruption?.signal.aborted) {\n interrupted = true;\n break;\n }\n }\n\n if (interrupted) break;\n\n if (ws.isClosed) break;\n\n // response.function_call_arguments.done\n sendEvent(\n ws,\n {\n type: \"response.function_call_arguments.done\",\n response_id: responseId,\n item_id: itemId,\n output_index: tcIdx,\n call_id: callId,\n arguments: args,\n },\n isBeta,\n );\n\n // response.output_item.done\n sendEvent(\n ws,\n {\n type: \"response.output_item.done\",\n response_id: responseId,\n output_index: tcIdx,\n item: { ...outputItem, phase: \"final_answer\" },\n },\n isBeta,\n );\n\n // conversation.item.done (tool call)\n sendEvent(\n ws,\n {\n type: \"conversation.item.done\",\n item: {\n id: itemId,\n object: \"realtime.item\",\n type: \"function_call\",\n status: \"completed\",\n call_id: callId,\n name: tc.name,\n arguments: args,\n },\n },\n isBeta,\n );\n\n outputItems.push(outputItem);\n }\n\n if (interrupted) {\n ws.destroy();\n journalEntry.response.interrupted = true;\n journalEntry.response.interruptReason = interruption?.reason();\n interruption?.cleanup();\n return;\n }\n\n interruption?.cleanup();\n\n if (ws.isClosed) return;\n\n // response.done\n sendEvent(\n ws,\n {\n type: \"response.done\",\n response: {\n id: responseId,\n object: \"realtime.response\",\n status: \"completed\",\n output: outputItems,\n usage: { total_tokens: 0, input_tokens: 0, output_tokens: 0 },\n },\n },\n isBeta,\n );\n\n // Accumulate assistant tool calls into conversation for multi-turn\n // Reuse outputItems (which already have the correct call_id) to avoid generating divergent IDs\n for (const item of outputItems) {\n conversationItems.push(item as RealtimeItem);\n }\n return;\n }\n\n // Unknown response type\n journal.add({\n method: \"WS\",\n path: \"/v1/realtime\",\n headers: flattenHeaders(defaults.upgradeHeaders ?? {}),\n body: completionReq,\n response: { status: 500, fixture },\n });\n buildErrorRealtimeEvent(\n ws,\n \"Fixture response did not match any known type\",\n isBeta,\n \"server_error\",\n );\n}\n\n/**\n * Stream a content+toolCalls fixture's ordered `blocks` over the Realtime WS\n * surface (#274). Each block becomes its own output item, sequenced on the wire\n * with an incrementing `output_index` so a client observes the blocks in array\n * order (text and tool-call items can interleave in any order, unlike the\n * separate channels of chat-completions). Emits the same per-item event shapes\n * as the legacy text/tool branches; a blocks-only fixture therefore never\n * produces an empty payload.\n */\nasync function streamRealtimeBlocks(\n ws: WebSocketConnection,\n blocks: FixtureBlock[],\n responseId: string,\n fixture: Fixture,\n defaults: { latency: number; chunkSize: number; replaySpeed?: number; logger: Logger },\n latency: number,\n chunkSize: number,\n isBeta: boolean,\n conversationItems: RealtimeItem[],\n journalEntry: JournalEntry,\n): Promise<void> {\n // response.created\n sendEvent(\n ws,\n {\n type: \"response.created\",\n response: {\n id: responseId,\n object: \"realtime.response\",\n status: \"in_progress\",\n status_details: null,\n output: [],\n usage: null,\n },\n },\n isBeta,\n );\n\n const interruption = createInterruptionSignal(fixture);\n let interrupted = false;\n const allOutputItems: unknown[] = [];\n const replaySpeed = fixture.replaySpeed ?? defaults.replaySpeed;\n const { recordedTimings } = fixture;\n let eventIndex = 0;\n\n // A text block following a tool-call block (or any tool present in the array)\n // is \"commentary\"; a lone text block is the \"final_answer\".\n const hasToolCalls = blocks.some((b) => b.type === \"toolCall\");\n\n for (let outputIndex = 0; outputIndex < blocks.length; outputIndex++) {\n if (ws.isClosed || interrupted) break;\n const block = blocks[outputIndex];\n\n if (block.type === \"text\") {\n const textItemId = realtimeId(\"item\");\n const contentIndex = 0;\n const textPhase = hasToolCalls ? \"commentary\" : \"final_answer\";\n const text = block.text;\n\n const textOutputItem = {\n id: textItemId,\n type: \"message\",\n role: \"assistant\",\n status: \"completed\",\n content: [{ type: \"output_text\", text }],\n };\n\n sendEvent(\n ws,\n {\n type: \"response.output_item.added\",\n response_id: responseId,\n output_index: outputIndex,\n item: {\n id: textItemId,\n type: \"message\",\n role: \"assistant\",\n status: \"in_progress\",\n content: [],\n phase: textPhase,\n },\n },\n isBeta,\n );\n\n sendEvent(\n ws,\n {\n type: \"response.content_part.added\",\n response_id: responseId,\n item_id: textItemId,\n output_index: outputIndex,\n content_index: contentIndex,\n part: { type: \"output_text\", text: \"\" },\n },\n isBeta,\n );\n\n for (let i = 0; i < text.length; i += chunkSize) {\n if (ws.isClosed) break;\n const chunkDelay = calculateDelay(\n eventIndex,\n undefined,\n latency,\n recordedTimings,\n replaySpeed,\n );\n if (chunkDelay > 0) await delay(chunkDelay, interruption?.signal);\n if (interruption?.signal.aborted) {\n interrupted = true;\n break;\n }\n if (ws.isClosed) break;\n const chunk = text.slice(i, i + chunkSize);\n try {\n sendEvent(\n ws,\n {\n type: \"response.output_text.delta\",\n response_id: responseId,\n item_id: textItemId,\n output_index: outputIndex,\n content_index: contentIndex,\n delta: chunk,\n },\n isBeta,\n );\n } catch (err) {\n defaults.logger.debug(\n \"[ws-realtime] send failed during block text streaming, closing\",\n err,\n );\n break;\n }\n eventIndex++;\n interruption?.tick();\n if (interruption?.signal.aborted) {\n interrupted = true;\n break;\n }\n }\n\n if (interrupted || ws.isClosed) break;\n\n sendEvent(\n ws,\n {\n type: \"response.output_text.done\",\n response_id: responseId,\n item_id: textItemId,\n output_index: outputIndex,\n content_index: contentIndex,\n text,\n },\n isBeta,\n );\n\n if (ws.isClosed) break;\n\n sendEvent(\n ws,\n {\n type: \"response.content_part.done\",\n response_id: responseId,\n item_id: textItemId,\n output_index: outputIndex,\n content_index: contentIndex,\n part: { type: \"output_text\", text },\n },\n isBeta,\n );\n\n if (ws.isClosed) break;\n\n sendEvent(\n ws,\n {\n type: \"response.output_item.done\",\n response_id: responseId,\n output_index: outputIndex,\n item: { ...textOutputItem, phase: textPhase },\n },\n isBeta,\n );\n\n sendEvent(\n ws,\n {\n type: \"conversation.item.done\",\n item: {\n id: textItemId,\n object: \"realtime.item\",\n type: \"message\",\n role: \"assistant\",\n status: \"completed\",\n content: textOutputItem.content,\n },\n },\n isBeta,\n );\n\n if (ws.isClosed) break;\n allOutputItems.push(textOutputItem);\n } else {\n const callId = block.id ?? generateToolCallId();\n const itemId = realtimeId(\"item\");\n const args = block.arguments;\n\n const toolOutputItem = {\n id: itemId,\n type: \"function_call\",\n status: \"completed\",\n call_id: callId,\n name: block.name,\n arguments: args,\n };\n\n sendEvent(\n ws,\n {\n type: \"response.output_item.added\",\n response_id: responseId,\n output_index: outputIndex,\n item: {\n id: itemId,\n type: \"function_call\",\n status: \"in_progress\",\n call_id: callId,\n name: block.name,\n arguments: \"\",\n phase: \"final_answer\",\n },\n },\n isBeta,\n );\n\n for (let i = 0; i < args.length; i += chunkSize) {\n if (ws.isClosed) break;\n const chunkDelay = calculateDelay(\n eventIndex,\n undefined,\n latency,\n recordedTimings,\n replaySpeed,\n );\n if (chunkDelay > 0) await delay(chunkDelay, interruption?.signal);\n if (interruption?.signal.aborted) {\n interrupted = true;\n break;\n }\n if (ws.isClosed) break;\n const chunk = args.slice(i, i + chunkSize);\n try {\n sendEvent(\n ws,\n {\n type: \"response.function_call_arguments.delta\",\n response_id: responseId,\n item_id: itemId,\n output_index: outputIndex,\n call_id: callId,\n delta: chunk,\n },\n isBeta,\n );\n } catch (err) {\n defaults.logger.debug(\n \"[ws-realtime] send failed during block tool call streaming, closing\",\n err,\n );\n break;\n }\n eventIndex++;\n interruption?.tick();\n if (interruption?.signal.aborted) {\n interrupted = true;\n break;\n }\n }\n\n if (interrupted || ws.isClosed) break;\n\n sendEvent(\n ws,\n {\n type: \"response.function_call_arguments.done\",\n response_id: responseId,\n item_id: itemId,\n output_index: outputIndex,\n call_id: callId,\n arguments: args,\n },\n isBeta,\n );\n\n if (ws.isClosed) break;\n\n sendEvent(\n ws,\n {\n type: \"response.output_item.done\",\n response_id: responseId,\n output_index: outputIndex,\n item: { ...toolOutputItem, phase: \"final_answer\" },\n },\n isBeta,\n );\n\n sendEvent(\n ws,\n {\n type: \"conversation.item.done\",\n item: {\n id: itemId,\n object: \"realtime.item\",\n type: \"function_call\",\n status: \"completed\",\n call_id: callId,\n name: block.name,\n arguments: args,\n },\n },\n isBeta,\n );\n\n if (ws.isClosed) break;\n allOutputItems.push(toolOutputItem);\n }\n }\n\n if (interrupted) {\n ws.destroy();\n journalEntry.response.interrupted = true;\n journalEntry.response.interruptReason = interruption?.reason();\n interruption?.cleanup();\n return;\n }\n\n interruption?.cleanup();\n\n if (ws.isClosed) return;\n\n // response.done\n sendEvent(\n ws,\n {\n type: \"response.done\",\n response: {\n id: responseId,\n object: \"realtime.response\",\n status: \"completed\",\n output: allOutputItems,\n usage: { total_tokens: 0, input_tokens: 0, output_tokens: 0 },\n },\n },\n isBeta,\n );\n\n // Accumulate into conversation for multi-turn (each output item, in order).\n for (const item of allOutputItems) {\n const it = item as { type: string; id: string; content?: unknown };\n if (it.type === \"message\") {\n conversationItems.push({\n type: \"message\",\n id: it.id,\n role: \"assistant\",\n content: [\n {\n type: \"text\",\n text: ((it.content as Array<{ text?: string }>)?.[0]?.text ?? \"\") as string,\n },\n ],\n });\n } else {\n conversationItems.push(item as RealtimeItem);\n }\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ws-realtime.cjs","names":["generateToolCallId","DEFAULT_TEST_ID","matchFixtureDiagnostic","resolveStrictMode","strictNoMatchMessage","strictNoMatchLogLine","flattenHeaders","strictOverrideField","resolveResponse","isErrorResponse","isTranscriptionResponse","createInterruptionSignal","calculateDelay","delay","isContentWithToolCallsResponse","resolveFixtureBlocks","isTextResponse","isToolCallResponse"],"sources":["../src/ws-realtime.ts"],"sourcesContent":["/**\n * WebSocket handler for OpenAI Realtime API.\n *\n * Accepts Realtime API messages (session.update, conversation.item.create,\n * response.create) over WebSocket and sends back Realtime API events as\n * individual WebSocket text frames.\n */\n\nimport { randomBytes } from \"node:crypto\";\nimport type {\n ChatCompletionRequest,\n ChatMessage,\n Fixture,\n FixtureBlock,\n JournalEntry,\n} from \"./types.js\";\nimport { matchFixtureDiagnostic } from \"./router.js\";\nimport {\n generateToolCallId,\n flattenHeaders,\n isTextResponse,\n isToolCallResponse,\n isContentWithToolCallsResponse,\n isTranscriptionResponse,\n isErrorResponse,\n resolveFixtureBlocks,\n resolveResponse,\n resolveStrictMode,\n strictOverrideField,\n strictNoMatchMessage,\n strictNoMatchLogLine,\n} from \"./helpers.js\";\nimport { createInterruptionSignal } from \"./interruption.js\";\nimport { delay, calculateDelay } from \"./sse-writer.js\";\nimport { DEFAULT_TEST_ID, type Journal } from \"./journal.js\";\nimport type { Logger } from \"./logger.js\";\nimport type { WebSocketConnection } from \"./ws-framing.js\";\n\n/** Generate a Realtime-API-style ID with underscore separator (e.g. event_xxx, item_xxx). */\nfunction realtimeId(prefix: string): string {\n return `${prefix}_${randomBytes(12).toString(\"base64url\")}`;\n}\n\n// ─── Realtime protocol types ────────────────────────────────────────────────\n\ninterface RealtimeItem {\n type: \"message\" | \"function_call\" | \"function_call_output\";\n id?: string;\n role?: \"user\" | \"assistant\" | \"system\";\n content?: Array<{ type: string; text?: string; url?: string; transcript?: string | null }>;\n name?: string;\n call_id?: string;\n arguments?: string;\n output?: string;\n}\n\ninterface SessionConfig {\n model: string;\n modalities: string[];\n instructions: string;\n tools: unknown[];\n voice: string | null;\n input_audio_format: Record<string, unknown> | null;\n output_audio_format: Record<string, unknown> | null;\n input_audio_noise_reduction: { type: string } | null;\n input_audio_transcription: { model: string; language?: string; prompt?: string } | null;\n turn_detection: unknown | null;\n temperature: number;\n type: \"conversation\" | \"realtime\" | \"transcription\" | \"translation\";\n reasoning: { effort: string } | null;\n}\n\ninterface RealtimeMessage {\n type: string;\n event_id?: string;\n session?: Partial<SessionConfig>;\n item?: RealtimeItem;\n response?: {\n modalities?: string[];\n instructions?: string;\n [key: string]: unknown;\n };\n}\n\n/**\n * Transcription `usage` is MODEL-DEPENDENT on the live API, so the admitted\n * models are split by the usage shape they actually report rather than kept in\n * one flat list. Verified against api.openai.com with a single 5s clip on BOTH\n * the realtime WS (`conversation.item.input_audio_transcription.completed`) and\n * HTTP (`POST /v1/audio/transcriptions`) surfaces:\n *\n * whisper-1 → {\"type\":\"duration\",\"seconds\":5}\n * gpt-transcribe → {\"type\":\"duration\",\"seconds\":5}\n * gpt-live-transcribe → {\"type\":\"duration\",\"seconds\":5} (realtime only)\n * gpt-4o-transcribe → {\"type\":\"tokens\",\"total_tokens\":66,…}\n * gpt-4o-mini-transcribe → {\"type\":\"tokens\",\"total_tokens\":66,…}\n *\n * Keeping the split here — instead of a second list beside the admission\n * predicate — means a newly admitted model has to declare its family, and the\n * two can never disagree about which models exist.\n */\nfunction isDurationUsageTranscriptionModel(model: string): boolean {\n return (\n model === \"whisper-1\" ||\n model === \"gpt-transcribe\" ||\n model === \"gpt-live-transcribe\" ||\n model.startsWith(\"gpt-live-transcribe-\")\n );\n}\n\nfunction isTokenUsageTranscriptionModel(model: string): boolean {\n return model === \"gpt-4o-transcribe\" || model.startsWith(\"gpt-4o-mini-transcribe\");\n}\n\nfunction isLiveTranscriptionModel(model: string): boolean {\n return isDurationUsageTranscriptionModel(model) || isTokenUsageTranscriptionModel(model);\n}\n\n/**\n * The synthesized `usage` for a transcription with no fixture-supplied one.\n * aimock has no real audio to measure, so the magnitudes are zero — but the\n * DISCRIMINATOR must match the model's live family, otherwise a consumer\n * branching on `usage.type` reads `undefined` counts and fails silently.\n */\nfunction defaultTranscriptionUsage(model: string): Record<string, unknown> {\n if (isTokenUsageTranscriptionModel(model)) {\n return {\n type: \"tokens\",\n total_tokens: 0,\n input_tokens: 0,\n input_token_details: { text_tokens: 0, audio_tokens: 0 },\n output_tokens: 0,\n };\n }\n return { type: \"duration\", seconds: 0 };\n}\n\nfunction normalizeAudioFormat(value: unknown): Record<string, unknown> | null {\n if (value === null) return null;\n if (typeof value === \"string\") return { type: value };\n if (\n value &&\n typeof value === \"object\" &&\n typeof (value as Record<string, unknown>).type === \"string\"\n ) {\n return { ...(value as Record<string, unknown>) };\n }\n return null;\n}\n\nfunction transcriptionModel(session: SessionConfig): string {\n return session.input_audio_transcription?.model ?? session.model;\n}\n\n/**\n * A live-transcription session is discriminated by the session TYPE, never by\n * the mere presence of `input_audio_transcription`.\n *\n * `input_audio_transcription` is the documented way to configure input\n * transcription on an ordinary realtime CONVERSATION session, so treating it as\n * the discriminator made the canonical realtime setup (`{model:\"whisper-1\"}`)\n * route `input_audio_buffer.commit` into the transcription path. That silently\n * corrupted the conversation: it appended a phantom `[audio]` turn and consumed\n * a fixture match, so the next `response.create` replayed the FOLLOWING turn's\n * content. The client saw a well-formed — but wrong — response.\n *\n * A transcription session is established explicitly, either by connecting with\n * `?intent=transcription` or via `transcription_session.update` (both set\n * `session.type`), which is what the OpenAI realtime API requires too.\n */\nfunction isLiveTranscriptionSession(session: SessionConfig): boolean {\n return isLiveTranscriptionModel(transcriptionModel(session)) && session.type === \"transcription\";\n}\n\nfunction serializeSession(session: SessionConfig, sessionId?: string): Record<string, unknown> {\n // A transcription session is a DIFFERENT resource on the wire. Live GA sends:\n // {\"type\":\"transcription\",\"object\":\"realtime.transcription_session\",\n // \"id\":\"sess_…\",\"expires_at\":…,\"audio\":{\"input\":{…}},\"include\":null}\n // No model (there is no session-level model for a transcription session — the\n // model lives in audio.input.transcription), and none of the conversation-only\n // fields: modalities, instructions, tools, tool_choice, temperature,\n // max_response_output_tokens, reasoning, or audio.output.\n if (session.type === \"transcription\") {\n return {\n ...(sessionId ? { id: sessionId } : {}),\n object: \"realtime.transcription_session\",\n type: \"transcription\",\n expires_at: Math.floor(Date.now() / 1000) + 3600,\n audio: {\n input: {\n format: session.input_audio_format,\n noise_reduction: session.input_audio_noise_reduction,\n transcription: session.input_audio_transcription,\n turn_detection: session.turn_detection,\n },\n },\n include: null,\n };\n }\n return {\n ...(sessionId ? { id: sessionId } : {}),\n object: \"realtime.session\",\n model: session.model,\n expires_at: Math.floor(Date.now() / 1000) + 3600,\n modalities: session.modalities,\n instructions: session.instructions,\n tools: session.tools,\n tool_choice: \"auto\",\n temperature: session.temperature,\n max_response_output_tokens: \"inf\",\n audio: {\n input: {\n format: session.input_audio_format,\n noise_reduction: session.input_audio_noise_reduction,\n transcription: session.input_audio_transcription,\n turn_detection: session.turn_detection,\n },\n output: {\n format: session.output_audio_format,\n voice: session.voice,\n },\n },\n type:\n session.type === \"conversation\" || session.type === \"realtime\" ? \"realtime\" : session.type,\n reasoning: session.reasoning,\n };\n}\n\n// ─── Conversion helpers ─────────────────────────────────────────────────────\n\nexport function realtimeItemsToMessages(\n items: RealtimeItem[],\n instructions?: string,\n logger?: Logger,\n): ChatMessage[] {\n const messages: ChatMessage[] = [];\n\n if (instructions) {\n messages.push({ role: \"system\", content: instructions });\n }\n\n for (const item of items) {\n if (item.type === \"message\") {\n const role =\n item.role === \"assistant\" ? \"assistant\" : item.role === \"system\" ? \"system\" : \"user\";\n\n // Check if content contains multimodal input types (input_text, input_image, input_audio)\n const hasMultimodal = item.content?.some(\n (p) => p.type === \"input_text\" || p.type === \"input_image\" || p.type === \"input_audio\",\n );\n\n if (hasMultimodal && item.content) {\n // Map realtime input content types to ChatMessage content parts\n const mappedContent = item.content.map((part) => {\n if (part.type === \"input_text\") {\n return { type: \"text\" as const, text: part.text ?? \"\" };\n }\n if (part.type === \"input_image\") {\n return {\n type: \"image_url\" as const,\n image_url: { url: part.url ?? \"\" },\n };\n }\n if (part.type === \"input_audio\") {\n return { type: \"text\" as const, text: \"[audio input]\" };\n }\n // Pass through unknown content types as-is\n return part;\n });\n messages.push({ role, content: mappedContent });\n } else {\n // Join all text content parts (not just the first)\n const text =\n item.content\n ?.map((c) => c.text)\n .filter(Boolean)\n .join(\"\") ?? \"\";\n messages.push({ role, content: text });\n }\n } else if (item.type === \"function_call\") {\n if (!item.name) {\n logger?.warn(\"Realtime function_call item missing 'name'\");\n }\n messages.push({\n role: \"assistant\",\n content: null,\n tool_calls: [\n {\n id: item.call_id ?? generateToolCallId(),\n type: \"function\",\n function: {\n name: item.name ?? \"\",\n arguments: item.arguments ?? \"\",\n },\n },\n ],\n });\n } else if (item.type === \"function_call_output\") {\n if (!item.output) {\n logger?.warn(\"Realtime function_call_output item missing 'output'\");\n }\n messages.push({\n role: \"tool\",\n content: item.output ?? \"\",\n tool_call_id: item.call_id,\n });\n }\n }\n\n return messages;\n}\n\n// ─── GA -> Beta translation ─────────────────────────────────────────────────\n\n/** GA -> Beta event name mapping */\nconst GA_TO_BETA_EVENT: Record<string, string> = {\n \"response.output_text.delta\": \"response.text.delta\",\n \"response.output_text.done\": \"response.text.done\",\n \"response.output_audio.delta\": \"response.audio.delta\",\n \"response.output_audio.done\": \"response.audio.done\",\n \"response.output_audio_transcript.delta\": \"response.audio_transcript.delta\",\n \"response.output_audio_transcript.done\": \"response.audio_transcript.done\",\n \"conversation.item.added\": \"conversation.item.created\",\n};\n\n/** GA -> Beta content type mapping */\nconst GA_TO_BETA_CONTENT_TYPE: Record<string, string> = {\n output_text: \"text\",\n output_audio: \"audio\",\n};\n\n/** Events suppressed in Beta mode (GA-only events) */\nconst BETA_SUPPRESSED_EVENTS = new Set([\"conversation.item.done\"]);\n\nfunction translateGAToBeta(event: Record<string, unknown>): Record<string, unknown> | null {\n const type = event.type as string;\n if (BETA_SUPPRESSED_EVENTS.has(type)) return null;\n\n const translated = { ...event };\n if (GA_TO_BETA_EVENT[type]) {\n translated.type = GA_TO_BETA_EVENT[type];\n }\n\n // Translate content types in nested structures\n if (translated.part && typeof translated.part === \"object\") {\n const part = { ...(translated.part as Record<string, unknown>) };\n if (typeof part.type === \"string\" && GA_TO_BETA_CONTENT_TYPE[part.type]) {\n part.type = GA_TO_BETA_CONTENT_TYPE[part.type];\n }\n translated.part = part;\n }\n if (translated.content_part && typeof translated.content_part === \"object\") {\n const cp = { ...(translated.content_part as Record<string, unknown>) };\n if (typeof cp.type === \"string\" && GA_TO_BETA_CONTENT_TYPE[cp.type]) {\n cp.type = GA_TO_BETA_CONTENT_TYPE[cp.type];\n }\n translated.content_part = cp;\n }\n // Translate content arrays\n if (Array.isArray(translated.content)) {\n translated.content = (translated.content as Record<string, unknown>[]).map((c) => {\n if (typeof c.type === \"string\" && GA_TO_BETA_CONTENT_TYPE[c.type]) {\n return { ...c, type: GA_TO_BETA_CONTENT_TYPE[c.type] };\n }\n return c;\n });\n }\n // Translate item.content arrays (response.output_item.added/done, conversation.item.added)\n if (translated.item && typeof translated.item === \"object\") {\n const item = { ...(translated.item as Record<string, unknown>) };\n delete item.phase; // GA-only field\n if (Array.isArray(item.content)) {\n item.content = (item.content as Record<string, unknown>[]).map((c) => {\n if (typeof c.type === \"string\" && GA_TO_BETA_CONTENT_TYPE[c.type]) {\n return { ...c, type: GA_TO_BETA_CONTENT_TYPE[c.type] };\n }\n return c;\n });\n }\n translated.item = item;\n }\n // Translate response.output[].content arrays (response.done)\n if (translated.response && typeof translated.response === \"object\") {\n const resp = { ...(translated.response as Record<string, unknown>) };\n if (Array.isArray(resp.output)) {\n resp.output = (resp.output as Record<string, unknown>[]).map((outItem) => {\n const o = { ...(outItem as Record<string, unknown>) };\n if (Array.isArray(o.content)) {\n o.content = (o.content as Record<string, unknown>[]).map((c) =>\n typeof c.type === \"string\" && GA_TO_BETA_CONTENT_TYPE[c.type]\n ? { ...c, type: GA_TO_BETA_CONTENT_TYPE[c.type] }\n : c,\n );\n }\n return o;\n });\n }\n translated.response = resp;\n }\n\n // Flatten GA session config for Beta (session.created / session.updated)\n if (type === \"session.created\" || type === \"session.updated\") {\n if (translated.session && typeof translated.session === \"object\") {\n const session = { ...(translated.session as Record<string, unknown>) };\n if (session.audio && typeof session.audio === \"object\") {\n const audio = session.audio as Record<string, unknown>;\n const input = audio.input as Record<string, unknown> | undefined;\n const output = audio.output as Record<string, unknown> | undefined;\n session.voice = output?.voice;\n session.input_audio_format = (input?.format as Record<string, unknown> | undefined)?.type;\n session.output_audio_format = (output?.format as Record<string, unknown> | undefined)?.type;\n session.input_audio_transcription = input?.transcription;\n session.input_audio_noise_reduction = input?.noise_reduction;\n session.turn_detection = input?.turn_detection ?? null;\n delete session.audio;\n }\n delete session.type;\n delete session.reasoning;\n translated.session = session;\n }\n }\n\n return translated;\n}\n\n// ─── Event sending ──────────────────────────────────────────────────────────\n\nfunction sendEvent(ws: WebSocketConnection, event: Record<string, unknown>, isBeta: boolean): void {\n const out = { ...event, event_id: event.event_id ?? realtimeId(\"event\") };\n if (isBeta) {\n const translated = translateGAToBeta(out);\n if (translated === null) return; // suppressed in Beta mode\n ws.send(JSON.stringify(translated));\n } else {\n ws.send(JSON.stringify(out));\n }\n}\n\nfunction buildErrorRealtimeEvent(\n ws: WebSocketConnection,\n message: string,\n isBeta: boolean,\n type = \"invalid_request_error\",\n code?: string,\n): void {\n sendEvent(ws, { type: \"error\", error: { message, type, code } }, isBeta);\n}\n\n// ─── Main handler ───────────────────────────────────────────────────────────\n\nexport function handleWebSocketRealtime(\n ws: WebSocketConnection,\n fixtures: Fixture[],\n journal: Journal,\n defaults: {\n latency: number;\n chunkSize: number;\n replaySpeed?: number;\n model: string;\n logger: Logger;\n strict?: boolean;\n requestTransform?: (req: ChatCompletionRequest) => ChatCompletionRequest;\n testId?: string;\n upgradeHeaders?: import(\"node:http\").IncomingHttpHeaders;\n transcriptionIntent?: boolean;\n },\n): void {\n const { logger } = defaults;\n const sessionId = realtimeId(\"sess\");\n\n const isBeta = defaults.upgradeHeaders?.[\"openai-beta\"]\n ? String(defaults.upgradeHeaders[\"openai-beta\"]).includes(\"realtime=v1\")\n : false;\n\n const session: SessionConfig = {\n model: defaults.model,\n modalities: [\"text\"],\n instructions: \"\",\n tools: [],\n voice: null,\n input_audio_format: null,\n output_audio_format: null,\n input_audio_noise_reduction: null,\n input_audio_transcription: null,\n turn_detection: null,\n temperature: 0.8,\n type: defaults.transcriptionIntent ? \"transcription\" : \"conversation\",\n reasoning: null,\n };\n\n const conversationItems: RealtimeItem[] = [];\n\n // Send session.created immediately on connect (GA format — shim flattens for Beta)\n sendEvent(\n ws,\n {\n type: \"session.created\",\n session: serializeSession(session, sessionId),\n },\n isBeta,\n );\n\n // Serialize message processing to prevent event interleaving\n let pending = Promise.resolve();\n ws.on(\"message\", (raw: string) => {\n pending = pending.then(() =>\n processMessage(\n raw,\n ws,\n fixtures,\n journal,\n defaults,\n session,\n conversationItems,\n isBeta,\n ).catch((err: unknown) => {\n const msg = err instanceof Error ? err.message : \"Internal error\";\n logger.error(`WebSocket realtime error: ${msg}`);\n try {\n buildErrorRealtimeEvent(ws, msg, isBeta, \"server_error\");\n } catch (sendErr) {\n defaults.logger.debug(\n `Failed to send error to client: ${sendErr instanceof Error ? sendErr.message : \"unknown\"}`,\n );\n }\n }),\n );\n });\n}\n\nasync function processMessage(\n raw: string,\n ws: WebSocketConnection,\n fixtures: Fixture[],\n journal: Journal,\n defaults: {\n latency: number;\n chunkSize: number;\n replaySpeed?: number;\n model: string;\n logger: Logger;\n strict?: boolean;\n requestTransform?: (req: ChatCompletionRequest) => ChatCompletionRequest;\n testId?: string;\n upgradeHeaders?: import(\"node:http\").IncomingHttpHeaders;\n transcriptionIntent?: boolean;\n },\n session: SessionConfig,\n conversationItems: RealtimeItem[],\n isBeta: boolean,\n): Promise<void> {\n let parsed: RealtimeMessage;\n try {\n parsed = JSON.parse(raw) as RealtimeMessage;\n } catch (parseErr) {\n const detail = parseErr instanceof Error ? parseErr.message : \"unknown\";\n buildErrorRealtimeEvent(\n ws,\n `Malformed JSON: ${detail}`,\n isBeta,\n \"invalid_request_error\",\n \"invalid_json\",\n );\n return;\n }\n\n const msgType = parsed.type;\n\n // ── session.update / transcription_session.update ─────────────────────\n const isTranscriptionSessionUpdate = msgType === \"transcription_session.update\";\n if (msgType === \"session.update\" || isTranscriptionSessionUpdate) {\n if (isTranscriptionSessionUpdate && !defaults.transcriptionIntent) {\n buildErrorRealtimeEvent(\n ws,\n \"transcription_session.update requires a realtime connection with intent=transcription\",\n isBeta,\n \"invalid_request_error\",\n \"invalid_session_config\",\n );\n return;\n }\n if (parsed.session) {\n const s = parsed.session;\n\n // Validate session.type value before applying any mutations\n const validTypes = new Set([\"conversation\", \"realtime\", \"transcription\", \"translation\"]);\n if ((s as Record<string, unknown>).type !== undefined) {\n if (!validTypes.has((s as Record<string, unknown>).type as string)) {\n sendEvent(\n ws,\n {\n type: \"error\",\n error: {\n message: `Invalid session type: ${(s as Record<string, unknown>).type}`,\n type: \"invalid_request_error\",\n code: \"invalid_session_config\",\n },\n },\n isBeta,\n );\n return;\n }\n }\n\n // Capture full pre-mutation snapshot for rollback on validation failure\n const prevSession = { ...session };\n\n // `model` is deliberately NOT applied and NOT rejected. The session model\n // is fixed when the connection is established, and live GA silently\n // ignores the field: a probe that sent model:\"gpt-realtime\" on a\n // \"gpt-realtime-mini\" connection got a normal `session.updated` echoing\n // the ORIGINAL model with the rest of the update applied. Erroring here\n // stranded the very common client that echoes back the session object it\n // received — no `session.updated` ever arrived, so clients awaiting it\n // hung until their own timeout.\n\n if (isTranscriptionSessionUpdate) session.type = \"transcription\";\n\n if (s.instructions !== undefined) session.instructions = s.instructions;\n if (s.tools !== undefined) session.tools = s.tools;\n if (s.modalities !== undefined) session.modalities = s.modalities;\n if (s.temperature !== undefined) session.temperature = s.temperature;\n if ((s as Record<string, unknown>).type !== undefined)\n session.type = (s as Record<string, unknown>).type as SessionConfig[\"type\"];\n // GA nested audio config\n if ((s as Record<string, unknown>).audio) {\n const audio = (s as Record<string, unknown>).audio as Record<string, unknown>;\n if (audio.voice !== undefined) session.voice = audio.voice as string | null;\n if (audio.input_audio_format !== undefined)\n session.input_audio_format = normalizeAudioFormat(audio.input_audio_format);\n if (audio.output_audio_format !== undefined)\n session.output_audio_format = normalizeAudioFormat(audio.output_audio_format);\n if (audio.input_audio_noise_reduction !== undefined)\n session.input_audio_noise_reduction = audio.input_audio_noise_reduction as {\n type: string;\n } | null;\n if (audio.input_audio_transcription !== undefined)\n session.input_audio_transcription = audio.input_audio_transcription as {\n model: string;\n language?: string;\n prompt?: string;\n } | null;\n // Current Realtime session shape nests input transcription under\n // session.audio.input.transcription.\n if (audio.input && typeof audio.input === \"object\") {\n const input = audio.input as Record<string, unknown>;\n if (input.transcription !== undefined)\n session.input_audio_transcription = input.transcription as {\n model: string;\n language?: string;\n prompt?: string;\n } | null;\n if (input.noise_reduction !== undefined)\n session.input_audio_noise_reduction = input.noise_reduction as { type: string } | null;\n if (input.turn_detection !== undefined) session.turn_detection = input.turn_detection;\n if (input.format !== undefined)\n session.input_audio_format = normalizeAudioFormat(input.format);\n }\n if (audio.output && typeof audio.output === \"object\") {\n const output = audio.output as Record<string, unknown>;\n if (output.voice !== undefined) session.voice = output.voice as string | null;\n if (output.format !== undefined)\n session.output_audio_format = normalizeAudioFormat(output.format);\n }\n }\n // Beta flat fields (backward compat)\n if (s.voice !== undefined) session.voice = s.voice;\n if (s.input_audio_format !== undefined)\n session.input_audio_format = normalizeAudioFormat(s.input_audio_format);\n if (s.output_audio_format !== undefined)\n session.output_audio_format = normalizeAudioFormat(s.output_audio_format);\n if (s.input_audio_noise_reduction !== undefined)\n session.input_audio_noise_reduction = s.input_audio_noise_reduction;\n if (s.input_audio_transcription !== undefined)\n session.input_audio_transcription = s.input_audio_transcription;\n // turn_detection config\n if (s.turn_detection !== undefined) session.turn_detection = s.turn_detection;\n // reasoning config\n if ((s as Record<string, unknown>).reasoning !== undefined)\n session.reasoning = (s as Record<string, unknown>).reasoning as {\n effort: string;\n } | null;\n\n // Validate model+type combinations (rollback on failure)\n const transcriptionModels = new Set([\n \"gpt-4o-transcribe\",\n \"gpt-4o-mini-transcribe\",\n \"gpt-realtime-whisper\",\n \"whisper-1\",\n ]);\n const translationModels = new Set([\n \"gpt-4o-transcribe\",\n \"gpt-4o-mini-transcribe\",\n \"gpt-realtime-translate\",\n ]);\n\n const candidateTranscriptionModel = transcriptionModel(session);\n if (\n session.type === \"transcription\" &&\n !transcriptionModels.has(candidateTranscriptionModel) &&\n !isLiveTranscriptionSession(session)\n ) {\n Object.assign(session, prevSession);\n sendEvent(\n ws,\n {\n type: \"error\",\n error: {\n message: `Model ${candidateTranscriptionModel} does not support session type transcription`,\n type: \"invalid_request_error\",\n code: \"invalid_session_config\",\n },\n },\n isBeta,\n );\n return;\n }\n if (session.type === \"translation\" && !translationModels.has(session.model)) {\n Object.assign(session, prevSession);\n sendEvent(\n ws,\n {\n type: \"error\",\n error: {\n message: `Model ${s.model ?? prevSession.model} does not support session type translation`,\n type: \"invalid_request_error\",\n code: \"invalid_session_config\",\n },\n },\n isBeta,\n );\n return;\n }\n }\n\n sendEvent(\n ws,\n {\n type: isTranscriptionSessionUpdate ? \"transcription_session.updated\" : \"session.updated\",\n session: serializeSession(session),\n },\n isBeta,\n );\n return;\n }\n\n // ── conversation.item.create ──────────────────────────────────────────\n if (msgType === \"conversation.item.create\") {\n if (!parsed.item) {\n buildErrorRealtimeEvent(\n ws,\n \"Missing 'item' in conversation.item.create\",\n isBeta,\n \"invalid_request_error\",\n );\n return;\n }\n const item = parsed.item;\n if (!item.id) {\n item.id = realtimeId(\"item\");\n }\n const previousId =\n conversationItems.length > 0\n ? (conversationItems[conversationItems.length - 1].id ?? null)\n : null;\n conversationItems.push(item);\n sendEvent(ws, { type: \"conversation.item.added\", previous_item_id: previousId, item }, isBeta);\n return;\n }\n\n // ── response.create ───────────────────────────────────────────────────\n if (msgType === \"response.create\") {\n await handleResponseCreate(\n ws,\n fixtures,\n journal,\n defaults,\n session,\n conversationItems,\n isBeta,\n parsed.response,\n );\n return;\n }\n\n // ── input_audio_buffer.append ────────────────────────────────────────\n if (msgType === \"input_audio_buffer.append\") {\n // Accept silently — aimock doesn't process actual audio\n return;\n }\n\n // ── input_audio_buffer.commit ──────────────────────────────────────\n if (msgType === \"input_audio_buffer.commit\") {\n sendEvent(ws, { type: \"input_audio_buffer.committed\" }, isBeta);\n // In transcription/translation mode, add a placeholder user item. A plain\n // conversation session gets only the `committed` ack, even when it\n // configures `input_audio_transcription` — see isLiveTranscriptionSession.\n if (session.type === \"transcription\" || session.type === \"translation\") {\n const audioItem: RealtimeItem = {\n type: \"message\",\n id: realtimeId(\"item\"),\n role: \"user\",\n content: [{ type: \"input_audio\", transcript: null }],\n };\n conversationItems.push(audioItem);\n sendEvent(\n ws,\n {\n type: \"conversation.item.added\",\n item: audioItem,\n },\n isBeta,\n );\n if (isLiveTranscriptionSession(session)) {\n await emitLiveTranscriptionEvents(\n ws,\n fixtures,\n journal,\n defaults,\n session,\n audioItem,\n isBeta,\n );\n }\n }\n return;\n }\n\n // ── input_audio_buffer.clear ───────────────────────────────────────\n if (msgType === \"input_audio_buffer.clear\") {\n sendEvent(ws, { type: \"input_audio_buffer.cleared\" }, isBeta);\n return;\n }\n\n // ── response.cancel ────────────────────────────────────────────────\n if (msgType === \"response.cancel\") {\n sendEvent(ws, { type: \"response.cancelled\" }, isBeta);\n return;\n }\n\n // Unknown message type — ignore silently (matches OpenAI behavior)\n}\n\nasync function emitLiveTranscriptionEvents(\n ws: WebSocketConnection,\n fixtures: Fixture[],\n journal: Journal,\n defaults: {\n latency: number;\n chunkSize: number;\n replaySpeed?: number;\n model: string;\n logger: Logger;\n strict?: boolean;\n requestTransform?: (req: ChatCompletionRequest) => ChatCompletionRequest;\n testId?: string;\n upgradeHeaders?: import(\"node:http\").IncomingHttpHeaders;\n },\n session: SessionConfig,\n audioItem: RealtimeItem,\n isBeta: boolean,\n): Promise<void> {\n const realtimeContextHeader = defaults.upgradeHeaders?.[\"x-aimock-context\"];\n const realtimeContext =\n typeof realtimeContextHeader === \"string\"\n ? realtimeContextHeader\n : Array.isArray(realtimeContextHeader) && realtimeContextHeader.length > 0\n ? realtimeContextHeader[0]\n : undefined;\n const request: ChatCompletionRequest = {\n model: transcriptionModel(session),\n messages: realtimeItemsToMessages([audioItem], undefined, defaults.logger),\n _endpointType: \"realtime-transcription\",\n _context: realtimeContext,\n };\n const testId = defaults.testId ?? DEFAULT_TEST_ID;\n const { fixture, skippedBySequenceOrTurn } = matchFixtureDiagnostic(\n fixtures,\n request,\n journal.getFixtureMatchCountsForTest(testId),\n defaults.requestTransform,\n );\n const itemId = audioItem.id ?? realtimeId(\"item\");\n\n if (!fixture) {\n if (resolveStrictMode(defaults.strict, defaults.upgradeHeaders)) {\n const strictMessage = strictNoMatchMessage(skippedBySequenceOrTurn);\n defaults.logger.error(strictNoMatchLogLine(\"WS\", \"/v1/realtime\", skippedBySequenceOrTurn));\n journal.add({\n method: \"WS\",\n path: \"/v1/realtime\",\n headers: flattenHeaders(defaults.upgradeHeaders ?? {}),\n body: request,\n response: {\n status: 503,\n fixture: null,\n ...strictOverrideField(defaults.strict, defaults.upgradeHeaders),\n },\n });\n ws.close(1008, strictMessage);\n return;\n }\n\n journal.add({\n method: \"WS\",\n path: \"/v1/realtime\",\n headers: flattenHeaders(defaults.upgradeHeaders ?? {}),\n body: request,\n response: {\n status: 404,\n fixture: null,\n ...strictOverrideField(defaults.strict, defaults.upgradeHeaders),\n },\n });\n sendLiveTranscriptionFailure(\n ws,\n itemId,\n { message: \"No fixture matched\", type: \"invalid_request_error\", code: \"no_fixture_match\" },\n isBeta,\n );\n return;\n }\n // The match count is burned only once the response is confirmed usable for\n // this endpoint. router.ts exempts `realtime*` requests from the\n // response-shape gate, so a generic chat fixture still matches this lookup;\n // counting it here — before the shape check below — silently advanced a\n // sequenced conversation by one turn. An error fixture IS a deliberate match\n // and still counts, matching the chat path.\n const response = await resolveResponse(fixture, request);\n if (isErrorResponse(response)) {\n journal.incrementFixtureMatchCount(fixture, fixtures, testId);\n journal.add({\n method: \"WS\",\n path: \"/v1/realtime\",\n headers: flattenHeaders(defaults.upgradeHeaders ?? {}),\n body: request,\n response: { status: response.status ?? 500, fixture },\n });\n sendLiveTranscriptionFailure(\n ws,\n itemId,\n {\n message: response.error.message,\n type: response.error.type ?? \"server_error\",\n ...(response.error.code !== undefined && { code: response.error.code }),\n },\n isBeta,\n );\n return;\n }\n if (!isTranscriptionResponse(response)) {\n journal.add({\n method: \"WS\",\n path: \"/v1/realtime\",\n headers: flattenHeaders(defaults.upgradeHeaders ?? {}),\n body: request,\n response: { status: 500, fixture },\n });\n sendLiveTranscriptionFailure(\n ws,\n itemId,\n { message: \"Fixture response is not a transcription type\", type: \"server_error\" },\n isBeta,\n );\n return;\n }\n journal.incrementFixtureMatchCount(fixture, fixtures, testId);\n\n const transcript = response.transcription.text;\n // The synthesized usage shape is MODEL-AWARE — duration for whisper-1 /\n // gpt-transcribe / gpt-live-transcribe, a token breakdown for the\n // gpt-4o-transcribe families (see defaultTranscriptionUsage). A\n // fixture-supplied usage always wins.\n const usage = response.transcription.usage ?? defaultTranscriptionUsage(request.model);\n const journalEntry = journal.add({\n method: \"WS\",\n path: \"/v1/realtime\",\n headers: flattenHeaders(defaults.upgradeHeaders ?? {}),\n body: request,\n response: { status: 200, fixture },\n });\n const latency = fixture.latency ?? defaults.latency;\n const chunkSize = Math.max(1, fixture.chunkSize ?? defaults.chunkSize);\n const replaySpeed = fixture.replaySpeed ?? defaults.replaySpeed;\n const interruption = createInterruptionSignal(fixture);\n let chunkIndex = 0;\n for (let index = 0; index < transcript.length; index += chunkSize) {\n const chunkDelay = calculateDelay(\n chunkIndex,\n fixture.streamingProfile,\n latency,\n fixture.recordedTimings,\n replaySpeed,\n );\n if (chunkDelay > 0) await delay(chunkDelay, interruption?.signal);\n if (interruption?.signal.aborted || ws.isClosed) {\n if (interruption?.signal.aborted) {\n journalEntry.response.interrupted = true;\n journalEntry.response.interruptReason = interruption.reason();\n ws.destroy();\n }\n interruption?.cleanup();\n return;\n }\n sendEvent(\n ws,\n {\n type: \"conversation.item.input_audio_transcription.delta\",\n item_id: itemId,\n content_index: 0,\n delta: transcript.slice(index, index + chunkSize),\n },\n isBeta,\n );\n interruption?.tick();\n chunkIndex++;\n }\n if (interruption?.signal.aborted) {\n journalEntry.response.interrupted = true;\n journalEntry.response.interruptReason = interruption.reason();\n ws.destroy();\n interruption.cleanup();\n return;\n }\n sendEvent(\n ws,\n {\n type: \"conversation.item.input_audio_transcription.completed\",\n item_id: itemId,\n content_index: 0,\n transcript,\n usage,\n ...(response.transcription.languages !== undefined\n ? { languages: response.transcription.languages }\n : {}),\n },\n isBeta,\n );\n interruption?.cleanup();\n}\n\nfunction sendLiveTranscriptionFailure(\n ws: WebSocketConnection,\n itemId: string,\n error: { message: string; type: string; code?: string },\n isBeta: boolean,\n): void {\n sendEvent(\n ws,\n {\n type: \"conversation.item.input_audio_transcription.failed\",\n item_id: itemId,\n content_index: 0,\n error,\n },\n isBeta,\n );\n}\n\nasync function handleResponseCreate(\n ws: WebSocketConnection,\n fixtures: Fixture[],\n journal: Journal,\n defaults: {\n latency: number;\n chunkSize: number;\n replaySpeed?: number;\n model: string;\n logger: Logger;\n strict?: boolean;\n requestTransform?: (req: ChatCompletionRequest) => ChatCompletionRequest;\n testId?: string;\n upgradeHeaders?: import(\"node:http\").IncomingHttpHeaders;\n },\n session: SessionConfig,\n conversationItems: RealtimeItem[],\n isBeta: boolean,\n responseOverrides?: { instructions?: string; [key: string]: unknown },\n): Promise<void> {\n const instructions = (responseOverrides?.instructions ?? session.instructions) || undefined;\n const messages = realtimeItemsToMessages(conversationItems, instructions, defaults.logger);\n\n const endpointTypeMap: Record<string, string> = {\n conversation: \"realtime\",\n transcription: \"realtime-transcription\",\n translation: \"realtime-translation\",\n };\n const endpointType = endpointTypeMap[session.type] ?? \"realtime\";\n\n const realtimeContextHeader = defaults.upgradeHeaders?.[\"x-aimock-context\"];\n const realtimeContext =\n typeof realtimeContextHeader === \"string\"\n ? realtimeContextHeader\n : Array.isArray(realtimeContextHeader) && realtimeContextHeader.length > 0\n ? realtimeContextHeader[0]\n : undefined;\n\n const completionReq: ChatCompletionRequest = {\n model: session.model,\n messages,\n _endpointType: endpointType,\n _context: realtimeContext,\n };\n\n const testId = defaults.testId ?? DEFAULT_TEST_ID;\n const { fixture, skippedBySequenceOrTurn } = matchFixtureDiagnostic(\n fixtures,\n completionReq,\n journal.getFixtureMatchCountsForTest(testId),\n defaults.requestTransform,\n );\n const responseId = realtimeId(\"resp\");\n\n if (fixture) {\n journal.incrementFixtureMatchCount(fixture, fixtures, testId);\n }\n\n if (!fixture) {\n if (resolveStrictMode(defaults.strict, defaults.upgradeHeaders)) {\n const strictMessage = strictNoMatchMessage(skippedBySequenceOrTurn);\n defaults.logger.error(strictNoMatchLogLine(\"WS\", \"/v1/realtime\", skippedBySequenceOrTurn));\n journal.add({\n method: \"WS\",\n path: \"/v1/realtime\",\n headers: flattenHeaders(defaults.upgradeHeaders ?? {}),\n body: completionReq,\n response: {\n status: 503,\n fixture: null,\n ...strictOverrideField(defaults.strict, defaults.upgradeHeaders),\n },\n });\n ws.close(1008, strictMessage);\n return;\n }\n journal.add({\n method: \"WS\",\n path: \"/v1/realtime\",\n headers: flattenHeaders(defaults.upgradeHeaders ?? {}),\n body: completionReq,\n response: {\n status: 404,\n fixture: null,\n ...strictOverrideField(defaults.strict, defaults.upgradeHeaders),\n },\n });\n // Send response.created with failed status then response.done with error\n sendEvent(\n ws,\n {\n type: \"response.created\",\n response: {\n id: responseId,\n object: \"realtime.response\",\n status: \"failed\",\n status_details: null,\n output: [],\n usage: null,\n },\n },\n isBeta,\n );\n sendEvent(\n ws,\n {\n type: \"response.done\",\n response: {\n id: responseId,\n object: \"realtime.response\",\n status: \"failed\",\n output: [],\n status_details: {\n type: \"error\",\n error: {\n message: \"No fixture matched\",\n type: \"invalid_request_error\",\n code: \"no_fixture_match\",\n },\n },\n usage: { total_tokens: 0, input_tokens: 0, output_tokens: 0 },\n },\n },\n isBeta,\n );\n return;\n }\n\n const response = await resolveResponse(fixture, completionReq);\n const latency = fixture.latency ?? defaults.latency;\n const chunkSize = Math.max(1, fixture.chunkSize ?? defaults.chunkSize);\n\n // ── Error fixture ───────────────────────────────────────────────────\n if (isErrorResponse(response)) {\n const status = response.status ?? 500;\n journal.add({\n method: \"WS\",\n path: \"/v1/realtime\",\n headers: flattenHeaders(defaults.upgradeHeaders ?? {}),\n body: completionReq,\n response: { status, fixture },\n });\n sendEvent(\n ws,\n {\n type: \"response.created\",\n response: {\n id: responseId,\n object: \"realtime.response\",\n status: \"failed\",\n status_details: null,\n output: [],\n usage: null,\n },\n },\n isBeta,\n );\n sendEvent(\n ws,\n {\n type: \"response.done\",\n response: {\n id: responseId,\n object: \"realtime.response\",\n status: \"failed\",\n output: [],\n status_details: {\n type: \"error\",\n error: {\n message: response.error.message,\n type: response.error.type,\n code: response.error.code,\n },\n },\n usage: { total_tokens: 0, input_tokens: 0, output_tokens: 0 },\n },\n },\n isBeta,\n );\n return;\n }\n\n // ── Content + tool calls response ──────────────────────────────────\n if (isContentWithToolCallsResponse(response)) {\n const journalEntry = journal.add({\n method: \"WS\",\n path: \"/v1/realtime\",\n headers: flattenHeaders(defaults.upgradeHeaders ?? {}),\n body: completionReq,\n response: { status: 200, fixture },\n });\n\n // ── Ordered blocks path (#274) ──────────────────────────────────\n // When the fixture supplies an ordered `blocks` array, stream the items in\n // array order so a blocks-only fixture (no `content`/`toolCalls`) never\n // emits an empty payload, and a combined `{content,toolCalls,blocks}`\n // fixture preserves block ordering vs the HTTP path. Unlike OpenAI\n // chat-completions (separate, non-positional content/tool channels), the\n // Realtime WS protocol sequences output items on the wire with explicit\n // `output_index`, so block order — including tool-before-text — IS\n // observable to a client and is honored here.\n if (response.blocks && response.blocks.length > 0) {\n await streamRealtimeBlocks(\n ws,\n resolveFixtureBlocks(response.blocks),\n responseId,\n fixture,\n defaults,\n latency,\n chunkSize,\n isBeta,\n conversationItems,\n journalEntry,\n );\n return;\n }\n\n // response.created\n sendEvent(\n ws,\n {\n type: \"response.created\",\n response: {\n id: responseId,\n object: \"realtime.response\",\n status: \"in_progress\",\n status_details: null,\n output: [],\n usage: null,\n },\n },\n isBeta,\n );\n\n const interruption = createInterruptionSignal(fixture);\n let interrupted = false;\n const allOutputItems: unknown[] = [];\n\n // ── Text content part ──────────────────────────────────────────\n const textItemId = realtimeId(\"item\");\n const contentIndex = 0;\n const textOutputIndex = 0;\n\n const textOutputItem = {\n id: textItemId,\n type: \"message\",\n role: \"assistant\",\n status: \"completed\",\n content: [{ type: \"output_text\", text: response.content }],\n };\n\n // Determine phase: text is \"commentary\" when tool calls are also present\n const hasToolCalls = response.toolCalls && response.toolCalls.length > 0;\n const textPhase = hasToolCalls ? \"commentary\" : \"final_answer\";\n\n // response.output_item.added (text)\n sendEvent(\n ws,\n {\n type: \"response.output_item.added\",\n response_id: responseId,\n output_index: textOutputIndex,\n item: {\n id: textItemId,\n type: \"message\",\n role: \"assistant\",\n status: \"in_progress\",\n content: [],\n phase: textPhase,\n },\n },\n isBeta,\n );\n\n // response.content_part.added\n sendEvent(\n ws,\n {\n type: \"response.content_part.added\",\n response_id: responseId,\n item_id: textItemId,\n output_index: textOutputIndex,\n content_index: contentIndex,\n part: { type: \"output_text\", text: \"\" },\n },\n isBeta,\n );\n\n // response.output_text.delta (chunked) — GA name\n const content = response.content ?? \"\";\n const replaySpeed = fixture.replaySpeed ?? defaults.replaySpeed;\n const { recordedTimings } = fixture;\n let eventIndex = 0;\n for (let i = 0; i < content.length; i += chunkSize) {\n if (ws.isClosed) break;\n const chunkDelay = calculateDelay(\n eventIndex,\n undefined,\n latency,\n recordedTimings,\n replaySpeed,\n );\n if (chunkDelay > 0) await delay(chunkDelay, interruption?.signal);\n if (interruption?.signal.aborted) {\n interrupted = true;\n break;\n }\n if (ws.isClosed) break;\n const chunk = content.slice(i, i + chunkSize);\n try {\n sendEvent(\n ws,\n {\n type: \"response.output_text.delta\",\n response_id: responseId,\n item_id: textItemId,\n output_index: textOutputIndex,\n content_index: contentIndex,\n delta: chunk,\n },\n isBeta,\n );\n } catch (err) {\n defaults.logger.debug(\"[ws-realtime] send failed during text streaming, closing\", err);\n break;\n }\n eventIndex++;\n interruption?.tick();\n if (interruption?.signal.aborted) {\n interrupted = true;\n break;\n }\n }\n\n if (interrupted) {\n ws.destroy();\n journalEntry.response.interrupted = true;\n journalEntry.response.interruptReason = interruption?.reason();\n interruption?.cleanup();\n return;\n }\n\n if (ws.isClosed) {\n interruption?.cleanup();\n return;\n }\n\n // response.output_text.done\n sendEvent(\n ws,\n {\n type: \"response.output_text.done\",\n response_id: responseId,\n item_id: textItemId,\n output_index: textOutputIndex,\n content_index: contentIndex,\n text: content,\n },\n isBeta,\n );\n\n if (ws.isClosed) {\n interruption?.cleanup();\n return;\n }\n\n // response.content_part.done\n sendEvent(\n ws,\n {\n type: \"response.content_part.done\",\n response_id: responseId,\n item_id: textItemId,\n output_index: textOutputIndex,\n content_index: contentIndex,\n part: { type: \"output_text\", text: content },\n },\n isBeta,\n );\n\n if (ws.isClosed) {\n interruption?.cleanup();\n return;\n }\n\n // response.output_item.done (text)\n sendEvent(\n ws,\n {\n type: \"response.output_item.done\",\n response_id: responseId,\n output_index: textOutputIndex,\n item: { ...textOutputItem, phase: textPhase },\n },\n isBeta,\n );\n\n // conversation.item.done (text message)\n sendEvent(\n ws,\n {\n type: \"conversation.item.done\",\n item: {\n id: textItemId,\n object: \"realtime.item\",\n type: \"message\",\n role: \"assistant\",\n status: \"completed\",\n content: textOutputItem.content,\n },\n },\n isBeta,\n );\n\n if (ws.isClosed) {\n interruption?.cleanup();\n return;\n }\n\n allOutputItems.push(textOutputItem);\n\n // ── Tool call parts ────────────────────────────────────────────\n const toolCalls = response.toolCalls ?? [];\n for (let tcIdx = 0; tcIdx < toolCalls.length; tcIdx++) {\n const tc = toolCalls[tcIdx];\n const callId = tc.id ?? generateToolCallId();\n const itemId = realtimeId(\"item\");\n const outputIndex = tcIdx + 1; // offset by 1 for the text item\n\n const toolOutputItem = {\n id: itemId,\n type: \"function_call\",\n status: \"completed\",\n call_id: callId,\n name: tc.name,\n arguments: tc.arguments,\n };\n\n // response.output_item.added\n sendEvent(\n ws,\n {\n type: \"response.output_item.added\",\n response_id: responseId,\n output_index: outputIndex,\n item: {\n id: itemId,\n type: \"function_call\",\n status: \"in_progress\",\n call_id: callId,\n name: tc.name,\n arguments: \"\",\n phase: \"final_answer\",\n },\n },\n isBeta,\n );\n\n // response.function_call_arguments.delta (chunked)\n // Continue eventIndex from content chunks to avoid re-triggering TTFT\n const args = tc.arguments;\n for (let i = 0; i < args.length; i += chunkSize) {\n if (ws.isClosed) break;\n const chunkDelay = calculateDelay(\n eventIndex,\n undefined,\n latency,\n recordedTimings,\n replaySpeed,\n );\n if (chunkDelay > 0) await delay(chunkDelay, interruption?.signal);\n if (interruption?.signal.aborted) {\n interrupted = true;\n break;\n }\n if (ws.isClosed) break;\n const chunk = args.slice(i, i + chunkSize);\n try {\n sendEvent(\n ws,\n {\n type: \"response.function_call_arguments.delta\",\n response_id: responseId,\n item_id: itemId,\n output_index: outputIndex,\n call_id: callId,\n delta: chunk,\n },\n isBeta,\n );\n } catch (err) {\n defaults.logger.debug(\n \"[ws-realtime] send failed during tool call streaming, closing\",\n err,\n );\n break;\n }\n eventIndex++;\n interruption?.tick();\n if (interruption?.signal.aborted) {\n interrupted = true;\n break;\n }\n }\n\n if (interrupted) break;\n\n if (ws.isClosed) break;\n\n // response.function_call_arguments.done\n sendEvent(\n ws,\n {\n type: \"response.function_call_arguments.done\",\n response_id: responseId,\n item_id: itemId,\n output_index: outputIndex,\n call_id: callId,\n arguments: args,\n },\n isBeta,\n );\n\n if (ws.isClosed) break;\n\n // response.output_item.done\n sendEvent(\n ws,\n {\n type: \"response.output_item.done\",\n response_id: responseId,\n output_index: outputIndex,\n item: { ...toolOutputItem, phase: \"final_answer\" },\n },\n isBeta,\n );\n\n // conversation.item.done (tool call)\n sendEvent(\n ws,\n {\n type: \"conversation.item.done\",\n item: {\n id: itemId,\n object: \"realtime.item\",\n type: \"function_call\",\n status: \"completed\",\n call_id: callId,\n name: tc.name,\n arguments: args,\n },\n },\n isBeta,\n );\n\n if (ws.isClosed) break;\n\n allOutputItems.push(toolOutputItem);\n }\n\n if (interrupted) {\n ws.destroy();\n journalEntry.response.interrupted = true;\n journalEntry.response.interruptReason = interruption?.reason();\n interruption?.cleanup();\n return;\n }\n\n interruption?.cleanup();\n\n if (ws.isClosed) return;\n\n // response.done\n sendEvent(\n ws,\n {\n type: \"response.done\",\n response: {\n id: responseId,\n object: \"realtime.response\",\n status: \"completed\",\n output: allOutputItems,\n usage: { total_tokens: 0, input_tokens: 0, output_tokens: 0 },\n },\n },\n isBeta,\n );\n\n // Accumulate into conversation for multi-turn\n conversationItems.push({\n type: \"message\",\n id: textItemId,\n role: \"assistant\",\n content: [{ type: \"text\", text: content }],\n });\n for (const item of allOutputItems.slice(1)) {\n conversationItems.push(item as RealtimeItem);\n }\n return;\n }\n\n // ── Text response ───────────────────────────────────────────────────\n if (isTextResponse(response)) {\n const journalEntry = journal.add({\n method: \"WS\",\n path: \"/v1/realtime\",\n headers: flattenHeaders(defaults.upgradeHeaders ?? {}),\n body: completionReq,\n response: { status: 200, fixture },\n });\n\n const itemId = realtimeId(\"item\");\n const contentIndex = 0;\n const outputIndex = 0;\n\n const outputItem = {\n id: itemId,\n type: \"message\",\n role: \"assistant\",\n status: \"completed\",\n content: [{ type: \"output_text\", text: response.content }],\n };\n\n // response.created\n sendEvent(\n ws,\n {\n type: \"response.created\",\n response: {\n id: responseId,\n object: \"realtime.response\",\n status: \"in_progress\",\n status_details: null,\n output: [],\n usage: null,\n },\n },\n isBeta,\n );\n\n // response.output_item.added\n sendEvent(\n ws,\n {\n type: \"response.output_item.added\",\n response_id: responseId,\n output_index: outputIndex,\n item: {\n id: itemId,\n type: \"message\",\n role: \"assistant\",\n status: \"in_progress\",\n content: [],\n phase: \"final_answer\",\n },\n },\n isBeta,\n );\n\n // response.content_part.added\n sendEvent(\n ws,\n {\n type: \"response.content_part.added\",\n response_id: responseId,\n item_id: itemId,\n output_index: outputIndex,\n content_index: contentIndex,\n part: { type: \"output_text\", text: \"\" },\n },\n isBeta,\n );\n\n // response.output_text.delta (chunked) — GA name\n const content = response.content ?? \"\";\n const replaySpeed = fixture.replaySpeed ?? defaults.replaySpeed;\n const { recordedTimings } = fixture;\n const interruption = createInterruptionSignal(fixture);\n let interrupted = false;\n let eventIndex = 0;\n\n for (let i = 0; i < content.length; i += chunkSize) {\n if (ws.isClosed) break;\n const chunkDelay = calculateDelay(\n eventIndex,\n undefined,\n latency,\n recordedTimings,\n replaySpeed,\n );\n if (chunkDelay > 0) await delay(chunkDelay, interruption?.signal);\n if (interruption?.signal.aborted) {\n interrupted = true;\n break;\n }\n if (ws.isClosed) break;\n const chunk = content.slice(i, i + chunkSize);\n try {\n sendEvent(\n ws,\n {\n type: \"response.output_text.delta\",\n response_id: responseId,\n item_id: itemId,\n output_index: outputIndex,\n content_index: contentIndex,\n delta: chunk,\n },\n isBeta,\n );\n } catch (err) {\n defaults.logger.debug(\"[ws-realtime] send failed during text streaming, closing\", err);\n break;\n }\n eventIndex++;\n interruption?.tick();\n if (interruption?.signal.aborted) {\n interrupted = true;\n break;\n }\n }\n\n if (interrupted) {\n ws.destroy();\n journalEntry.response.interrupted = true;\n journalEntry.response.interruptReason = interruption?.reason();\n interruption?.cleanup();\n return;\n }\n\n interruption?.cleanup();\n\n if (ws.isClosed) return;\n\n // response.output_text.done\n sendEvent(\n ws,\n {\n type: \"response.output_text.done\",\n response_id: responseId,\n item_id: itemId,\n output_index: outputIndex,\n content_index: contentIndex,\n text: content,\n },\n isBeta,\n );\n\n // response.content_part.done\n sendEvent(\n ws,\n {\n type: \"response.content_part.done\",\n response_id: responseId,\n item_id: itemId,\n output_index: outputIndex,\n content_index: contentIndex,\n part: { type: \"output_text\", text: content },\n },\n isBeta,\n );\n\n // response.output_item.done\n sendEvent(\n ws,\n {\n type: \"response.output_item.done\",\n response_id: responseId,\n output_index: outputIndex,\n item: { ...outputItem, phase: \"final_answer\" },\n },\n isBeta,\n );\n\n // conversation.item.done (text message)\n sendEvent(\n ws,\n {\n type: \"conversation.item.done\",\n item: {\n id: itemId,\n object: \"realtime.item\",\n type: \"message\",\n role: \"assistant\",\n status: \"completed\",\n content: outputItem.content,\n },\n },\n isBeta,\n );\n\n // response.done\n sendEvent(\n ws,\n {\n type: \"response.done\",\n response: {\n id: responseId,\n object: \"realtime.response\",\n status: \"completed\",\n output: [outputItem],\n usage: { total_tokens: 0, input_tokens: 0, output_tokens: 0 },\n },\n },\n isBeta,\n );\n\n // Accumulate assistant response into conversation for multi-turn\n conversationItems.push({\n type: \"message\",\n id: itemId,\n role: \"assistant\",\n content: [{ type: \"text\", text: content }],\n });\n return;\n }\n\n // ── Tool call response ──────────────────────────────────────────────\n if (isToolCallResponse(response)) {\n const journalEntry = journal.add({\n method: \"WS\",\n path: \"/v1/realtime\",\n headers: flattenHeaders(defaults.upgradeHeaders ?? {}),\n body: completionReq,\n response: { status: 200, fixture },\n });\n\n // response.created\n sendEvent(\n ws,\n {\n type: \"response.created\",\n response: {\n id: responseId,\n object: \"realtime.response\",\n status: \"in_progress\",\n status_details: null,\n output: [],\n usage: null,\n },\n },\n isBeta,\n );\n\n const outputItems: unknown[] = [];\n const interruption = createInterruptionSignal(fixture);\n const replaySpeed = fixture.replaySpeed ?? defaults.replaySpeed;\n const { recordedTimings } = fixture;\n let interrupted = false;\n let eventIndex = 0;\n\n for (let tcIdx = 0; tcIdx < response.toolCalls.length; tcIdx++) {\n const tc = response.toolCalls[tcIdx];\n const callId = tc.id ?? generateToolCallId();\n const itemId = realtimeId(\"item\");\n\n const outputItem = {\n id: itemId,\n type: \"function_call\",\n status: \"completed\",\n call_id: callId,\n name: tc.name,\n arguments: tc.arguments,\n };\n\n // response.output_item.added\n sendEvent(\n ws,\n {\n type: \"response.output_item.added\",\n response_id: responseId,\n output_index: tcIdx,\n item: {\n id: itemId,\n type: \"function_call\",\n status: \"in_progress\",\n call_id: callId,\n name: tc.name,\n arguments: \"\",\n phase: \"final_answer\",\n },\n },\n isBeta,\n );\n\n // response.function_call_arguments.delta (chunked)\n // eventIndex is continuous across all tool calls to avoid re-triggering TTFT\n const args = tc.arguments;\n for (let i = 0; i < args.length; i += chunkSize) {\n if (ws.isClosed) break;\n const chunkDelay = calculateDelay(\n eventIndex,\n undefined,\n latency,\n recordedTimings,\n replaySpeed,\n );\n if (chunkDelay > 0) await delay(chunkDelay, interruption?.signal);\n if (interruption?.signal.aborted) {\n interrupted = true;\n break;\n }\n if (ws.isClosed) break;\n const chunk = args.slice(i, i + chunkSize);\n try {\n sendEvent(\n ws,\n {\n type: \"response.function_call_arguments.delta\",\n response_id: responseId,\n item_id: itemId,\n output_index: tcIdx,\n call_id: callId,\n delta: chunk,\n },\n isBeta,\n );\n } catch (err) {\n defaults.logger.debug(\n \"[ws-realtime] send failed during tool call streaming, closing\",\n err,\n );\n break;\n }\n eventIndex++;\n interruption?.tick();\n if (interruption?.signal.aborted) {\n interrupted = true;\n break;\n }\n }\n\n if (interrupted) break;\n\n if (ws.isClosed) break;\n\n // response.function_call_arguments.done\n sendEvent(\n ws,\n {\n type: \"response.function_call_arguments.done\",\n response_id: responseId,\n item_id: itemId,\n output_index: tcIdx,\n call_id: callId,\n arguments: args,\n },\n isBeta,\n );\n\n // response.output_item.done\n sendEvent(\n ws,\n {\n type: \"response.output_item.done\",\n response_id: responseId,\n output_index: tcIdx,\n item: { ...outputItem, phase: \"final_answer\" },\n },\n isBeta,\n );\n\n // conversation.item.done (tool call)\n sendEvent(\n ws,\n {\n type: \"conversation.item.done\",\n item: {\n id: itemId,\n object: \"realtime.item\",\n type: \"function_call\",\n status: \"completed\",\n call_id: callId,\n name: tc.name,\n arguments: args,\n },\n },\n isBeta,\n );\n\n outputItems.push(outputItem);\n }\n\n if (interrupted) {\n ws.destroy();\n journalEntry.response.interrupted = true;\n journalEntry.response.interruptReason = interruption?.reason();\n interruption?.cleanup();\n return;\n }\n\n interruption?.cleanup();\n\n if (ws.isClosed) return;\n\n // response.done\n sendEvent(\n ws,\n {\n type: \"response.done\",\n response: {\n id: responseId,\n object: \"realtime.response\",\n status: \"completed\",\n output: outputItems,\n usage: { total_tokens: 0, input_tokens: 0, output_tokens: 0 },\n },\n },\n isBeta,\n );\n\n // Accumulate assistant tool calls into conversation for multi-turn\n // Reuse outputItems (which already have the correct call_id) to avoid generating divergent IDs\n for (const item of outputItems) {\n conversationItems.push(item as RealtimeItem);\n }\n return;\n }\n\n // Unknown response type\n journal.add({\n method: \"WS\",\n path: \"/v1/realtime\",\n headers: flattenHeaders(defaults.upgradeHeaders ?? {}),\n body: completionReq,\n response: { status: 500, fixture },\n });\n buildErrorRealtimeEvent(\n ws,\n \"Fixture response did not match any known type\",\n isBeta,\n \"server_error\",\n );\n}\n\n/**\n * Stream a content+toolCalls fixture's ordered `blocks` over the Realtime WS\n * surface (#274). Each block becomes its own output item, sequenced on the wire\n * with an incrementing `output_index` so a client observes the blocks in array\n * order (text and tool-call items can interleave in any order, unlike the\n * separate channels of chat-completions). Emits the same per-item event shapes\n * as the legacy text/tool branches; a blocks-only fixture therefore never\n * produces an empty payload.\n */\nasync function streamRealtimeBlocks(\n ws: WebSocketConnection,\n blocks: FixtureBlock[],\n responseId: string,\n fixture: Fixture,\n defaults: { latency: number; chunkSize: number; replaySpeed?: number; logger: Logger },\n latency: number,\n chunkSize: number,\n isBeta: boolean,\n conversationItems: RealtimeItem[],\n journalEntry: JournalEntry,\n): Promise<void> {\n // response.created\n sendEvent(\n ws,\n {\n type: \"response.created\",\n response: {\n id: responseId,\n object: \"realtime.response\",\n status: \"in_progress\",\n status_details: null,\n output: [],\n usage: null,\n },\n },\n isBeta,\n );\n\n const interruption = createInterruptionSignal(fixture);\n let interrupted = false;\n const allOutputItems: unknown[] = [];\n const replaySpeed = fixture.replaySpeed ?? defaults.replaySpeed;\n const { recordedTimings } = fixture;\n let eventIndex = 0;\n\n // A text block following a tool-call block (or any tool present in the array)\n // is \"commentary\"; a lone text block is the \"final_answer\".\n const hasToolCalls = blocks.some((b) => b.type === \"toolCall\");\n\n for (let outputIndex = 0; outputIndex < blocks.length; outputIndex++) {\n if (ws.isClosed || interrupted) break;\n const block = blocks[outputIndex];\n\n if (block.type === \"text\") {\n const textItemId = realtimeId(\"item\");\n const contentIndex = 0;\n const textPhase = hasToolCalls ? \"commentary\" : \"final_answer\";\n const text = block.text;\n\n const textOutputItem = {\n id: textItemId,\n type: \"message\",\n role: \"assistant\",\n status: \"completed\",\n content: [{ type: \"output_text\", text }],\n };\n\n sendEvent(\n ws,\n {\n type: \"response.output_item.added\",\n response_id: responseId,\n output_index: outputIndex,\n item: {\n id: textItemId,\n type: \"message\",\n role: \"assistant\",\n status: \"in_progress\",\n content: [],\n phase: textPhase,\n },\n },\n isBeta,\n );\n\n sendEvent(\n ws,\n {\n type: \"response.content_part.added\",\n response_id: responseId,\n item_id: textItemId,\n output_index: outputIndex,\n content_index: contentIndex,\n part: { type: \"output_text\", text: \"\" },\n },\n isBeta,\n );\n\n for (let i = 0; i < text.length; i += chunkSize) {\n if (ws.isClosed) break;\n const chunkDelay = calculateDelay(\n eventIndex,\n undefined,\n latency,\n recordedTimings,\n replaySpeed,\n );\n if (chunkDelay > 0) await delay(chunkDelay, interruption?.signal);\n if (interruption?.signal.aborted) {\n interrupted = true;\n break;\n }\n if (ws.isClosed) break;\n const chunk = text.slice(i, i + chunkSize);\n try {\n sendEvent(\n ws,\n {\n type: \"response.output_text.delta\",\n response_id: responseId,\n item_id: textItemId,\n output_index: outputIndex,\n content_index: contentIndex,\n delta: chunk,\n },\n isBeta,\n );\n } catch (err) {\n defaults.logger.debug(\n \"[ws-realtime] send failed during block text streaming, closing\",\n err,\n );\n break;\n }\n eventIndex++;\n interruption?.tick();\n if (interruption?.signal.aborted) {\n interrupted = true;\n break;\n }\n }\n\n if (interrupted || ws.isClosed) break;\n\n sendEvent(\n ws,\n {\n type: \"response.output_text.done\",\n response_id: responseId,\n item_id: textItemId,\n output_index: outputIndex,\n content_index: contentIndex,\n text,\n },\n isBeta,\n );\n\n if (ws.isClosed) break;\n\n sendEvent(\n ws,\n {\n type: \"response.content_part.done\",\n response_id: responseId,\n item_id: textItemId,\n output_index: outputIndex,\n content_index: contentIndex,\n part: { type: \"output_text\", text },\n },\n isBeta,\n );\n\n if (ws.isClosed) break;\n\n sendEvent(\n ws,\n {\n type: \"response.output_item.done\",\n response_id: responseId,\n output_index: outputIndex,\n item: { ...textOutputItem, phase: textPhase },\n },\n isBeta,\n );\n\n sendEvent(\n ws,\n {\n type: \"conversation.item.done\",\n item: {\n id: textItemId,\n object: \"realtime.item\",\n type: \"message\",\n role: \"assistant\",\n status: \"completed\",\n content: textOutputItem.content,\n },\n },\n isBeta,\n );\n\n if (ws.isClosed) break;\n allOutputItems.push(textOutputItem);\n } else {\n const callId = block.id ?? generateToolCallId();\n const itemId = realtimeId(\"item\");\n const args = block.arguments;\n\n const toolOutputItem = {\n id: itemId,\n type: \"function_call\",\n status: \"completed\",\n call_id: callId,\n name: block.name,\n arguments: args,\n };\n\n sendEvent(\n ws,\n {\n type: \"response.output_item.added\",\n response_id: responseId,\n output_index: outputIndex,\n item: {\n id: itemId,\n type: \"function_call\",\n status: \"in_progress\",\n call_id: callId,\n name: block.name,\n arguments: \"\",\n phase: \"final_answer\",\n },\n },\n isBeta,\n );\n\n for (let i = 0; i < args.length; i += chunkSize) {\n if (ws.isClosed) break;\n const chunkDelay = calculateDelay(\n eventIndex,\n undefined,\n latency,\n recordedTimings,\n replaySpeed,\n );\n if (chunkDelay > 0) await delay(chunkDelay, interruption?.signal);\n if (interruption?.signal.aborted) {\n interrupted = true;\n break;\n }\n if (ws.isClosed) break;\n const chunk = args.slice(i, i + chunkSize);\n try {\n sendEvent(\n ws,\n {\n type: \"response.function_call_arguments.delta\",\n response_id: responseId,\n item_id: itemId,\n output_index: outputIndex,\n call_id: callId,\n delta: chunk,\n },\n isBeta,\n );\n } catch (err) {\n defaults.logger.debug(\n \"[ws-realtime] send failed during block tool call streaming, closing\",\n err,\n );\n break;\n }\n eventIndex++;\n interruption?.tick();\n if (interruption?.signal.aborted) {\n interrupted = true;\n break;\n }\n }\n\n if (interrupted || ws.isClosed) break;\n\n sendEvent(\n ws,\n {\n type: \"response.function_call_arguments.done\",\n response_id: responseId,\n item_id: itemId,\n output_index: outputIndex,\n call_id: callId,\n arguments: args,\n },\n isBeta,\n );\n\n if (ws.isClosed) break;\n\n sendEvent(\n ws,\n {\n type: \"response.output_item.done\",\n response_id: responseId,\n output_index: outputIndex,\n item: { ...toolOutputItem, phase: \"final_answer\" },\n },\n isBeta,\n );\n\n sendEvent(\n ws,\n {\n type: \"conversation.item.done\",\n item: {\n id: itemId,\n object: \"realtime.item\",\n type: \"function_call\",\n status: \"completed\",\n call_id: callId,\n name: block.name,\n arguments: args,\n },\n },\n isBeta,\n );\n\n if (ws.isClosed) break;\n allOutputItems.push(toolOutputItem);\n }\n }\n\n if (interrupted) {\n ws.destroy();\n journalEntry.response.interrupted = true;\n journalEntry.response.interruptReason = interruption?.reason();\n interruption?.cleanup();\n return;\n }\n\n interruption?.cleanup();\n\n if (ws.isClosed) return;\n\n // response.done\n sendEvent(\n ws,\n {\n type: \"response.done\",\n response: {\n id: responseId,\n object: \"realtime.response\",\n status: \"completed\",\n output: allOutputItems,\n usage: { total_tokens: 0, input_tokens: 0, output_tokens: 0 },\n },\n },\n isBeta,\n );\n\n // Accumulate into conversation for multi-turn (each output item, in order).\n for (const item of allOutputItems) {\n const it = item as { type: string; id: string; content?: unknown };\n if (it.type === \"message\") {\n conversationItems.push({\n type: \"message\",\n id: it.id,\n role: \"assistant\",\n content: [\n {\n type: \"text\",\n text: ((it.content as Array<{ text?: string }>)?.[0]?.text ?? \"\") as string,\n },\n ],\n });\n } else {\n conversationItems.push(item as RealtimeItem);\n }\n }\n}\n"],"mappings":"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"}
|
package/dist/ws-realtime.d.cts
CHANGED
|
@@ -16,6 +16,7 @@ declare function handleWebSocketRealtime(ws: WebSocketConnection, fixtures: Fixt
|
|
|
16
16
|
requestTransform?: (req: ChatCompletionRequest) => ChatCompletionRequest;
|
|
17
17
|
testId?: string;
|
|
18
18
|
upgradeHeaders?: node_http0.IncomingHttpHeaders;
|
|
19
|
+
transcriptionIntent?: boolean;
|
|
19
20
|
}): void;
|
|
20
21
|
//#endregion
|
|
21
22
|
export { handleWebSocketRealtime };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ws-realtime.d.cts","names":[],"sources":["../src/ws-realtime.ts"],"sourcesContent":[],"mappings":";;;;;;;;
|
|
1
|
+
{"version":3,"file":"ws-realtime.d.cts","names":[],"sources":["../src/ws-realtime.ts"],"sourcesContent":[],"mappings":";;;;;;;;iBAkcgB,uBAAA,KACV,+BACM,oBACD;;;;;UAMC;;2BAEiB,0BAA0B;;mBAAqB,UAAA,CAEnC"}
|
package/dist/ws-realtime.d.ts
CHANGED
|
@@ -16,6 +16,7 @@ declare function handleWebSocketRealtime(ws: WebSocketConnection, fixtures: Fixt
|
|
|
16
16
|
requestTransform?: (req: ChatCompletionRequest) => ChatCompletionRequest;
|
|
17
17
|
testId?: string;
|
|
18
18
|
upgradeHeaders?: node_http0.IncomingHttpHeaders;
|
|
19
|
+
transcriptionIntent?: boolean;
|
|
19
20
|
}): void;
|
|
20
21
|
//#endregion
|
|
21
22
|
export { handleWebSocketRealtime };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ws-realtime.d.ts","names":[],"sources":["../src/ws-realtime.ts"],"sourcesContent":[],"mappings":";;;;;;;;
|
|
1
|
+
{"version":3,"file":"ws-realtime.d.ts","names":[],"sources":["../src/ws-realtime.ts"],"sourcesContent":[],"mappings":";;;;;;;;iBAkcgB,uBAAA,KACV,+BACM,oBACD;;;;;UAMC;;2BAEiB,0BAA0B;;mBAAqB,UAAA,CAEnC"}
|