@copilotkit/aimock 1.18.0 → 1.19.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 +13 -0
- package/dist/agui-types.d.ts.map +1 -1
- package/dist/bedrock-converse.cjs +2 -2
- package/dist/bedrock-converse.cjs.map +1 -1
- package/dist/bedrock-converse.d.cts.map +1 -1
- package/dist/bedrock-converse.d.ts.map +1 -1
- package/dist/bedrock-converse.js +3 -3
- package/dist/bedrock-converse.js.map +1 -1
- package/dist/bedrock.cjs +2 -2
- package/dist/bedrock.cjs.map +1 -1
- package/dist/bedrock.d.cts.map +1 -1
- package/dist/bedrock.d.ts.map +1 -1
- package/dist/bedrock.js +3 -3
- package/dist/bedrock.js.map +1 -1
- package/dist/cohere.cjs +1 -1
- package/dist/cohere.cjs.map +1 -1
- package/dist/cohere.d.cts.map +1 -1
- package/dist/cohere.d.ts.map +1 -1
- package/dist/cohere.js +2 -2
- package/dist/cohere.js.map +1 -1
- package/dist/config-loader.d.cts.map +1 -1
- package/dist/elevenlabs-audio.cjs +1 -1
- package/dist/elevenlabs-audio.cjs.map +1 -1
- package/dist/elevenlabs-audio.d.cts.map +1 -1
- package/dist/elevenlabs-audio.d.ts.map +1 -1
- package/dist/elevenlabs-audio.js +2 -2
- package/dist/elevenlabs-audio.js.map +1 -1
- package/dist/embeddings.cjs +1 -1
- package/dist/embeddings.cjs.map +1 -1
- package/dist/embeddings.d.cts.map +1 -1
- package/dist/embeddings.d.ts.map +1 -1
- package/dist/embeddings.js +2 -2
- package/dist/embeddings.js.map +1 -1
- package/dist/fal-audio.cjs +2 -2
- 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 +3 -3
- package/dist/fal-audio.js.map +1 -1
- package/dist/fal.cjs +1 -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 +2 -2
- package/dist/fal.js.map +1 -1
- package/dist/fixture-loader.cjs +128 -126
- package/dist/fixture-loader.cjs.map +1 -1
- package/dist/fixture-loader.js +128 -126
- package/dist/fixture-loader.js.map +1 -1
- package/dist/gemini-interactions.cjs +1 -1
- package/dist/gemini-interactions.cjs.map +1 -1
- package/dist/gemini-interactions.d.cts.map +1 -1
- package/dist/gemini-interactions.d.ts.map +1 -1
- package/dist/gemini-interactions.js +2 -2
- package/dist/gemini-interactions.js.map +1 -1
- package/dist/gemini.cjs +1 -1
- package/dist/gemini.cjs.map +1 -1
- package/dist/gemini.d.cts.map +1 -1
- package/dist/gemini.d.ts.map +1 -1
- package/dist/gemini.js +2 -2
- package/dist/gemini.js.map +1 -1
- package/dist/helpers.cjs +25 -0
- 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 +24 -1
- package/dist/helpers.js.map +1 -1
- package/dist/images.cjs +1 -1
- package/dist/images.cjs.map +1 -1
- package/dist/images.d.cts.map +1 -1
- package/dist/images.d.ts.map +1 -1
- package/dist/images.js +2 -2
- package/dist/images.js.map +1 -1
- package/dist/llmock.cjs +1 -1
- package/dist/llmock.cjs.map +1 -1
- package/dist/llmock.d.cts +8 -7
- package/dist/llmock.d.cts.map +1 -1
- package/dist/llmock.d.ts +8 -7
- package/dist/llmock.d.ts.map +1 -1
- package/dist/llmock.js +1 -1
- package/dist/llmock.js.map +1 -1
- package/dist/messages.cjs +1 -1
- package/dist/messages.cjs.map +1 -1
- package/dist/messages.d.cts.map +1 -1
- package/dist/messages.d.ts.map +1 -1
- package/dist/messages.js +2 -2
- package/dist/messages.js.map +1 -1
- package/dist/ollama.cjs +2 -2
- package/dist/ollama.cjs.map +1 -1
- package/dist/ollama.d.cts.map +1 -1
- package/dist/ollama.d.ts.map +1 -1
- package/dist/ollama.js +3 -3
- package/dist/ollama.js.map +1 -1
- package/dist/recorder.cjs +27 -4
- 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 +27 -4
- package/dist/recorder.js.map +1 -1
- package/dist/responses.cjs +1 -1
- package/dist/responses.cjs.map +1 -1
- package/dist/responses.d.cts.map +1 -1
- package/dist/responses.d.ts.map +1 -1
- package/dist/responses.js +2 -2
- package/dist/responses.js.map +1 -1
- package/dist/router.cjs +3 -1
- package/dist/router.cjs.map +1 -1
- package/dist/router.js +3 -1
- package/dist/router.js.map +1 -1
- package/dist/server.cjs +2 -2
- package/dist/server.cjs.map +1 -1
- package/dist/server.d.cts.map +1 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +3 -3
- package/dist/server.js.map +1 -1
- package/dist/speech.cjs +1 -1
- package/dist/speech.cjs.map +1 -1
- package/dist/speech.d.cts.map +1 -1
- package/dist/speech.d.ts.map +1 -1
- package/dist/speech.js +2 -2
- package/dist/speech.js.map +1 -1
- package/dist/transcription.cjs +1 -1
- 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 +2 -2
- package/dist/transcription.js.map +1 -1
- package/dist/types.d.cts +3 -2
- package/dist/types.d.cts.map +1 -1
- package/dist/types.d.ts +3 -2
- package/dist/types.d.ts.map +1 -1
- package/dist/vector-types.d.ts.map +1 -1
- package/dist/video.cjs +1 -1
- package/dist/video.cjs.map +1 -1
- package/dist/video.d.cts.map +1 -1
- package/dist/video.d.ts.map +1 -1
- package/dist/video.js +2 -2
- package/dist/video.js.map +1 -1
- package/dist/ws-gemini-live.cjs +1 -1
- package/dist/ws-gemini-live.cjs.map +1 -1
- package/dist/ws-gemini-live.d.cts.map +1 -1
- package/dist/ws-gemini-live.d.ts.map +1 -1
- package/dist/ws-gemini-live.js +2 -2
- package/dist/ws-gemini-live.js.map +1 -1
- package/dist/ws-realtime.cjs +1 -1
- package/dist/ws-realtime.cjs.map +1 -1
- package/dist/ws-realtime.d.cts.map +1 -1
- package/dist/ws-realtime.d.ts.map +1 -1
- package/dist/ws-realtime.js +2 -2
- package/dist/ws-realtime.js.map +1 -1
- package/dist/ws-responses.cjs +1 -1
- package/dist/ws-responses.cjs.map +1 -1
- package/dist/ws-responses.js +2 -2
- package/dist/ws-responses.js.map +1 -1
- package/package.json +1 -1
package/dist/helpers.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"helpers.cjs","names":[],"sources":["../src/helpers.ts"],"sourcesContent":["import { createHash, randomBytes } from \"node:crypto\";\nimport type * as http from \"node:http\";\nimport type {\n FixtureResponse,\n TextResponse,\n ToolCallResponse,\n ContentWithToolCallsResponse,\n ErrorResponse,\n EmbeddingResponse,\n ImageResponse,\n AudioResponse,\n TranscriptionResponse,\n VideoResponse,\n RawJSONResponse,\n SSEChunk,\n ToolCall,\n ChatCompletion,\n ResponseOverrides,\n} from \"./types.js\";\n\nconst REDACTED_HEADERS = new Set([\"authorization\", \"x-api-key\", \"api-key\"]);\n\nexport function flattenHeaders(headers: http.IncomingHttpHeaders): Record<string, string> {\n const flat: Record<string, string> = {};\n for (const [key, value] of Object.entries(headers)) {\n if (value === undefined) continue;\n if (REDACTED_HEADERS.has(key.toLowerCase())) {\n flat[key] = \"[REDACTED]\";\n } else {\n flat[key] = Array.isArray(value) ? value.join(\", \") : value;\n }\n }\n return flat;\n}\n\nexport function generateId(prefix = \"chatcmpl\"): string {\n return `${prefix}-${randomBytes(12).toString(\"base64url\")}`;\n}\n\nexport function generateToolCallId(): string {\n return `call_${randomBytes(12).toString(\"base64url\")}`;\n}\n\nexport function generateMessageId(): string {\n return `msg_${randomBytes(12).toString(\"base64url\")}`;\n}\n\nexport function generateToolUseId(): string {\n return `toolu_${randomBytes(12).toString(\"base64url\")}`;\n}\n\nexport function isTextResponse(r: FixtureResponse): r is TextResponse {\n return \"content\" in r && typeof (r as TextResponse).content === \"string\" && !(\"toolCalls\" in r);\n}\n\nexport function isToolCallResponse(r: FixtureResponse): r is ToolCallResponse {\n return (\n \"toolCalls\" in r &&\n Array.isArray((r as ToolCallResponse).toolCalls) &&\n !(\"content\" in r && typeof (r as unknown as Record<string, unknown>).content === \"string\")\n );\n}\n\nexport function isContentWithToolCallsResponse(\n r: FixtureResponse,\n): r is ContentWithToolCallsResponse {\n return (\n \"content\" in r &&\n typeof (r as ContentWithToolCallsResponse).content === \"string\" &&\n \"toolCalls\" in r &&\n Array.isArray((r as ContentWithToolCallsResponse).toolCalls)\n );\n}\n\nexport function isErrorResponse(r: FixtureResponse): r is ErrorResponse {\n return (\n \"error\" in r &&\n (r as ErrorResponse).error !== null &&\n typeof (r as ErrorResponse).error === \"object\"\n );\n}\n\nexport function isEmbeddingResponse(r: FixtureResponse): r is EmbeddingResponse {\n return \"embedding\" in r && Array.isArray((r as EmbeddingResponse).embedding);\n}\n\nexport function isImageResponse(r: FixtureResponse): r is ImageResponse {\n return (\n (\"image\" in r && r.image != null) ||\n (\"images\" in r && Array.isArray((r as ImageResponse).images))\n );\n}\n\nexport function isAudioResponse(r: FixtureResponse): r is AudioResponse {\n if (!(\"audio\" in r)) return false;\n const a = (r as AudioResponse).audio;\n return typeof a === \"string\" || (typeof a === \"object\" && a !== null && \"b64Json\" in a);\n}\n\n/**\n * Map audio format shorthand to MIME content types.\n * Shared between speech, ElevenLabs, and fal audio handlers.\n */\nexport const FORMAT_TO_CONTENT_TYPE: Record<string, string> = {\n mp3: \"audio/mpeg\",\n opus: \"audio/opus\",\n aac: \"audio/aac\",\n flac: \"audio/flac\",\n wav: \"audio/wav\",\n pcm: \"audio/pcm\",\n};\n\n/**\n * Resolve a format string (e.g. \"mp3\", \"opus\") to its MIME content type.\n * Falls back to \"application/octet-stream\" for unknown formats.\n */\nexport function formatToMime(format: string): string {\n return FORMAT_TO_CONTENT_TYPE[format] ?? \"application/octet-stream\";\n}\n\nexport function isTranscriptionResponse(r: FixtureResponse): r is TranscriptionResponse {\n return (\n \"transcription\" in r &&\n (r as TranscriptionResponse).transcription != null &&\n typeof (r as TranscriptionResponse).transcription === \"object\"\n );\n}\n\nexport function isVideoResponse(r: FixtureResponse): r is VideoResponse {\n return (\n \"video\" in r &&\n (r as VideoResponse).video != null &&\n typeof (r as VideoResponse).video === \"object\"\n );\n}\n\nexport function isJSONResponse(r: FixtureResponse): r is RawJSONResponse {\n return \"json\" in r && (r as RawJSONResponse).json !== undefined;\n}\n\nexport function extractOverrides(\n response: TextResponse | ToolCallResponse | ContentWithToolCallsResponse,\n): ResponseOverrides {\n const r = response;\n return {\n ...(r.id !== undefined && { id: r.id }),\n ...(r.created !== undefined && { created: r.created }),\n ...(r.model !== undefined && { model: r.model }),\n ...(r.usage !== undefined && { usage: r.usage }),\n ...(r.systemFingerprint !== undefined && { systemFingerprint: r.systemFingerprint }),\n ...(r.finishReason !== undefined && { finishReason: r.finishReason }),\n ...(r.role !== undefined && { role: r.role }),\n };\n}\n\nexport function buildTextChunks(\n content: string,\n model: string,\n chunkSize: number,\n reasoning?: string,\n overrides?: ResponseOverrides,\n): SSEChunk[] {\n const id = overrides?.id ?? generateId();\n const created = overrides?.created ?? Math.floor(Date.now() / 1000);\n const effectiveModel = overrides?.model ?? model;\n const chunks: SSEChunk[] = [];\n const fingerprint = overrides?.systemFingerprint;\n\n // Reasoning chunks (emitted before content, OpenRouter format)\n if (reasoning) {\n for (let i = 0; i < reasoning.length; i += chunkSize) {\n const slice = reasoning.slice(i, i + chunkSize);\n chunks.push({\n id,\n object: \"chat.completion.chunk\",\n created,\n model: effectiveModel,\n choices: [{ index: 0, delta: { reasoning_content: slice }, finish_reason: null }],\n ...(fingerprint !== undefined && { system_fingerprint: fingerprint }),\n });\n }\n }\n\n // Role chunk\n chunks.push({\n id,\n object: \"chat.completion.chunk\",\n created,\n model: effectiveModel,\n choices: [\n {\n index: 0,\n delta: { role: overrides?.role ?? \"assistant\", content: \"\" },\n finish_reason: null,\n },\n ],\n ...(fingerprint !== undefined && { system_fingerprint: fingerprint }),\n });\n\n // Content chunks\n for (let i = 0; i < content.length; i += chunkSize) {\n const slice = content.slice(i, i + chunkSize);\n chunks.push({\n id,\n object: \"chat.completion.chunk\",\n created,\n model: effectiveModel,\n choices: [{ index: 0, delta: { content: slice }, finish_reason: null }],\n ...(fingerprint !== undefined && { system_fingerprint: fingerprint }),\n });\n }\n\n // Finish chunk\n chunks.push({\n id,\n object: \"chat.completion.chunk\",\n created,\n model: effectiveModel,\n choices: [{ index: 0, delta: {}, finish_reason: overrides?.finishReason ?? \"stop\" }],\n ...(fingerprint !== undefined && { system_fingerprint: fingerprint }),\n });\n\n return chunks;\n}\n\nexport function buildToolCallChunks(\n toolCalls: ToolCall[],\n model: string,\n chunkSize: number,\n overrides?: ResponseOverrides,\n): SSEChunk[] {\n const id = overrides?.id ?? generateId();\n const created = overrides?.created ?? Math.floor(Date.now() / 1000);\n const effectiveModel = overrides?.model ?? model;\n const chunks: SSEChunk[] = [];\n const fingerprint = overrides?.systemFingerprint;\n\n // Role chunk\n chunks.push({\n id,\n object: \"chat.completion.chunk\",\n created,\n model: effectiveModel,\n choices: [\n {\n index: 0,\n delta: { role: overrides?.role ?? \"assistant\", content: null },\n finish_reason: null,\n },\n ],\n ...(fingerprint !== undefined && { system_fingerprint: fingerprint }),\n });\n\n // Tool call chunks — one initial chunk per tool call, then argument chunks\n for (let tcIdx = 0; tcIdx < toolCalls.length; tcIdx++) {\n const tc = toolCalls[tcIdx];\n const tcId = tc.id || generateToolCallId();\n\n // Initial tool call chunk (id + function name)\n chunks.push({\n id,\n object: \"chat.completion.chunk\",\n created,\n model: effectiveModel,\n choices: [\n {\n index: 0,\n delta: {\n tool_calls: [\n {\n index: tcIdx,\n id: tcId,\n type: \"function\",\n function: { name: tc.name, arguments: \"\" },\n },\n ],\n },\n finish_reason: null,\n },\n ],\n ...(fingerprint !== undefined && { system_fingerprint: fingerprint }),\n });\n\n // Argument streaming chunks\n const args = tc.arguments;\n for (let i = 0; i < args.length; i += chunkSize) {\n const slice = args.slice(i, i + chunkSize);\n chunks.push({\n id,\n object: \"chat.completion.chunk\",\n created,\n model: effectiveModel,\n choices: [\n {\n index: 0,\n delta: {\n tool_calls: [{ index: tcIdx, function: { arguments: slice } }],\n },\n finish_reason: null,\n },\n ],\n ...(fingerprint !== undefined && { system_fingerprint: fingerprint }),\n });\n }\n }\n\n // Finish chunk\n chunks.push({\n id,\n object: \"chat.completion.chunk\",\n created,\n model: effectiveModel,\n choices: [{ index: 0, delta: {}, finish_reason: overrides?.finishReason ?? \"tool_calls\" }],\n ...(fingerprint !== undefined && { system_fingerprint: fingerprint }),\n });\n\n return chunks;\n}\n\n// Non-streaming response builders\n\nexport function buildTextCompletion(\n content: string,\n model: string,\n reasoning?: string,\n overrides?: ResponseOverrides,\n): ChatCompletion {\n const defaultUsage = { prompt_tokens: 0, completion_tokens: 0, total_tokens: 0 };\n return {\n id: overrides?.id ?? generateId(),\n object: \"chat.completion\",\n created: overrides?.created ?? Math.floor(Date.now() / 1000),\n model: overrides?.model ?? model,\n choices: [\n {\n index: 0,\n message: {\n role: overrides?.role ?? \"assistant\",\n content,\n refusal: null,\n ...(reasoning ? { reasoning_content: reasoning } : {}),\n },\n finish_reason: overrides?.finishReason ?? \"stop\",\n },\n ],\n usage: overrides?.usage\n ? {\n prompt_tokens: overrides.usage.prompt_tokens ?? defaultUsage.prompt_tokens,\n completion_tokens: overrides.usage.completion_tokens ?? defaultUsage.completion_tokens,\n total_tokens:\n overrides.usage.total_tokens ??\n (overrides.usage.prompt_tokens ?? 0) + (overrides.usage.completion_tokens ?? 0),\n }\n : defaultUsage,\n ...(overrides?.systemFingerprint !== undefined && {\n system_fingerprint: overrides.systemFingerprint,\n }),\n };\n}\n\nexport function buildToolCallCompletion(\n toolCalls: ToolCall[],\n model: string,\n overrides?: ResponseOverrides,\n): ChatCompletion {\n const defaultUsage = { prompt_tokens: 0, completion_tokens: 0, total_tokens: 0 };\n return {\n id: overrides?.id ?? generateId(),\n object: \"chat.completion\",\n created: overrides?.created ?? Math.floor(Date.now() / 1000),\n model: overrides?.model ?? model,\n choices: [\n {\n index: 0,\n message: {\n role: overrides?.role ?? \"assistant\",\n content: null,\n refusal: null,\n tool_calls: toolCalls.map((tc) => ({\n id: tc.id || generateToolCallId(),\n type: \"function\" as const,\n function: { name: tc.name, arguments: tc.arguments },\n })),\n },\n finish_reason: overrides?.finishReason ?? \"tool_calls\",\n },\n ],\n usage: overrides?.usage\n ? {\n prompt_tokens: overrides.usage.prompt_tokens ?? defaultUsage.prompt_tokens,\n completion_tokens: overrides.usage.completion_tokens ?? defaultUsage.completion_tokens,\n total_tokens:\n overrides.usage.total_tokens ??\n (overrides.usage.prompt_tokens ?? 0) + (overrides.usage.completion_tokens ?? 0),\n }\n : defaultUsage,\n ...(overrides?.systemFingerprint !== undefined && {\n system_fingerprint: overrides.systemFingerprint,\n }),\n };\n}\n\nexport function buildContentWithToolCallsChunks(\n content: string,\n toolCalls: ToolCall[],\n model: string,\n chunkSize: number,\n reasoning?: string,\n overrides?: ResponseOverrides,\n): SSEChunk[] {\n const id = overrides?.id ?? generateId();\n const created = overrides?.created ?? Math.floor(Date.now() / 1000);\n const effectiveModel = overrides?.model ?? model;\n const chunks: SSEChunk[] = [];\n const fingerprint = overrides?.systemFingerprint;\n\n // Reasoning chunks (emitted before content, OpenRouter format)\n if (reasoning) {\n for (let i = 0; i < reasoning.length; i += chunkSize) {\n const slice = reasoning.slice(i, i + chunkSize);\n chunks.push({\n id,\n object: \"chat.completion.chunk\",\n created,\n model: effectiveModel,\n choices: [{ index: 0, delta: { reasoning_content: slice }, finish_reason: null }],\n ...(fingerprint !== undefined && { system_fingerprint: fingerprint }),\n });\n }\n }\n\n // Role chunk\n chunks.push({\n id,\n object: \"chat.completion.chunk\",\n created,\n model: effectiveModel,\n choices: [\n {\n index: 0,\n delta: { role: overrides?.role ?? \"assistant\", content: \"\" },\n finish_reason: null,\n },\n ],\n ...(fingerprint !== undefined && { system_fingerprint: fingerprint }),\n });\n\n // Content chunks\n for (let i = 0; i < content.length; i += chunkSize) {\n const slice = content.slice(i, i + chunkSize);\n chunks.push({\n id,\n object: \"chat.completion.chunk\",\n created,\n model: effectiveModel,\n choices: [{ index: 0, delta: { content: slice }, finish_reason: null }],\n ...(fingerprint !== undefined && { system_fingerprint: fingerprint }),\n });\n }\n\n // Tool call chunks — one initial chunk per tool call, then argument chunks\n for (let tcIdx = 0; tcIdx < toolCalls.length; tcIdx++) {\n const tc = toolCalls[tcIdx];\n const tcId = tc.id || generateToolCallId();\n\n // Initial tool call chunk (id + function name)\n chunks.push({\n id,\n object: \"chat.completion.chunk\",\n created,\n model: effectiveModel,\n choices: [\n {\n index: 0,\n delta: {\n tool_calls: [\n {\n index: tcIdx,\n id: tcId,\n type: \"function\",\n function: { name: tc.name, arguments: \"\" },\n },\n ],\n },\n finish_reason: null,\n },\n ],\n ...(fingerprint !== undefined && { system_fingerprint: fingerprint }),\n });\n\n // Argument streaming chunks\n const args = tc.arguments;\n for (let i = 0; i < args.length; i += chunkSize) {\n const slice = args.slice(i, i + chunkSize);\n chunks.push({\n id,\n object: \"chat.completion.chunk\",\n created,\n model: effectiveModel,\n choices: [\n {\n index: 0,\n delta: {\n tool_calls: [{ index: tcIdx, function: { arguments: slice } }],\n },\n finish_reason: null,\n },\n ],\n ...(fingerprint !== undefined && { system_fingerprint: fingerprint }),\n });\n }\n }\n\n // Finish chunk\n chunks.push({\n id,\n object: \"chat.completion.chunk\",\n created,\n model: effectiveModel,\n choices: [{ index: 0, delta: {}, finish_reason: overrides?.finishReason ?? \"tool_calls\" }],\n ...(fingerprint !== undefined && { system_fingerprint: fingerprint }),\n });\n\n return chunks;\n}\n\nexport function buildContentWithToolCallsCompletion(\n content: string,\n toolCalls: ToolCall[],\n model: string,\n reasoning?: string,\n overrides?: ResponseOverrides,\n): ChatCompletion {\n const defaultUsage = { prompt_tokens: 0, completion_tokens: 0, total_tokens: 0 };\n return {\n id: overrides?.id ?? generateId(),\n object: \"chat.completion\",\n created: overrides?.created ?? Math.floor(Date.now() / 1000),\n model: overrides?.model ?? model,\n choices: [\n {\n index: 0,\n message: {\n role: overrides?.role ?? \"assistant\",\n content,\n refusal: null,\n ...(reasoning ? { reasoning_content: reasoning } : {}),\n tool_calls: toolCalls.map((tc) => ({\n id: tc.id || generateToolCallId(),\n type: \"function\" as const,\n function: { name: tc.name, arguments: tc.arguments },\n })),\n },\n finish_reason: overrides?.finishReason ?? \"tool_calls\",\n },\n ],\n usage: overrides?.usage\n ? {\n prompt_tokens: overrides.usage.prompt_tokens ?? defaultUsage.prompt_tokens,\n completion_tokens: overrides.usage.completion_tokens ?? defaultUsage.completion_tokens,\n total_tokens:\n overrides.usage.total_tokens ??\n (overrides.usage.prompt_tokens ?? 0) + (overrides.usage.completion_tokens ?? 0),\n }\n : defaultUsage,\n ...(overrides?.systemFingerprint !== undefined && {\n system_fingerprint: overrides.systemFingerprint,\n }),\n };\n}\n\n// ─── HTTP helpers ─────────────────────────────────────────────────────────\n\nexport function readBody(req: http.IncomingMessage): Promise<string> {\n return new Promise((resolve, reject) => {\n const chunks: Buffer[] = [];\n req.on(\"data\", (chunk: Buffer) => chunks.push(chunk));\n req.on(\"end\", () => resolve(Buffer.concat(chunks).toString()));\n req.on(\"error\", reject);\n });\n}\n\n// ─── Pattern matching ─────────────────────────────────────────────────────\n\n/**\n * Case-insensitive substring/regex match used for search, rerank, and\n * moderation endpoints where exact casing rarely matters. String patterns\n * are lowercased on both sides before comparison.\n *\n * Note: This intentionally differs from the case-sensitive matching in\n * {@link matchFixture} (router.ts), where fixture authors expect exact\n * string matching against chat completion user messages.\n */\nexport function matchesPattern(text: string, pattern: string | RegExp): boolean {\n if (typeof pattern === \"string\") {\n return text.toLowerCase().includes(pattern.toLowerCase());\n }\n return pattern.test(text);\n}\n\nexport function getTestId(req: http.IncomingMessage): string {\n const headerValue = req.headers[\"x-test-id\"];\n if (Array.isArray(headerValue)) {\n if (headerValue.length > 0 && headerValue[0]) return headerValue[0];\n } else if (typeof headerValue === \"string\" && headerValue) {\n return headerValue;\n }\n\n const url = req.url ?? \"/\";\n const qIdx = url.indexOf(\"?\");\n if (qIdx !== -1) {\n const params = new URLSearchParams(url.slice(qIdx + 1));\n const queryValue = params.get(\"testId\");\n if (queryValue) return queryValue;\n }\n\n // Duplicated from journal.ts DEFAULT_TEST_ID — importing it here would create\n // a circular dependency (journal.ts imports from helpers.ts).\n return \"__default__\";\n}\n\n// ─── Embedding helpers ─────────────────────────────────────────────────────\n\nconst DEFAULT_EMBEDDING_DIMENSIONS = 1536;\n\n/**\n * Generate a deterministic embedding vector from input text.\n * Hashes the input with SHA-256 and spreads the hash bytes across\n * the requested number of dimensions, producing values in [-1, 1].\n */\nexport function generateDeterministicEmbedding(\n input: string,\n dimensions: number = DEFAULT_EMBEDDING_DIMENSIONS,\n): number[] {\n let currentHash = createHash(\"sha256\").update(input).digest();\n const embedding: number[] = new Array(dimensions);\n for (let i = 0; i < dimensions; i++) {\n if (i > 0 && i % 32 === 0) {\n currentHash = createHash(\"sha256\").update(currentHash).digest();\n }\n // Map 0-255 → -1.0 to 1.0\n embedding[i] = currentHash[i % 32] / 127.5 - 1;\n }\n return embedding;\n}\n\nexport interface EmbeddingAPIResponse {\n object: \"list\";\n data: { object: \"embedding\"; index: number; embedding: number[] }[];\n model: string;\n usage: { prompt_tokens: number; total_tokens: number };\n}\n\n/**\n * Build an OpenAI-format embeddings API response for one or more inputs.\n */\nexport function buildEmbeddingResponse(\n embeddings: number[][],\n model: string,\n): EmbeddingAPIResponse {\n return {\n object: \"list\",\n data: embeddings.map((embedding, index) => ({\n object: \"embedding\" as const,\n index,\n embedding,\n })),\n model,\n usage: { prompt_tokens: 0, total_tokens: 0 },\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"helpers.cjs","names":[],"sources":["../src/helpers.ts"],"sourcesContent":["import { createHash, randomBytes } from \"node:crypto\";\nimport type * as http from \"node:http\";\nimport type {\n ChatCompletionRequest,\n Fixture,\n FixtureResponse,\n ResponseFactory,\n TextResponse,\n ToolCallResponse,\n ContentWithToolCallsResponse,\n ErrorResponse,\n EmbeddingResponse,\n ImageResponse,\n AudioResponse,\n TranscriptionResponse,\n VideoResponse,\n RawJSONResponse,\n SSEChunk,\n ToolCall,\n ChatCompletion,\n ResponseOverrides,\n} from \"./types.js\";\n\nconst REDACTED_HEADERS = new Set([\"authorization\", \"x-api-key\", \"api-key\"]);\n\nexport function flattenHeaders(headers: http.IncomingHttpHeaders): Record<string, string> {\n const flat: Record<string, string> = {};\n for (const [key, value] of Object.entries(headers)) {\n if (value === undefined) continue;\n if (REDACTED_HEADERS.has(key.toLowerCase())) {\n flat[key] = \"[REDACTED]\";\n } else {\n flat[key] = Array.isArray(value) ? value.join(\", \") : value;\n }\n }\n return flat;\n}\n\nexport function isResponseFactory(r: FixtureResponse | ResponseFactory): r is ResponseFactory {\n return typeof r === \"function\";\n}\n\nexport async function resolveResponse(\n fixture: Fixture,\n request: ChatCompletionRequest,\n): Promise<FixtureResponse> {\n if (typeof fixture.response === \"function\") {\n const raw = await fixture.response(request);\n return normalizeFactoryResponse(raw);\n }\n return fixture.response;\n}\n\nfunction normalizeFactoryResponse(raw: FixtureResponse): FixtureResponse {\n const r = { ...raw } as Record<string, unknown>;\n if (typeof r.content === \"object\" && r.content !== null) {\n r.content = JSON.stringify(r.content);\n }\n if (Array.isArray(r.toolCalls)) {\n r.toolCalls = (r.toolCalls as Array<Record<string, unknown>>).map((tc) => {\n if (typeof tc.arguments === \"object\" && tc.arguments !== null) {\n return { ...tc, arguments: JSON.stringify(tc.arguments) };\n }\n return tc;\n });\n }\n return r as unknown as FixtureResponse;\n}\n\nexport function generateId(prefix = \"chatcmpl\"): string {\n return `${prefix}-${randomBytes(12).toString(\"base64url\")}`;\n}\n\nexport function generateToolCallId(): string {\n return `call_${randomBytes(12).toString(\"base64url\")}`;\n}\n\nexport function generateMessageId(): string {\n return `msg_${randomBytes(12).toString(\"base64url\")}`;\n}\n\nexport function generateToolUseId(): string {\n return `toolu_${randomBytes(12).toString(\"base64url\")}`;\n}\n\nexport function isTextResponse(r: FixtureResponse): r is TextResponse {\n return \"content\" in r && typeof (r as TextResponse).content === \"string\" && !(\"toolCalls\" in r);\n}\n\nexport function isToolCallResponse(r: FixtureResponse): r is ToolCallResponse {\n return (\n \"toolCalls\" in r &&\n Array.isArray((r as ToolCallResponse).toolCalls) &&\n !(\"content\" in r && typeof (r as unknown as Record<string, unknown>).content === \"string\")\n );\n}\n\nexport function isContentWithToolCallsResponse(\n r: FixtureResponse,\n): r is ContentWithToolCallsResponse {\n return (\n \"content\" in r &&\n typeof (r as ContentWithToolCallsResponse).content === \"string\" &&\n \"toolCalls\" in r &&\n Array.isArray((r as ContentWithToolCallsResponse).toolCalls)\n );\n}\n\nexport function isErrorResponse(r: FixtureResponse): r is ErrorResponse {\n return (\n \"error\" in r &&\n (r as ErrorResponse).error !== null &&\n typeof (r as ErrorResponse).error === \"object\"\n );\n}\n\nexport function isEmbeddingResponse(r: FixtureResponse): r is EmbeddingResponse {\n return \"embedding\" in r && Array.isArray((r as EmbeddingResponse).embedding);\n}\n\nexport function isImageResponse(r: FixtureResponse): r is ImageResponse {\n return (\n (\"image\" in r && r.image != null) ||\n (\"images\" in r && Array.isArray((r as ImageResponse).images))\n );\n}\n\nexport function isAudioResponse(r: FixtureResponse): r is AudioResponse {\n if (!(\"audio\" in r)) return false;\n const a = (r as AudioResponse).audio;\n return typeof a === \"string\" || (typeof a === \"object\" && a !== null && \"b64Json\" in a);\n}\n\n/**\n * Map audio format shorthand to MIME content types.\n * Shared between speech, ElevenLabs, and fal audio handlers.\n */\nexport const FORMAT_TO_CONTENT_TYPE: Record<string, string> = {\n mp3: \"audio/mpeg\",\n opus: \"audio/opus\",\n aac: \"audio/aac\",\n flac: \"audio/flac\",\n wav: \"audio/wav\",\n pcm: \"audio/pcm\",\n};\n\n/**\n * Resolve a format string (e.g. \"mp3\", \"opus\") to its MIME content type.\n * Falls back to \"application/octet-stream\" for unknown formats.\n */\nexport function formatToMime(format: string): string {\n return FORMAT_TO_CONTENT_TYPE[format] ?? \"application/octet-stream\";\n}\n\nexport function isTranscriptionResponse(r: FixtureResponse): r is TranscriptionResponse {\n return (\n \"transcription\" in r &&\n (r as TranscriptionResponse).transcription != null &&\n typeof (r as TranscriptionResponse).transcription === \"object\"\n );\n}\n\nexport function isVideoResponse(r: FixtureResponse): r is VideoResponse {\n return (\n \"video\" in r &&\n (r as VideoResponse).video != null &&\n typeof (r as VideoResponse).video === \"object\"\n );\n}\n\nexport function isJSONResponse(r: FixtureResponse): r is RawJSONResponse {\n return \"json\" in r && (r as RawJSONResponse).json !== undefined;\n}\n\nexport function extractOverrides(\n response: TextResponse | ToolCallResponse | ContentWithToolCallsResponse,\n): ResponseOverrides {\n const r = response;\n return {\n ...(r.id !== undefined && { id: r.id }),\n ...(r.created !== undefined && { created: r.created }),\n ...(r.model !== undefined && { model: r.model }),\n ...(r.usage !== undefined && { usage: r.usage }),\n ...(r.systemFingerprint !== undefined && { systemFingerprint: r.systemFingerprint }),\n ...(r.finishReason !== undefined && { finishReason: r.finishReason }),\n ...(r.role !== undefined && { role: r.role }),\n };\n}\n\nexport function buildTextChunks(\n content: string,\n model: string,\n chunkSize: number,\n reasoning?: string,\n overrides?: ResponseOverrides,\n): SSEChunk[] {\n const id = overrides?.id ?? generateId();\n const created = overrides?.created ?? Math.floor(Date.now() / 1000);\n const effectiveModel = overrides?.model ?? model;\n const chunks: SSEChunk[] = [];\n const fingerprint = overrides?.systemFingerprint;\n\n // Reasoning chunks (emitted before content, OpenRouter format)\n if (reasoning) {\n for (let i = 0; i < reasoning.length; i += chunkSize) {\n const slice = reasoning.slice(i, i + chunkSize);\n chunks.push({\n id,\n object: \"chat.completion.chunk\",\n created,\n model: effectiveModel,\n choices: [{ index: 0, delta: { reasoning_content: slice }, finish_reason: null }],\n ...(fingerprint !== undefined && { system_fingerprint: fingerprint }),\n });\n }\n }\n\n // Role chunk\n chunks.push({\n id,\n object: \"chat.completion.chunk\",\n created,\n model: effectiveModel,\n choices: [\n {\n index: 0,\n delta: { role: overrides?.role ?? \"assistant\", content: \"\" },\n finish_reason: null,\n },\n ],\n ...(fingerprint !== undefined && { system_fingerprint: fingerprint }),\n });\n\n // Content chunks\n for (let i = 0; i < content.length; i += chunkSize) {\n const slice = content.slice(i, i + chunkSize);\n chunks.push({\n id,\n object: \"chat.completion.chunk\",\n created,\n model: effectiveModel,\n choices: [{ index: 0, delta: { content: slice }, finish_reason: null }],\n ...(fingerprint !== undefined && { system_fingerprint: fingerprint }),\n });\n }\n\n // Finish chunk\n chunks.push({\n id,\n object: \"chat.completion.chunk\",\n created,\n model: effectiveModel,\n choices: [{ index: 0, delta: {}, finish_reason: overrides?.finishReason ?? \"stop\" }],\n ...(fingerprint !== undefined && { system_fingerprint: fingerprint }),\n });\n\n return chunks;\n}\n\nexport function buildToolCallChunks(\n toolCalls: ToolCall[],\n model: string,\n chunkSize: number,\n overrides?: ResponseOverrides,\n): SSEChunk[] {\n const id = overrides?.id ?? generateId();\n const created = overrides?.created ?? Math.floor(Date.now() / 1000);\n const effectiveModel = overrides?.model ?? model;\n const chunks: SSEChunk[] = [];\n const fingerprint = overrides?.systemFingerprint;\n\n // Role chunk\n chunks.push({\n id,\n object: \"chat.completion.chunk\",\n created,\n model: effectiveModel,\n choices: [\n {\n index: 0,\n delta: { role: overrides?.role ?? \"assistant\", content: null },\n finish_reason: null,\n },\n ],\n ...(fingerprint !== undefined && { system_fingerprint: fingerprint }),\n });\n\n // Tool call chunks — one initial chunk per tool call, then argument chunks\n for (let tcIdx = 0; tcIdx < toolCalls.length; tcIdx++) {\n const tc = toolCalls[tcIdx];\n const tcId = tc.id || generateToolCallId();\n\n // Initial tool call chunk (id + function name)\n chunks.push({\n id,\n object: \"chat.completion.chunk\",\n created,\n model: effectiveModel,\n choices: [\n {\n index: 0,\n delta: {\n tool_calls: [\n {\n index: tcIdx,\n id: tcId,\n type: \"function\",\n function: { name: tc.name, arguments: \"\" },\n },\n ],\n },\n finish_reason: null,\n },\n ],\n ...(fingerprint !== undefined && { system_fingerprint: fingerprint }),\n });\n\n // Argument streaming chunks\n const args = tc.arguments;\n for (let i = 0; i < args.length; i += chunkSize) {\n const slice = args.slice(i, i + chunkSize);\n chunks.push({\n id,\n object: \"chat.completion.chunk\",\n created,\n model: effectiveModel,\n choices: [\n {\n index: 0,\n delta: {\n tool_calls: [{ index: tcIdx, function: { arguments: slice } }],\n },\n finish_reason: null,\n },\n ],\n ...(fingerprint !== undefined && { system_fingerprint: fingerprint }),\n });\n }\n }\n\n // Finish chunk\n chunks.push({\n id,\n object: \"chat.completion.chunk\",\n created,\n model: effectiveModel,\n choices: [{ index: 0, delta: {}, finish_reason: overrides?.finishReason ?? \"tool_calls\" }],\n ...(fingerprint !== undefined && { system_fingerprint: fingerprint }),\n });\n\n return chunks;\n}\n\n// Non-streaming response builders\n\nexport function buildTextCompletion(\n content: string,\n model: string,\n reasoning?: string,\n overrides?: ResponseOverrides,\n): ChatCompletion {\n const defaultUsage = { prompt_tokens: 0, completion_tokens: 0, total_tokens: 0 };\n return {\n id: overrides?.id ?? generateId(),\n object: \"chat.completion\",\n created: overrides?.created ?? Math.floor(Date.now() / 1000),\n model: overrides?.model ?? model,\n choices: [\n {\n index: 0,\n message: {\n role: overrides?.role ?? \"assistant\",\n content,\n refusal: null,\n ...(reasoning ? { reasoning_content: reasoning } : {}),\n },\n finish_reason: overrides?.finishReason ?? \"stop\",\n },\n ],\n usage: overrides?.usage\n ? {\n prompt_tokens: overrides.usage.prompt_tokens ?? defaultUsage.prompt_tokens,\n completion_tokens: overrides.usage.completion_tokens ?? defaultUsage.completion_tokens,\n total_tokens:\n overrides.usage.total_tokens ??\n (overrides.usage.prompt_tokens ?? 0) + (overrides.usage.completion_tokens ?? 0),\n }\n : defaultUsage,\n ...(overrides?.systemFingerprint !== undefined && {\n system_fingerprint: overrides.systemFingerprint,\n }),\n };\n}\n\nexport function buildToolCallCompletion(\n toolCalls: ToolCall[],\n model: string,\n overrides?: ResponseOverrides,\n): ChatCompletion {\n const defaultUsage = { prompt_tokens: 0, completion_tokens: 0, total_tokens: 0 };\n return {\n id: overrides?.id ?? generateId(),\n object: \"chat.completion\",\n created: overrides?.created ?? Math.floor(Date.now() / 1000),\n model: overrides?.model ?? model,\n choices: [\n {\n index: 0,\n message: {\n role: overrides?.role ?? \"assistant\",\n content: null,\n refusal: null,\n tool_calls: toolCalls.map((tc) => ({\n id: tc.id || generateToolCallId(),\n type: \"function\" as const,\n function: { name: tc.name, arguments: tc.arguments },\n })),\n },\n finish_reason: overrides?.finishReason ?? \"tool_calls\",\n },\n ],\n usage: overrides?.usage\n ? {\n prompt_tokens: overrides.usage.prompt_tokens ?? defaultUsage.prompt_tokens,\n completion_tokens: overrides.usage.completion_tokens ?? defaultUsage.completion_tokens,\n total_tokens:\n overrides.usage.total_tokens ??\n (overrides.usage.prompt_tokens ?? 0) + (overrides.usage.completion_tokens ?? 0),\n }\n : defaultUsage,\n ...(overrides?.systemFingerprint !== undefined && {\n system_fingerprint: overrides.systemFingerprint,\n }),\n };\n}\n\nexport function buildContentWithToolCallsChunks(\n content: string,\n toolCalls: ToolCall[],\n model: string,\n chunkSize: number,\n reasoning?: string,\n overrides?: ResponseOverrides,\n): SSEChunk[] {\n const id = overrides?.id ?? generateId();\n const created = overrides?.created ?? Math.floor(Date.now() / 1000);\n const effectiveModel = overrides?.model ?? model;\n const chunks: SSEChunk[] = [];\n const fingerprint = overrides?.systemFingerprint;\n\n // Reasoning chunks (emitted before content, OpenRouter format)\n if (reasoning) {\n for (let i = 0; i < reasoning.length; i += chunkSize) {\n const slice = reasoning.slice(i, i + chunkSize);\n chunks.push({\n id,\n object: \"chat.completion.chunk\",\n created,\n model: effectiveModel,\n choices: [{ index: 0, delta: { reasoning_content: slice }, finish_reason: null }],\n ...(fingerprint !== undefined && { system_fingerprint: fingerprint }),\n });\n }\n }\n\n // Role chunk\n chunks.push({\n id,\n object: \"chat.completion.chunk\",\n created,\n model: effectiveModel,\n choices: [\n {\n index: 0,\n delta: { role: overrides?.role ?? \"assistant\", content: \"\" },\n finish_reason: null,\n },\n ],\n ...(fingerprint !== undefined && { system_fingerprint: fingerprint }),\n });\n\n // Content chunks\n for (let i = 0; i < content.length; i += chunkSize) {\n const slice = content.slice(i, i + chunkSize);\n chunks.push({\n id,\n object: \"chat.completion.chunk\",\n created,\n model: effectiveModel,\n choices: [{ index: 0, delta: { content: slice }, finish_reason: null }],\n ...(fingerprint !== undefined && { system_fingerprint: fingerprint }),\n });\n }\n\n // Tool call chunks — one initial chunk per tool call, then argument chunks\n for (let tcIdx = 0; tcIdx < toolCalls.length; tcIdx++) {\n const tc = toolCalls[tcIdx];\n const tcId = tc.id || generateToolCallId();\n\n // Initial tool call chunk (id + function name)\n chunks.push({\n id,\n object: \"chat.completion.chunk\",\n created,\n model: effectiveModel,\n choices: [\n {\n index: 0,\n delta: {\n tool_calls: [\n {\n index: tcIdx,\n id: tcId,\n type: \"function\",\n function: { name: tc.name, arguments: \"\" },\n },\n ],\n },\n finish_reason: null,\n },\n ],\n ...(fingerprint !== undefined && { system_fingerprint: fingerprint }),\n });\n\n // Argument streaming chunks\n const args = tc.arguments;\n for (let i = 0; i < args.length; i += chunkSize) {\n const slice = args.slice(i, i + chunkSize);\n chunks.push({\n id,\n object: \"chat.completion.chunk\",\n created,\n model: effectiveModel,\n choices: [\n {\n index: 0,\n delta: {\n tool_calls: [{ index: tcIdx, function: { arguments: slice } }],\n },\n finish_reason: null,\n },\n ],\n ...(fingerprint !== undefined && { system_fingerprint: fingerprint }),\n });\n }\n }\n\n // Finish chunk\n chunks.push({\n id,\n object: \"chat.completion.chunk\",\n created,\n model: effectiveModel,\n choices: [{ index: 0, delta: {}, finish_reason: overrides?.finishReason ?? \"tool_calls\" }],\n ...(fingerprint !== undefined && { system_fingerprint: fingerprint }),\n });\n\n return chunks;\n}\n\nexport function buildContentWithToolCallsCompletion(\n content: string,\n toolCalls: ToolCall[],\n model: string,\n reasoning?: string,\n overrides?: ResponseOverrides,\n): ChatCompletion {\n const defaultUsage = { prompt_tokens: 0, completion_tokens: 0, total_tokens: 0 };\n return {\n id: overrides?.id ?? generateId(),\n object: \"chat.completion\",\n created: overrides?.created ?? Math.floor(Date.now() / 1000),\n model: overrides?.model ?? model,\n choices: [\n {\n index: 0,\n message: {\n role: overrides?.role ?? \"assistant\",\n content,\n refusal: null,\n ...(reasoning ? { reasoning_content: reasoning } : {}),\n tool_calls: toolCalls.map((tc) => ({\n id: tc.id || generateToolCallId(),\n type: \"function\" as const,\n function: { name: tc.name, arguments: tc.arguments },\n })),\n },\n finish_reason: overrides?.finishReason ?? \"tool_calls\",\n },\n ],\n usage: overrides?.usage\n ? {\n prompt_tokens: overrides.usage.prompt_tokens ?? defaultUsage.prompt_tokens,\n completion_tokens: overrides.usage.completion_tokens ?? defaultUsage.completion_tokens,\n total_tokens:\n overrides.usage.total_tokens ??\n (overrides.usage.prompt_tokens ?? 0) + (overrides.usage.completion_tokens ?? 0),\n }\n : defaultUsage,\n ...(overrides?.systemFingerprint !== undefined && {\n system_fingerprint: overrides.systemFingerprint,\n }),\n };\n}\n\n// ─── HTTP helpers ─────────────────────────────────────────────────────────\n\nexport function readBody(req: http.IncomingMessage): Promise<string> {\n return new Promise((resolve, reject) => {\n const chunks: Buffer[] = [];\n req.on(\"data\", (chunk: Buffer) => chunks.push(chunk));\n req.on(\"end\", () => resolve(Buffer.concat(chunks).toString()));\n req.on(\"error\", reject);\n });\n}\n\n// ─── Pattern matching ─────────────────────────────────────────────────────\n\n/**\n * Case-insensitive substring/regex match used for search, rerank, and\n * moderation endpoints where exact casing rarely matters. String patterns\n * are lowercased on both sides before comparison.\n *\n * Note: This intentionally differs from the case-sensitive matching in\n * {@link matchFixture} (router.ts), where fixture authors expect exact\n * string matching against chat completion user messages.\n */\nexport function matchesPattern(text: string, pattern: string | RegExp): boolean {\n if (typeof pattern === \"string\") {\n return text.toLowerCase().includes(pattern.toLowerCase());\n }\n return pattern.test(text);\n}\n\nexport function getTestId(req: http.IncomingMessage): string {\n const headerValue = req.headers[\"x-test-id\"];\n if (Array.isArray(headerValue)) {\n if (headerValue.length > 0 && headerValue[0]) return headerValue[0];\n } else if (typeof headerValue === \"string\" && headerValue) {\n return headerValue;\n }\n\n const url = req.url ?? \"/\";\n const qIdx = url.indexOf(\"?\");\n if (qIdx !== -1) {\n const params = new URLSearchParams(url.slice(qIdx + 1));\n const queryValue = params.get(\"testId\");\n if (queryValue) return queryValue;\n }\n\n // Duplicated from journal.ts DEFAULT_TEST_ID — importing it here would create\n // a circular dependency (journal.ts imports from helpers.ts).\n return \"__default__\";\n}\n\n// ─── Snapshot recording helpers ──────────────────────────────────────────────\n\n/**\n * Convert a test ID (e.g. Playwright titlePath) into a filesystem-safe slug\n * suitable for use as a directory name in snapshot-style recording.\n */\nexport function slugifyTestId(testId: string): string {\n return testId\n .replace(/^.*?\\.(?:spec|test|e2e)\\.(?:tsx|ts|jsx|js|mjs|cjs)(?=\\s|›|$)\\s*›?\\s*/i, \"\") // strip test file extension prefix\n .replace(/\\s*[›>]\\s*/g, \"--\") // Playwright titlePath separator → double dash\n .replace(/[^\\w-]/g, \"-\") // non-word chars → dash\n .replace(/-{3,}/g, \"--\") // collapse 3+ dashes to double\n .replace(/^-+|-+$/g, \"\") // trim leading/trailing dashes\n .toLowerCase();\n}\n\n// ─── Embedding helpers ─────────────────────────────────────────────────────\n\nconst DEFAULT_EMBEDDING_DIMENSIONS = 1536;\n\n/**\n * Generate a deterministic embedding vector from input text.\n * Hashes the input with SHA-256 and spreads the hash bytes across\n * the requested number of dimensions, producing values in [-1, 1].\n */\nexport function generateDeterministicEmbedding(\n input: string,\n dimensions: number = DEFAULT_EMBEDDING_DIMENSIONS,\n): number[] {\n let currentHash = createHash(\"sha256\").update(input).digest();\n const embedding: number[] = new Array(dimensions);\n for (let i = 0; i < dimensions; i++) {\n if (i > 0 && i % 32 === 0) {\n currentHash = createHash(\"sha256\").update(currentHash).digest();\n }\n // Map 0-255 → -1.0 to 1.0\n embedding[i] = currentHash[i % 32] / 127.5 - 1;\n }\n return embedding;\n}\n\nexport interface EmbeddingAPIResponse {\n object: \"list\";\n data: { object: \"embedding\"; index: number; embedding: number[] }[];\n model: string;\n usage: { prompt_tokens: number; total_tokens: number };\n}\n\n/**\n * Build an OpenAI-format embeddings API response for one or more inputs.\n */\nexport function buildEmbeddingResponse(\n embeddings: number[][],\n model: string,\n): EmbeddingAPIResponse {\n return {\n object: \"list\",\n data: embeddings.map((embedding, index) => ({\n object: \"embedding\" as const,\n index,\n embedding,\n })),\n model,\n usage: { prompt_tokens: 0, total_tokens: 0 },\n };\n}\n"],"mappings":";;;;AAuBA,MAAM,mBAAmB,IAAI,IAAI;CAAC;CAAiB;CAAa;CAAU,CAAC;AAE3E,SAAgB,eAAe,SAA2D;CACxF,MAAM,OAA+B,EAAE;AACvC,MAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,QAAQ,EAAE;AAClD,MAAI,UAAU,OAAW;AACzB,MAAI,iBAAiB,IAAI,IAAI,aAAa,CAAC,CACzC,MAAK,OAAO;MAEZ,MAAK,OAAO,MAAM,QAAQ,MAAM,GAAG,MAAM,KAAK,KAAK,GAAG;;AAG1D,QAAO;;AAOT,eAAsB,gBACpB,SACA,SAC0B;AAC1B,KAAI,OAAO,QAAQ,aAAa,WAE9B,QAAO,yBADK,MAAM,QAAQ,SAAS,QAAQ,CACP;AAEtC,QAAO,QAAQ;;AAGjB,SAAS,yBAAyB,KAAuC;CACvE,MAAM,IAAI,EAAE,GAAG,KAAK;AACpB,KAAI,OAAO,EAAE,YAAY,YAAY,EAAE,YAAY,KACjD,GAAE,UAAU,KAAK,UAAU,EAAE,QAAQ;AAEvC,KAAI,MAAM,QAAQ,EAAE,UAAU,CAC5B,GAAE,YAAa,EAAE,UAA6C,KAAK,OAAO;AACxE,MAAI,OAAO,GAAG,cAAc,YAAY,GAAG,cAAc,KACvD,QAAO;GAAE,GAAG;GAAI,WAAW,KAAK,UAAU,GAAG,UAAU;GAAE;AAE3D,SAAO;GACP;AAEJ,QAAO;;AAGT,SAAgB,WAAW,SAAS,YAAoB;AACtD,QAAO,GAAG,OAAO,gCAAe,GAAG,CAAC,SAAS,YAAY;;AAG3D,SAAgB,qBAA6B;AAC3C,QAAO,qCAAoB,GAAG,CAAC,SAAS,YAAY;;AAGtD,SAAgB,oBAA4B;AAC1C,QAAO,oCAAmB,GAAG,CAAC,SAAS,YAAY;;AAGrD,SAAgB,oBAA4B;AAC1C,QAAO,sCAAqB,GAAG,CAAC,SAAS,YAAY;;AAGvD,SAAgB,eAAe,GAAuC;AACpE,QAAO,aAAa,KAAK,OAAQ,EAAmB,YAAY,YAAY,EAAE,eAAe;;AAG/F,SAAgB,mBAAmB,GAA2C;AAC5E,QACE,eAAe,KACf,MAAM,QAAS,EAAuB,UAAU,IAChD,EAAE,aAAa,KAAK,OAAQ,EAAyC,YAAY;;AAIrF,SAAgB,+BACd,GACmC;AACnC,QACE,aAAa,KACb,OAAQ,EAAmC,YAAY,YACvD,eAAe,KACf,MAAM,QAAS,EAAmC,UAAU;;AAIhE,SAAgB,gBAAgB,GAAwC;AACtE,QACE,WAAW,KACV,EAAoB,UAAU,QAC/B,OAAQ,EAAoB,UAAU;;AAI1C,SAAgB,oBAAoB,GAA4C;AAC9E,QAAO,eAAe,KAAK,MAAM,QAAS,EAAwB,UAAU;;AAG9E,SAAgB,gBAAgB,GAAwC;AACtE,QACG,WAAW,KAAK,EAAE,SAAS,QAC3B,YAAY,KAAK,MAAM,QAAS,EAAoB,OAAO;;AAIhE,SAAgB,gBAAgB,GAAwC;AACtE,KAAI,EAAE,WAAW,GAAI,QAAO;CAC5B,MAAM,IAAK,EAAoB;AAC/B,QAAO,OAAO,MAAM,YAAa,OAAO,MAAM,YAAY,MAAM,QAAQ,aAAa;;;;;;AAOvF,MAAa,yBAAiD;CAC5D,KAAK;CACL,MAAM;CACN,KAAK;CACL,MAAM;CACN,KAAK;CACL,KAAK;CACN;;;;;AAMD,SAAgB,aAAa,QAAwB;AACnD,QAAO,uBAAuB,WAAW;;AAG3C,SAAgB,wBAAwB,GAAgD;AACtF,QACE,mBAAmB,KAClB,EAA4B,iBAAiB,QAC9C,OAAQ,EAA4B,kBAAkB;;AAI1D,SAAgB,gBAAgB,GAAwC;AACtE,QACE,WAAW,KACV,EAAoB,SAAS,QAC9B,OAAQ,EAAoB,UAAU;;AAI1C,SAAgB,eAAe,GAA0C;AACvE,QAAO,UAAU,KAAM,EAAsB,SAAS;;AAGxD,SAAgB,iBACd,UACmB;CACnB,MAAM,IAAI;AACV,QAAO;EACL,GAAI,EAAE,OAAO,UAAa,EAAE,IAAI,EAAE,IAAI;EACtC,GAAI,EAAE,YAAY,UAAa,EAAE,SAAS,EAAE,SAAS;EACrD,GAAI,EAAE,UAAU,UAAa,EAAE,OAAO,EAAE,OAAO;EAC/C,GAAI,EAAE,UAAU,UAAa,EAAE,OAAO,EAAE,OAAO;EAC/C,GAAI,EAAE,sBAAsB,UAAa,EAAE,mBAAmB,EAAE,mBAAmB;EACnF,GAAI,EAAE,iBAAiB,UAAa,EAAE,cAAc,EAAE,cAAc;EACpE,GAAI,EAAE,SAAS,UAAa,EAAE,MAAM,EAAE,MAAM;EAC7C;;AAGH,SAAgB,gBACd,SACA,OACA,WACA,WACA,WACY;CACZ,MAAM,KAAK,WAAW,MAAM,YAAY;CACxC,MAAM,UAAU,WAAW,WAAW,KAAK,MAAM,KAAK,KAAK,GAAG,IAAK;CACnE,MAAM,iBAAiB,WAAW,SAAS;CAC3C,MAAM,SAAqB,EAAE;CAC7B,MAAM,cAAc,WAAW;AAG/B,KAAI,UACF,MAAK,IAAI,IAAI,GAAG,IAAI,UAAU,QAAQ,KAAK,WAAW;EACpD,MAAM,QAAQ,UAAU,MAAM,GAAG,IAAI,UAAU;AAC/C,SAAO,KAAK;GACV;GACA,QAAQ;GACR;GACA,OAAO;GACP,SAAS,CAAC;IAAE,OAAO;IAAG,OAAO,EAAE,mBAAmB,OAAO;IAAE,eAAe;IAAM,CAAC;GACjF,GAAI,gBAAgB,UAAa,EAAE,oBAAoB,aAAa;GACrE,CAAC;;AAKN,QAAO,KAAK;EACV;EACA,QAAQ;EACR;EACA,OAAO;EACP,SAAS,CACP;GACE,OAAO;GACP,OAAO;IAAE,MAAM,WAAW,QAAQ;IAAa,SAAS;IAAI;GAC5D,eAAe;GAChB,CACF;EACD,GAAI,gBAAgB,UAAa,EAAE,oBAAoB,aAAa;EACrE,CAAC;AAGF,MAAK,IAAI,IAAI,GAAG,IAAI,QAAQ,QAAQ,KAAK,WAAW;EAClD,MAAM,QAAQ,QAAQ,MAAM,GAAG,IAAI,UAAU;AAC7C,SAAO,KAAK;GACV;GACA,QAAQ;GACR;GACA,OAAO;GACP,SAAS,CAAC;IAAE,OAAO;IAAG,OAAO,EAAE,SAAS,OAAO;IAAE,eAAe;IAAM,CAAC;GACvE,GAAI,gBAAgB,UAAa,EAAE,oBAAoB,aAAa;GACrE,CAAC;;AAIJ,QAAO,KAAK;EACV;EACA,QAAQ;EACR;EACA,OAAO;EACP,SAAS,CAAC;GAAE,OAAO;GAAG,OAAO,EAAE;GAAE,eAAe,WAAW,gBAAgB;GAAQ,CAAC;EACpF,GAAI,gBAAgB,UAAa,EAAE,oBAAoB,aAAa;EACrE,CAAC;AAEF,QAAO;;AAGT,SAAgB,oBACd,WACA,OACA,WACA,WACY;CACZ,MAAM,KAAK,WAAW,MAAM,YAAY;CACxC,MAAM,UAAU,WAAW,WAAW,KAAK,MAAM,KAAK,KAAK,GAAG,IAAK;CACnE,MAAM,iBAAiB,WAAW,SAAS;CAC3C,MAAM,SAAqB,EAAE;CAC7B,MAAM,cAAc,WAAW;AAG/B,QAAO,KAAK;EACV;EACA,QAAQ;EACR;EACA,OAAO;EACP,SAAS,CACP;GACE,OAAO;GACP,OAAO;IAAE,MAAM,WAAW,QAAQ;IAAa,SAAS;IAAM;GAC9D,eAAe;GAChB,CACF;EACD,GAAI,gBAAgB,UAAa,EAAE,oBAAoB,aAAa;EACrE,CAAC;AAGF,MAAK,IAAI,QAAQ,GAAG,QAAQ,UAAU,QAAQ,SAAS;EACrD,MAAM,KAAK,UAAU;EACrB,MAAM,OAAO,GAAG,MAAM,oBAAoB;AAG1C,SAAO,KAAK;GACV;GACA,QAAQ;GACR;GACA,OAAO;GACP,SAAS,CACP;IACE,OAAO;IACP,OAAO,EACL,YAAY,CACV;KACE,OAAO;KACP,IAAI;KACJ,MAAM;KACN,UAAU;MAAE,MAAM,GAAG;MAAM,WAAW;MAAI;KAC3C,CACF,EACF;IACD,eAAe;IAChB,CACF;GACD,GAAI,gBAAgB,UAAa,EAAE,oBAAoB,aAAa;GACrE,CAAC;EAGF,MAAM,OAAO,GAAG;AAChB,OAAK,IAAI,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK,WAAW;GAC/C,MAAM,QAAQ,KAAK,MAAM,GAAG,IAAI,UAAU;AAC1C,UAAO,KAAK;IACV;IACA,QAAQ;IACR;IACA,OAAO;IACP,SAAS,CACP;KACE,OAAO;KACP,OAAO,EACL,YAAY,CAAC;MAAE,OAAO;MAAO,UAAU,EAAE,WAAW,OAAO;MAAE,CAAC,EAC/D;KACD,eAAe;KAChB,CACF;IACD,GAAI,gBAAgB,UAAa,EAAE,oBAAoB,aAAa;IACrE,CAAC;;;AAKN,QAAO,KAAK;EACV;EACA,QAAQ;EACR;EACA,OAAO;EACP,SAAS,CAAC;GAAE,OAAO;GAAG,OAAO,EAAE;GAAE,eAAe,WAAW,gBAAgB;GAAc,CAAC;EAC1F,GAAI,gBAAgB,UAAa,EAAE,oBAAoB,aAAa;EACrE,CAAC;AAEF,QAAO;;AAKT,SAAgB,oBACd,SACA,OACA,WACA,WACgB;CAChB,MAAM,eAAe;EAAE,eAAe;EAAG,mBAAmB;EAAG,cAAc;EAAG;AAChF,QAAO;EACL,IAAI,WAAW,MAAM,YAAY;EACjC,QAAQ;EACR,SAAS,WAAW,WAAW,KAAK,MAAM,KAAK,KAAK,GAAG,IAAK;EAC5D,OAAO,WAAW,SAAS;EAC3B,SAAS,CACP;GACE,OAAO;GACP,SAAS;IACP,MAAM,WAAW,QAAQ;IACzB;IACA,SAAS;IACT,GAAI,YAAY,EAAE,mBAAmB,WAAW,GAAG,EAAE;IACtD;GACD,eAAe,WAAW,gBAAgB;GAC3C,CACF;EACD,OAAO,WAAW,QACd;GACE,eAAe,UAAU,MAAM,iBAAiB,aAAa;GAC7D,mBAAmB,UAAU,MAAM,qBAAqB,aAAa;GACrE,cACE,UAAU,MAAM,iBACf,UAAU,MAAM,iBAAiB,MAAM,UAAU,MAAM,qBAAqB;GAChF,GACD;EACJ,GAAI,WAAW,sBAAsB,UAAa,EAChD,oBAAoB,UAAU,mBAC/B;EACF;;AAGH,SAAgB,wBACd,WACA,OACA,WACgB;CAChB,MAAM,eAAe;EAAE,eAAe;EAAG,mBAAmB;EAAG,cAAc;EAAG;AAChF,QAAO;EACL,IAAI,WAAW,MAAM,YAAY;EACjC,QAAQ;EACR,SAAS,WAAW,WAAW,KAAK,MAAM,KAAK,KAAK,GAAG,IAAK;EAC5D,OAAO,WAAW,SAAS;EAC3B,SAAS,CACP;GACE,OAAO;GACP,SAAS;IACP,MAAM,WAAW,QAAQ;IACzB,SAAS;IACT,SAAS;IACT,YAAY,UAAU,KAAK,QAAQ;KACjC,IAAI,GAAG,MAAM,oBAAoB;KACjC,MAAM;KACN,UAAU;MAAE,MAAM,GAAG;MAAM,WAAW,GAAG;MAAW;KACrD,EAAE;IACJ;GACD,eAAe,WAAW,gBAAgB;GAC3C,CACF;EACD,OAAO,WAAW,QACd;GACE,eAAe,UAAU,MAAM,iBAAiB,aAAa;GAC7D,mBAAmB,UAAU,MAAM,qBAAqB,aAAa;GACrE,cACE,UAAU,MAAM,iBACf,UAAU,MAAM,iBAAiB,MAAM,UAAU,MAAM,qBAAqB;GAChF,GACD;EACJ,GAAI,WAAW,sBAAsB,UAAa,EAChD,oBAAoB,UAAU,mBAC/B;EACF;;AAGH,SAAgB,gCACd,SACA,WACA,OACA,WACA,WACA,WACY;CACZ,MAAM,KAAK,WAAW,MAAM,YAAY;CACxC,MAAM,UAAU,WAAW,WAAW,KAAK,MAAM,KAAK,KAAK,GAAG,IAAK;CACnE,MAAM,iBAAiB,WAAW,SAAS;CAC3C,MAAM,SAAqB,EAAE;CAC7B,MAAM,cAAc,WAAW;AAG/B,KAAI,UACF,MAAK,IAAI,IAAI,GAAG,IAAI,UAAU,QAAQ,KAAK,WAAW;EACpD,MAAM,QAAQ,UAAU,MAAM,GAAG,IAAI,UAAU;AAC/C,SAAO,KAAK;GACV;GACA,QAAQ;GACR;GACA,OAAO;GACP,SAAS,CAAC;IAAE,OAAO;IAAG,OAAO,EAAE,mBAAmB,OAAO;IAAE,eAAe;IAAM,CAAC;GACjF,GAAI,gBAAgB,UAAa,EAAE,oBAAoB,aAAa;GACrE,CAAC;;AAKN,QAAO,KAAK;EACV;EACA,QAAQ;EACR;EACA,OAAO;EACP,SAAS,CACP;GACE,OAAO;GACP,OAAO;IAAE,MAAM,WAAW,QAAQ;IAAa,SAAS;IAAI;GAC5D,eAAe;GAChB,CACF;EACD,GAAI,gBAAgB,UAAa,EAAE,oBAAoB,aAAa;EACrE,CAAC;AAGF,MAAK,IAAI,IAAI,GAAG,IAAI,QAAQ,QAAQ,KAAK,WAAW;EAClD,MAAM,QAAQ,QAAQ,MAAM,GAAG,IAAI,UAAU;AAC7C,SAAO,KAAK;GACV;GACA,QAAQ;GACR;GACA,OAAO;GACP,SAAS,CAAC;IAAE,OAAO;IAAG,OAAO,EAAE,SAAS,OAAO;IAAE,eAAe;IAAM,CAAC;GACvE,GAAI,gBAAgB,UAAa,EAAE,oBAAoB,aAAa;GACrE,CAAC;;AAIJ,MAAK,IAAI,QAAQ,GAAG,QAAQ,UAAU,QAAQ,SAAS;EACrD,MAAM,KAAK,UAAU;EACrB,MAAM,OAAO,GAAG,MAAM,oBAAoB;AAG1C,SAAO,KAAK;GACV;GACA,QAAQ;GACR;GACA,OAAO;GACP,SAAS,CACP;IACE,OAAO;IACP,OAAO,EACL,YAAY,CACV;KACE,OAAO;KACP,IAAI;KACJ,MAAM;KACN,UAAU;MAAE,MAAM,GAAG;MAAM,WAAW;MAAI;KAC3C,CACF,EACF;IACD,eAAe;IAChB,CACF;GACD,GAAI,gBAAgB,UAAa,EAAE,oBAAoB,aAAa;GACrE,CAAC;EAGF,MAAM,OAAO,GAAG;AAChB,OAAK,IAAI,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK,WAAW;GAC/C,MAAM,QAAQ,KAAK,MAAM,GAAG,IAAI,UAAU;AAC1C,UAAO,KAAK;IACV;IACA,QAAQ;IACR;IACA,OAAO;IACP,SAAS,CACP;KACE,OAAO;KACP,OAAO,EACL,YAAY,CAAC;MAAE,OAAO;MAAO,UAAU,EAAE,WAAW,OAAO;MAAE,CAAC,EAC/D;KACD,eAAe;KAChB,CACF;IACD,GAAI,gBAAgB,UAAa,EAAE,oBAAoB,aAAa;IACrE,CAAC;;;AAKN,QAAO,KAAK;EACV;EACA,QAAQ;EACR;EACA,OAAO;EACP,SAAS,CAAC;GAAE,OAAO;GAAG,OAAO,EAAE;GAAE,eAAe,WAAW,gBAAgB;GAAc,CAAC;EAC1F,GAAI,gBAAgB,UAAa,EAAE,oBAAoB,aAAa;EACrE,CAAC;AAEF,QAAO;;AAGT,SAAgB,oCACd,SACA,WACA,OACA,WACA,WACgB;CAChB,MAAM,eAAe;EAAE,eAAe;EAAG,mBAAmB;EAAG,cAAc;EAAG;AAChF,QAAO;EACL,IAAI,WAAW,MAAM,YAAY;EACjC,QAAQ;EACR,SAAS,WAAW,WAAW,KAAK,MAAM,KAAK,KAAK,GAAG,IAAK;EAC5D,OAAO,WAAW,SAAS;EAC3B,SAAS,CACP;GACE,OAAO;GACP,SAAS;IACP,MAAM,WAAW,QAAQ;IACzB;IACA,SAAS;IACT,GAAI,YAAY,EAAE,mBAAmB,WAAW,GAAG,EAAE;IACrD,YAAY,UAAU,KAAK,QAAQ;KACjC,IAAI,GAAG,MAAM,oBAAoB;KACjC,MAAM;KACN,UAAU;MAAE,MAAM,GAAG;MAAM,WAAW,GAAG;MAAW;KACrD,EAAE;IACJ;GACD,eAAe,WAAW,gBAAgB;GAC3C,CACF;EACD,OAAO,WAAW,QACd;GACE,eAAe,UAAU,MAAM,iBAAiB,aAAa;GAC7D,mBAAmB,UAAU,MAAM,qBAAqB,aAAa;GACrE,cACE,UAAU,MAAM,iBACf,UAAU,MAAM,iBAAiB,MAAM,UAAU,MAAM,qBAAqB;GAChF,GACD;EACJ,GAAI,WAAW,sBAAsB,UAAa,EAChD,oBAAoB,UAAU,mBAC/B;EACF;;AAKH,SAAgB,SAAS,KAA4C;AACnE,QAAO,IAAI,SAAS,SAAS,WAAW;EACtC,MAAM,SAAmB,EAAE;AAC3B,MAAI,GAAG,SAAS,UAAkB,OAAO,KAAK,MAAM,CAAC;AACrD,MAAI,GAAG,aAAa,QAAQ,OAAO,OAAO,OAAO,CAAC,UAAU,CAAC,CAAC;AAC9D,MAAI,GAAG,SAAS,OAAO;GACvB;;;;;;;;;;;AAcJ,SAAgB,eAAe,MAAc,SAAmC;AAC9E,KAAI,OAAO,YAAY,SACrB,QAAO,KAAK,aAAa,CAAC,SAAS,QAAQ,aAAa,CAAC;AAE3D,QAAO,QAAQ,KAAK,KAAK;;AAG3B,SAAgB,UAAU,KAAmC;CAC3D,MAAM,cAAc,IAAI,QAAQ;AAChC,KAAI,MAAM,QAAQ,YAAY,EAC5B;MAAI,YAAY,SAAS,KAAK,YAAY,GAAI,QAAO,YAAY;YACxD,OAAO,gBAAgB,YAAY,YAC5C,QAAO;CAGT,MAAM,MAAM,IAAI,OAAO;CACvB,MAAM,OAAO,IAAI,QAAQ,IAAI;AAC7B,KAAI,SAAS,IAAI;EAEf,MAAM,aADS,IAAI,gBAAgB,IAAI,MAAM,OAAO,EAAE,CAAC,CAC7B,IAAI,SAAS;AACvC,MAAI,WAAY,QAAO;;AAKzB,QAAO;;;;;;AAST,SAAgB,cAAc,QAAwB;AACpD,QAAO,OACJ,QAAQ,yEAAyE,GAAG,CACpF,QAAQ,eAAe,KAAK,CAC5B,QAAQ,WAAW,IAAI,CACvB,QAAQ,UAAU,KAAK,CACvB,QAAQ,YAAY,GAAG,CACvB,aAAa;;AAKlB,MAAM,+BAA+B;;;;;;AAOrC,SAAgB,+BACd,OACA,aAAqB,8BACX;CACV,IAAI,0CAAyB,SAAS,CAAC,OAAO,MAAM,CAAC,QAAQ;CAC7D,MAAM,YAAsB,IAAI,MAAM,WAAW;AACjD,MAAK,IAAI,IAAI,GAAG,IAAI,YAAY,KAAK;AACnC,MAAI,IAAI,KAAK,IAAI,OAAO,EACtB,2CAAyB,SAAS,CAAC,OAAO,YAAY,CAAC,QAAQ;AAGjE,YAAU,KAAK,YAAY,IAAI,MAAM,QAAQ;;AAE/C,QAAO;;;;;AAaT,SAAgB,uBACd,YACA,OACsB;AACtB,QAAO;EACL,QAAQ;EACR,MAAM,WAAW,KAAK,WAAW,WAAW;GAC1C,QAAQ;GACR;GACA;GACD,EAAE;EACH;EACA,OAAO;GAAE,eAAe;GAAG,cAAc;GAAG;EAC7C"}
|
package/dist/helpers.d.cts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"helpers.d.cts","names":[],"sources":["../src/helpers.ts"],"sourcesContent":[],"mappings":";;;;
|
|
1
|
+
{"version":3,"file":"helpers.d.cts","names":[],"sources":["../src/helpers.ts"],"sourcesContent":[],"mappings":"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"}
|
package/dist/helpers.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"helpers.d.ts","names":[],"sources":["../src/helpers.ts"],"sourcesContent":[],"mappings":";;;;
|
|
1
|
+
{"version":3,"file":"helpers.d.ts","names":[],"sources":["../src/helpers.ts"],"sourcesContent":[],"mappings":"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"}
|
package/dist/helpers.js
CHANGED
|
@@ -15,6 +15,22 @@ function flattenHeaders(headers) {
|
|
|
15
15
|
}
|
|
16
16
|
return flat;
|
|
17
17
|
}
|
|
18
|
+
async function resolveResponse(fixture, request) {
|
|
19
|
+
if (typeof fixture.response === "function") return normalizeFactoryResponse(await fixture.response(request));
|
|
20
|
+
return fixture.response;
|
|
21
|
+
}
|
|
22
|
+
function normalizeFactoryResponse(raw) {
|
|
23
|
+
const r = { ...raw };
|
|
24
|
+
if (typeof r.content === "object" && r.content !== null) r.content = JSON.stringify(r.content);
|
|
25
|
+
if (Array.isArray(r.toolCalls)) r.toolCalls = r.toolCalls.map((tc) => {
|
|
26
|
+
if (typeof tc.arguments === "object" && tc.arguments !== null) return {
|
|
27
|
+
...tc,
|
|
28
|
+
arguments: JSON.stringify(tc.arguments)
|
|
29
|
+
};
|
|
30
|
+
return tc;
|
|
31
|
+
});
|
|
32
|
+
return r;
|
|
33
|
+
}
|
|
18
34
|
function generateId(prefix = "chatcmpl") {
|
|
19
35
|
return `${prefix}-${randomBytes(12).toString("base64url")}`;
|
|
20
36
|
}
|
|
@@ -477,6 +493,13 @@ function getTestId(req) {
|
|
|
477
493
|
}
|
|
478
494
|
return "__default__";
|
|
479
495
|
}
|
|
496
|
+
/**
|
|
497
|
+
* Convert a test ID (e.g. Playwright titlePath) into a filesystem-safe slug
|
|
498
|
+
* suitable for use as a directory name in snapshot-style recording.
|
|
499
|
+
*/
|
|
500
|
+
function slugifyTestId(testId) {
|
|
501
|
+
return testId.replace(/^.*?\.(?:spec|test|e2e)\.(?:tsx|ts|jsx|js|mjs|cjs)(?=\s|›|$)\s*›?\s*/i, "").replace(/\s*[›>]\s*/g, "--").replace(/[^\w-]/g, "-").replace(/-{3,}/g, "--").replace(/^-+|-+$/g, "").toLowerCase();
|
|
502
|
+
}
|
|
480
503
|
const DEFAULT_EMBEDDING_DIMENSIONS = 1536;
|
|
481
504
|
/**
|
|
482
505
|
* Generate a deterministic embedding vector from input text.
|
|
@@ -512,5 +535,5 @@ function buildEmbeddingResponse(embeddings, model) {
|
|
|
512
535
|
}
|
|
513
536
|
|
|
514
537
|
//#endregion
|
|
515
|
-
export { FORMAT_TO_CONTENT_TYPE, buildContentWithToolCallsChunks, buildContentWithToolCallsCompletion, buildEmbeddingResponse, buildTextChunks, buildTextCompletion, buildToolCallChunks, buildToolCallCompletion, extractOverrides, flattenHeaders, formatToMime, generateDeterministicEmbedding, generateId, generateMessageId, generateToolCallId, generateToolUseId, getTestId, isAudioResponse, isContentWithToolCallsResponse, isEmbeddingResponse, isErrorResponse, isImageResponse, isJSONResponse, isTextResponse, isToolCallResponse, isTranscriptionResponse, isVideoResponse, matchesPattern, readBody };
|
|
538
|
+
export { FORMAT_TO_CONTENT_TYPE, buildContentWithToolCallsChunks, buildContentWithToolCallsCompletion, buildEmbeddingResponse, buildTextChunks, buildTextCompletion, buildToolCallChunks, buildToolCallCompletion, extractOverrides, flattenHeaders, formatToMime, generateDeterministicEmbedding, generateId, generateMessageId, generateToolCallId, generateToolUseId, getTestId, isAudioResponse, isContentWithToolCallsResponse, isEmbeddingResponse, isErrorResponse, isImageResponse, isJSONResponse, isTextResponse, isToolCallResponse, isTranscriptionResponse, isVideoResponse, matchesPattern, readBody, resolveResponse, slugifyTestId };
|
|
516
539
|
//# sourceMappingURL=helpers.js.map
|
package/dist/helpers.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"helpers.js","names":[],"sources":["../src/helpers.ts"],"sourcesContent":["import { createHash, randomBytes } from \"node:crypto\";\nimport type * as http from \"node:http\";\nimport type {\n FixtureResponse,\n TextResponse,\n ToolCallResponse,\n ContentWithToolCallsResponse,\n ErrorResponse,\n EmbeddingResponse,\n ImageResponse,\n AudioResponse,\n TranscriptionResponse,\n VideoResponse,\n RawJSONResponse,\n SSEChunk,\n ToolCall,\n ChatCompletion,\n ResponseOverrides,\n} from \"./types.js\";\n\nconst REDACTED_HEADERS = new Set([\"authorization\", \"x-api-key\", \"api-key\"]);\n\nexport function flattenHeaders(headers: http.IncomingHttpHeaders): Record<string, string> {\n const flat: Record<string, string> = {};\n for (const [key, value] of Object.entries(headers)) {\n if (value === undefined) continue;\n if (REDACTED_HEADERS.has(key.toLowerCase())) {\n flat[key] = \"[REDACTED]\";\n } else {\n flat[key] = Array.isArray(value) ? value.join(\", \") : value;\n }\n }\n return flat;\n}\n\nexport function generateId(prefix = \"chatcmpl\"): string {\n return `${prefix}-${randomBytes(12).toString(\"base64url\")}`;\n}\n\nexport function generateToolCallId(): string {\n return `call_${randomBytes(12).toString(\"base64url\")}`;\n}\n\nexport function generateMessageId(): string {\n return `msg_${randomBytes(12).toString(\"base64url\")}`;\n}\n\nexport function generateToolUseId(): string {\n return `toolu_${randomBytes(12).toString(\"base64url\")}`;\n}\n\nexport function isTextResponse(r: FixtureResponse): r is TextResponse {\n return \"content\" in r && typeof (r as TextResponse).content === \"string\" && !(\"toolCalls\" in r);\n}\n\nexport function isToolCallResponse(r: FixtureResponse): r is ToolCallResponse {\n return (\n \"toolCalls\" in r &&\n Array.isArray((r as ToolCallResponse).toolCalls) &&\n !(\"content\" in r && typeof (r as unknown as Record<string, unknown>).content === \"string\")\n );\n}\n\nexport function isContentWithToolCallsResponse(\n r: FixtureResponse,\n): r is ContentWithToolCallsResponse {\n return (\n \"content\" in r &&\n typeof (r as ContentWithToolCallsResponse).content === \"string\" &&\n \"toolCalls\" in r &&\n Array.isArray((r as ContentWithToolCallsResponse).toolCalls)\n );\n}\n\nexport function isErrorResponse(r: FixtureResponse): r is ErrorResponse {\n return (\n \"error\" in r &&\n (r as ErrorResponse).error !== null &&\n typeof (r as ErrorResponse).error === \"object\"\n );\n}\n\nexport function isEmbeddingResponse(r: FixtureResponse): r is EmbeddingResponse {\n return \"embedding\" in r && Array.isArray((r as EmbeddingResponse).embedding);\n}\n\nexport function isImageResponse(r: FixtureResponse): r is ImageResponse {\n return (\n (\"image\" in r && r.image != null) ||\n (\"images\" in r && Array.isArray((r as ImageResponse).images))\n );\n}\n\nexport function isAudioResponse(r: FixtureResponse): r is AudioResponse {\n if (!(\"audio\" in r)) return false;\n const a = (r as AudioResponse).audio;\n return typeof a === \"string\" || (typeof a === \"object\" && a !== null && \"b64Json\" in a);\n}\n\n/**\n * Map audio format shorthand to MIME content types.\n * Shared between speech, ElevenLabs, and fal audio handlers.\n */\nexport const FORMAT_TO_CONTENT_TYPE: Record<string, string> = {\n mp3: \"audio/mpeg\",\n opus: \"audio/opus\",\n aac: \"audio/aac\",\n flac: \"audio/flac\",\n wav: \"audio/wav\",\n pcm: \"audio/pcm\",\n};\n\n/**\n * Resolve a format string (e.g. \"mp3\", \"opus\") to its MIME content type.\n * Falls back to \"application/octet-stream\" for unknown formats.\n */\nexport function formatToMime(format: string): string {\n return FORMAT_TO_CONTENT_TYPE[format] ?? \"application/octet-stream\";\n}\n\nexport function isTranscriptionResponse(r: FixtureResponse): r is TranscriptionResponse {\n return (\n \"transcription\" in r &&\n (r as TranscriptionResponse).transcription != null &&\n typeof (r as TranscriptionResponse).transcription === \"object\"\n );\n}\n\nexport function isVideoResponse(r: FixtureResponse): r is VideoResponse {\n return (\n \"video\" in r &&\n (r as VideoResponse).video != null &&\n typeof (r as VideoResponse).video === \"object\"\n );\n}\n\nexport function isJSONResponse(r: FixtureResponse): r is RawJSONResponse {\n return \"json\" in r && (r as RawJSONResponse).json !== undefined;\n}\n\nexport function extractOverrides(\n response: TextResponse | ToolCallResponse | ContentWithToolCallsResponse,\n): ResponseOverrides {\n const r = response;\n return {\n ...(r.id !== undefined && { id: r.id }),\n ...(r.created !== undefined && { created: r.created }),\n ...(r.model !== undefined && { model: r.model }),\n ...(r.usage !== undefined && { usage: r.usage }),\n ...(r.systemFingerprint !== undefined && { systemFingerprint: r.systemFingerprint }),\n ...(r.finishReason !== undefined && { finishReason: r.finishReason }),\n ...(r.role !== undefined && { role: r.role }),\n };\n}\n\nexport function buildTextChunks(\n content: string,\n model: string,\n chunkSize: number,\n reasoning?: string,\n overrides?: ResponseOverrides,\n): SSEChunk[] {\n const id = overrides?.id ?? generateId();\n const created = overrides?.created ?? Math.floor(Date.now() / 1000);\n const effectiveModel = overrides?.model ?? model;\n const chunks: SSEChunk[] = [];\n const fingerprint = overrides?.systemFingerprint;\n\n // Reasoning chunks (emitted before content, OpenRouter format)\n if (reasoning) {\n for (let i = 0; i < reasoning.length; i += chunkSize) {\n const slice = reasoning.slice(i, i + chunkSize);\n chunks.push({\n id,\n object: \"chat.completion.chunk\",\n created,\n model: effectiveModel,\n choices: [{ index: 0, delta: { reasoning_content: slice }, finish_reason: null }],\n ...(fingerprint !== undefined && { system_fingerprint: fingerprint }),\n });\n }\n }\n\n // Role chunk\n chunks.push({\n id,\n object: \"chat.completion.chunk\",\n created,\n model: effectiveModel,\n choices: [\n {\n index: 0,\n delta: { role: overrides?.role ?? \"assistant\", content: \"\" },\n finish_reason: null,\n },\n ],\n ...(fingerprint !== undefined && { system_fingerprint: fingerprint }),\n });\n\n // Content chunks\n for (let i = 0; i < content.length; i += chunkSize) {\n const slice = content.slice(i, i + chunkSize);\n chunks.push({\n id,\n object: \"chat.completion.chunk\",\n created,\n model: effectiveModel,\n choices: [{ index: 0, delta: { content: slice }, finish_reason: null }],\n ...(fingerprint !== undefined && { system_fingerprint: fingerprint }),\n });\n }\n\n // Finish chunk\n chunks.push({\n id,\n object: \"chat.completion.chunk\",\n created,\n model: effectiveModel,\n choices: [{ index: 0, delta: {}, finish_reason: overrides?.finishReason ?? \"stop\" }],\n ...(fingerprint !== undefined && { system_fingerprint: fingerprint }),\n });\n\n return chunks;\n}\n\nexport function buildToolCallChunks(\n toolCalls: ToolCall[],\n model: string,\n chunkSize: number,\n overrides?: ResponseOverrides,\n): SSEChunk[] {\n const id = overrides?.id ?? generateId();\n const created = overrides?.created ?? Math.floor(Date.now() / 1000);\n const effectiveModel = overrides?.model ?? model;\n const chunks: SSEChunk[] = [];\n const fingerprint = overrides?.systemFingerprint;\n\n // Role chunk\n chunks.push({\n id,\n object: \"chat.completion.chunk\",\n created,\n model: effectiveModel,\n choices: [\n {\n index: 0,\n delta: { role: overrides?.role ?? \"assistant\", content: null },\n finish_reason: null,\n },\n ],\n ...(fingerprint !== undefined && { system_fingerprint: fingerprint }),\n });\n\n // Tool call chunks — one initial chunk per tool call, then argument chunks\n for (let tcIdx = 0; tcIdx < toolCalls.length; tcIdx++) {\n const tc = toolCalls[tcIdx];\n const tcId = tc.id || generateToolCallId();\n\n // Initial tool call chunk (id + function name)\n chunks.push({\n id,\n object: \"chat.completion.chunk\",\n created,\n model: effectiveModel,\n choices: [\n {\n index: 0,\n delta: {\n tool_calls: [\n {\n index: tcIdx,\n id: tcId,\n type: \"function\",\n function: { name: tc.name, arguments: \"\" },\n },\n ],\n },\n finish_reason: null,\n },\n ],\n ...(fingerprint !== undefined && { system_fingerprint: fingerprint }),\n });\n\n // Argument streaming chunks\n const args = tc.arguments;\n for (let i = 0; i < args.length; i += chunkSize) {\n const slice = args.slice(i, i + chunkSize);\n chunks.push({\n id,\n object: \"chat.completion.chunk\",\n created,\n model: effectiveModel,\n choices: [\n {\n index: 0,\n delta: {\n tool_calls: [{ index: tcIdx, function: { arguments: slice } }],\n },\n finish_reason: null,\n },\n ],\n ...(fingerprint !== undefined && { system_fingerprint: fingerprint }),\n });\n }\n }\n\n // Finish chunk\n chunks.push({\n id,\n object: \"chat.completion.chunk\",\n created,\n model: effectiveModel,\n choices: [{ index: 0, delta: {}, finish_reason: overrides?.finishReason ?? \"tool_calls\" }],\n ...(fingerprint !== undefined && { system_fingerprint: fingerprint }),\n });\n\n return chunks;\n}\n\n// Non-streaming response builders\n\nexport function buildTextCompletion(\n content: string,\n model: string,\n reasoning?: string,\n overrides?: ResponseOverrides,\n): ChatCompletion {\n const defaultUsage = { prompt_tokens: 0, completion_tokens: 0, total_tokens: 0 };\n return {\n id: overrides?.id ?? generateId(),\n object: \"chat.completion\",\n created: overrides?.created ?? Math.floor(Date.now() / 1000),\n model: overrides?.model ?? model,\n choices: [\n {\n index: 0,\n message: {\n role: overrides?.role ?? \"assistant\",\n content,\n refusal: null,\n ...(reasoning ? { reasoning_content: reasoning } : {}),\n },\n finish_reason: overrides?.finishReason ?? \"stop\",\n },\n ],\n usage: overrides?.usage\n ? {\n prompt_tokens: overrides.usage.prompt_tokens ?? defaultUsage.prompt_tokens,\n completion_tokens: overrides.usage.completion_tokens ?? defaultUsage.completion_tokens,\n total_tokens:\n overrides.usage.total_tokens ??\n (overrides.usage.prompt_tokens ?? 0) + (overrides.usage.completion_tokens ?? 0),\n }\n : defaultUsage,\n ...(overrides?.systemFingerprint !== undefined && {\n system_fingerprint: overrides.systemFingerprint,\n }),\n };\n}\n\nexport function buildToolCallCompletion(\n toolCalls: ToolCall[],\n model: string,\n overrides?: ResponseOverrides,\n): ChatCompletion {\n const defaultUsage = { prompt_tokens: 0, completion_tokens: 0, total_tokens: 0 };\n return {\n id: overrides?.id ?? generateId(),\n object: \"chat.completion\",\n created: overrides?.created ?? Math.floor(Date.now() / 1000),\n model: overrides?.model ?? model,\n choices: [\n {\n index: 0,\n message: {\n role: overrides?.role ?? \"assistant\",\n content: null,\n refusal: null,\n tool_calls: toolCalls.map((tc) => ({\n id: tc.id || generateToolCallId(),\n type: \"function\" as const,\n function: { name: tc.name, arguments: tc.arguments },\n })),\n },\n finish_reason: overrides?.finishReason ?? \"tool_calls\",\n },\n ],\n usage: overrides?.usage\n ? {\n prompt_tokens: overrides.usage.prompt_tokens ?? defaultUsage.prompt_tokens,\n completion_tokens: overrides.usage.completion_tokens ?? defaultUsage.completion_tokens,\n total_tokens:\n overrides.usage.total_tokens ??\n (overrides.usage.prompt_tokens ?? 0) + (overrides.usage.completion_tokens ?? 0),\n }\n : defaultUsage,\n ...(overrides?.systemFingerprint !== undefined && {\n system_fingerprint: overrides.systemFingerprint,\n }),\n };\n}\n\nexport function buildContentWithToolCallsChunks(\n content: string,\n toolCalls: ToolCall[],\n model: string,\n chunkSize: number,\n reasoning?: string,\n overrides?: ResponseOverrides,\n): SSEChunk[] {\n const id = overrides?.id ?? generateId();\n const created = overrides?.created ?? Math.floor(Date.now() / 1000);\n const effectiveModel = overrides?.model ?? model;\n const chunks: SSEChunk[] = [];\n const fingerprint = overrides?.systemFingerprint;\n\n // Reasoning chunks (emitted before content, OpenRouter format)\n if (reasoning) {\n for (let i = 0; i < reasoning.length; i += chunkSize) {\n const slice = reasoning.slice(i, i + chunkSize);\n chunks.push({\n id,\n object: \"chat.completion.chunk\",\n created,\n model: effectiveModel,\n choices: [{ index: 0, delta: { reasoning_content: slice }, finish_reason: null }],\n ...(fingerprint !== undefined && { system_fingerprint: fingerprint }),\n });\n }\n }\n\n // Role chunk\n chunks.push({\n id,\n object: \"chat.completion.chunk\",\n created,\n model: effectiveModel,\n choices: [\n {\n index: 0,\n delta: { role: overrides?.role ?? \"assistant\", content: \"\" },\n finish_reason: null,\n },\n ],\n ...(fingerprint !== undefined && { system_fingerprint: fingerprint }),\n });\n\n // Content chunks\n for (let i = 0; i < content.length; i += chunkSize) {\n const slice = content.slice(i, i + chunkSize);\n chunks.push({\n id,\n object: \"chat.completion.chunk\",\n created,\n model: effectiveModel,\n choices: [{ index: 0, delta: { content: slice }, finish_reason: null }],\n ...(fingerprint !== undefined && { system_fingerprint: fingerprint }),\n });\n }\n\n // Tool call chunks — one initial chunk per tool call, then argument chunks\n for (let tcIdx = 0; tcIdx < toolCalls.length; tcIdx++) {\n const tc = toolCalls[tcIdx];\n const tcId = tc.id || generateToolCallId();\n\n // Initial tool call chunk (id + function name)\n chunks.push({\n id,\n object: \"chat.completion.chunk\",\n created,\n model: effectiveModel,\n choices: [\n {\n index: 0,\n delta: {\n tool_calls: [\n {\n index: tcIdx,\n id: tcId,\n type: \"function\",\n function: { name: tc.name, arguments: \"\" },\n },\n ],\n },\n finish_reason: null,\n },\n ],\n ...(fingerprint !== undefined && { system_fingerprint: fingerprint }),\n });\n\n // Argument streaming chunks\n const args = tc.arguments;\n for (let i = 0; i < args.length; i += chunkSize) {\n const slice = args.slice(i, i + chunkSize);\n chunks.push({\n id,\n object: \"chat.completion.chunk\",\n created,\n model: effectiveModel,\n choices: [\n {\n index: 0,\n delta: {\n tool_calls: [{ index: tcIdx, function: { arguments: slice } }],\n },\n finish_reason: null,\n },\n ],\n ...(fingerprint !== undefined && { system_fingerprint: fingerprint }),\n });\n }\n }\n\n // Finish chunk\n chunks.push({\n id,\n object: \"chat.completion.chunk\",\n created,\n model: effectiveModel,\n choices: [{ index: 0, delta: {}, finish_reason: overrides?.finishReason ?? \"tool_calls\" }],\n ...(fingerprint !== undefined && { system_fingerprint: fingerprint }),\n });\n\n return chunks;\n}\n\nexport function buildContentWithToolCallsCompletion(\n content: string,\n toolCalls: ToolCall[],\n model: string,\n reasoning?: string,\n overrides?: ResponseOverrides,\n): ChatCompletion {\n const defaultUsage = { prompt_tokens: 0, completion_tokens: 0, total_tokens: 0 };\n return {\n id: overrides?.id ?? generateId(),\n object: \"chat.completion\",\n created: overrides?.created ?? Math.floor(Date.now() / 1000),\n model: overrides?.model ?? model,\n choices: [\n {\n index: 0,\n message: {\n role: overrides?.role ?? \"assistant\",\n content,\n refusal: null,\n ...(reasoning ? { reasoning_content: reasoning } : {}),\n tool_calls: toolCalls.map((tc) => ({\n id: tc.id || generateToolCallId(),\n type: \"function\" as const,\n function: { name: tc.name, arguments: tc.arguments },\n })),\n },\n finish_reason: overrides?.finishReason ?? \"tool_calls\",\n },\n ],\n usage: overrides?.usage\n ? {\n prompt_tokens: overrides.usage.prompt_tokens ?? defaultUsage.prompt_tokens,\n completion_tokens: overrides.usage.completion_tokens ?? defaultUsage.completion_tokens,\n total_tokens:\n overrides.usage.total_tokens ??\n (overrides.usage.prompt_tokens ?? 0) + (overrides.usage.completion_tokens ?? 0),\n }\n : defaultUsage,\n ...(overrides?.systemFingerprint !== undefined && {\n system_fingerprint: overrides.systemFingerprint,\n }),\n };\n}\n\n// ─── HTTP helpers ─────────────────────────────────────────────────────────\n\nexport function readBody(req: http.IncomingMessage): Promise<string> {\n return new Promise((resolve, reject) => {\n const chunks: Buffer[] = [];\n req.on(\"data\", (chunk: Buffer) => chunks.push(chunk));\n req.on(\"end\", () => resolve(Buffer.concat(chunks).toString()));\n req.on(\"error\", reject);\n });\n}\n\n// ─── Pattern matching ─────────────────────────────────────────────────────\n\n/**\n * Case-insensitive substring/regex match used for search, rerank, and\n * moderation endpoints where exact casing rarely matters. String patterns\n * are lowercased on both sides before comparison.\n *\n * Note: This intentionally differs from the case-sensitive matching in\n * {@link matchFixture} (router.ts), where fixture authors expect exact\n * string matching against chat completion user messages.\n */\nexport function matchesPattern(text: string, pattern: string | RegExp): boolean {\n if (typeof pattern === \"string\") {\n return text.toLowerCase().includes(pattern.toLowerCase());\n }\n return pattern.test(text);\n}\n\nexport function getTestId(req: http.IncomingMessage): string {\n const headerValue = req.headers[\"x-test-id\"];\n if (Array.isArray(headerValue)) {\n if (headerValue.length > 0 && headerValue[0]) return headerValue[0];\n } else if (typeof headerValue === \"string\" && headerValue) {\n return headerValue;\n }\n\n const url = req.url ?? \"/\";\n const qIdx = url.indexOf(\"?\");\n if (qIdx !== -1) {\n const params = new URLSearchParams(url.slice(qIdx + 1));\n const queryValue = params.get(\"testId\");\n if (queryValue) return queryValue;\n }\n\n // Duplicated from journal.ts DEFAULT_TEST_ID — importing it here would create\n // a circular dependency (journal.ts imports from helpers.ts).\n return \"__default__\";\n}\n\n// ─── Embedding helpers ─────────────────────────────────────────────────────\n\nconst DEFAULT_EMBEDDING_DIMENSIONS = 1536;\n\n/**\n * Generate a deterministic embedding vector from input text.\n * Hashes the input with SHA-256 and spreads the hash bytes across\n * the requested number of dimensions, producing values in [-1, 1].\n */\nexport function generateDeterministicEmbedding(\n input: string,\n dimensions: number = DEFAULT_EMBEDDING_DIMENSIONS,\n): number[] {\n let currentHash = createHash(\"sha256\").update(input).digest();\n const embedding: number[] = new Array(dimensions);\n for (let i = 0; i < dimensions; i++) {\n if (i > 0 && i % 32 === 0) {\n currentHash = createHash(\"sha256\").update(currentHash).digest();\n }\n // Map 0-255 → -1.0 to 1.0\n embedding[i] = currentHash[i % 32] / 127.5 - 1;\n }\n return embedding;\n}\n\nexport interface EmbeddingAPIResponse {\n object: \"list\";\n data: { object: \"embedding\"; index: number; embedding: number[] }[];\n model: string;\n usage: { prompt_tokens: number; total_tokens: number };\n}\n\n/**\n * Build an OpenAI-format embeddings API response for one or more inputs.\n */\nexport function buildEmbeddingResponse(\n embeddings: number[][],\n model: string,\n): EmbeddingAPIResponse {\n return {\n object: \"list\",\n data: embeddings.map((embedding, index) => ({\n object: \"embedding\" as const,\n index,\n embedding,\n })),\n model,\n usage: { prompt_tokens: 0, total_tokens: 0 },\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"helpers.js","names":[],"sources":["../src/helpers.ts"],"sourcesContent":["import { createHash, randomBytes } from \"node:crypto\";\nimport type * as http from \"node:http\";\nimport type {\n ChatCompletionRequest,\n Fixture,\n FixtureResponse,\n ResponseFactory,\n TextResponse,\n ToolCallResponse,\n ContentWithToolCallsResponse,\n ErrorResponse,\n EmbeddingResponse,\n ImageResponse,\n AudioResponse,\n TranscriptionResponse,\n VideoResponse,\n RawJSONResponse,\n SSEChunk,\n ToolCall,\n ChatCompletion,\n ResponseOverrides,\n} from \"./types.js\";\n\nconst REDACTED_HEADERS = new Set([\"authorization\", \"x-api-key\", \"api-key\"]);\n\nexport function flattenHeaders(headers: http.IncomingHttpHeaders): Record<string, string> {\n const flat: Record<string, string> = {};\n for (const [key, value] of Object.entries(headers)) {\n if (value === undefined) continue;\n if (REDACTED_HEADERS.has(key.toLowerCase())) {\n flat[key] = \"[REDACTED]\";\n } else {\n flat[key] = Array.isArray(value) ? value.join(\", \") : value;\n }\n }\n return flat;\n}\n\nexport function isResponseFactory(r: FixtureResponse | ResponseFactory): r is ResponseFactory {\n return typeof r === \"function\";\n}\n\nexport async function resolveResponse(\n fixture: Fixture,\n request: ChatCompletionRequest,\n): Promise<FixtureResponse> {\n if (typeof fixture.response === \"function\") {\n const raw = await fixture.response(request);\n return normalizeFactoryResponse(raw);\n }\n return fixture.response;\n}\n\nfunction normalizeFactoryResponse(raw: FixtureResponse): FixtureResponse {\n const r = { ...raw } as Record<string, unknown>;\n if (typeof r.content === \"object\" && r.content !== null) {\n r.content = JSON.stringify(r.content);\n }\n if (Array.isArray(r.toolCalls)) {\n r.toolCalls = (r.toolCalls as Array<Record<string, unknown>>).map((tc) => {\n if (typeof tc.arguments === \"object\" && tc.arguments !== null) {\n return { ...tc, arguments: JSON.stringify(tc.arguments) };\n }\n return tc;\n });\n }\n return r as unknown as FixtureResponse;\n}\n\nexport function generateId(prefix = \"chatcmpl\"): string {\n return `${prefix}-${randomBytes(12).toString(\"base64url\")}`;\n}\n\nexport function generateToolCallId(): string {\n return `call_${randomBytes(12).toString(\"base64url\")}`;\n}\n\nexport function generateMessageId(): string {\n return `msg_${randomBytes(12).toString(\"base64url\")}`;\n}\n\nexport function generateToolUseId(): string {\n return `toolu_${randomBytes(12).toString(\"base64url\")}`;\n}\n\nexport function isTextResponse(r: FixtureResponse): r is TextResponse {\n return \"content\" in r && typeof (r as TextResponse).content === \"string\" && !(\"toolCalls\" in r);\n}\n\nexport function isToolCallResponse(r: FixtureResponse): r is ToolCallResponse {\n return (\n \"toolCalls\" in r &&\n Array.isArray((r as ToolCallResponse).toolCalls) &&\n !(\"content\" in r && typeof (r as unknown as Record<string, unknown>).content === \"string\")\n );\n}\n\nexport function isContentWithToolCallsResponse(\n r: FixtureResponse,\n): r is ContentWithToolCallsResponse {\n return (\n \"content\" in r &&\n typeof (r as ContentWithToolCallsResponse).content === \"string\" &&\n \"toolCalls\" in r &&\n Array.isArray((r as ContentWithToolCallsResponse).toolCalls)\n );\n}\n\nexport function isErrorResponse(r: FixtureResponse): r is ErrorResponse {\n return (\n \"error\" in r &&\n (r as ErrorResponse).error !== null &&\n typeof (r as ErrorResponse).error === \"object\"\n );\n}\n\nexport function isEmbeddingResponse(r: FixtureResponse): r is EmbeddingResponse {\n return \"embedding\" in r && Array.isArray((r as EmbeddingResponse).embedding);\n}\n\nexport function isImageResponse(r: FixtureResponse): r is ImageResponse {\n return (\n (\"image\" in r && r.image != null) ||\n (\"images\" in r && Array.isArray((r as ImageResponse).images))\n );\n}\n\nexport function isAudioResponse(r: FixtureResponse): r is AudioResponse {\n if (!(\"audio\" in r)) return false;\n const a = (r as AudioResponse).audio;\n return typeof a === \"string\" || (typeof a === \"object\" && a !== null && \"b64Json\" in a);\n}\n\n/**\n * Map audio format shorthand to MIME content types.\n * Shared between speech, ElevenLabs, and fal audio handlers.\n */\nexport const FORMAT_TO_CONTENT_TYPE: Record<string, string> = {\n mp3: \"audio/mpeg\",\n opus: \"audio/opus\",\n aac: \"audio/aac\",\n flac: \"audio/flac\",\n wav: \"audio/wav\",\n pcm: \"audio/pcm\",\n};\n\n/**\n * Resolve a format string (e.g. \"mp3\", \"opus\") to its MIME content type.\n * Falls back to \"application/octet-stream\" for unknown formats.\n */\nexport function formatToMime(format: string): string {\n return FORMAT_TO_CONTENT_TYPE[format] ?? \"application/octet-stream\";\n}\n\nexport function isTranscriptionResponse(r: FixtureResponse): r is TranscriptionResponse {\n return (\n \"transcription\" in r &&\n (r as TranscriptionResponse).transcription != null &&\n typeof (r as TranscriptionResponse).transcription === \"object\"\n );\n}\n\nexport function isVideoResponse(r: FixtureResponse): r is VideoResponse {\n return (\n \"video\" in r &&\n (r as VideoResponse).video != null &&\n typeof (r as VideoResponse).video === \"object\"\n );\n}\n\nexport function isJSONResponse(r: FixtureResponse): r is RawJSONResponse {\n return \"json\" in r && (r as RawJSONResponse).json !== undefined;\n}\n\nexport function extractOverrides(\n response: TextResponse | ToolCallResponse | ContentWithToolCallsResponse,\n): ResponseOverrides {\n const r = response;\n return {\n ...(r.id !== undefined && { id: r.id }),\n ...(r.created !== undefined && { created: r.created }),\n ...(r.model !== undefined && { model: r.model }),\n ...(r.usage !== undefined && { usage: r.usage }),\n ...(r.systemFingerprint !== undefined && { systemFingerprint: r.systemFingerprint }),\n ...(r.finishReason !== undefined && { finishReason: r.finishReason }),\n ...(r.role !== undefined && { role: r.role }),\n };\n}\n\nexport function buildTextChunks(\n content: string,\n model: string,\n chunkSize: number,\n reasoning?: string,\n overrides?: ResponseOverrides,\n): SSEChunk[] {\n const id = overrides?.id ?? generateId();\n const created = overrides?.created ?? Math.floor(Date.now() / 1000);\n const effectiveModel = overrides?.model ?? model;\n const chunks: SSEChunk[] = [];\n const fingerprint = overrides?.systemFingerprint;\n\n // Reasoning chunks (emitted before content, OpenRouter format)\n if (reasoning) {\n for (let i = 0; i < reasoning.length; i += chunkSize) {\n const slice = reasoning.slice(i, i + chunkSize);\n chunks.push({\n id,\n object: \"chat.completion.chunk\",\n created,\n model: effectiveModel,\n choices: [{ index: 0, delta: { reasoning_content: slice }, finish_reason: null }],\n ...(fingerprint !== undefined && { system_fingerprint: fingerprint }),\n });\n }\n }\n\n // Role chunk\n chunks.push({\n id,\n object: \"chat.completion.chunk\",\n created,\n model: effectiveModel,\n choices: [\n {\n index: 0,\n delta: { role: overrides?.role ?? \"assistant\", content: \"\" },\n finish_reason: null,\n },\n ],\n ...(fingerprint !== undefined && { system_fingerprint: fingerprint }),\n });\n\n // Content chunks\n for (let i = 0; i < content.length; i += chunkSize) {\n const slice = content.slice(i, i + chunkSize);\n chunks.push({\n id,\n object: \"chat.completion.chunk\",\n created,\n model: effectiveModel,\n choices: [{ index: 0, delta: { content: slice }, finish_reason: null }],\n ...(fingerprint !== undefined && { system_fingerprint: fingerprint }),\n });\n }\n\n // Finish chunk\n chunks.push({\n id,\n object: \"chat.completion.chunk\",\n created,\n model: effectiveModel,\n choices: [{ index: 0, delta: {}, finish_reason: overrides?.finishReason ?? \"stop\" }],\n ...(fingerprint !== undefined && { system_fingerprint: fingerprint }),\n });\n\n return chunks;\n}\n\nexport function buildToolCallChunks(\n toolCalls: ToolCall[],\n model: string,\n chunkSize: number,\n overrides?: ResponseOverrides,\n): SSEChunk[] {\n const id = overrides?.id ?? generateId();\n const created = overrides?.created ?? Math.floor(Date.now() / 1000);\n const effectiveModel = overrides?.model ?? model;\n const chunks: SSEChunk[] = [];\n const fingerprint = overrides?.systemFingerprint;\n\n // Role chunk\n chunks.push({\n id,\n object: \"chat.completion.chunk\",\n created,\n model: effectiveModel,\n choices: [\n {\n index: 0,\n delta: { role: overrides?.role ?? \"assistant\", content: null },\n finish_reason: null,\n },\n ],\n ...(fingerprint !== undefined && { system_fingerprint: fingerprint }),\n });\n\n // Tool call chunks — one initial chunk per tool call, then argument chunks\n for (let tcIdx = 0; tcIdx < toolCalls.length; tcIdx++) {\n const tc = toolCalls[tcIdx];\n const tcId = tc.id || generateToolCallId();\n\n // Initial tool call chunk (id + function name)\n chunks.push({\n id,\n object: \"chat.completion.chunk\",\n created,\n model: effectiveModel,\n choices: [\n {\n index: 0,\n delta: {\n tool_calls: [\n {\n index: tcIdx,\n id: tcId,\n type: \"function\",\n function: { name: tc.name, arguments: \"\" },\n },\n ],\n },\n finish_reason: null,\n },\n ],\n ...(fingerprint !== undefined && { system_fingerprint: fingerprint }),\n });\n\n // Argument streaming chunks\n const args = tc.arguments;\n for (let i = 0; i < args.length; i += chunkSize) {\n const slice = args.slice(i, i + chunkSize);\n chunks.push({\n id,\n object: \"chat.completion.chunk\",\n created,\n model: effectiveModel,\n choices: [\n {\n index: 0,\n delta: {\n tool_calls: [{ index: tcIdx, function: { arguments: slice } }],\n },\n finish_reason: null,\n },\n ],\n ...(fingerprint !== undefined && { system_fingerprint: fingerprint }),\n });\n }\n }\n\n // Finish chunk\n chunks.push({\n id,\n object: \"chat.completion.chunk\",\n created,\n model: effectiveModel,\n choices: [{ index: 0, delta: {}, finish_reason: overrides?.finishReason ?? \"tool_calls\" }],\n ...(fingerprint !== undefined && { system_fingerprint: fingerprint }),\n });\n\n return chunks;\n}\n\n// Non-streaming response builders\n\nexport function buildTextCompletion(\n content: string,\n model: string,\n reasoning?: string,\n overrides?: ResponseOverrides,\n): ChatCompletion {\n const defaultUsage = { prompt_tokens: 0, completion_tokens: 0, total_tokens: 0 };\n return {\n id: overrides?.id ?? generateId(),\n object: \"chat.completion\",\n created: overrides?.created ?? Math.floor(Date.now() / 1000),\n model: overrides?.model ?? model,\n choices: [\n {\n index: 0,\n message: {\n role: overrides?.role ?? \"assistant\",\n content,\n refusal: null,\n ...(reasoning ? { reasoning_content: reasoning } : {}),\n },\n finish_reason: overrides?.finishReason ?? \"stop\",\n },\n ],\n usage: overrides?.usage\n ? {\n prompt_tokens: overrides.usage.prompt_tokens ?? defaultUsage.prompt_tokens,\n completion_tokens: overrides.usage.completion_tokens ?? defaultUsage.completion_tokens,\n total_tokens:\n overrides.usage.total_tokens ??\n (overrides.usage.prompt_tokens ?? 0) + (overrides.usage.completion_tokens ?? 0),\n }\n : defaultUsage,\n ...(overrides?.systemFingerprint !== undefined && {\n system_fingerprint: overrides.systemFingerprint,\n }),\n };\n}\n\nexport function buildToolCallCompletion(\n toolCalls: ToolCall[],\n model: string,\n overrides?: ResponseOverrides,\n): ChatCompletion {\n const defaultUsage = { prompt_tokens: 0, completion_tokens: 0, total_tokens: 0 };\n return {\n id: overrides?.id ?? generateId(),\n object: \"chat.completion\",\n created: overrides?.created ?? Math.floor(Date.now() / 1000),\n model: overrides?.model ?? model,\n choices: [\n {\n index: 0,\n message: {\n role: overrides?.role ?? \"assistant\",\n content: null,\n refusal: null,\n tool_calls: toolCalls.map((tc) => ({\n id: tc.id || generateToolCallId(),\n type: \"function\" as const,\n function: { name: tc.name, arguments: tc.arguments },\n })),\n },\n finish_reason: overrides?.finishReason ?? \"tool_calls\",\n },\n ],\n usage: overrides?.usage\n ? {\n prompt_tokens: overrides.usage.prompt_tokens ?? defaultUsage.prompt_tokens,\n completion_tokens: overrides.usage.completion_tokens ?? defaultUsage.completion_tokens,\n total_tokens:\n overrides.usage.total_tokens ??\n (overrides.usage.prompt_tokens ?? 0) + (overrides.usage.completion_tokens ?? 0),\n }\n : defaultUsage,\n ...(overrides?.systemFingerprint !== undefined && {\n system_fingerprint: overrides.systemFingerprint,\n }),\n };\n}\n\nexport function buildContentWithToolCallsChunks(\n content: string,\n toolCalls: ToolCall[],\n model: string,\n chunkSize: number,\n reasoning?: string,\n overrides?: ResponseOverrides,\n): SSEChunk[] {\n const id = overrides?.id ?? generateId();\n const created = overrides?.created ?? Math.floor(Date.now() / 1000);\n const effectiveModel = overrides?.model ?? model;\n const chunks: SSEChunk[] = [];\n const fingerprint = overrides?.systemFingerprint;\n\n // Reasoning chunks (emitted before content, OpenRouter format)\n if (reasoning) {\n for (let i = 0; i < reasoning.length; i += chunkSize) {\n const slice = reasoning.slice(i, i + chunkSize);\n chunks.push({\n id,\n object: \"chat.completion.chunk\",\n created,\n model: effectiveModel,\n choices: [{ index: 0, delta: { reasoning_content: slice }, finish_reason: null }],\n ...(fingerprint !== undefined && { system_fingerprint: fingerprint }),\n });\n }\n }\n\n // Role chunk\n chunks.push({\n id,\n object: \"chat.completion.chunk\",\n created,\n model: effectiveModel,\n choices: [\n {\n index: 0,\n delta: { role: overrides?.role ?? \"assistant\", content: \"\" },\n finish_reason: null,\n },\n ],\n ...(fingerprint !== undefined && { system_fingerprint: fingerprint }),\n });\n\n // Content chunks\n for (let i = 0; i < content.length; i += chunkSize) {\n const slice = content.slice(i, i + chunkSize);\n chunks.push({\n id,\n object: \"chat.completion.chunk\",\n created,\n model: effectiveModel,\n choices: [{ index: 0, delta: { content: slice }, finish_reason: null }],\n ...(fingerprint !== undefined && { system_fingerprint: fingerprint }),\n });\n }\n\n // Tool call chunks — one initial chunk per tool call, then argument chunks\n for (let tcIdx = 0; tcIdx < toolCalls.length; tcIdx++) {\n const tc = toolCalls[tcIdx];\n const tcId = tc.id || generateToolCallId();\n\n // Initial tool call chunk (id + function name)\n chunks.push({\n id,\n object: \"chat.completion.chunk\",\n created,\n model: effectiveModel,\n choices: [\n {\n index: 0,\n delta: {\n tool_calls: [\n {\n index: tcIdx,\n id: tcId,\n type: \"function\",\n function: { name: tc.name, arguments: \"\" },\n },\n ],\n },\n finish_reason: null,\n },\n ],\n ...(fingerprint !== undefined && { system_fingerprint: fingerprint }),\n });\n\n // Argument streaming chunks\n const args = tc.arguments;\n for (let i = 0; i < args.length; i += chunkSize) {\n const slice = args.slice(i, i + chunkSize);\n chunks.push({\n id,\n object: \"chat.completion.chunk\",\n created,\n model: effectiveModel,\n choices: [\n {\n index: 0,\n delta: {\n tool_calls: [{ index: tcIdx, function: { arguments: slice } }],\n },\n finish_reason: null,\n },\n ],\n ...(fingerprint !== undefined && { system_fingerprint: fingerprint }),\n });\n }\n }\n\n // Finish chunk\n chunks.push({\n id,\n object: \"chat.completion.chunk\",\n created,\n model: effectiveModel,\n choices: [{ index: 0, delta: {}, finish_reason: overrides?.finishReason ?? \"tool_calls\" }],\n ...(fingerprint !== undefined && { system_fingerprint: fingerprint }),\n });\n\n return chunks;\n}\n\nexport function buildContentWithToolCallsCompletion(\n content: string,\n toolCalls: ToolCall[],\n model: string,\n reasoning?: string,\n overrides?: ResponseOverrides,\n): ChatCompletion {\n const defaultUsage = { prompt_tokens: 0, completion_tokens: 0, total_tokens: 0 };\n return {\n id: overrides?.id ?? generateId(),\n object: \"chat.completion\",\n created: overrides?.created ?? Math.floor(Date.now() / 1000),\n model: overrides?.model ?? model,\n choices: [\n {\n index: 0,\n message: {\n role: overrides?.role ?? \"assistant\",\n content,\n refusal: null,\n ...(reasoning ? { reasoning_content: reasoning } : {}),\n tool_calls: toolCalls.map((tc) => ({\n id: tc.id || generateToolCallId(),\n type: \"function\" as const,\n function: { name: tc.name, arguments: tc.arguments },\n })),\n },\n finish_reason: overrides?.finishReason ?? \"tool_calls\",\n },\n ],\n usage: overrides?.usage\n ? {\n prompt_tokens: overrides.usage.prompt_tokens ?? defaultUsage.prompt_tokens,\n completion_tokens: overrides.usage.completion_tokens ?? defaultUsage.completion_tokens,\n total_tokens:\n overrides.usage.total_tokens ??\n (overrides.usage.prompt_tokens ?? 0) + (overrides.usage.completion_tokens ?? 0),\n }\n : defaultUsage,\n ...(overrides?.systemFingerprint !== undefined && {\n system_fingerprint: overrides.systemFingerprint,\n }),\n };\n}\n\n// ─── HTTP helpers ─────────────────────────────────────────────────────────\n\nexport function readBody(req: http.IncomingMessage): Promise<string> {\n return new Promise((resolve, reject) => {\n const chunks: Buffer[] = [];\n req.on(\"data\", (chunk: Buffer) => chunks.push(chunk));\n req.on(\"end\", () => resolve(Buffer.concat(chunks).toString()));\n req.on(\"error\", reject);\n });\n}\n\n// ─── Pattern matching ─────────────────────────────────────────────────────\n\n/**\n * Case-insensitive substring/regex match used for search, rerank, and\n * moderation endpoints where exact casing rarely matters. String patterns\n * are lowercased on both sides before comparison.\n *\n * Note: This intentionally differs from the case-sensitive matching in\n * {@link matchFixture} (router.ts), where fixture authors expect exact\n * string matching against chat completion user messages.\n */\nexport function matchesPattern(text: string, pattern: string | RegExp): boolean {\n if (typeof pattern === \"string\") {\n return text.toLowerCase().includes(pattern.toLowerCase());\n }\n return pattern.test(text);\n}\n\nexport function getTestId(req: http.IncomingMessage): string {\n const headerValue = req.headers[\"x-test-id\"];\n if (Array.isArray(headerValue)) {\n if (headerValue.length > 0 && headerValue[0]) return headerValue[0];\n } else if (typeof headerValue === \"string\" && headerValue) {\n return headerValue;\n }\n\n const url = req.url ?? \"/\";\n const qIdx = url.indexOf(\"?\");\n if (qIdx !== -1) {\n const params = new URLSearchParams(url.slice(qIdx + 1));\n const queryValue = params.get(\"testId\");\n if (queryValue) return queryValue;\n }\n\n // Duplicated from journal.ts DEFAULT_TEST_ID — importing it here would create\n // a circular dependency (journal.ts imports from helpers.ts).\n return \"__default__\";\n}\n\n// ─── Snapshot recording helpers ──────────────────────────────────────────────\n\n/**\n * Convert a test ID (e.g. Playwright titlePath) into a filesystem-safe slug\n * suitable for use as a directory name in snapshot-style recording.\n */\nexport function slugifyTestId(testId: string): string {\n return testId\n .replace(/^.*?\\.(?:spec|test|e2e)\\.(?:tsx|ts|jsx|js|mjs|cjs)(?=\\s|›|$)\\s*›?\\s*/i, \"\") // strip test file extension prefix\n .replace(/\\s*[›>]\\s*/g, \"--\") // Playwright titlePath separator → double dash\n .replace(/[^\\w-]/g, \"-\") // non-word chars → dash\n .replace(/-{3,}/g, \"--\") // collapse 3+ dashes to double\n .replace(/^-+|-+$/g, \"\") // trim leading/trailing dashes\n .toLowerCase();\n}\n\n// ─── Embedding helpers ─────────────────────────────────────────────────────\n\nconst DEFAULT_EMBEDDING_DIMENSIONS = 1536;\n\n/**\n * Generate a deterministic embedding vector from input text.\n * Hashes the input with SHA-256 and spreads the hash bytes across\n * the requested number of dimensions, producing values in [-1, 1].\n */\nexport function generateDeterministicEmbedding(\n input: string,\n dimensions: number = DEFAULT_EMBEDDING_DIMENSIONS,\n): number[] {\n let currentHash = createHash(\"sha256\").update(input).digest();\n const embedding: number[] = new Array(dimensions);\n for (let i = 0; i < dimensions; i++) {\n if (i > 0 && i % 32 === 0) {\n currentHash = createHash(\"sha256\").update(currentHash).digest();\n }\n // Map 0-255 → -1.0 to 1.0\n embedding[i] = currentHash[i % 32] / 127.5 - 1;\n }\n return embedding;\n}\n\nexport interface EmbeddingAPIResponse {\n object: \"list\";\n data: { object: \"embedding\"; index: number; embedding: number[] }[];\n model: string;\n usage: { prompt_tokens: number; total_tokens: number };\n}\n\n/**\n * Build an OpenAI-format embeddings API response for one or more inputs.\n */\nexport function buildEmbeddingResponse(\n embeddings: number[][],\n model: string,\n): EmbeddingAPIResponse {\n return {\n object: \"list\",\n data: embeddings.map((embedding, index) => ({\n object: \"embedding\" as const,\n index,\n embedding,\n })),\n model,\n usage: { prompt_tokens: 0, total_tokens: 0 },\n };\n}\n"],"mappings":"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"}
|
package/dist/images.cjs
CHANGED
|
@@ -115,7 +115,7 @@ async function handleImages(req, res, raw, fixtures, journal, defaults, setCorsH
|
|
|
115
115
|
} }));
|
|
116
116
|
return;
|
|
117
117
|
}
|
|
118
|
-
const response = fixture
|
|
118
|
+
const response = await require_helpers.resolveResponse(fixture, syntheticReq);
|
|
119
119
|
if (require_helpers.isErrorResponse(response)) {
|
|
120
120
|
const status = response.status ?? 500;
|
|
121
121
|
journal.add({
|
package/dist/images.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"images.cjs","names":["flattenHeaders","getTestId","matchFixture","applyChaos","proxyAndRecord","isErrorResponse","isImageResponse"],"sources":["../src/images.ts"],"sourcesContent":["import type * as http from \"node:http\";\nimport type { ChatCompletionRequest, Fixture, HandlerDefaults } from \"./types.js\";\nimport {
|
|
1
|
+
{"version":3,"file":"images.cjs","names":["flattenHeaders","getTestId","matchFixture","applyChaos","proxyAndRecord","resolveResponse","isErrorResponse","isImageResponse"],"sources":["../src/images.ts"],"sourcesContent":["import type * as http from \"node:http\";\nimport type { ChatCompletionRequest, Fixture, HandlerDefaults } from \"./types.js\";\nimport {\n isImageResponse,\n isErrorResponse,\n flattenHeaders,\n getTestId,\n resolveResponse,\n} from \"./helpers.js\";\nimport { matchFixture } from \"./router.js\";\nimport { writeErrorResponse } from \"./sse-writer.js\";\nimport type { Journal } from \"./journal.js\";\nimport { applyChaos } from \"./chaos.js\";\nimport { proxyAndRecord } from \"./recorder.js\";\n\ninterface OpenAIImageRequest {\n model?: string;\n prompt: string;\n n?: number;\n size?: string;\n response_format?: \"url\" | \"b64_json\";\n [key: string]: unknown;\n}\n\ninterface GeminiPredictRequest {\n instances: Array<{ prompt: string }>;\n parameters?: { sampleCount?: number };\n [key: string]: unknown;\n}\n\nfunction buildSyntheticRequest(model: string, prompt: string): ChatCompletionRequest {\n return {\n model,\n messages: [{ role: \"user\", content: prompt }],\n _endpointType: \"image\",\n };\n}\n\nexport async function handleImages(\n req: http.IncomingMessage,\n res: http.ServerResponse,\n raw: string,\n fixtures: Fixture[],\n journal: Journal,\n defaults: HandlerDefaults,\n setCorsHeaders: (res: http.ServerResponse) => void,\n format: \"openai\" | \"gemini\" = \"openai\",\n geminiModel?: string,\n): Promise<void> {\n setCorsHeaders(res);\n const path = req.url ?? \"/v1/images/generations\";\n const method = req.method ?? \"POST\";\n\n let model: string;\n let prompt: string;\n\n try {\n const body = JSON.parse(raw);\n if (format === \"gemini\") {\n const geminiReq = body as GeminiPredictRequest;\n prompt = geminiReq.instances?.[0]?.prompt ?? \"\";\n model = geminiModel ?? \"imagen\";\n } else {\n const openaiReq = body as OpenAIImageRequest;\n prompt = openaiReq.prompt ?? \"\";\n model = openaiReq.model ?? \"dall-e-3\";\n }\n } catch {\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: null,\n response: { status: 400, fixture: null },\n });\n writeErrorResponse(\n res,\n 400,\n JSON.stringify({\n error: { message: \"Malformed JSON\", type: \"invalid_request_error\", code: \"invalid_json\" },\n }),\n );\n return;\n }\n\n if (!prompt) {\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: null,\n response: { status: 400, fixture: null },\n });\n writeErrorResponse(\n res,\n 400,\n JSON.stringify({\n error: { message: \"Missing required parameter: 'prompt'\", type: \"invalid_request_error\" },\n }),\n );\n return;\n }\n\n const syntheticReq = buildSyntheticRequest(model, prompt);\n const testId = getTestId(req);\n const fixture = matchFixture(\n fixtures,\n syntheticReq,\n journal.getFixtureMatchCountsForTest(testId),\n defaults.requestTransform,\n );\n\n if (fixture) {\n journal.incrementFixtureMatchCount(fixture, fixtures, testId);\n defaults.logger.debug(`Fixture matched: ${JSON.stringify(fixture.match).slice(0, 120)}`);\n } else {\n defaults.logger.debug(`No fixture matched for request`);\n }\n\n if (\n applyChaos(\n res,\n fixture,\n defaults.chaos,\n req.headers,\n journal,\n { method, path, headers: flattenHeaders(req.headers), body: syntheticReq },\n fixture ? \"fixture\" : \"proxy\",\n defaults.registry,\n defaults.logger,\n )\n )\n return;\n\n if (!fixture) {\n if (defaults.record) {\n const outcome = await proxyAndRecord(\n req,\n res,\n syntheticReq,\n format === \"gemini\" ? \"gemini\" : \"openai\",\n req.url ?? \"/v1/images/generations\",\n fixtures,\n defaults,\n raw,\n );\n if (outcome !== \"not_configured\") {\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: syntheticReq,\n response: { status: res.statusCode ?? 200, fixture: null, source: \"proxy\" },\n });\n return;\n }\n }\n\n const strictStatus = defaults.strict ? 503 : 404;\n const strictMessage = defaults.strict\n ? \"Strict mode: no fixture matched\"\n : \"No fixture matched\";\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: syntheticReq,\n response: { status: strictStatus, fixture: null },\n });\n writeErrorResponse(\n res,\n strictStatus,\n JSON.stringify({\n error: { message: strictMessage, type: \"invalid_request_error\", code: \"no_fixture_match\" },\n }),\n );\n return;\n }\n\n const response = await resolveResponse(fixture, syntheticReq);\n\n if (isErrorResponse(response)) {\n const status = response.status ?? 500;\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: syntheticReq,\n response: { status, fixture },\n });\n writeErrorResponse(res, status, JSON.stringify(response));\n return;\n }\n\n if (!isImageResponse(response)) {\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: syntheticReq,\n response: { status: 500, fixture },\n });\n writeErrorResponse(\n res,\n 500,\n JSON.stringify({\n error: { message: \"Fixture response is not an image type\", type: \"server_error\" },\n }),\n );\n return;\n }\n\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: syntheticReq,\n response: { status: 200, fixture },\n });\n\n // Normalize to array of image items\n const items = response.images ?? (response.image ? [response.image] : []);\n\n if (format === \"gemini\") {\n const predictions = items.map((item) => ({\n bytesBase64Encoded: item.b64Json ?? \"\",\n mimeType: \"image/png\" as const,\n }));\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ predictions }));\n } else {\n const data = items.map((item) => {\n const entry: Record<string, string> = {};\n if (item.url) entry.url = item.url;\n if (item.b64Json) entry.b64_json = item.b64Json;\n if (item.revisedPrompt) entry.revised_prompt = item.revisedPrompt;\n return entry;\n });\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ created: Math.floor(Date.now() / 1000), data }));\n }\n}\n"],"mappings":"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"}
|
package/dist/images.d.cts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"images.d.cts","names":[],"sources":["../src/images.ts"],"sourcesContent":[],"mappings":";;;;;
|
|
1
|
+
{"version":3,"file":"images.d.cts","names":[],"sources":["../src/images.ts"],"sourcesContent":[],"mappings":";;;;;iBAsCsB,YAAA,MACf,MAAA,CAAK,sBACL,MAAA,CAAK,uCAEA,oBACD,mBACC,uCACY,MAAA,CAAK,8EAG1B"}
|
package/dist/images.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"images.d.ts","names":[],"sources":["../src/images.ts"],"sourcesContent":[],"mappings":";;;;;
|
|
1
|
+
{"version":3,"file":"images.d.ts","names":[],"sources":["../src/images.ts"],"sourcesContent":[],"mappings":";;;;;iBAsCsB,YAAA,MACf,MAAA,CAAK,sBACL,MAAA,CAAK,uCAEA,oBACD,mBACC,uCACY,MAAA,CAAK,8EAG1B"}
|
package/dist/images.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { flattenHeaders, getTestId, isErrorResponse, isImageResponse } from "./helpers.js";
|
|
1
|
+
import { flattenHeaders, getTestId, isErrorResponse, isImageResponse, resolveResponse } from "./helpers.js";
|
|
2
2
|
import { matchFixture } from "./router.js";
|
|
3
3
|
import { writeErrorResponse } from "./sse-writer.js";
|
|
4
4
|
import { applyChaos } from "./chaos.js";
|
|
@@ -115,7 +115,7 @@ async function handleImages(req, res, raw, fixtures, journal, defaults, setCorsH
|
|
|
115
115
|
} }));
|
|
116
116
|
return;
|
|
117
117
|
}
|
|
118
|
-
const response = fixture
|
|
118
|
+
const response = await resolveResponse(fixture, syntheticReq);
|
|
119
119
|
if (isErrorResponse(response)) {
|
|
120
120
|
const status = response.status ?? 500;
|
|
121
121
|
journal.add({
|
package/dist/images.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"images.js","names":[],"sources":["../src/images.ts"],"sourcesContent":["import type * as http from \"node:http\";\nimport type { ChatCompletionRequest, Fixture, HandlerDefaults } from \"./types.js\";\nimport {
|
|
1
|
+
{"version":3,"file":"images.js","names":[],"sources":["../src/images.ts"],"sourcesContent":["import type * as http from \"node:http\";\nimport type { ChatCompletionRequest, Fixture, HandlerDefaults } from \"./types.js\";\nimport {\n isImageResponse,\n isErrorResponse,\n flattenHeaders,\n getTestId,\n resolveResponse,\n} from \"./helpers.js\";\nimport { matchFixture } from \"./router.js\";\nimport { writeErrorResponse } from \"./sse-writer.js\";\nimport type { Journal } from \"./journal.js\";\nimport { applyChaos } from \"./chaos.js\";\nimport { proxyAndRecord } from \"./recorder.js\";\n\ninterface OpenAIImageRequest {\n model?: string;\n prompt: string;\n n?: number;\n size?: string;\n response_format?: \"url\" | \"b64_json\";\n [key: string]: unknown;\n}\n\ninterface GeminiPredictRequest {\n instances: Array<{ prompt: string }>;\n parameters?: { sampleCount?: number };\n [key: string]: unknown;\n}\n\nfunction buildSyntheticRequest(model: string, prompt: string): ChatCompletionRequest {\n return {\n model,\n messages: [{ role: \"user\", content: prompt }],\n _endpointType: \"image\",\n };\n}\n\nexport async function handleImages(\n req: http.IncomingMessage,\n res: http.ServerResponse,\n raw: string,\n fixtures: Fixture[],\n journal: Journal,\n defaults: HandlerDefaults,\n setCorsHeaders: (res: http.ServerResponse) => void,\n format: \"openai\" | \"gemini\" = \"openai\",\n geminiModel?: string,\n): Promise<void> {\n setCorsHeaders(res);\n const path = req.url ?? \"/v1/images/generations\";\n const method = req.method ?? \"POST\";\n\n let model: string;\n let prompt: string;\n\n try {\n const body = JSON.parse(raw);\n if (format === \"gemini\") {\n const geminiReq = body as GeminiPredictRequest;\n prompt = geminiReq.instances?.[0]?.prompt ?? \"\";\n model = geminiModel ?? \"imagen\";\n } else {\n const openaiReq = body as OpenAIImageRequest;\n prompt = openaiReq.prompt ?? \"\";\n model = openaiReq.model ?? \"dall-e-3\";\n }\n } catch {\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: null,\n response: { status: 400, fixture: null },\n });\n writeErrorResponse(\n res,\n 400,\n JSON.stringify({\n error: { message: \"Malformed JSON\", type: \"invalid_request_error\", code: \"invalid_json\" },\n }),\n );\n return;\n }\n\n if (!prompt) {\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: null,\n response: { status: 400, fixture: null },\n });\n writeErrorResponse(\n res,\n 400,\n JSON.stringify({\n error: { message: \"Missing required parameter: 'prompt'\", type: \"invalid_request_error\" },\n }),\n );\n return;\n }\n\n const syntheticReq = buildSyntheticRequest(model, prompt);\n const testId = getTestId(req);\n const fixture = matchFixture(\n fixtures,\n syntheticReq,\n journal.getFixtureMatchCountsForTest(testId),\n defaults.requestTransform,\n );\n\n if (fixture) {\n journal.incrementFixtureMatchCount(fixture, fixtures, testId);\n defaults.logger.debug(`Fixture matched: ${JSON.stringify(fixture.match).slice(0, 120)}`);\n } else {\n defaults.logger.debug(`No fixture matched for request`);\n }\n\n if (\n applyChaos(\n res,\n fixture,\n defaults.chaos,\n req.headers,\n journal,\n { method, path, headers: flattenHeaders(req.headers), body: syntheticReq },\n fixture ? \"fixture\" : \"proxy\",\n defaults.registry,\n defaults.logger,\n )\n )\n return;\n\n if (!fixture) {\n if (defaults.record) {\n const outcome = await proxyAndRecord(\n req,\n res,\n syntheticReq,\n format === \"gemini\" ? \"gemini\" : \"openai\",\n req.url ?? \"/v1/images/generations\",\n fixtures,\n defaults,\n raw,\n );\n if (outcome !== \"not_configured\") {\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: syntheticReq,\n response: { status: res.statusCode ?? 200, fixture: null, source: \"proxy\" },\n });\n return;\n }\n }\n\n const strictStatus = defaults.strict ? 503 : 404;\n const strictMessage = defaults.strict\n ? \"Strict mode: no fixture matched\"\n : \"No fixture matched\";\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: syntheticReq,\n response: { status: strictStatus, fixture: null },\n });\n writeErrorResponse(\n res,\n strictStatus,\n JSON.stringify({\n error: { message: strictMessage, type: \"invalid_request_error\", code: \"no_fixture_match\" },\n }),\n );\n return;\n }\n\n const response = await resolveResponse(fixture, syntheticReq);\n\n if (isErrorResponse(response)) {\n const status = response.status ?? 500;\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: syntheticReq,\n response: { status, fixture },\n });\n writeErrorResponse(res, status, JSON.stringify(response));\n return;\n }\n\n if (!isImageResponse(response)) {\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: syntheticReq,\n response: { status: 500, fixture },\n });\n writeErrorResponse(\n res,\n 500,\n JSON.stringify({\n error: { message: \"Fixture response is not an image type\", type: \"server_error\" },\n }),\n );\n return;\n }\n\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: syntheticReq,\n response: { status: 200, fixture },\n });\n\n // Normalize to array of image items\n const items = response.images ?? (response.image ? [response.image] : []);\n\n if (format === \"gemini\") {\n const predictions = items.map((item) => ({\n bytesBase64Encoded: item.b64Json ?? \"\",\n mimeType: \"image/png\" as const,\n }));\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ predictions }));\n } else {\n const data = items.map((item) => {\n const entry: Record<string, string> = {};\n if (item.url) entry.url = item.url;\n if (item.b64Json) entry.b64_json = item.b64Json;\n if (item.revisedPrompt) entry.revised_prompt = item.revisedPrompt;\n return entry;\n });\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ created: Math.floor(Date.now() / 1000), data }));\n }\n}\n"],"mappings":"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"}
|
package/dist/llmock.cjs
CHANGED
|
@@ -56,7 +56,7 @@ var LLMock = class LLMock {
|
|
|
56
56
|
on(match, response, opts) {
|
|
57
57
|
return this.addFixture({
|
|
58
58
|
match,
|
|
59
|
-
response: require_fixture_loader.normalizeResponse(response),
|
|
59
|
+
response: typeof response === "function" ? response : require_fixture_loader.normalizeResponse(response),
|
|
60
60
|
...opts
|
|
61
61
|
});
|
|
62
62
|
}
|