UNPKG

@langchain/anthropic

Version:
1 lines 15.4 kB
{"version":3,"file":"stream_events.cjs","names":[],"sources":["../../src/utils/stream_events.ts"],"sourcesContent":["/**\n * Converts a raw Anthropic SSE event stream into LangChain ChatModelStreamEvents.\n *\n * @module\n */\n\nimport type Anthropic from \"@anthropic-ai/sdk\";\nimport type {\n ChatModelStreamEvent,\n ContentBlockDelta,\n FinishReason,\n} from \"@langchain/core/language_models/event\";\nimport type { ContentBlock, UsageMetadata } from \"@langchain/core/messages\";\nimport type { AnthropicMessageStreamEvent } from \"../types.js\";\n\n// ─── Public API ─────────────────────────────────────────────────\n\nexport interface ConvertAnthropicStreamOptions {\n streamUsage?: boolean;\n}\n\n/**\n * Convert an async iterable of raw Anthropic stream events into\n * LangChain `ChatModelStreamEvent`s with typed deltas.\n */\nexport async function* convertAnthropicStream(\n source: AsyncIterable<AnthropicMessageStreamEvent>,\n options: ConvertAnthropicStreamOptions = {}\n): AsyncGenerator<ChatModelStreamEvent> {\n const shouldStreamUsage = options.streamUsage ?? true;\n\n // Track accumulated state per content block (for finalization)\n const blockAccumulators = new Map<\n number,\n // oxlint-disable-next-line @typescript-eslint/no-explicit-any\n Record<string, any>\n >();\n let usageSnapshot: UsageMetadata | undefined;\n let stopReason: string | null = null;\n\n for await (const data of source) {\n switch (data.type) {\n // ── Message lifecycle ──────────────────────────────────\n case \"message_start\": {\n const { usage, id, model } = data.message;\n if (usage && shouldStreamUsage) {\n usageSnapshot = buildUsageSnapshot(usage);\n }\n yield {\n event: \"message-start\" as const,\n id,\n ...(usageSnapshot ? { usage: usageSnapshot } : {}),\n };\n yield {\n event: \"provider\" as const,\n provider: \"anthropic\",\n name: \"message_start\",\n payload: { model, id },\n };\n break;\n }\n\n case \"message_delta\": {\n stopReason = data.delta.stop_reason;\n if (shouldStreamUsage && data.usage) {\n if (!usageSnapshot) {\n usageSnapshot = {\n input_tokens: 0,\n output_tokens: data.usage.output_tokens,\n total_tokens: data.usage.output_tokens,\n };\n } else {\n usageSnapshot = {\n ...usageSnapshot,\n output_tokens:\n usageSnapshot.output_tokens + data.usage.output_tokens,\n total_tokens:\n usageSnapshot.input_tokens +\n usageSnapshot.output_tokens +\n data.usage.output_tokens,\n };\n }\n yield { event: \"usage\" as const, usage: usageSnapshot };\n }\n if (\n \"context_management\" in data.delta &&\n data.delta.context_management\n ) {\n yield {\n event: \"provider\" as const,\n provider: \"anthropic\",\n name: \"context_management\",\n payload: data.delta.context_management,\n };\n }\n break;\n }\n\n case \"message_stop\": {\n yield {\n event: \"message-finish\" as const,\n reason: mapStopReason(stopReason),\n ...(usageSnapshot ? { usage: usageSnapshot } : {}),\n metadata: { model_provider: \"anthropic\" },\n };\n break;\n }\n\n // ── Content block lifecycle ───────────────────────────\n case \"content_block_start\": {\n const { index, content_block } = data;\n const mapped = mapBlockToContentBlock(content_block, index);\n blockAccumulators.set(index, { ...mapped });\n yield {\n event: \"content-block-start\" as const,\n index,\n content: mapped,\n };\n break;\n }\n\n case \"content_block_delta\": {\n const { index, delta } = data;\n const acc = blockAccumulators.get(index);\n if (!acc) break;\n\n const { contentDelta, accumulated } = applyAnthropicDelta(acc, delta);\n blockAccumulators.set(index, accumulated);\n\n yield {\n event: \"content-block-delta\" as const,\n index,\n delta: contentDelta,\n };\n break;\n }\n\n case \"content_block_stop\": {\n const { index } = data;\n const acc = blockAccumulators.get(index);\n if (!acc) break;\n\n const finalized = finalizeBlock(acc);\n yield {\n event: \"content-block-finish\" as const,\n index,\n content: finalized,\n };\n blockAccumulators.delete(index);\n break;\n }\n\n // ── Unhandled → provider passthrough ───────────────────\n default: {\n yield {\n event: \"provider\" as const,\n provider: \"anthropic\",\n name: data.type,\n payload: data,\n };\n break;\n }\n }\n }\n}\n\n// ─── Internal helpers ───────────────────────────────────────────\n\nfunction mapStopReason(stopReason: string | null | undefined): FinishReason {\n switch (stopReason) {\n case \"end_turn\":\n case \"stop_sequence\":\n return \"stop\";\n case \"tool_use\":\n return \"tool_use\";\n case \"max_tokens\":\n return \"length\";\n default:\n return \"stop\";\n }\n}\n\nfunction buildUsageSnapshot(\n usage: Anthropic.Messages.Usage | Record<string, number>\n): UsageMetadata {\n const cacheCreation =\n (usage as Record<string, number>).cache_creation_input_tokens ?? 0;\n const cacheRead =\n (usage as Record<string, number>).cache_read_input_tokens ?? 0;\n const totalInput = usage.input_tokens + cacheCreation + cacheRead;\n return {\n input_tokens: totalInput,\n output_tokens: usage.output_tokens,\n total_tokens: totalInput + usage.output_tokens,\n input_token_details: {\n cache_creation: cacheCreation,\n cache_read: cacheRead,\n },\n };\n}\n\nfunction mapBlockToContentBlock(\n // oxlint-disable-next-line @typescript-eslint/no-explicit-any\n block: any,\n index: number\n // oxlint-disable-next-line @typescript-eslint/no-explicit-any\n): Record<string, any> {\n switch (block.type) {\n case \"text\":\n return { type: \"text\" as const, text: block.text ?? \"\", index };\n case \"thinking\":\n return {\n type: \"reasoning\" as const,\n reasoning: block.thinking ?? \"\",\n index,\n };\n case \"redacted_thinking\":\n return { type: \"non_standard\" as const, value: { ...block }, index };\n case \"tool_use\":\n return {\n type: \"tool_call_chunk\" as const,\n id: block.id,\n name: block.name,\n args: \"\",\n index,\n };\n case \"server_tool_use\":\n return {\n type: \"server_tool_call_chunk\" as const,\n id: block.id,\n name: block.name,\n args: \"\",\n index,\n };\n default:\n return { type: \"non_standard\" as const, value: { ...block }, index };\n }\n}\n\n/**\n * Map an Anthropic content_block_delta to a content block delta\n * and update the accumulated state.\n */\nfunction applyAnthropicDelta(\n // oxlint-disable-next-line @typescript-eslint/no-explicit-any\n accumulated: Record<string, any>,\n // oxlint-disable-next-line @typescript-eslint/no-explicit-any\n delta: any\n): {\n contentDelta: ContentBlockDelta;\n // oxlint-disable-next-line @typescript-eslint/no-explicit-any\n accumulated: Record<string, any>;\n} {\n switch (delta.type) {\n case \"text_delta\":\n return {\n contentDelta: { type: \"text-delta\" as const, text: delta.text },\n accumulated: {\n ...accumulated,\n text: (accumulated.text ?? \"\") + delta.text,\n },\n };\n\n case \"thinking_delta\":\n return {\n contentDelta: {\n type: \"reasoning-delta\" as const,\n reasoning: delta.thinking,\n },\n accumulated: {\n ...accumulated,\n reasoning: (accumulated.reasoning ?? \"\") + delta.thinking,\n },\n };\n\n case \"input_json_delta\": {\n const newArgs = (accumulated.args ?? \"\") + delta.partial_json;\n return {\n contentDelta: {\n type: \"block-delta\" as const,\n fields: { type: accumulated.type, args: newArgs },\n },\n accumulated: { ...accumulated, args: newArgs },\n };\n }\n\n case \"citations_delta\": {\n const annotations = [...(accumulated.annotations ?? []), delta.citation];\n return {\n contentDelta: {\n type: \"block-delta\" as const,\n fields: {\n type: accumulated.type,\n annotations,\n },\n },\n accumulated: {\n ...accumulated,\n annotations,\n },\n };\n }\n\n case \"signature_delta\":\n return {\n contentDelta: {\n type: \"block-delta\" as const,\n fields: { type: accumulated.type, signature: delta.signature },\n },\n accumulated: { ...accumulated, signature: delta.signature },\n };\n\n case \"compaction_delta\":\n return {\n contentDelta: {\n type: \"block-delta\" as const,\n fields: {\n type: \"non_standard\",\n value: { ...(accumulated.value ?? {}), compaction: delta },\n },\n },\n accumulated: {\n ...accumulated,\n value: { ...(accumulated.value ?? {}), compaction: delta },\n },\n };\n\n default:\n return {\n contentDelta: {\n type: \"block-delta\" as const,\n fields: { type: accumulated.type, ...delta },\n },\n accumulated,\n };\n }\n}\n\nfunction finalizeBlock(\n // oxlint-disable-next-line @typescript-eslint/no-explicit-any\n accumulated: Record<string, any>\n): ContentBlock {\n if (\n accumulated.type === \"tool_call_chunk\" ||\n accumulated.type === \"server_tool_call_chunk\"\n ) {\n const finalType =\n accumulated.type === \"tool_call_chunk\"\n ? (\"tool_call\" as const)\n : (\"server_tool_call\" as const);\n let parsedArgs: unknown;\n try {\n parsedArgs = JSON.parse(accumulated.args || \"{}\");\n } catch {\n return {\n type: \"invalid_tool_call\" as const,\n id: accumulated.id,\n name: accumulated.name,\n args: accumulated.args,\n error: \"Failed to parse tool call arguments as JSON\",\n } as ContentBlock.Tools.InvalidToolCall;\n }\n return {\n type: finalType,\n id: accumulated.id,\n name: accumulated.name,\n args: parsedArgs,\n // oxlint-disable-next-line @typescript-eslint/no-explicit-any\n } as any;\n }\n\n const { index: _index, ...rest } = accumulated;\n return rest as ContentBlock;\n}\n"],"mappings":"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"}