{"version":3,"file":"assembled-to-message.cjs","names":["HumanMessage","SystemMessage","ToolMessage","AIMessageChunk","AIMessage"],"sources":["../../src/stream/assembled-to-message.ts"],"sourcesContent":["/**\n * Convert the protocol-native {@link AssembledMessage} (a namespace +\n * content-block bag) into a class instance from\n * `@langchain/core/messages`.\n *\n * The v2 messages channel carries `role` on the `message-start`\n * event, but `MessageAssembler` drops it by the time it produces an\n * `AssembledMessage`. The selector-side caller therefore captures the\n * role separately (via the assembler's per-update hook, see\n * `projections/messages.ts`) and passes it in.\n *\n * The conversion is intentionally lossless in the limit: when the\n * message finishes we emit the same BaseMessage shape that a\n * `values.messages` entry would round-trip to via\n * {@link tryCoerceMessageLikeToMessage}. Mid-stream, partial blocks\n * (e.g. tool_call_chunk) are folded into the same shape so the UI\n * can render an incrementally-completing message.\n */\nimport {\n  AIMessage,\n  AIMessageChunk,\n  HumanMessage,\n  SystemMessage,\n  ToolMessage,\n  type BaseMessage,\n} from \"@langchain/core/messages\";\nimport type { ContentBlock, MessageRole, UsageInfo } from \"@langchain/protocol\";\nimport type { AssembledMessage } from \"../client/stream/messages.js\";\n\nexport type ExtendedMessageRole = MessageRole | \"tool\";\n\nexport interface AssembledToMessageInput {\n  /** Stable message id (from `MessageStartData.id`). */\n  id?: string;\n  /** Author role captured from the `message-start` event. */\n  role: ExtendedMessageRole;\n  /** Content blocks assembled so far. */\n  blocks: ContentBlock[];\n  /** Tool-call id a `role: \"tool\"` message is responding to, if any. */\n  toolCallId?: string;\n  /** Final-token usage (populated on `message-finish`). */\n  usage?: UsageInfo;\n}\n\n/**\n * Produce a `BaseMessage` class instance from an in-progress or\n * finished assembled message. Safe to call repeatedly across deltas —\n * each call returns a new instance whose content reflects the\n * currently-observed blocks.\n */\nexport function assembledToBaseMessage(\n  input: AssembledToMessageInput\n): BaseMessage {\n  const { id, role, blocks, toolCallId, usage } = input;\n  const textContent = extractContentString(blocks);\n  const toolCalls = extractToolCalls(blocks);\n  const toolCallChunks = extractToolCallChunks(blocks);\n  const additionalKwargs =\n    usage != null ? ({ usage } as Record<string, unknown>) : undefined;\n\n  switch (role) {\n    case \"human\":\n      return new HumanMessage({\n        ...(id != null ? { id } : {}),\n        content: textContent,\n        ...(additionalKwargs != null\n          ? { additional_kwargs: additionalKwargs }\n          : {}),\n      });\n    case \"system\":\n      return new SystemMessage({\n        ...(id != null ? { id } : {}),\n        content: textContent,\n        ...(additionalKwargs != null\n          ? { additional_kwargs: additionalKwargs }\n          : {}),\n      });\n    case \"tool\":\n      return new ToolMessage({\n        ...(id != null ? { id } : {}),\n        content: textContent,\n        tool_call_id: toolCallId ?? \"\",\n      });\n    case \"ai\":\n    default: {\n      // Use `AIMessageChunk` whenever tool_call_chunks are present —\n      // the concrete `AIMessage` class silently DROPS the\n      // `tool_call_chunks` field, which would leave mid-stream tool\n      // calls invisible to the UI (it sees an AI message with empty\n      // content and no tool calls, rendering a blank bubble until\n      // `content-block-finish` finally promotes the chunks to\n      // finalized `tool_calls`). The chunk class is assignment-compatible\n      // with `BaseMessage` and round-trips through the merge logic.\n      const payload = {\n        ...(id != null ? { id } : {}),\n        content: cloneContentBlocks(blocks),\n        ...(toolCalls.length > 0 ? { tool_calls: toolCalls } : {}),\n        ...(toolCallChunks.length > 0\n          ? { tool_call_chunks: toolCallChunks }\n          : {}),\n        ...(additionalKwargs != null\n          ? { additional_kwargs: additionalKwargs }\n          : {}),\n        response_metadata: { output_version: \"v1\" as const },\n      };\n      return toolCallChunks.length > 0\n        ? new AIMessageChunk(\n            payload as ConstructorParameters<typeof AIMessageChunk>[0]\n          )\n        : new AIMessage(payload as ConstructorParameters<typeof AIMessage>[0]);\n    }\n  }\n}\n\n/**\n * Convenience: given the raw assembled message + the role captured\n * from `message-start`, produce a `BaseMessage` with the same id.\n */\nexport function assembledMessageToBaseMessage(\n  assembled: AssembledMessage,\n  role: ExtendedMessageRole,\n  extras: { toolCallId?: string } = {}\n): BaseMessage {\n  return assembledToBaseMessage({\n    id: assembled.id,\n    role,\n    blocks: assembled.blocks,\n    toolCallId: extras.toolCallId,\n    usage: assembled.usage,\n  });\n}\n\n// ---------- helpers ----------\n\nfunction extractContentString(blocks: ContentBlock[]): string {\n  let out = \"\";\n  for (const block of blocks) {\n    if (\n      block.type === \"text\" &&\n      typeof (block as { text?: unknown }).text === \"string\"\n    ) {\n      out += (block as { text: string }).text;\n    }\n  }\n  return out;\n}\n\nfunction cloneContentBlocks(blocks: ContentBlock[]): ContentBlock[] {\n  return blocks.map((block) => structuredClone(block));\n}\n\ninterface LooseToolCall {\n  id: string;\n  name: string;\n  args: Record<string, unknown>;\n  type: \"tool_call\";\n}\n\nfunction extractToolCalls(blocks: ContentBlock[]): LooseToolCall[] {\n  const out: LooseToolCall[] = [];\n  for (const block of blocks) {\n    if (block.type !== \"tool_call\" && block.type !== \"tool_use\") continue;\n    const tc = block as ToolCallLikeBlock;\n    out.push({\n      id: tc.id ?? \"\",\n      name: tc.name ?? \"\",\n      args: normalizeToolCallArgs(tc.args ?? tc.input),\n      type: \"tool_call\",\n    });\n  }\n  return out;\n}\n\ninterface ToolCallLikeBlock {\n  id?: string;\n  name?: string;\n  args?: unknown;\n  input?: unknown;\n}\n\ninterface LooseToolCallChunk {\n  id?: string;\n  name?: string;\n  args?: string;\n  index?: number;\n  type: \"tool_call_chunk\";\n}\n\nfunction extractToolCallChunks(blocks: ContentBlock[]): LooseToolCallChunk[] {\n  const out: LooseToolCallChunk[] = [];\n  for (const block of blocks) {\n    if (block.type !== \"tool_call_chunk\") continue;\n    const tc = block as {\n      id?: string;\n      name?: string;\n      args?: string;\n      index?: number;\n    };\n    out.push({\n      id: tc.id,\n      name: tc.name,\n      args: tc.args,\n      index: tc.index,\n      type: \"tool_call_chunk\",\n    });\n  }\n  return out;\n}\n\nfunction normalizeToolCallArgs(value: unknown): Record<string, unknown> {\n  if (value != null && typeof value === \"object\" && !Array.isArray(value)) {\n    return value as Record<string, unknown>;\n  }\n  if (typeof value === \"string\" && value.length > 0) {\n    try {\n      const parsed = JSON.parse(value);\n      if (\n        parsed != null &&\n        typeof parsed === \"object\" &&\n        !Array.isArray(parsed)\n      ) {\n        return parsed as Record<string, unknown>;\n      }\n    } catch {\n      // Partial streaming input is represented via tool_call_chunks.\n    }\n  }\n  return {};\n}\n"],"mappings":"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"}