{"version":3,"file":"helpers.cjs","names":[],"sources":["../../../../src/batteries/llm/transformers_js/helpers.ts"],"sourcesContent":["/**\n * Translation helpers for the transformers.js LLM adapter.\n *\n * @module @nhtio/adk/batteries/llm/transformers_js/helpers\n *\n * @remarks\n * Two layers, like the other LLM batteries:\n * 1. **Re-exported format-agnostic helpers** from `chat_common` (string/trust-envelope renderers,\n *    the joi→JSON-Schema converter, thought rendering/filtering) — reused verbatim.\n * 2. **transformers.js-native mappers** defined here — building the `{role,content}[]` message array\n *    + the `tools` definitions, and a stream accumulator that collects decoded text (parsing of tool\n *    calls / reasoning happens once after the stream drains, via the shared parser layer).\n */\n\nimport { Media } from '@nhtio/adk'\nimport { isError } from '@nhtio/adk/guards'\nimport { SpooledArtifact } from '@nhtio/adk'\nimport { E_UNSUPPORTED_MEDIA_MODALITY } from './exceptions'\nimport {\n  neutraliseDeveloperRulesTag,\n  sanitizeMimeType,\n  sanitizeFilenameForDescription,\n  defaultRenderArtifactHandleBody,\n} from '../chat_common/helpers'\nimport {\n  descriptionToChatCompletionsJsonSchema,\n  renderUntrustedContent as commonRenderUntrustedContent,\n  renderTrustedContent as commonRenderTrustedContent,\n  renderChatCompletionsSystemPrompt,\n  renderStandingInstructions,\n  renderMemories,\n  renderRetrievables,\n  renderRetrievableSafetyDirective,\n  renderFirstPartyRetrievables,\n  renderThirdPartyPublicRetrievables,\n  renderThirdPartyPrivateRetrievables,\n  renderRetrievableHandleBody,\n  renderThought,\n  filterThoughts,\n} from '../openai_chat_completions/helpers'\nimport type { Tokenizable } from '@nhtio/adk'\nimport type { ArtifactTool, Tool } from '@nhtio/adk'\nimport type { ChatCompletionsTool } from '../openai_chat_completions/types'\nimport type { Message, Memory, Retrievable, Thought, ToolCall, ToolRegistry } from '@nhtio/adk'\nimport type {\n  TransformersJsMessage,\n  TransformersJsBucketOrder,\n  UnsupportedMediaPolicy,\n  DescriptionLike,\n  JsonSchema,\n} from './types'\n\n// ── Re-export the entire format-agnostic layer (reused verbatim) ──────────────────────────────────\n\nexport {\n  descriptionToChatCompletionsJsonSchema,\n  defaultDescriptionToChatCompletionsJsonSchema,\n  renderUntrustedContent,\n  defaultRenderUntrustedContent,\n  renderTrustedContent,\n  defaultRenderTrustedContent,\n  renderStandingInstructions,\n  defaultRenderStandingInstructions,\n  renderMemories,\n  defaultRenderMemories,\n  renderRetrievables,\n  defaultRenderRetrievables,\n  renderRetrievableHandleBody,\n  defaultRenderRetrievableHandleBody,\n  renderRetrievableSafetyDirective,\n  defaultRenderRetrievableSafetyDirective,\n  renderFirstPartyRetrievables,\n  defaultRenderFirstPartyRetrievables,\n  renderThirdPartyPublicRetrievables,\n  defaultRenderThirdPartyPublicRetrievables,\n  renderThirdPartyPrivateRetrievables,\n  defaultRenderThirdPartyPrivateRetrievables,\n  renderThought,\n  defaultRenderThought,\n  filterThoughts,\n  defaultFilterThoughts,\n  renderChatCompletionsSystemPrompt,\n  defaultRenderChatCompletionsSystemPrompt,\n  extractReasoningFields,\n} from '../openai_chat_completions/helpers'\n\n// The shared SpooledArtifact handle-pattern machinery + its structural guard, surfaced on the barrel\n// so consumers can override/compose it like any other render helper.\nexport {\n  renderArtifactHandleBody,\n  defaultRenderArtifactHandleBody,\n  looksLikeSpooledArtifact,\n} from '../chat_common/helpers'\n\n// Re-export the shared parser layer so consumers import everything from this battery's barrel.\nexport * from '../chat_common/tool_parsers'\nexport * from '../chat_common/reasoning_parsers'\n// Re-export the shared lifecycle/boot-progress contract (emitLifecycle + types).\nexport * from '../chat_common/lifecycle'\n// Re-export the shared portable generation contract (ChatGenerationOptions + resolveGenerationOptions).\nexport * from '../chat_common/generation'\n// Re-export the shared WebGPU memory observability surface (budget probe, OOM detector, live instrument).\nexport * from '../chat_common/gpu_budget'\n\n// ── transformers.js-native mappers ────────────────────────────────────────────────────────────────\n\n/** A tool definition in the transformers.js `tools` array (OpenAI-function-shaped). */\nexport interface TransformersJsTool {\n  /** Always `'function'` — the only tool type transformers.js chat templates understand. */\n  type: 'function'\n  /** The function descriptor: name, optional description, and JSON-Schema parameters. */\n  function: {\n    name: string\n    description?: string\n    parameters?: JsonSchema\n  }\n}\n\n/**\n * Convert ADK {@link @nhtio/adk!Tool} / {@link @nhtio/adk!ArtifactTool} instances into the\n * transformers.js `tools` array shape (OpenAI-function-shaped — what `apply_chat_template` expects).\n */\nexport const toolsToTransformersJsTools = (\n  tools: ReadonlyArray<Tool | ArtifactTool>,\n  deps: { descriptionToChatCompletionsJsonSchema: (d: DescriptionLike) => JsonSchema } = {\n    descriptionToChatCompletionsJsonSchema,\n  }\n): TransformersJsTool[] => {\n  const out: TransformersJsTool[] = []\n  for (const tool of tools) {\n    const described = tool.describe()\n    const parameters = deps.descriptionToChatCompletionsJsonSchema(\n      described.inputSchema as unknown as DescriptionLike\n    )\n    out.push({\n      type: 'function',\n      function: {\n        name: described.name,\n        description: described.description,\n        parameters:\n          parameters && Object.keys(parameters).length > 0\n            ? parameters\n            : { type: 'object', properties: {} },\n      },\n    })\n  }\n  return out\n}\n\n/** Default {@link toolsToTransformersJsTools}. */\nexport const defaultToolsToTransformersJsTools = toolsToTransformersJsTools\n\n/** Resolve a media instance to fallback text per policy ('throw' raises; others degrade). */\nconst resolveMediaFallbackText = async (\n  media: Media,\n  policy: UnsupportedMediaPolicy,\n  warn?: (msg: string) => void\n): Promise<string> => {\n  if (policy === 'throw') {\n    throw new E_UNSUPPORTED_MEDIA_MODALITY([media.kind, media.mimeType, media.filename])\n  }\n  const syntheticDescription = `[media: ${sanitizeFilenameForDescription(media.filename)}, kind=${media.kind}, mime=${sanitizeMimeType(media.mimeType, media.kind === 'image' || media.kind === 'audio' ? media.kind : undefined)}]`\n  if (\n    policy === 'fallback-stash' ||\n    (typeof policy === 'object' && policy.mode === 'fallback-stash')\n  ) {\n    const stashKeys =\n      typeof policy === 'object'\n        ? policy.stashKeys\n        : ['text:transcript', 'text:caption', 'text:description']\n    for (const key of stashKeys) {\n      const entry = media.stash.get<{ value?: unknown } | undefined>(key)\n      const value = entry && typeof entry === 'object' ? entry.value : undefined\n      if (typeof value === 'string' && value.length > 0) return value\n    }\n    warn?.(\n      `unsupportedMediaPolicy='fallback-stash' for ${media.filename} (${media.kind}): no matching stash entry — falling through to synthetic description.`\n    )\n    return syntheticDescription\n  }\n  return syntheticDescription\n}\n\n/**\n * Render a {@link @nhtio/adk!ToolCall}'s `results` into a plain-text tool message body.\n *\n * @remarks\n * transformers.js chat templates take a `tool`-role message whose `content` is a string. A\n * `SpooledArtifact` result renders as a HANDLE (metadata + the forged `artifact_*` tools to read it)\n * when its `ToolCall.inline === false` — the secure default — and inline via `asString()` only when a\n * producer opted into `inline: true`. Applies the trust envelope and degrades Media to text.\n */\nexport const renderTransformersJsToolResult = async (input: {\n  toolCall: ToolCall\n  results: Tokenizable | SpooledArtifact | SpooledArtifact[] | Media | Media[]\n  tool: Tool | ArtifactTool | undefined\n  unsupportedMediaPolicy: UnsupportedMediaPolicy\n  renderUntrustedContent: typeof commonRenderUntrustedContent\n  renderTrustedContent: typeof commonRenderTrustedContent\n  /**\n   * Override for the artifact-handle body renderer (see {@link renderArtifactHandleBody}). Defaults to\n   * the shared {@link defaultRenderArtifactHandleBody}. The adapter threads the consumer's\n   * `helpers.renderArtifactHandleBody` here so an app can change which forged `artifact_*` reader the\n   * model is steered toward first.\n   */\n  renderArtifactHandleBody?: typeof defaultRenderArtifactHandleBody\n  warn?: (msg: string) => void\n}): Promise<string> => {\n  const { results, toolCall, tool } = input\n  const isTrusted = tool?.trusted === true\n  const renderHandle = input.renderArtifactHandleBody ?? defaultRenderArtifactHandleBody\n\n  let body: string\n  // Whether `body` is a non-inlined artifact HANDLE (vs. inlined content). Only the `kind` label on\n  // the envelope changes — the trust TIER stays the producing tool's, since a handle to a third-party\n  // artifact carries the same injection hazard as its inlined content would.\n  let isHandle = false\n  if (\n    Media.isMedia(results) ||\n    (Array.isArray(results) && results.every((r) => Media.isMedia(r)))\n  ) {\n    const mediaList = Media.isMedia(results) ? [results] : (results as Media[])\n    const parts: string[] = []\n    for (const m of mediaList)\n      parts.push(await resolveMediaFallbackText(m, input.unsupportedMediaPolicy, input.warn))\n    body = parts.join('\\n\\n')\n  } else if (\n    !Array.isArray(results) &&\n    SpooledArtifact.isSpooledArtifact(results) &&\n    toolCall.inline === false\n  ) {\n    // Handle pattern: producer marked this result non-inline → emit a directions-bearing handle\n    // (metadata + the forged artifact_* tools to read it) instead of dumping the body. This is the\n    // machinery that makes the spool/thrift pattern usable. Parity with OpenAI + Ollama.\n    let byteLength = 0\n    let lineCount = 0\n    try {\n      byteLength = await results.byteLength()\n    } catch {\n      /* best-effort metadata */\n    }\n    try {\n      lineCount = await results.lineCount()\n    } catch {\n      /* best-effort metadata */\n    }\n    body = renderHandle({\n      callId: toolCall.id,\n      artifact: results,\n      byteLength,\n      lineCount,\n    })\n    isHandle = true\n  } else if (Array.isArray(results)) {\n    const parts: string[] = []\n    for (const a of results) parts.push(await (a as SpooledArtifact).asString())\n    body = parts.join('\\n\\n')\n  } else if (SpooledArtifact.isSpooledArtifact(results)) {\n    body = await results.asString()\n  } else {\n    body = (results as Tokenizable).toString()\n  }\n\n  return isTrusted\n    ? input.renderTrustedContent(body, {\n        nonce: toolCall.checksum,\n        kind: isHandle ? 'trusted-artifact-handle' : 'trusted-tool-result',\n        tool: toolCall.tool,\n      } as never)\n    : input.renderUntrustedContent(body, {\n        nonce: toolCall.checksum,\n        kind: isHandle ? 'artifact-handle' : 'tool-result',\n        tool: toolCall.tool,\n      } as never)\n}\n\n/** Default {@link renderTransformersJsToolResult}. */\nexport const defaultRenderTransformersJsToolResult = renderTransformersJsToolResult\n\n/**\n * Build the transformers.js `messages` array + `tools` from the ADK dispatch context buckets.\n *\n * @remarks\n * Leading buckets (system prompt + standing instructions / memories / retrievables) render into a\n * single `system` message; the timeline (messages, surviving thoughts, tool calls — chronological)\n * renders into `user`/`assistant`/`tool` messages. Tools are returned separately for the `tools`\n * generate-kwarg. Mirrors `buildLiteRtConversationInput`.\n */\nexport const buildTransformersJsMessages = async (input: {\n  systemPrompt: Tokenizable\n  standingInstructions: Iterable<Tokenizable>\n  memories: Iterable<Memory>\n  retrievables: Iterable<Retrievable>\n  messages: Iterable<Message>\n  thoughts: Iterable<Thought>\n  toolCalls: Iterable<ToolCall>\n  tools: ToolRegistry\n  renderedToolCallResults: Map<ToolCall, string>\n  bucketOrder: TransformersJsBucketOrder\n  selfIdentity: string\n  thoughtSurfacing: 'all-self' | 'latest-self' | 'all'\n  replayCompatibility: ReadonlyArray<string>\n  toolsToTransformersJsTools: typeof toolsToTransformersJsTools\n  renderThought: typeof renderThought\n  filterThoughts: typeof filterThoughts\n  renderUntrustedContent: typeof commonRenderUntrustedContent\n  renderTrustedContent: typeof commonRenderTrustedContent\n  renderChatCompletionsSystemPrompt: typeof renderChatCompletionsSystemPrompt\n  renderStandingInstructions: typeof renderStandingInstructions\n  renderMemories: typeof renderMemories\n  renderRetrievables: typeof renderRetrievables\n  renderRetrievableSafetyDirective: typeof renderRetrievableSafetyDirective\n  renderFirstPartyRetrievables: typeof renderFirstPartyRetrievables\n  renderThirdPartyPublicRetrievables: typeof renderThirdPartyPublicRetrievables\n  renderThirdPartyPrivateRetrievables: typeof renderThirdPartyPrivateRetrievables\n  renderRetrievableHandleBody?: typeof renderRetrievableHandleBody\n  /**\n   * Multimodal config. Absent/false → text-only (every message renders a plain string `content`, the\n   * byte-for-byte original behavior). When set, a message carrying `attachments` of an enabled kind\n   * renders a content-array (text + `{type:'image'|'audio'}` placeholders) and the decoded media is\n   * collected into `images`/`audio` (consumed positionally by `processor(prompt, images, audio)`).\n   */\n  multimodal?: { image: boolean; audio: boolean }\n  /** Decodes a Media instance to a transformers.js input (RawImage / audio samples). Adapter-injected. */\n  decodeMedia?: (media: Media) => Promise<{ kind: 'image' | 'audio'; data: unknown }>\n  unsupportedMediaPolicy?: UnsupportedMediaPolicy\n  warn?: (msg: string) => void\n}): Promise<{\n  messages: TransformersJsMessage[]\n  tools: TransformersJsTool[]\n  images: unknown[]\n  audio: unknown[]\n}> => {\n  const systemText = await input.renderChatCompletionsSystemPrompt({\n    systemPrompt: input.systemPrompt,\n    standingInstructions: input.standingInstructions,\n    memories: input.memories,\n    retrievables: input.retrievables,\n    bucketOrder: input.bucketOrder,\n    renderStandingInstructions: input.renderStandingInstructions,\n    renderMemories: input.renderMemories,\n    renderRetrievables: input.renderRetrievables,\n    renderRetrievableSafetyDirective: input.renderRetrievableSafetyDirective,\n    renderFirstPartyRetrievables: input.renderFirstPartyRetrievables,\n    renderThirdPartyPublicRetrievables: input.renderThirdPartyPublicRetrievables,\n    renderThirdPartyPrivateRetrievables: input.renderThirdPartyPrivateRetrievables,\n    renderRetrievableHandleBody: input.renderRetrievableHandleBody,\n    renderUntrustedContent: input.renderUntrustedContent,\n  })\n\n  const messages: TransformersJsMessage[] = []\n  const images: unknown[] = []\n  const audio: unknown[] = []\n  const mm = input.multimodal\n  if (typeof systemText === 'string' && systemText.length > 0) {\n    messages.push({ role: 'system', content: systemText } as TransformersJsMessage)\n  }\n\n  const survivingThoughts = input.filterThoughts(\n    input.thoughts,\n    input.thoughtSurfacing,\n    input.selfIdentity,\n    input.replayCompatibility\n  )\n  type Item =\n    | { kind: 'message'; at: number; value: Message }\n    | { kind: 'thought'; at: number; value: Thought }\n    | { kind: 'toolCall'; at: number; value: ToolCall }\n  const items: Item[] = []\n  for (const m of input.messages)\n    items.push({ kind: 'message', at: m.createdAt.toMillis(), value: m })\n  for (const t of survivingThoughts)\n    items.push({ kind: 'thought', at: t.createdAt.toMillis(), value: t })\n  for (const tc of input.toolCalls)\n    items.push({ kind: 'toolCall', at: tc.createdAt.toMillis(), value: tc })\n  items.sort((a, b) => a.at - b.at)\n\n  for (const item of items) {\n    if (item.kind === 'message') {\n      const m = item.value\n      const role = m.role === 'user' ? 'user' : 'assistant'\n      // Neutralise a body-embedded no-nonce developer-rules tier (envelope-mimicry defense).\n      const text = neutraliseDeveloperRulesTag(m.content !== undefined ? m.content.toString() : '')\n      const attachments = m.attachments ?? []\n      // Multimodal branch: only when enabled, the message has attachments, and we can decode them.\n      if (mm && attachments.length > 0 && input.decodeMedia) {\n        const parts: Array<{ type: string; text?: string }> = []\n        if (text.length > 0) parts.push({ type: 'text', text })\n        for (const media of attachments) {\n          const enabled =\n            (media.kind === 'image' && mm.image) || (media.kind === 'audio' && mm.audio)\n          let decoded: { kind: 'image' | 'audio'; data: unknown } | undefined\n          if (enabled) {\n            // A malformed payload (zero-byte, truncated, polyglot, resolution bomb) must not throw out\n            // of the turn — a decode failure becomes a policy decision, not a turn-killer. On throw we\n            // fall through to the SAME unsupported-media policy path as a disabled modality.\n            try {\n              decoded = await input.decodeMedia(media)\n            } catch (err) {\n              input.warn?.(\n                `decodeMedia failed for ${media.filename} (${media.kind}/${media.mimeType}): ${\n                  isError(err) ? err.message : String(err)\n                } — degrading via unsupportedMediaPolicy.`\n              )\n            }\n          }\n          if (decoded) {\n            if (decoded.kind === 'image') {\n              images.push(decoded.data)\n              parts.push({ type: 'image' })\n            } else {\n              audio.push(decoded.data)\n              parts.push({ type: 'audio' })\n            }\n          } else {\n            // Disabled modality / video / document / decode-failure → degrade to text via the shared\n            // policy ('throw' still raises here — an undecodable attachment under 'throw' is fatal by\n            // design; 'synthetic-description'/'fallback-stash' degrade gracefully).\n            const fallback = await resolveMediaFallbackText(\n              media,\n              input.unsupportedMediaPolicy ?? 'throw',\n              input.warn\n            )\n            parts.push({ type: 'text', text: fallback })\n          }\n        }\n        messages.push({ role, content: parts } as unknown as TransformersJsMessage)\n      } else {\n        messages.push({ role, content: text } as TransformersJsMessage)\n      }\n    } else if (item.kind === 'thought') {\n      const t = item.value\n      const envelope = input.renderThought(t.content.toString(), {\n        nonce: t.id,\n        kind: 'self-reasoning',\n        from: t.identity?.identifier ?? input.selfIdentity,\n      } as never)\n      messages.push({ role: 'assistant', content: envelope } as TransformersJsMessage)\n    } else {\n      const tc = item.value\n      const rendered = input.renderedToolCallResults.get(tc)\n      messages.push({ role: 'tool', content: rendered ?? '' } as TransformersJsMessage)\n    }\n  }\n\n  const tools = input.toolsToTransformersJsTools(input.tools.visible())\n  return { messages, tools, images, audio }\n}\n\n/** Default {@link buildTransformersJsMessages}. */\nexport const defaultBuildTransformersJsMessages = buildTransformersJsMessages\n\n/**\n * Decode an ADK {@link @nhtio/adk!Media} into a transformers.js multimodal input.\n *\n * @remarks\n * Image → `RawImage.fromBlob(...)`; audio → `Float32Array` PCM at the model's sample rate (16 kHz\n * default) via `read_audio` over a `data:` URL. Imports `@huggingface/transformers` lazily, so it's\n * only loaded on the multimodal path. (Verified against a real Gemma-4 run — see plan 0a.)\n */\nexport const mediaToTransformersInput = async (\n  media: Media,\n  opts?: { audioSampleRate?: number }\n): Promise<{ kind: 'image' | 'audio'; data: unknown }> => {\n  const bytes = await media.asBytes()\n  if (media.kind === 'image') {\n    // Sanitise the mime before it becomes a Blob type (a `;base64,`/`\\r\\n`-laden mime is an injection\n    // vector if reflected; an invalid type degrades to the generic safe subtype).\n    const tf = await import('@huggingface/transformers')\n    const blob = new Blob([bytes as Uint8Array<ArrayBuffer>], {\n      type: sanitizeMimeType(media.mimeType, 'image'),\n    })\n    const image = await tf.RawImage.fromBlob(blob)\n    return { kind: 'image', data: image }\n  }\n  // audio — produce a mono Float32Array at the target rate. transformers.js's `read_audio` decodes via\n  // the Web Audio API (`AudioContext`), which exists in browsers but NOT in Node (it throws\n  // \"AudioContext is not available in your environment\" — verified). So for PCM WAV — the canonical\n  // 16 kHz speech container these processors expect — we decode the RIFF ourselves: dependency-free,\n  // env-neutral (Node + browser), no codec peer, AND no `@huggingface/transformers` import (a heavy ONNX/\n  // wasm peer that hangs the vitest browser project on import). The PCM-WAV fast path returns BEFORE any\n  // peer load. `read_audio` is the fallback only for compressed containers (mp3/flac/ogg) where a real\n  // decoder is genuinely needed (browser only, by that path's nature).\n  const targetRate = opts?.audioSampleRate ?? 16000\n  const pcm = decodePcmWav(bytes as Uint8Array, targetRate)\n  if (pcm) return { kind: 'audio', data: pcm }\n  const tf = await import('@huggingface/transformers')\n  const b64 = await media.asBase64()\n  const samples = await tf.read_audio(\n    `data:${sanitizeMimeType(media.mimeType, 'audio')};base64,${b64}`,\n    targetRate\n  )\n  return { kind: 'audio', data: samples }\n}\n\n/**\n * Decode a PCM/IEEE-float RIFF/WAVE buffer to a mono {@link Float32Array} at `targetRate`, with **no**\n * `AudioContext` and no codec peer — so it runs in Node and the browser alike. Returns `undefined` for\n * anything that isn't a parseable uncompressed WAV (compressed containers fall back to `read_audio`).\n *\n * @remarks\n * Walks RIFF chunks to find `fmt ` + `data` (so a `LIST`/`fact`/`cue ` chunk before `data` is skipped),\n * supports 8/16/24/32-bit integer PCM (format 1) and 32/64-bit IEEE float (format 3 / 0xFFFE extensible),\n * downmixes to mono by averaging channels, then resamples to `targetRate` with linear interpolation. The\n * sample-rate match is the common path (the fixture + most speech models are 16 kHz mono) so resampling\n * is usually a no-op.\n */\nconst decodePcmWav = (bytes: Uint8Array, targetRate: number): Float32Array | undefined => {\n  if (bytes.length < 44) return undefined\n  const dv = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength)\n  const tag = (off: number): string =>\n    String.fromCharCode(bytes[off], bytes[off + 1], bytes[off + 2], bytes[off + 3])\n  if (tag(0) !== 'RIFF' || tag(8) !== 'WAVE') return undefined\n  let format = 0\n  let channels = 0\n  let sampleRate = 0\n  let bits = 0\n  let dataOff = -1\n  let dataLen = 0\n  let p = 12\n  while (p + 8 <= bytes.length) {\n    const id = tag(p)\n    const size = dv.getUint32(p + 4, true)\n    const body = p + 8\n    if (id === 'fmt ' && body + 16 <= bytes.length) {\n      format = dv.getUint16(body, true)\n      channels = dv.getUint16(body + 2, true)\n      sampleRate = dv.getUint32(body + 4, true)\n      bits = dv.getUint16(body + 14, true)\n      // WAVE_FORMAT_EXTENSIBLE (0xFFFE): the real format is the first 2 bytes of the subformat GUID.\n      if (format === 0xfffe && size >= 24 && body + 26 <= bytes.length) {\n        format = dv.getUint16(body + 24, true)\n      }\n    } else if (id === 'data') {\n      dataOff = body\n      dataLen = Math.min(size, bytes.length - body)\n      break\n    }\n    p = body + size + (size & 1) // chunks are word-aligned\n  }\n  if (dataOff < 0 || channels < 1 || sampleRate < 1) return undefined\n  const isFloat = format === 3\n  const isInt = format === 1\n  if (!isFloat && !isInt) return undefined\n  const bytesPerSample = bits >> 3\n  if (bytesPerSample < 1) return undefined\n  const frames = Math.floor(dataLen / (bytesPerSample * channels))\n  if (frames < 1) return undefined\n  const readSample = (off: number): number => {\n    if (isFloat) return bits === 64 ? dv.getFloat64(off, true) : dv.getFloat32(off, true)\n    if (bits === 8) return (dv.getUint8(off) - 128) / 128 // 8-bit PCM is unsigned\n    if (bits === 16) return dv.getInt16(off, true) / 32768\n    if (bits === 24) {\n      const v = dv.getUint8(off) | (dv.getUint8(off + 1) << 8) | (dv.getUint8(off + 2) << 16)\n      return (v & 0x800000 ? v - 0x1000000 : v) / 8388608\n    }\n    if (bits === 32) return dv.getInt32(off, true) / 2147483648\n    return 0\n  }\n  const mono = new Float32Array(frames)\n  for (let f = 0; f < frames; f++) {\n    let sum = 0\n    const base = dataOff + f * bytesPerSample * channels\n    for (let c = 0; c < channels; c++) sum += readSample(base + c * bytesPerSample)\n    mono[f] = sum / channels\n  }\n  if (sampleRate === targetRate) return mono\n  // Linear-interpolation resample to the model's expected rate.\n  const outLen = Math.max(1, Math.round((frames * targetRate) / sampleRate))\n  const out = new Float32Array(outLen)\n  const ratio = (frames - 1) / Math.max(1, outLen - 1)\n  for (let i = 0; i < outLen; i++) {\n    const x = i * ratio\n    const i0 = Math.floor(x)\n    const i1 = Math.min(frames - 1, i0 + 1)\n    out[i] = mono[i0] + (mono[i1] - mono[i0]) * (x - i0)\n  }\n  return out\n}\n\n/** Default {@link mediaToTransformersInput}. */\nexport const defaultMediaToTransformersInput = mediaToTransformersInput\n\n/**\n * A streaming accumulator over transformers.js `TextStreamer` decoded-text deltas.\n *\n * @remarks\n * transformers.js streams **decoded text** (via the `TextStreamer` callback), not structured events.\n * This accumulator just concatenates the deltas; tool-call and reasoning extraction run **once after\n * the stream drains**, over `content()`, via the shared parser layer.\n */\nexport interface TransformersJsStreamAccumulator {\n  /** Feed one decoded-text delta; returns it (for live prose reporting). */\n  feed(delta: string): string\n  /** The full accumulated text. */\n  content(): string\n}\n\n/** Create a {@link TransformersJsStreamAccumulator}. */\nexport const createTransformersJsStreamAccumulator = (): TransformersJsStreamAccumulator => {\n  let buf = ''\n  return {\n    feed(delta) {\n      if (typeof delta !== 'string' || delta.length === 0) return ''\n      buf += delta\n      return delta\n    },\n    content: () => buf,\n  }\n}\n\n/** Default {@link createTransformersJsStreamAccumulator}. */\nexport const defaultCreateTransformersJsStreamAccumulator = createTransformersJsStreamAccumulator\n\n// Re-export the tool definition shape under the chat-completions tool alias for convenience.\nexport type { ChatCompletionsTool }\n"],"mappings":"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"}