Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
const require_runtime = require("../_virtual/_rolldown/runtime.cjs");
const require_ai = require("../messages/ai.cjs");
//#region src/language_models/compat.ts
/**
* Compatibility bridge: converts legacy `_streamResponseChunks`
* (`ChatGenerationChunk` / `AIMessageChunk`) output to the new
* `ChatModelStreamEvent` protocol.
*
* @module
*/
var compat_exports = /* @__PURE__ */ require_runtime.__exportAll({
	convertChunksToEvents: () => convertChunksToEvents,
	finalizeContentBlock: () => finalizeContentBlock
});
const MIME_TYPE_BY_AUDIO_FORMAT = {
	wav: "audio/wav",
	mp3: "audio/mpeg",
	flac: "audio/flac",
	opus: "audio/opus",
	aac: "audio/aac",
	pcm16: "audio/pcm"
};
const MIME_TYPE_BY_IMAGE_FORMAT = {
	png: "image/png",
	jpeg: "image/jpeg",
	jpg: "image/jpeg",
	webp: "image/webp",
	gif: "image/gif"
};
function nextBlockIndex(activeBlocks) {
	let next = 0;
	for (const index of activeBlocks.keys()) if (index >= next) next = index + 1;
	return next;
}
function getAdditionalKwargs(message) {
	const additional = message.additional_kwargs;
	return additional != null && typeof additional === "object" ? additional : {};
}
function extractImageBlocksFromToolOutputs(message) {
	const toolOutputs = getAdditionalKwargs(message).tool_outputs;
	if (!Array.isArray(toolOutputs)) return [];
	const blocks = [];
	for (const entry of toolOutputs) {
		if (entry == null || typeof entry !== "object") continue;
		const record = entry;
		if (record.type !== "image_generation_call") continue;
		const data = typeof record.result === "string" ? record.result : void 0;
		const url = typeof record.url === "string" ? record.url : void 0;
		if (data == null && url == null) continue;
		const outputFormat = typeof record.output_format === "string" ? record.output_format.toLowerCase() : void 0;
		const mimeType = (outputFormat != null ? MIME_TYPE_BY_IMAGE_FORMAT[outputFormat] : void 0) ?? "image/png";
		blocks.push({
			type: "image",
			...typeof record.id === "string" ? { id: record.id } : {},
			...url != null ? { url } : {},
			...data != null ? { data } : {},
			mimeType
		});
	}
	return blocks;
}
/**
* Get the audio payload from the message.
*
* This handles the OpenAI-shaped `additional_kwargs.audio` payload used by
* legacy chunk streams; other providers must normalize into this shape first.
*
* @param message - The message to get the audio payload from.
* @returns The audio payload.
* @internal
*/
function getAudioPayload(message) {
	const audio = getAdditionalKwargs(message).audio;
	if (audio == null || typeof audio !== "object") return void 0;
	const record = audio;
	const data = typeof record.data === "string" ? record.data : void 0;
	const url = typeof record.url === "string" ? record.url : void 0;
	const transcript = typeof record.transcript === "string" ? record.transcript : void 0;
	if (data == null && url == null && transcript == null) return void 0;
	const explicitMimeType = typeof record.mime_type === "string" ? record.mime_type : typeof record.mimeType === "string" ? record.mimeType : void 0;
	const format = typeof record.format === "string" ? record.format.toLowerCase() : void 0;
	const mimeType = explicitMimeType ?? (format != null ? MIME_TYPE_BY_AUDIO_FORMAT[format] : void 0) ?? (data != null ? "audio/wav" : "audio/pcm");
	return {
		...typeof record.id === "string" ? { id: record.id } : {},
		...data != null ? { data } : {},
		...url != null ? { url } : {},
		...transcript != null ? { transcript } : {},
		mimeType
	};
}
/**
* Convert an async iterable of legacy `ChatGenerationChunk`s into
* `ChatModelStreamEvent`s with typed deltas.
*/
async function* convertChunksToEvents(chunks, options) {
	const activeBlocks = /* @__PURE__ */ new Map();
	let messageStarted = false;
	let lastUsage;
	let audioStream;
	const emittedImageKeys = /* @__PURE__ */ new Set();
	for await (const chunk of chunks) {
		options?.signal?.throwIfAborted();
		const msg = chunk.message;
		let usageHandledInStart = false;
		if (!messageStarted) {
			messageStarted = true;
			const startEvent = {
				event: "message-start",
				id: msg.id ?? void 0
			};
			if (require_ai.AIMessageChunk.isInstance(msg) && msg.usage_metadata) {
				startEvent.usage = msg.usage_metadata;
				lastUsage = { ...msg.usage_metadata };
				usageHandledInStart = true;
			}
			yield startEvent;
		}
		const content = msg.content;
		if (typeof content === "string") {
			if (content !== "") {
				const blockIndex = 0;
				if (!activeBlocks.has(blockIndex)) {
					const initial = {
						type: "text",
						text: ""
					};
					activeBlocks.set(blockIndex, {
						type: "text",
						accumulated: initial
					});
					yield {
						event: "content-block-start",
						index: blockIndex,
						content: initial
					};
				}
				const block = activeBlocks.get(blockIndex);
				block.accumulated = {
					...block.accumulated,
					text: (block.accumulated.text ?? "") + content
				};
				yield {
					event: "content-block-delta",
					index: blockIndex,
					delta: {
						type: "text-delta",
						text: content
					}
				};
			}
		} else if (Array.isArray(content)) for (const part of content) {
			const blockIndex = typeof part.index === "number" ? part.index : activeBlocks.size;
			if (!activeBlocks.has(blockIndex)) {
				activeBlocks.set(blockIndex, {
					type: part.type,
					accumulated: { ...part }
				});
				yield {
					event: "content-block-start",
					index: blockIndex,
					content: { ...part }
				};
			} else {
				const block = activeBlocks.get(blockIndex);
				const delta = contentBlockToDelta(part);
				block.accumulated = applyDeltaToBlock(block.accumulated, delta);
				yield {
					event: "content-block-delta",
					index: blockIndex,
					delta
				};
			}
		}
		if (require_ai.AIMessageChunk.isInstance(msg) && msg.tool_call_chunks && msg.tool_call_chunks.length > 0) for (const toolChunk of msg.tool_call_chunks) {
			const blockIndex = typeof toolChunk.index === "number" ? toolChunk.index : activeBlocks.size;
			if (!activeBlocks.has(blockIndex)) {
				const initial = {
					type: "tool_call_chunk",
					id: toolChunk.id,
					name: toolChunk.name,
					args: "",
					index: blockIndex
				};
				activeBlocks.set(blockIndex, {
					type: "tool_call_chunk",
					accumulated: initial
				});
				yield {
					event: "content-block-start",
					index: blockIndex,
					content: initial
				};
			}
			const acc = activeBlocks.get(blockIndex).accumulated;
			if (toolChunk.id != null) acc.id = toolChunk.id;
			if (toolChunk.name != null) acc.name = toolChunk.name;
			acc.args = (acc.args ?? "") + (toolChunk.args ?? "");
			yield {
				event: "content-block-delta",
				index: blockIndex,
				delta: {
					type: "block-delta",
					fields: {
						type: "tool_call_chunk",
						..."id" in acc && acc.id != null ? { id: acc.id } : {},
						..."name" in acc && acc.name != null ? { name: acc.name } : {},
						args: acc.args
					}
				}
			};
		}
		const audioPayload = getAudioPayload(msg);
		if (audioPayload != null) {
			if (audioStream == null) {
				const index = nextBlockIndex(activeBlocks);
				audioStream = {
					index,
					id: audioPayload.id,
					mimeType: audioPayload.mimeType,
					transcript: ""
				};
				const initial = {
					type: "audio",
					...audioPayload.id != null ? { id: audioPayload.id } : {},
					...audioPayload.url != null ? { url: audioPayload.url } : {},
					data: "",
					mimeType: audioPayload.mimeType
				};
				activeBlocks.set(index, {
					type: "audio",
					accumulated: initial
				});
				yield {
					event: "content-block-start",
					index,
					content: initial
				};
			}
			const activeAudio = activeBlocks.get(audioStream.index);
			if (activeAudio != null) {
				const accumulated = activeAudio.accumulated;
				if (audioPayload.id != null && audioStream.id == null) {
					audioStream.id = audioPayload.id;
					accumulated.id = audioPayload.id;
				}
				if (audioPayload.transcript != null) {
					audioStream.transcript += audioPayload.transcript;
					accumulated.transcript = audioStream.transcript;
					yield {
						event: "content-block-delta",
						index: audioStream.index,
						delta: {
							type: "block-delta",
							fields: {
								type: "audio",
								transcript: audioStream.transcript
							}
						}
					};
				}
				if (audioPayload.data != null && audioPayload.data.length > 0) {
					accumulated.data = (accumulated.data ?? "") + audioPayload.data;
					yield {
						event: "content-block-delta",
						index: audioStream.index,
						delta: {
							type: "data-delta",
							data: audioPayload.data,
							encoding: "base64"
						}
					};
				}
			}
		}
		for (const imageBlock of extractImageBlocksFromToolOutputs(msg)) {
			const imageRecord = imageBlock;
			const imageKey = imageRecord.id ?? imageRecord.url ?? (imageRecord.data != null ? `${imageRecord.data.length}:${imageRecord.data.slice(0, 32)}` : void 0);
			if (imageKey != null && emittedImageKeys.has(imageKey)) continue;
			if (imageKey != null) emittedImageKeys.add(imageKey);
			const index = nextBlockIndex(activeBlocks);
			activeBlocks.set(index, {
				type: "image",
				accumulated: imageBlock
			});
			yield {
				event: "content-block-start",
				index,
				content: imageBlock
			};
		}
		if (!usageHandledInStart && require_ai.AIMessageChunk.isInstance(msg) && msg.usage_metadata) {
			const chunkUsage = msg.usage_metadata;
			if (!lastUsage) lastUsage = { ...chunkUsage };
			else lastUsage = {
				input_tokens: lastUsage.input_tokens + chunkUsage.input_tokens,
				output_tokens: lastUsage.output_tokens + chunkUsage.output_tokens,
				total_tokens: lastUsage.total_tokens + chunkUsage.total_tokens
			};
			yield {
				event: "usage",
				usage: { ...lastUsage }
			};
		}
	}
	for (const [index, block] of activeBlocks) yield {
		event: "content-block-finish",
		index,
		content: finalizeContentBlock(block.accumulated)
	};
	yield {
		event: "message-finish",
		reason: "stop",
		...lastUsage ? { usage: lastUsage } : {}
	};
}
/**
* Apply a typed delta to an accumulated content block.
* @internal
*/
function applyDeltaToBlock(block, delta) {
	switch (delta.type) {
		case "text-delta":
			if (block.type === "text") return {
				...block,
				text: (block.text ?? "") + delta.text
			};
			return block;
		case "reasoning-delta":
			if (block.type === "thinking") return {
				...block,
				thinking: (block.thinking ?? "") + delta.reasoning
			};
			if (block.type === "reasoning") return {
				...block,
				reasoning: (block.reasoning ?? "") + delta.reasoning
			};
			return block;
		case "data-delta": return {
			...block,
			data: (block.data ?? "") + delta.data
		};
		case "block-delta": return {
			...block,
			...delta.fields
		};
		default: throw new Error(`Unknown delta type: ${JSON.stringify(delta)}`);
	}
}
function contentBlockToDelta(block) {
	if (block.type === "text") return {
		type: "text-delta",
		text: block.text
	};
	if (block.type === "reasoning") return {
		type: "reasoning-delta",
		reasoning: block.reasoning
	};
	if (block.type === "thinking" && typeof block.thinking === "string") return {
		type: "reasoning-delta",
		reasoning: block.thinking
	};
	if (typeof block.data === "string") return {
		type: "data-delta",
		data: block.data,
		encoding: "base64"
	};
	if (typeof block.type === "string") return {
		type: "block-delta",
		fields: { ...block }
	};
	throw new Error(`Unsupported content block delta: ${JSON.stringify(block)}`);
}
/**
* Finalize a content block for the finish event.
* For tool calls, parse the accumulated JSON args string.
*/
function finalizeContentBlock(block) {
	if (block.type === "tool_call_chunk") {
		const chunk = block;
		let parsedArgs;
		try {
			parsedArgs = JSON.parse(chunk.args ?? "{}");
		} catch {
			return {
				type: "invalid_tool_call",
				id: chunk.id,
				name: chunk.name,
				args: chunk.args,
				error: "Failed to parse tool call arguments as JSON"
			};
		}
		return {
			type: "tool_call",
			id: chunk.id,
			name: chunk.name,
			args: parsedArgs
		};
	}
	return block;
}
//#endregion
Object.defineProperty(exports, "compat_exports", {
	enumerable: true,
	get: function() {
		return compat_exports;
	}
});
exports.convertChunksToEvents = convertChunksToEvents;
exports.finalizeContentBlock = finalizeContentBlock;

//# sourceMappingURL=compat.cjs.map