{"version":3,"file":"helpers.cjs","names":["isReasoningModel","DEFAULT_TEST_ID"],"sources":["../src/helpers.ts"],"sourcesContent":["import { createHash, randomBytes } from \"node:crypto\";\nimport type * as http from \"node:http\";\nimport type { IncomingHttpHeaders } from \"node:http\";\nimport { DEFAULT_TEST_ID } from \"./constants.js\";\nimport type { Logger } from \"./logger.js\";\nimport { isReasoningModel } from \"./model-utils.js\";\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\n/**\n * Resolve effective strict mode from per-request header and server default.\n * Header values override the server default — same precedence pattern as chaos\n * config headers (see resolveChaosConfig in chaos.ts).\n *\n * Header: `X-AIMock-Strict` — \"true\"/\"1\" → strict on, \"false\"/\"0\" → strict off.\n * When absent or unrecognised, falls back to the server-level default.\n */\nexport function resolveStrictMode(\n  serverDefault: boolean | undefined,\n  rawHeaders?: IncomingHttpHeaders,\n): boolean {\n  if (rawHeaders) {\n    const header = rawHeaders[\"x-aimock-strict\"];\n    const val = typeof header === \"string\" ? header : Array.isArray(header) ? header[0] : undefined;\n    if (val === \"true\" || val === \"1\") return true;\n    if (val === \"false\" || val === \"0\") return false;\n  }\n  return serverDefault ?? false;\n}\n\n/**\n * Returns `true` or `false` when the X-AIMock-Strict header overrides the\n * server default, or `undefined` when it doesn't. Designed to be spread\n * directly into a journal entry's `response` object:\n *\n *   response: { status, fixture, ...strictOverrideField(defaults.strict, req.headers) }\n */\nexport function strictOverrideField(\n  serverDefault: boolean | undefined,\n  rawHeaders?: IncomingHttpHeaders,\n): { strictOverride?: boolean } {\n  const effective = resolveStrictMode(serverDefault, rawHeaders);\n  if (effective !== (serverDefault ?? false)) {\n    return { strictOverride: effective };\n  }\n  return {};\n}\n\n/**\n * Build the strict-mode 503 error message, distinguishing a true no-match from\n * a sequence/turn-exhausted miss.\n *\n * `skippedBySequenceOrTurn` is the count reported by `matchFixtureDiagnostic`\n * (router.ts): the number of fixtures that matched the request SHAPE but were\n * rejected ONLY by their `sequenceIndex`/`turnIndex` count state.\n *\n *   - `0`  → `\"Strict mode: no fixture matched\"` (no candidate had a matching shape)\n *   - `>0` → `\"Strict mode: N candidate fixture(s) skipped by sequence/turn state\"`\n *\n * The HTTP status (503) and error envelope shape are unchanged at every call\n * site — only this message string differs. Endpoints with no sequence/turn\n * gates always pass `0` and therefore see the generic message.\n */\nexport function strictNoMatchMessage(skippedBySequenceOrTurn: number): string {\n  if (skippedBySequenceOrTurn > 0) {\n    return `Strict mode: ${skippedBySequenceOrTurn} candidate fixture(s) skipped by sequence/turn state`;\n  }\n  return \"Strict mode: no fixture matched\";\n}\n\n/**\n * Build the strict-mode error LOG line, mirroring {@link strictNoMatchMessage}'s\n * disambiguation so the error log distinguishes the two miss kinds too.\n */\nexport function strictNoMatchLogLine(\n  method: string,\n  url: string,\n  skippedBySequenceOrTurn: number,\n): string {\n  if (skippedBySequenceOrTurn > 0) {\n    return `STRICT: ${skippedBySequenceOrTurn} candidate fixture(s) skipped by sequence/turn state for ${method} ${url}`;\n  }\n  return `STRICT: No fixture matched for ${method} ${url}`;\n}\n\n/**\n * Resolve the reasoning string to actually emit for a given model.\n *\n * aimock synthesizes a reasoning channel whenever a fixture carries a\n * `reasoning` string, regardless of the requested model. But a non-reasoning\n * model (e.g. `gpt-4.1`) would emit no reasoning against the real provider, so\n * replaying it is a false green (see aimock#254). This gates the emission on\n * the requested model's capability:\n *\n *   - no fixture reasoning            → undefined (no-op, short-circuit)\n *   - reasoning-capable model         → emit unchanged, no log\n *   - non-reasoning model, strict OFF → `logger.warn`, still emit (preserves\n *                                       current behavior)\n *   - non-reasoning model, strict ON  → `logger.error`, suppress (return undefined)\n *\n * Capability is decided from the REQUESTED model id (what the backend was wired\n * to), not any `overrides.model` echoed in the payload.\n */\nexport function resolveReasoningForModel(\n  reasoning: string | undefined,\n  model: string | undefined,\n  strict: boolean,\n  logger: Logger,\n): string | undefined {\n  if (!reasoning) return undefined;\n  if (isReasoningModel(model)) return reasoning;\n  if (strict) {\n    logger.error(\n      `Strict mode: fixture has a reasoning channel but model \"${model}\" is not reasoning-capable — suppressing reasoning emission`,\n    );\n    return undefined;\n  }\n  logger.warn(\n    `Fixture has a reasoning channel but model \"${model}\" is not reasoning-capable — the real provider would emit no reasoning. Emitting anyway (set X-AIMock-Strict: true to suppress).`,\n  );\n  return reasoning;\n}\n\n/**\n * Resolve the encrypted reasoning artifacts (`reasoningSignature` and\n * `redactedThinking`) to actually emit for a given model.\n *\n * `redacted_thinking` blocks and a thinking `signature` ARE part of the\n * reasoning channel — they are just the encrypted form of it — so they must be\n * gated on the same model-capability resolution as the plaintext `reasoning`\n * string (see resolveReasoningForModel). Gating only the plaintext channel\n * leaves a half-gated reasoning path: replaying a fixture recorded from a\n * reasoning model against a non-reasoning model would strip the `thinking`\n * block but still emit `redacted_thinking` blocks, which the real provider for\n * that model would never produce.\n *\n * Capability is decided from the REQUESTED model id, independently of whether a\n * plaintext `reasoning` string is present — a fixture may carry only\n * `redactedThinking` with no plaintext reasoning.\n *\n *   - reasoning-capable model         → emit both unchanged, no log\n *   - non-reasoning model, no artifacts → no-op, nothing to suppress\n *   - non-reasoning model, strict OFF → `logger.warn`, still emit (preserves\n *                                       current behavior)\n *   - non-reasoning model, strict ON  → `logger.error`, suppress both\n *\n * Must be invoked alongside resolveReasoningForModel with identical model/strict\n * inputs so the plaintext and encrypted channels stay suppressed together.\n */\nexport function resolveReasoningArtifactsForModel(\n  reasoningSignature: string | undefined,\n  redactedThinking: string[] | undefined,\n  model: string | undefined,\n  strict: boolean,\n  logger: Logger,\n): { reasoningSignature?: string; redactedThinking?: string[] } {\n  const hasArtifacts = reasoningSignature !== undefined || (redactedThinking?.length ?? 0) > 0;\n  if (!hasArtifacts || isReasoningModel(model)) {\n    return { reasoningSignature, redactedThinking };\n  }\n  if (strict) {\n    logger.error(\n      `Strict mode: fixture has encrypted reasoning artifacts (redacted_thinking/signature) but model \"${model}\" is not reasoning-capable — suppressing reasoning emission`,\n    );\n    return {};\n  }\n  logger.warn(\n    `Fixture has encrypted reasoning artifacts (redacted_thinking/signature) but model \"${model}\" is not reasoning-capable — the real provider would emit no reasoning. Emitting anyway (set X-AIMock-Strict: true to suppress).`,\n  );\n  return { reasoningSignature, redactedThinking };\n}\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    try {\n      const raw = await fixture.response(request);\n      return normalizeFactoryResponse(raw);\n    } catch (err) {\n      const msg = err instanceof Error ? err.message : String(err);\n      throw new Error(`Response factory threw: ${msg}`, { cause: err });\n    }\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    \"message\" in ((r as ErrorResponse).error as Record<string, unknown>) &&\n    typeof ((r as ErrorResponse).error as Record<string, unknown>).message === \"string\"\n  );\n}\n\n/**\n * Serialize an ErrorResponse to JSON, stripping the internal-only `status`\n * field that controls the HTTP status code but should never appear in the\n * response body.  Real LLM APIs don't include it.\n */\nexport function serializeErrorResponse(response: ErrorResponse): string {\n  return JSON.stringify({\n    error: {\n      message: response.error.message,\n      type: response.error.type ?? \"server_error\",\n      param: response.error.param ?? null,\n      code: response.error.code ?? null,\n    },\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 && typeof r.image === \"object\" && 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\n// ─── Token estimation ────────────────────────────────────────────────────\n\n/**\n * Rough token count estimation based on character length.\n * Uses the ~4 characters per token heuristic common for English text.\n */\nexport function estimateTokens(text: string): number {\n  return Math.max(1, Math.ceil(text.length / 4));\n}\n\n/**\n * Estimate prompt tokens from a request's messages array.\n */\nexport function estimatePromptTokens(messages: ChatCompletionRequest[\"messages\"]): number {\n  let totalChars = 0;\n  for (const msg of messages) {\n    if (typeof msg.content === \"string\") {\n      totalChars += msg.content.length;\n    } else if (Array.isArray(msg.content)) {\n      for (const part of msg.content) {\n        if (part.text) totalChars += part.text.length;\n      }\n    }\n  }\n  return Math.max(1, Math.ceil(totalChars / 4));\n}\n\n/**\n * Build usage object: use explicit overrides if provided, otherwise estimate.\n */\nfunction resolveUsage(\n  overrides: ResponseOverrides | undefined,\n  promptText: string,\n  completionText: string,\n): { prompt_tokens: number; completion_tokens: number; total_tokens: number } {\n  if (overrides?.usage) {\n    const u = overrides.usage;\n    const prompt = u.prompt_tokens ?? u.input_tokens ?? u.promptTokenCount ?? 0;\n    const completion = u.completion_tokens ?? u.output_tokens ?? u.candidatesTokenCount ?? 0;\n    return {\n      prompt_tokens: prompt,\n      completion_tokens: completion,\n      total_tokens: u.total_tokens ?? u.totalTokenCount ?? prompt + completion,\n    };\n  }\n  const prompt = estimateTokens(promptText || \"x\");\n  const completion = estimateTokens(completionText || \"x\");\n  return {\n    prompt_tokens: prompt,\n    completion_tokens: completion,\n    total_tokens: prompt + completion,\n  };\n}\n\n/**\n * Build an SSE usage chunk for streaming responses.\n * OpenAI emits this as the final chunk before [DONE] when\n * stream_options.include_usage is true. It has an empty choices array.\n */\nexport function buildUsageChunk(\n  id: string,\n  model: string,\n  created: number,\n  usage: { prompt_tokens: number; completion_tokens: number; total_tokens: number },\n  fingerprint?: string,\n): SSEChunk {\n  return {\n    id,\n    object: \"chat.completion.chunk\",\n    created,\n    model,\n    choices: [],\n    usage,\n    ...(fingerprint !== undefined && { system_fingerprint: fingerprint }),\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: [\n          { index: 0, delta: { reasoning_content: slice }, logprobs: null, finish_reason: null },\n        ],\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        logprobs: null,\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 }, logprobs: null, 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: [\n      { index: 0, delta: {}, logprobs: null, finish_reason: overrides?.finishReason ?? \"stop\" },\n    ],\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  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 tool calls, 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: [\n          { index: 0, delta: { reasoning_content: slice }, logprobs: null, finish_reason: null },\n        ],\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: null },\n        logprobs: 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          logprobs: null,\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            logprobs: null,\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: [\n      {\n        index: 0,\n        delta: {},\n        logprobs: null,\n        finish_reason: overrides?.finishReason ?? \"tool_calls\",\n      },\n    ],\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  requestMessages?: ChatCompletionRequest[\"messages\"],\n): ChatCompletion {\n  const promptText = requestMessages\n    ? requestMessages\n        .map((m) =>\n          typeof m.content === \"string\"\n            ? m.content\n            : Array.isArray(m.content)\n              ? m.content.map((p) => p.text ?? \"\").join(\"\")\n              : \"\",\n        )\n        .join(\"\")\n    : \"\";\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        logprobs: null,\n        finish_reason: overrides?.finishReason ?? \"stop\",\n      },\n    ],\n    usage: resolveUsage(overrides, promptText, content),\n    ...(overrides?.systemFingerprint !== undefined && {\n      system_fingerprint: overrides.systemFingerprint,\n    }),\n  };\n}\n\nexport function buildToolCallCompletion(\n  toolCalls: ToolCall[],\n  model: string,\n  reasoning?: string,\n  overrides?: ResponseOverrides,\n  requestMessages?: ChatCompletionRequest[\"messages\"],\n): ChatCompletion {\n  const promptText = requestMessages\n    ? requestMessages\n        .map((m) =>\n          typeof m.content === \"string\"\n            ? m.content\n            : Array.isArray(m.content)\n              ? m.content.map((p) => p.text ?? \"\").join(\"\")\n              : \"\",\n        )\n        .join(\"\")\n    : \"\";\n  const completionText = toolCalls.map((tc) => tc.name + tc.arguments).join(\"\");\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          ...(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        logprobs: null,\n        finish_reason: overrides?.finishReason ?? \"tool_calls\",\n      },\n    ],\n    usage: resolveUsage(overrides, promptText, completionText),\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: [\n          { index: 0, delta: { reasoning_content: slice }, logprobs: null, finish_reason: null },\n        ],\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        logprobs: null,\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 }, logprobs: null, 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          logprobs: null,\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            logprobs: null,\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: [\n      {\n        index: 0,\n        delta: {},\n        logprobs: null,\n        finish_reason: overrides?.finishReason ?? \"tool_calls\",\n      },\n    ],\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  requestMessages?: ChatCompletionRequest[\"messages\"],\n): ChatCompletion {\n  const promptText = requestMessages\n    ? requestMessages\n        .map((m) =>\n          typeof m.content === \"string\"\n            ? m.content\n            : Array.isArray(m.content)\n              ? m.content.map((p) => p.text ?? \"\").join(\"\")\n              : \"\",\n        )\n        .join(\"\")\n    : \"\";\n  const completionText = content + toolCalls.map((tc) => tc.name + tc.arguments).join(\"\");\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        logprobs: null,\n        finish_reason: overrides?.finishReason ?? \"tool_calls\",\n      },\n    ],\n    usage: resolveUsage(overrides, promptText, completionText),\n    ...(overrides?.systemFingerprint !== undefined && {\n      system_fingerprint: overrides.systemFingerprint,\n    }),\n  };\n}\n\n// ─── HTTP helpers ─────────────────────────────────────────────────────────\n\nconst DEFAULT_MAX_BODY_BYTES = 10 * 1024 * 1024; // 10 MB\n\nexport function readBody(\n  req: http.IncomingMessage,\n  maxBytes: number = DEFAULT_MAX_BODY_BYTES,\n): Promise<string> {\n  return new Promise((resolve, reject) => {\n    const chunks: Buffer[] = [];\n    let totalBytes = 0;\n    let settled = false;\n    req.on(\"data\", (chunk: Buffer) => {\n      if (settled) return;\n      totalBytes += chunk.length;\n      if (totalBytes > maxBytes) {\n        settled = true;\n        req.destroy();\n        reject(new Error(`Request body exceeded size limit of ${maxBytes} bytes`));\n        return;\n      }\n      chunks.push(chunk);\n    });\n    req.on(\"end\", () => {\n      if (!settled) {\n        settled = true;\n        resolve(Buffer.concat(chunks).toString());\n      }\n    });\n    req.on(\"error\", (err) => {\n      if (!settled) {\n        settled = true;\n        reject(err);\n      }\n    });\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  // A global/sticky RegExp carries mutable `lastIndex` state that `.test()`\n  // advances. Save and restore it so callers reusing the same regex object\n  // (search/rerank/moderation filter loops) are not left with mutated state\n  // and get consistent results across repeated calls.\n  const savedLastIndex = pattern.lastIndex;\n  pattern.lastIndex = 0;\n  const result = pattern.test(text);\n  pattern.lastIndex = savedLastIndex;\n  return result;\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  return DEFAULT_TEST_ID;\n}\n\nexport function getContext(req: http.IncomingMessage): string | undefined {\n  const headerValue = req.headers[\"x-aimock-context\"];\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  return undefined;\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/**\n * Make a request context (the `X-AIMock-Context` header value) safe to use as\n * a single directory segment in a recorded-fixture path. The header is\n * attacker-controllable, so a raw value containing `../`, path separators, or\n * an absolute-path prefix would let the written fixture escape the configured\n * fixtures base directory. Mirrors `slugifyTestId`: non-word characters\n * (including `/`, `\\`, and `.`) collapse to dashes, so the result is always a\n * single flat segment with no traversal semantics. Returns \"\" when the value\n * sanitizes to nothing (caller treats that as \"no context segment\").\n */\nexport function slugifyContext(context: string): string {\n  return context\n    .replace(/[^\\w-]/g, \"-\") // non-word chars (incl. / \\ . :) → 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  usage?: { prompt_tokens?: number; total_tokens?: number },\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: usage?.prompt_tokens ?? 0, total_tokens: usage?.total_tokens ?? 0 },\n  };\n}\n"],"mappings":"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"}