UNPKG

@langchain/anthropic

Version:
301 lines (300 loc) 7.01 kB
//#region src/utils/stream_events.ts /** * Convert an async iterable of raw Anthropic stream events into * LangChain `ChatModelStreamEvent`s with typed deltas. */ async function* convertAnthropicStream(source, options = {}) { const shouldStreamUsage = options.streamUsage ?? true; const blockAccumulators = /* @__PURE__ */ new Map(); let usageSnapshot; let stopReason = null; for await (const data of source) switch (data.type) { case "message_start": { const { usage, id, model } = data.message; if (usage && shouldStreamUsage) usageSnapshot = buildUsageSnapshot(usage); yield { event: "message-start", id, ...usageSnapshot ? { usage: usageSnapshot } : {} }; yield { event: "provider", provider: "anthropic", name: "message_start", payload: { model, id } }; break; } case "message_delta": stopReason = data.delta.stop_reason; if (shouldStreamUsage && data.usage) { if (!usageSnapshot) usageSnapshot = { input_tokens: 0, output_tokens: data.usage.output_tokens, total_tokens: data.usage.output_tokens }; else usageSnapshot = { ...usageSnapshot, output_tokens: usageSnapshot.output_tokens + data.usage.output_tokens, total_tokens: usageSnapshot.input_tokens + usageSnapshot.output_tokens + data.usage.output_tokens }; yield { event: "usage", usage: usageSnapshot }; } if ("context_management" in data.delta && data.delta.context_management) yield { event: "provider", provider: "anthropic", name: "context_management", payload: data.delta.context_management }; break; case "message_stop": yield { event: "message-finish", reason: mapStopReason(stopReason), ...usageSnapshot ? { usage: usageSnapshot } : {}, metadata: { model_provider: "anthropic" } }; break; case "content_block_start": { const { index, content_block } = data; const mapped = mapBlockToContentBlock(content_block, index); blockAccumulators.set(index, { ...mapped }); yield { event: "content-block-start", index, content: mapped }; break; } case "content_block_delta": { const { index, delta } = data; const acc = blockAccumulators.get(index); if (!acc) break; const { contentDelta, accumulated } = applyAnthropicDelta(acc, delta); blockAccumulators.set(index, accumulated); yield { event: "content-block-delta", index, delta: contentDelta }; break; } case "content_block_stop": { const { index } = data; const acc = blockAccumulators.get(index); if (!acc) break; yield { event: "content-block-finish", index, content: finalizeBlock(acc) }; blockAccumulators.delete(index); break; } default: yield { event: "provider", provider: "anthropic", name: data.type, payload: data }; break; } } function mapStopReason(stopReason) { switch (stopReason) { case "end_turn": case "stop_sequence": return "stop"; case "tool_use": return "tool_use"; case "max_tokens": return "length"; default: return "stop"; } } function buildUsageSnapshot(usage) { const cacheCreation = usage.cache_creation_input_tokens ?? 0; const cacheRead = usage.cache_read_input_tokens ?? 0; const totalInput = usage.input_tokens + cacheCreation + cacheRead; return { input_tokens: totalInput, output_tokens: usage.output_tokens, total_tokens: totalInput + usage.output_tokens, input_token_details: { cache_creation: cacheCreation, cache_read: cacheRead } }; } function mapBlockToContentBlock(block, index) { switch (block.type) { case "text": return { type: "text", text: block.text ?? "", index }; case "thinking": return { type: "reasoning", reasoning: block.thinking ?? "", index }; case "redacted_thinking": return { type: "non_standard", value: { ...block }, index }; case "tool_use": return { type: "tool_call_chunk", id: block.id, name: block.name, args: "", index }; case "server_tool_use": return { type: "server_tool_call_chunk", id: block.id, name: block.name, args: "", index }; default: return { type: "non_standard", value: { ...block }, index }; } } /** * Map an Anthropic content_block_delta to a content block delta * and update the accumulated state. */ function applyAnthropicDelta(accumulated, delta) { switch (delta.type) { case "text_delta": return { contentDelta: { type: "text-delta", text: delta.text }, accumulated: { ...accumulated, text: (accumulated.text ?? "") + delta.text } }; case "thinking_delta": return { contentDelta: { type: "reasoning-delta", reasoning: delta.thinking }, accumulated: { ...accumulated, reasoning: (accumulated.reasoning ?? "") + delta.thinking } }; case "input_json_delta": { const newArgs = (accumulated.args ?? "") + delta.partial_json; return { contentDelta: { type: "block-delta", fields: { type: accumulated.type, args: newArgs } }, accumulated: { ...accumulated, args: newArgs } }; } case "citations_delta": { const annotations = [...accumulated.annotations ?? [], delta.citation]; return { contentDelta: { type: "block-delta", fields: { type: accumulated.type, annotations } }, accumulated: { ...accumulated, annotations } }; } case "signature_delta": return { contentDelta: { type: "block-delta", fields: { type: accumulated.type, signature: delta.signature } }, accumulated: { ...accumulated, signature: delta.signature } }; case "compaction_delta": return { contentDelta: { type: "block-delta", fields: { type: "non_standard", value: { ...accumulated.value ?? {}, compaction: delta } } }, accumulated: { ...accumulated, value: { ...accumulated.value ?? {}, compaction: delta } } }; default: return { contentDelta: { type: "block-delta", fields: { type: accumulated.type, ...delta } }, accumulated }; } } function finalizeBlock(accumulated) { if (accumulated.type === "tool_call_chunk" || accumulated.type === "server_tool_call_chunk") { const finalType = accumulated.type === "tool_call_chunk" ? "tool_call" : "server_tool_call"; let parsedArgs; try { parsedArgs = JSON.parse(accumulated.args || "{}"); } catch { return { type: "invalid_tool_call", id: accumulated.id, name: accumulated.name, args: accumulated.args, error: "Failed to parse tool call arguments as JSON" }; } return { type: finalType, id: accumulated.id, name: accumulated.name, args: parsedArgs }; } const { index: _index, ...rest } = accumulated; return rest; } //#endregion exports.convertAnthropicStream = convertAnthropicStream; //# sourceMappingURL=stream_events.cjs.map