const require_misc = require("../utils/misc.cjs");
const require_output = require("../utils/output.cjs");
let _langchain_core_messages = require("@langchain/core/messages");
let _langchain_core_output_parsers_openai_tools = require("@langchain/core/output_parsers/openai_tools");
//#region src/converters/completions.ts
/**
* @deprecated This converter is an internal detail of the OpenAI provider. Do not use it directly. This will be revisited in a future release.
*/
const completionsApiContentBlockConverter = {
	providerName: "ChatOpenAI",
	fromStandardTextBlock(block) {
		return {
			type: "text",
			text: block.text
		};
	},
	fromStandardImageBlock(block) {
		if (block.source_type === "url") return {
			type: "image_url",
			image_url: {
				url: block.url,
				...block.metadata?.detail ? { detail: block.metadata.detail } : {}
			}
		};
		if (block.source_type === "base64") return {
			type: "image_url",
			image_url: {
				url: `data:${block.mime_type ?? ""};base64,${block.data}`,
				...block.metadata?.detail ? { detail: block.metadata.detail } : {}
			}
		};
		throw new Error(`Image content blocks with source_type ${block.source_type} are not supported for ChatOpenAI`);
	},
	fromStandardAudioBlock(block) {
		if (block.source_type === "url") {
			const data = (0, _langchain_core_messages.parseBase64DataUrl)({ dataUrl: block.url });
			if (!data) throw new Error(`URL audio blocks with source_type ${block.source_type} must be formatted as a data URL for ChatOpenAI`);
			const rawMimeType = data.mime_type || block.mime_type || "";
			let mimeType;
			try {
				mimeType = (0, _langchain_core_messages.parseMimeType)(rawMimeType);
			} catch {
				throw new Error(`Audio blocks with source_type ${block.source_type} must have mime type of audio/wav or audio/mp3`);
			}
			if (mimeType.type !== "audio" || mimeType.subtype !== "wav" && mimeType.subtype !== "mp3") throw new Error(`Audio blocks with source_type ${block.source_type} must have mime type of audio/wav or audio/mp3`);
			return {
				type: "input_audio",
				input_audio: {
					format: mimeType.subtype,
					data: data.data
				}
			};
		}
		if (block.source_type === "base64") {
			let mimeType;
			try {
				mimeType = (0, _langchain_core_messages.parseMimeType)(block.mime_type ?? "");
			} catch {
				throw new Error(`Audio blocks with source_type ${block.source_type} must have mime type of audio/wav or audio/mp3`);
			}
			if (mimeType.type !== "audio" || mimeType.subtype !== "wav" && mimeType.subtype !== "mp3") throw new Error(`Audio blocks with source_type ${block.source_type} must have mime type of audio/wav or audio/mp3`);
			return {
				type: "input_audio",
				input_audio: {
					format: mimeType.subtype,
					data: block.data
				}
			};
		}
		throw new Error(`Audio content blocks with source_type ${block.source_type} are not supported for ChatOpenAI`);
	},
	fromStandardFileBlock(block) {
		if (block.source_type === "url") {
			const data = (0, _langchain_core_messages.parseBase64DataUrl)({ dataUrl: block.url });
			const filename = require_misc.getRequiredFilenameFromMetadata(block);
			if (!data) throw new Error(`URL file blocks with source_type ${block.source_type} must be formatted as a data URL for ChatOpenAI`);
			return {
				type: "file",
				file: {
					file_data: block.url,
					filename
				}
			};
		}
		if (block.source_type === "base64") {
			const filename = require_misc.getRequiredFilenameFromMetadata(block);
			return {
				type: "file",
				file: {
					file_data: `data:${block.mime_type ?? ""};base64,${block.data}`,
					filename
				}
			};
		}
		if (block.source_type === "id") return {
			type: "file",
			file: { file_id: block.id }
		};
		throw new Error(`File content blocks with source_type ${block.source_type} are not supported for ChatOpenAI`);
	}
};
/**
* Converts an OpenAI Chat Completions API message to a LangChain BaseMessage.
*
* This converter transforms messages from OpenAI's Chat Completions API format into
* LangChain's internal message representation, handling various message types and
* preserving metadata, tool calls, and other relevant information.
*
* @remarks
* The converter handles the following message roles:
* - `assistant`: Converted to {@link AIMessage} with support for tool calls, function calls,
*   audio content, and multi-modal outputs
* - Other roles: Converted to generic {@link ChatMessage}
*
* For assistant messages, the converter:
* - Parses and validates tool calls, separating valid and invalid calls
* - Preserves function call information in additional_kwargs
* - Includes usage statistics and system fingerprint in response_metadata
* - Handles multi-modal content (text, images, audio)
* - Optionally includes the raw API response for debugging
*
* @param params - Conversion parameters
* @param params.message - The OpenAI chat completion message to convert
* @param params.rawResponse - The complete raw response from OpenAI's API, used to extract
*   metadata like model name, usage statistics, and system fingerprint
* @param params.includeRawResponse - If true, includes the raw OpenAI response in the
*   message's additional_kwargs under the `__raw_response` key. Useful for debugging
*   or accessing provider-specific fields. Defaults to false.
*
* @returns A LangChain BaseMessage instance:
*   - {@link AIMessage} for assistant messages with tool calls, metadata, and content
*   - {@link ChatMessage} for all other message types
*
* @example
* ```typescript
* const baseMessage = convertCompletionsMessageToBaseMessage({
*   message: {
*     role: "assistant",
*     content: "Hello! How can I help you?",
*     tool_calls: [
*       {
*         id: "call_123",
*         type: "function",
*         function: { name: "get_weather", arguments: '{"location":"NYC"}' }
*       }
*     ]
*   },
*   rawResponse: completionResponse,
*   includeRawResponse: true
* });
* // Returns an AIMessage with parsed tool calls and metadata
* ```
*
* @throws {Error} If tool call parsing fails, the invalid tool call is captured in
*   the `invalid_tool_calls` array rather than throwing an error
*
*/
const convertCompletionsMessageToBaseMessage = ({ message, rawResponse, includeRawResponse }) => {
	const rawToolCalls = message.tool_calls;
	const providerReasoningContent = message.reasoning_content;
	switch (message.role) {
		case "assistant": {
			const toolCalls = [];
			const invalidToolCalls = [];
			for (const rawToolCall of rawToolCalls ?? []) try {
				toolCalls.push((0, _langchain_core_output_parsers_openai_tools.parseToolCall)(rawToolCall, { returnId: true }));
			} catch (e) {
				invalidToolCalls.push((0, _langchain_core_output_parsers_openai_tools.makeInvalidToolCall)(rawToolCall, e.message));
			}
			const additional_kwargs = {
				function_call: message.function_call,
				tool_calls: rawToolCalls
			};
			if (includeRawResponse !== void 0) additional_kwargs.__raw_response = rawResponse;
			if (providerReasoningContent !== void 0) additional_kwargs.reasoning_content = providerReasoningContent;
			const response_metadata = {
				model_provider: "openai",
				model_name: rawResponse.model,
				...rawResponse.system_fingerprint ? {
					usage: { ...rawResponse.usage },
					system_fingerprint: rawResponse.system_fingerprint
				} : {}
			};
			if (message.audio) additional_kwargs.audio = message.audio;
			return new _langchain_core_messages.AIMessage({
				content: require_output.handleMultiModalOutput(message.content || "", rawResponse.choices?.[0]?.message),
				tool_calls: toolCalls,
				invalid_tool_calls: invalidToolCalls,
				additional_kwargs,
				response_metadata,
				id: rawResponse.id
			});
		}
		default: return new _langchain_core_messages.ChatMessage(message.content || "", message.role ?? "unknown");
	}
};
/**
* Converts an OpenAI Chat Completions API delta (streaming chunk) to a LangChain BaseMessageChunk.
*
* This converter is used during streaming responses to transform incremental updates from OpenAI's
* Chat Completions API into LangChain message chunks. It handles various message types, tool calls,
* function calls, audio content, and role-specific message chunk creation.
*
* @param params - Conversion parameters
* @param params.delta - The delta object from an OpenAI streaming chunk containing incremental
*   message updates. May include content, role, tool_calls, function_call, audio, etc.
* @param params.rawResponse - The complete raw ChatCompletionChunk response from OpenAI,
*   containing metadata like model info, usage stats, and the delta
* @param params.includeRawResponse - Optional flag to include the raw OpenAI response in the
*   message chunk's additional_kwargs. Useful for debugging or accessing provider-specific data
* @param params.defaultRole - Optional default role to use if the delta doesn't specify one.
*   Typically used to maintain role consistency across chunks in a streaming response
*
* @returns A BaseMessageChunk subclass appropriate for the message role:
*   - HumanMessageChunk for "user" role
*   - AIMessageChunk for "assistant" role (includes tool call chunks)
*   - SystemMessageChunk for "system" or "developer" roles
*   - FunctionMessageChunk for "function" role
*   - ToolMessageChunk for "tool" role
*   - ChatMessageChunk for any other role
*
* @example
* Basic streaming text chunk:
* ```typescript
* const chunk = convertCompletionsDeltaToBaseMessageChunk({
*   delta: { role: "assistant", content: "Hello" },
*   rawResponse: { id: "chatcmpl-123", model: "gpt-4", ... }
* });
* // Returns: AIMessageChunk with content "Hello"
* ```
*
* @example
* Streaming chunk with tool call:
* ```typescript
* const chunk = convertCompletionsDeltaToBaseMessageChunk({
*   delta: {
*     role: "assistant",
*     tool_calls: [{
*       index: 0,
*       id: "call_123",
*       function: { name: "get_weather", arguments: '{"location":' }
*     }]
*   },
*   rawResponse: { id: "chatcmpl-123", ... }
* });
* // Returns: AIMessageChunk with tool_call_chunks containing partial tool call data
* ```
*
* @remarks
* - Tool calls are converted to ToolCallChunk objects with incremental data
* - Audio content includes the chunk index from the raw response
* - The "developer" role is mapped to SystemMessageChunk with a special marker
* - Response metadata includes model provider info and usage statistics
* - Function calls and tool calls are stored in additional_kwargs for compatibility
*/
const convertCompletionsDeltaToBaseMessageChunk = ({ delta, rawResponse, includeRawResponse, defaultRole }) => {
	const role = delta.role ?? defaultRole;
	const content = delta.content ?? "";
	let additional_kwargs;
	if (delta.function_call) additional_kwargs = { function_call: delta.function_call };
	else if (delta.tool_calls) additional_kwargs = { tool_calls: delta.tool_calls };
	else additional_kwargs = {};
	if (includeRawResponse) additional_kwargs.__raw_response = rawResponse;
	if (delta.reasoning_content !== void 0) additional_kwargs.reasoning_content = delta.reasoning_content;
	if (delta.audio) additional_kwargs.audio = {
		...delta.audio,
		index: rawResponse.choices[0].index
	};
	const response_metadata = {
		model_provider: "openai",
		usage: { ...rawResponse.usage }
	};
	if (role === "user") return new _langchain_core_messages.HumanMessageChunk({
		content,
		response_metadata
	});
	else if (role === "assistant") {
		const toolCallChunks = [];
		if (Array.isArray(delta.tool_calls)) for (const rawToolCall of delta.tool_calls) toolCallChunks.push({
			name: rawToolCall.function?.name,
			args: rawToolCall.function?.arguments,
			id: rawToolCall.id,
			index: rawToolCall.index,
			type: "tool_call_chunk"
		});
		return new _langchain_core_messages.AIMessageChunk({
			content,
			tool_call_chunks: toolCallChunks,
			additional_kwargs,
			id: rawResponse.id,
			response_metadata
		});
	} else if (role === "system") return new _langchain_core_messages.SystemMessageChunk({
		content,
		response_metadata
	});
	else if (role === "developer") return new _langchain_core_messages.SystemMessageChunk({
		content,
		response_metadata,
		additional_kwargs: { __openai_role__: "developer" }
	});
	else if (role === "function") return new _langchain_core_messages.FunctionMessageChunk({
		content,
		additional_kwargs,
		name: delta.name,
		response_metadata
	});
	else if (role === "tool") return new _langchain_core_messages.ToolMessageChunk({
		content,
		additional_kwargs,
		tool_call_id: delta.tool_call_id,
		response_metadata
	});
	else return new _langchain_core_messages.ChatMessageChunk({
		content,
		role,
		response_metadata
	});
};
/**
* Converts a standard LangChain content block to an OpenAI Completions API content part.
*
* This converter transforms LangChain's standardized content blocks (image, audio, file)
* into the format expected by OpenAI's Chat Completions API. It handles various content
* types including images (URL or base64), audio (base64), and files (data or file ID).
*
* @param block - The standard content block to convert. Can be an image, audio, or file block.
*
* @returns An OpenAI Chat Completions content part object, or undefined if the block
*   cannot be converted (e.g., missing required data).
*
* @example
* Image with URL:
* ```typescript
* const block = { type: "image", url: "https://example.com/image.jpg" };
* const part = convertStandardContentBlockToCompletionsContentPart(block);
* // Returns: { type: "image_url", image_url: { url: "https://example.com/image.jpg" } }
* ```
*
* @example
* Image with base64 data:
* ```typescript
* const block = { type: "image", data: "iVBORw0KGgo...", mimeType: "image/png" };
* const part = convertStandardContentBlockToCompletionsContentPart(block);
* // Returns: { type: "image_url", image_url: { url: "data:image/png;base64,iVBORw0KGgo..." } }
* ```
*/
const convertStandardContentBlockToCompletionsContentPart = (block) => {
	if (block.type === "image") {
		if (block.url) return {
			type: "image_url",
			image_url: { url: block.url }
		};
		else if (block.data) return {
			type: "image_url",
			image_url: { url: `data:${block.mimeType};base64,${block.data}` }
		};
	}
	if (block.type === "audio") {
		if (block.data) {
			const format = (0, _langchain_core_messages.iife)(() => {
				const [, format] = block.mimeType.split("/");
				if (format === "wav" || format === "mp3") return format;
				return "wav";
			});
			return {
				type: "input_audio",
				input_audio: {
					data: block.data.toString(),
					format
				}
			};
		}
	}
	if (block.type === "file") {
		if (block.data) {
			const filename = require_misc.getRequiredFilenameFromMetadata(block);
			return {
				type: "file",
				file: {
					file_data: `data:${block.mimeType};base64,${block.data}`,
					filename
				}
			};
		}
		if (block.fileId) return {
			type: "file",
			file: { file_id: block.fileId }
		};
	}
};
/**
* Converts a LangChain BaseMessage with standard content blocks to an OpenAI Chat Completions API message parameter.
*
* This converter transforms LangChain's standardized message format (using contentBlocks) into the format
* expected by OpenAI's Chat Completions API. It handles role mapping, content filtering, and multi-modal
* content conversion for various message types.
*
* @remarks
* The converter performs the following transformations:
* - Maps LangChain message roles to OpenAI API roles (user, assistant, system, developer, tool, function)
* - For reasoning models, automatically converts "system" role to "developer" role
* - Filters content blocks based on message role (most roles only include text blocks)
* - For user messages, converts multi-modal content blocks (images, audio, files) to OpenAI format
* - Preserves tool call IDs for tool messages and function names for function messages
*
* Role-specific behavior:
* - **developer**: Returns only text content blocks (used for reasoning models)
* - **system**: Returns only text content blocks
* - **assistant**: Returns only text content blocks
* - **tool**: Returns only text content blocks with tool_call_id preserved
* - **function**: Returns text content blocks joined as a single string with function name
* - **user** (default): Returns multi-modal content including text, images, audio, and files
*
* @param params - Conversion parameters
* @param params.message - The LangChain BaseMessage to convert. Must have contentBlocks property
*   containing an array of standard content blocks (text, image, audio, file, etc.)
* @param params.model - Optional model name. Used to determine if special role mapping is needed
*   (e.g., "system" -> "developer" for reasoning models like o1)
*
* @returns An OpenAI ChatCompletionMessageParam object formatted for the Chat Completions API.
*   The structure varies by role:
*   - Developer/System/Assistant: `{ role, content: TextBlock[] }`
*   - Tool: `{ role: "tool", tool_call_id, content: TextBlock[] }`
*   - Function: `{ role: "function", name, content: string }`
*   - User: `{ role: "user", content: Array<TextPart | ImagePart | AudioPart | FilePart> }`
*
* @example
* Simple text message:
* ```typescript
* const message = new HumanMessage({
*   content: [{ type: "text", text: "Hello!" }]
* });
* const param = convertStandardContentMessageToCompletionsMessage({ message });
* // Returns: { role: "user", content: [{ type: "text", text: "Hello!" }] }
* ```
*
* @example
* Multi-modal user message with image:
* ```typescript
* const message = new HumanMessage({
*   content: [
*     { type: "text", text: "What's in this image?" },
*     { type: "image", url: "https://example.com/image.jpg" }
*   ]
* });
* const param = convertStandardContentMessageToCompletionsMessage({ message });
* // Returns: {
* //   role: "user",
* //   content: [
* //     { type: "text", text: "What's in this image?" },
* //     { type: "image_url", image_url: { url: "https://example.com/image.jpg" } }
* //   ]
* // }
* ```
*/
const convertStandardContentMessageToCompletionsMessage = ({ message, model }) => {
	let role = require_misc.messageToOpenAIRole(message);
	if (role === "system" && require_misc.isReasoningModel(model)) role = "developer";
	if (role === "developer") return {
		role: "developer",
		content: message.contentBlocks.filter((block) => block.type === "text")
	};
	else if (role === "system") return {
		role: "system",
		content: message.contentBlocks.filter((block) => block.type === "text")
	};
	else if (role === "assistant") {
		const completionParam = {
			role: "assistant",
			content: message.contentBlocks.filter((block) => block.type === "text")
		};
		if (_langchain_core_messages.AIMessage.isInstance(message) && !!message.tool_calls?.length) completionParam.tool_calls = message.tool_calls.map(_langchain_core_output_parsers_openai_tools.convertLangChainToolCallToOpenAI);
		else if (message.additional_kwargs.tool_calls != null) completionParam.tool_calls = message.additional_kwargs.tool_calls;
		return completionParam;
	} else if (role === "tool" && _langchain_core_messages.ToolMessage.isInstance(message)) return {
		role: "tool",
		tool_call_id: message.tool_call_id,
		content: message.contentBlocks.filter((block) => block.type === "text")
	};
	else if (role === "function") return {
		role: "function",
		name: message.name ?? "",
		content: message.contentBlocks.filter((block) => block.type === "text").join("")
	};
	function* iterateUserContent(blocks) {
		for (const block of blocks) {
			if (block.type === "text") yield {
				type: "text",
				text: block.text
			};
			const data = convertStandardContentBlockToCompletionsContentPart(block);
			if (data) yield data;
		}
	}
	return {
		role: "user",
		content: Array.from(iterateUserContent(message.contentBlocks))
	};
};
/**
* Converts an array of LangChain BaseMessages to OpenAI Chat Completions API message parameters.
*
* This converter transforms LangChain's internal message representation into the format required
* by OpenAI's Chat Completions API. It handles various message types, roles, content formats,
* tool calls, function calls, audio messages, and special model-specific requirements.
*
* @remarks
* The converter performs several key transformations:
* - Maps LangChain message types to OpenAI roles (user, assistant, system, tool, function, developer)
* - Converts standard content blocks (v1 format) using a specialized converter
* - Handles multimodal content including text, images, audio, and data blocks
* - Preserves tool calls and function calls with proper formatting
* - Applies model-specific role mappings (e.g., "system" → "developer" for reasoning models)
* - Splits audio messages into separate message parameters when needed
*
* @param params - Conversion parameters
* @param params.messages - Array of LangChain BaseMessages to convert. Can include any message
*   type: HumanMessage, AIMessage, SystemMessage, ToolMessage, FunctionMessage, etc.
* @param params.model - Optional model name used to determine if special role mapping is needed.
*   For reasoning models (o1, o3, etc.), "system" role is converted to "developer" role.
*
* @returns Array of ChatCompletionMessageParam objects formatted for OpenAI's Chat Completions API.
*   Some messages may be split into multiple parameters (e.g., audio messages).
*
* @example
* Basic message conversion:
* ```typescript
* const messages = [
*   new HumanMessage("What's the weather like?"),
*   new AIMessage("Let me check that for you.")
* ];
*
* const params = convertMessagesToCompletionsMessageParams({
*   messages,
*   model: "gpt-4"
* });
* // Returns:
* // [
* //   { role: "user", content: "What's the weather like?" },
* //   { role: "assistant", content: "Let me check that for you." }
* // ]
* ```
*
* @example
* Message with tool calls:
* ```typescript
* const messages = [
*   new AIMessage({
*     content: "",
*     tool_calls: [{
*       id: "call_123",
*       name: "get_weather",
*       args: { location: "San Francisco" }
*     }]
*   })
* ];
*
* const params = convertMessagesToCompletionsMessageParams({ messages });
* // Returns:
* // [{
* //   role: "assistant",
* //   content: "",
* //   tool_calls: [{
* //     id: "call_123",
* //     type: "function",
* //     function: { name: "get_weather", arguments: '{"location":"San Francisco"}' }
* //   }]
* // }]
* ```
*/
const convertMessagesToCompletionsMessageParams = ({ messages, model }) => {
	return messages.flatMap((message) => {
		if ("output_version" in message.response_metadata && message.response_metadata?.output_version === "v1") return convertStandardContentMessageToCompletionsMessage({ message });
		let role = require_misc.messageToOpenAIRole(message);
		if (role === "system" && require_misc.isReasoningModel(model)) role = "developer";
		const content = typeof message.content === "string" ? message.content : message.content.flatMap((m) => {
			if ((0, _langchain_core_messages.isDataContentBlock)(m)) return (0, _langchain_core_messages.convertToProviderContentBlock)(m, completionsApiContentBlockConverter);
			if (typeof m === "object" && m !== null && "type" in m && (m.type === "tool_use" || m.type === "tool_call" || m.type === "reasoning" || m.type === "reasoning_content" || m.type === "thinking")) return [];
			return m;
		});
		const completionParam = {
			role,
			content
		};
		if (message.name != null) completionParam.name = message.name;
		if (message.additional_kwargs.function_call != null) completionParam.function_call = message.additional_kwargs.function_call;
		if (_langchain_core_messages.AIMessage.isInstance(message) && !!message.tool_calls?.length) completionParam.tool_calls = message.tool_calls.map(_langchain_core_output_parsers_openai_tools.convertLangChainToolCallToOpenAI);
		else {
			if (message.additional_kwargs.tool_calls != null) completionParam.tool_calls = message.additional_kwargs.tool_calls;
			if (_langchain_core_messages.ToolMessage.isInstance(message) && message.tool_call_id != null) completionParam.tool_call_id = message.tool_call_id;
		}
		if (message.additional_kwargs.audio && typeof message.additional_kwargs.audio === "object" && "id" in message.additional_kwargs.audio) return [completionParam, {
			role: "assistant",
			audio: { id: message.additional_kwargs.audio.id }
		}];
		return completionParam;
	});
};
//#endregion
exports.completionsApiContentBlockConverter = completionsApiContentBlockConverter;
exports.convertCompletionsDeltaToBaseMessageChunk = convertCompletionsDeltaToBaseMessageChunk;
exports.convertCompletionsMessageToBaseMessage = convertCompletionsMessageToBaseMessage;
exports.convertMessagesToCompletionsMessageParams = convertMessagesToCompletionsMessageParams;
exports.convertStandardContentBlockToCompletionsContentPart = convertStandardContentBlockToCompletionsContentPart;
exports.convertStandardContentMessageToCompletionsMessage = convertStandardContentMessageToCompletionsMessage;

//# sourceMappingURL=completions.cjs.map