@langchain/openai 1.0.0-alpha.3 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (139) hide show
  1. package/CHANGELOG.md +53 -0
  2. package/dist/azure/chat_models/common.cjs +130 -0
  3. package/dist/azure/chat_models/common.cjs.map +1 -0
  4. package/dist/azure/chat_models/common.d.cts +16 -0
  5. package/dist/azure/chat_models/common.d.cts.map +1 -0
  6. package/dist/azure/chat_models/common.d.ts +16 -0
  7. package/dist/azure/chat_models/common.d.ts.map +1 -0
  8. package/dist/azure/chat_models/common.js +124 -0
  9. package/dist/azure/chat_models/common.js.map +1 -0
  10. package/dist/azure/chat_models/completions.cjs +50 -0
  11. package/dist/azure/chat_models/completions.cjs.map +1 -0
  12. package/dist/azure/chat_models/completions.d.cts +29 -0
  13. package/dist/azure/chat_models/completions.d.cts.map +1 -0
  14. package/dist/azure/chat_models/completions.d.ts +29 -0
  15. package/dist/azure/chat_models/completions.d.ts.map +1 -0
  16. package/dist/azure/chat_models/completions.js +50 -0
  17. package/dist/azure/chat_models/completions.js.map +1 -0
  18. package/dist/azure/{chat_models.cjs → chat_models/index.cjs} +14 -214
  19. package/dist/azure/chat_models/index.cjs.map +1 -0
  20. package/dist/azure/{chat_models.d.ts → chat_models/index.d.cts} +6 -11
  21. package/dist/azure/chat_models/index.d.cts.map +1 -0
  22. package/dist/azure/{chat_models.d.cts → chat_models/index.d.ts} +6 -11
  23. package/dist/azure/chat_models/index.d.ts.map +1 -0
  24. package/dist/azure/{chat_models.js → chat_models/index.js} +6 -205
  25. package/dist/azure/chat_models/index.js.map +1 -0
  26. package/dist/azure/chat_models/responses.cjs +50 -0
  27. package/dist/azure/chat_models/responses.cjs.map +1 -0
  28. package/dist/azure/chat_models/responses.d.cts +29 -0
  29. package/dist/azure/chat_models/responses.d.cts.map +1 -0
  30. package/dist/azure/chat_models/responses.d.ts +29 -0
  31. package/dist/azure/chat_models/responses.d.ts.map +1 -0
  32. package/dist/azure/chat_models/responses.js +50 -0
  33. package/dist/azure/chat_models/responses.js.map +1 -0
  34. package/dist/chat_models/base.cjs +533 -0
  35. package/dist/chat_models/base.cjs.map +1 -0
  36. package/dist/chat_models/base.d.cts +272 -0
  37. package/dist/chat_models/base.d.cts.map +1 -0
  38. package/dist/chat_models/base.d.ts +272 -0
  39. package/dist/chat_models/base.d.ts.map +1 -0
  40. package/dist/chat_models/base.js +532 -0
  41. package/dist/chat_models/base.js.map +1 -0
  42. package/dist/chat_models/completions.cjs +258 -0
  43. package/dist/chat_models/completions.cjs.map +1 -0
  44. package/dist/chat_models/completions.d.cts +44 -0
  45. package/dist/chat_models/completions.d.cts.map +1 -0
  46. package/dist/chat_models/completions.d.ts +44 -0
  47. package/dist/chat_models/completions.d.ts.map +1 -0
  48. package/dist/chat_models/completions.js +257 -0
  49. package/dist/chat_models/completions.js.map +1 -0
  50. package/dist/chat_models/index.cjs +608 -0
  51. package/dist/chat_models/index.cjs.map +1 -0
  52. package/dist/chat_models/index.d.cts +593 -0
  53. package/dist/chat_models/index.d.cts.map +1 -0
  54. package/dist/chat_models/index.d.ts +593 -0
  55. package/dist/chat_models/index.d.ts.map +1 -0
  56. package/dist/chat_models/index.js +608 -0
  57. package/dist/chat_models/index.js.map +1 -0
  58. package/dist/chat_models/responses.cjs +189 -0
  59. package/dist/chat_models/responses.cjs.map +1 -0
  60. package/dist/chat_models/responses.d.cts +62 -0
  61. package/dist/chat_models/responses.d.cts.map +1 -0
  62. package/dist/chat_models/responses.d.ts +62 -0
  63. package/dist/chat_models/responses.d.ts.map +1 -0
  64. package/dist/chat_models/responses.js +188 -0
  65. package/dist/chat_models/responses.js.map +1 -0
  66. package/dist/converters/completions.cjs +603 -0
  67. package/dist/converters/completions.cjs.map +1 -0
  68. package/dist/converters/completions.js +599 -0
  69. package/dist/converters/completions.js.map +1 -0
  70. package/dist/converters/responses.cjs +892 -0
  71. package/dist/converters/responses.cjs.map +1 -0
  72. package/dist/converters/responses.js +889 -0
  73. package/dist/converters/responses.js.map +1 -0
  74. package/dist/index.cjs +14 -9
  75. package/dist/index.d.cts +9 -4
  76. package/dist/index.d.ts +9 -4
  77. package/dist/index.js +8 -4
  78. package/dist/langchain-core/dist/callbacks/base.d.ts +1 -0
  79. package/dist/langchain-core/dist/callbacks/manager.d.ts +2 -0
  80. package/dist/langchain-core/dist/language_models/base.d.ts +5 -0
  81. package/dist/langchain-core/dist/language_models/chat_models.d.cts +13 -0
  82. package/dist/langchain-core/dist/language_models/chat_models.d.cts.map +1 -0
  83. package/dist/langchain-core/dist/language_models/chat_models.d.ts +20 -0
  84. package/dist/langchain-core/dist/language_models/chat_models.d.ts.map +1 -0
  85. package/dist/langchain-core/dist/runnables/base.d.ts +7 -0
  86. package/dist/langchain-core/dist/runnables/graph.d.ts +1 -0
  87. package/dist/langchain-core/dist/runnables/types.d.ts +2 -0
  88. package/dist/langchain-core/dist/tools/types.d.ts +5 -0
  89. package/dist/langchain-core/dist/tracers/base.d.ts +2 -0
  90. package/dist/langchain-core/dist/tracers/event_stream.d.ts +1 -0
  91. package/dist/langchain-core/dist/tracers/log_stream.d.ts +3 -0
  92. package/dist/langchain-core/dist/utils/types/index.d.ts +1 -0
  93. package/dist/langchain-core/dist/utils/types/zod.d.ts +1 -0
  94. package/dist/llms.cjs +1 -1
  95. package/dist/llms.d.cts +1 -1
  96. package/dist/llms.js +1 -1
  97. package/dist/utils/errors.cjs +1 -1
  98. package/dist/utils/errors.cjs.map +1 -1
  99. package/dist/utils/errors.js +1 -1
  100. package/dist/utils/errors.js.map +1 -1
  101. package/dist/utils/misc.js +2 -2
  102. package/dist/utils/misc.js.map +1 -1
  103. package/dist/utils/output.cjs +0 -12
  104. package/dist/utils/output.cjs.map +1 -1
  105. package/dist/utils/output.js +1 -12
  106. package/dist/utils/output.js.map +1 -1
  107. package/dist/utils/prompts.cjs +2 -2
  108. package/dist/utils/prompts.cjs.map +1 -1
  109. package/dist/utils/prompts.d.cts +1 -0
  110. package/dist/utils/prompts.d.cts.map +1 -1
  111. package/dist/utils/prompts.d.ts +1 -0
  112. package/dist/utils/prompts.d.ts.map +1 -1
  113. package/dist/utils/prompts.js +2 -2
  114. package/dist/utils/prompts.js.map +1 -1
  115. package/package.json +10 -7
  116. package/dist/azure/chat_models.cjs.map +0 -1
  117. package/dist/azure/chat_models.d.cts.map +0 -1
  118. package/dist/azure/chat_models.d.ts.map +0 -1
  119. package/dist/azure/chat_models.js.map +0 -1
  120. package/dist/chat_models.cjs +0 -1967
  121. package/dist/chat_models.cjs.map +0 -1
  122. package/dist/chat_models.d.cts +0 -933
  123. package/dist/chat_models.d.cts.map +0 -1
  124. package/dist/chat_models.d.ts +0 -933
  125. package/dist/chat_models.d.ts.map +0 -1
  126. package/dist/chat_models.js +0 -1963
  127. package/dist/chat_models.js.map +0 -1
  128. package/dist/utils/message_inputs.cjs +0 -139
  129. package/dist/utils/message_inputs.cjs.map +0 -1
  130. package/dist/utils/message_inputs.d.cts +0 -9
  131. package/dist/utils/message_inputs.d.cts.map +0 -1
  132. package/dist/utils/message_inputs.d.ts +0 -9
  133. package/dist/utils/message_inputs.d.ts.map +0 -1
  134. package/dist/utils/message_inputs.js +0 -137
  135. package/dist/utils/message_inputs.js.map +0 -1
  136. package/dist/utils/standard.cjs +0 -315
  137. package/dist/utils/standard.cjs.map +0 -1
  138. package/dist/utils/standard.js +0 -313
  139. package/dist/utils/standard.js.map +0 -1
@@ -0,0 +1,599 @@
1
+ import { isReasoningModel, messageToOpenAIRole } from "../utils/misc.js";
2
+ import { handleMultiModalOutput } from "../utils/output.js";
3
+ import { convertLangChainToolCallToOpenAI, makeInvalidToolCall, parseToolCall } from "@langchain/core/output_parsers/openai_tools";
4
+ import { AIMessage, AIMessageChunk, ChatMessage, ChatMessageChunk, FunctionMessageChunk, HumanMessageChunk, SystemMessageChunk, ToolMessage, ToolMessageChunk, convertToProviderContentBlock, iife, isDataContentBlock, parseBase64DataUrl, parseMimeType } from "@langchain/core/messages";
5
+
6
+ //#region src/converters/completions.ts
7
+ /**
8
+ * @deprecated This converter is an internal detail of the OpenAI provider. Do not use it directly. This will be revisited in a future release.
9
+ */
10
+ const completionsApiContentBlockConverter = {
11
+ providerName: "ChatOpenAI",
12
+ fromStandardTextBlock(block) {
13
+ return {
14
+ type: "text",
15
+ text: block.text
16
+ };
17
+ },
18
+ fromStandardImageBlock(block) {
19
+ if (block.source_type === "url") return {
20
+ type: "image_url",
21
+ image_url: {
22
+ url: block.url,
23
+ ...block.metadata?.detail ? { detail: block.metadata.detail } : {}
24
+ }
25
+ };
26
+ if (block.source_type === "base64") {
27
+ const url = `data:${block.mime_type ?? ""};base64,${block.data}`;
28
+ return {
29
+ type: "image_url",
30
+ image_url: {
31
+ url,
32
+ ...block.metadata?.detail ? { detail: block.metadata.detail } : {}
33
+ }
34
+ };
35
+ }
36
+ throw new Error(`Image content blocks with source_type ${block.source_type} are not supported for ChatOpenAI`);
37
+ },
38
+ fromStandardAudioBlock(block) {
39
+ if (block.source_type === "url") {
40
+ const data = parseBase64DataUrl({ dataUrl: block.url });
41
+ if (!data) throw new Error(`URL audio blocks with source_type ${block.source_type} must be formatted as a data URL for ChatOpenAI`);
42
+ const rawMimeType = data.mime_type || block.mime_type || "";
43
+ let mimeType;
44
+ try {
45
+ mimeType = parseMimeType(rawMimeType);
46
+ } catch {
47
+ throw new Error(`Audio blocks with source_type ${block.source_type} must have mime type of audio/wav or audio/mp3`);
48
+ }
49
+ 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`);
50
+ return {
51
+ type: "input_audio",
52
+ input_audio: {
53
+ format: mimeType.subtype,
54
+ data: data.data
55
+ }
56
+ };
57
+ }
58
+ if (block.source_type === "base64") {
59
+ let mimeType;
60
+ try {
61
+ mimeType = parseMimeType(block.mime_type ?? "");
62
+ } catch {
63
+ throw new Error(`Audio blocks with source_type ${block.source_type} must have mime type of audio/wav or audio/mp3`);
64
+ }
65
+ 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`);
66
+ return {
67
+ type: "input_audio",
68
+ input_audio: {
69
+ format: mimeType.subtype,
70
+ data: block.data
71
+ }
72
+ };
73
+ }
74
+ throw new Error(`Audio content blocks with source_type ${block.source_type} are not supported for ChatOpenAI`);
75
+ },
76
+ fromStandardFileBlock(block) {
77
+ if (block.source_type === "url") {
78
+ const data = parseBase64DataUrl({ dataUrl: block.url });
79
+ if (!data) throw new Error(`URL file blocks with source_type ${block.source_type} must be formatted as a data URL for ChatOpenAI`);
80
+ return {
81
+ type: "file",
82
+ file: {
83
+ file_data: block.url,
84
+ ...block.metadata?.filename || block.metadata?.name ? { filename: block.metadata?.filename || block.metadata?.name } : {}
85
+ }
86
+ };
87
+ }
88
+ if (block.source_type === "base64") return {
89
+ type: "file",
90
+ file: {
91
+ file_data: `data:${block.mime_type ?? ""};base64,${block.data}`,
92
+ ...block.metadata?.filename || block.metadata?.name || block.metadata?.title ? { filename: block.metadata?.filename || block.metadata?.name || block.metadata?.title } : {}
93
+ }
94
+ };
95
+ if (block.source_type === "id") return {
96
+ type: "file",
97
+ file: { file_id: block.id }
98
+ };
99
+ throw new Error(`File content blocks with source_type ${block.source_type} are not supported for ChatOpenAI`);
100
+ }
101
+ };
102
+ /**
103
+ * Converts an OpenAI Chat Completions API message to a LangChain BaseMessage.
104
+ *
105
+ * This converter transforms messages from OpenAI's Chat Completions API format into
106
+ * LangChain's internal message representation, handling various message types and
107
+ * preserving metadata, tool calls, and other relevant information.
108
+ *
109
+ * @remarks
110
+ * The converter handles the following message roles:
111
+ * - `assistant`: Converted to {@link AIMessage} with support for tool calls, function calls,
112
+ * audio content, and multi-modal outputs
113
+ * - Other roles: Converted to generic {@link ChatMessage}
114
+ *
115
+ * For assistant messages, the converter:
116
+ * - Parses and validates tool calls, separating valid and invalid calls
117
+ * - Preserves function call information in additional_kwargs
118
+ * - Includes usage statistics and system fingerprint in response_metadata
119
+ * - Handles multi-modal content (text, images, audio)
120
+ * - Optionally includes the raw API response for debugging
121
+ *
122
+ * @param params - Conversion parameters
123
+ * @param params.message - The OpenAI chat completion message to convert
124
+ * @param params.rawResponse - The complete raw response from OpenAI's API, used to extract
125
+ * metadata like model name, usage statistics, and system fingerprint
126
+ * @param params.includeRawResponse - If true, includes the raw OpenAI response in the
127
+ * message's additional_kwargs under the `__raw_response` key. Useful for debugging
128
+ * or accessing provider-specific fields. Defaults to false.
129
+ *
130
+ * @returns A LangChain BaseMessage instance:
131
+ * - {@link AIMessage} for assistant messages with tool calls, metadata, and content
132
+ * - {@link ChatMessage} for all other message types
133
+ *
134
+ * @example
135
+ * ```typescript
136
+ * const baseMessage = convertCompletionsMessageToBaseMessage({
137
+ * message: {
138
+ * role: "assistant",
139
+ * content: "Hello! How can I help you?",
140
+ * tool_calls: [
141
+ * {
142
+ * id: "call_123",
143
+ * type: "function",
144
+ * function: { name: "get_weather", arguments: '{"location":"NYC"}' }
145
+ * }
146
+ * ]
147
+ * },
148
+ * rawResponse: completionResponse,
149
+ * includeRawResponse: true
150
+ * });
151
+ * // Returns an AIMessage with parsed tool calls and metadata
152
+ * ```
153
+ *
154
+ * @throws {Error} If tool call parsing fails, the invalid tool call is captured in
155
+ * the `invalid_tool_calls` array rather than throwing an error
156
+ *
157
+ */
158
+ const convertCompletionsMessageToBaseMessage = ({ message, rawResponse, includeRawResponse }) => {
159
+ const rawToolCalls = message.tool_calls;
160
+ switch (message.role) {
161
+ case "assistant": {
162
+ const toolCalls = [];
163
+ const invalidToolCalls = [];
164
+ for (const rawToolCall of rawToolCalls ?? []) try {
165
+ toolCalls.push(parseToolCall(rawToolCall, { returnId: true }));
166
+ } catch (e) {
167
+ invalidToolCalls.push(makeInvalidToolCall(rawToolCall, e.message));
168
+ }
169
+ const additional_kwargs = {
170
+ function_call: message.function_call,
171
+ tool_calls: rawToolCalls
172
+ };
173
+ if (includeRawResponse !== void 0) additional_kwargs.__raw_response = rawResponse;
174
+ const response_metadata = {
175
+ model_provider: "openai",
176
+ model_name: rawResponse.model,
177
+ ...rawResponse.system_fingerprint ? {
178
+ usage: { ...rawResponse.usage },
179
+ system_fingerprint: rawResponse.system_fingerprint
180
+ } : {}
181
+ };
182
+ if (message.audio) additional_kwargs.audio = message.audio;
183
+ const content = handleMultiModalOutput(message.content || "", rawResponse.choices?.[0]?.message);
184
+ return new AIMessage({
185
+ content,
186
+ tool_calls: toolCalls,
187
+ invalid_tool_calls: invalidToolCalls,
188
+ additional_kwargs,
189
+ response_metadata,
190
+ id: rawResponse.id
191
+ });
192
+ }
193
+ default: return new ChatMessage(message.content || "", message.role ?? "unknown");
194
+ }
195
+ };
196
+ /**
197
+ * Converts an OpenAI Chat Completions API delta (streaming chunk) to a LangChain BaseMessageChunk.
198
+ *
199
+ * This converter is used during streaming responses to transform incremental updates from OpenAI's
200
+ * Chat Completions API into LangChain message chunks. It handles various message types, tool calls,
201
+ * function calls, audio content, and role-specific message chunk creation.
202
+ *
203
+ * @param params - Conversion parameters
204
+ * @param params.delta - The delta object from an OpenAI streaming chunk containing incremental
205
+ * message updates. May include content, role, tool_calls, function_call, audio, etc.
206
+ * @param params.rawResponse - The complete raw ChatCompletionChunk response from OpenAI,
207
+ * containing metadata like model info, usage stats, and the delta
208
+ * @param params.includeRawResponse - Optional flag to include the raw OpenAI response in the
209
+ * message chunk's additional_kwargs. Useful for debugging or accessing provider-specific data
210
+ * @param params.defaultRole - Optional default role to use if the delta doesn't specify one.
211
+ * Typically used to maintain role consistency across chunks in a streaming response
212
+ *
213
+ * @returns A BaseMessageChunk subclass appropriate for the message role:
214
+ * - HumanMessageChunk for "user" role
215
+ * - AIMessageChunk for "assistant" role (includes tool call chunks)
216
+ * - SystemMessageChunk for "system" or "developer" roles
217
+ * - FunctionMessageChunk for "function" role
218
+ * - ToolMessageChunk for "tool" role
219
+ * - ChatMessageChunk for any other role
220
+ *
221
+ * @example
222
+ * Basic streaming text chunk:
223
+ * ```typescript
224
+ * const chunk = convertCompletionsDeltaToBaseMessageChunk({
225
+ * delta: { role: "assistant", content: "Hello" },
226
+ * rawResponse: { id: "chatcmpl-123", model: "gpt-4", ... }
227
+ * });
228
+ * // Returns: AIMessageChunk with content "Hello"
229
+ * ```
230
+ *
231
+ * @example
232
+ * Streaming chunk with tool call:
233
+ * ```typescript
234
+ * const chunk = convertCompletionsDeltaToBaseMessageChunk({
235
+ * delta: {
236
+ * role: "assistant",
237
+ * tool_calls: [{
238
+ * index: 0,
239
+ * id: "call_123",
240
+ * function: { name: "get_weather", arguments: '{"location":' }
241
+ * }]
242
+ * },
243
+ * rawResponse: { id: "chatcmpl-123", ... }
244
+ * });
245
+ * // Returns: AIMessageChunk with tool_call_chunks containing partial tool call data
246
+ * ```
247
+ *
248
+ * @remarks
249
+ * - Tool calls are converted to ToolCallChunk objects with incremental data
250
+ * - Audio content includes the chunk index from the raw response
251
+ * - The "developer" role is mapped to SystemMessageChunk with a special marker
252
+ * - Response metadata includes model provider info and usage statistics
253
+ * - Function calls and tool calls are stored in additional_kwargs for compatibility
254
+ */
255
+ const convertCompletionsDeltaToBaseMessageChunk = ({ delta, rawResponse, includeRawResponse, defaultRole }) => {
256
+ const role = delta.role ?? defaultRole;
257
+ const content = delta.content ?? "";
258
+ let additional_kwargs;
259
+ if (delta.function_call) additional_kwargs = { function_call: delta.function_call };
260
+ else if (delta.tool_calls) additional_kwargs = { tool_calls: delta.tool_calls };
261
+ else additional_kwargs = {};
262
+ if (includeRawResponse) additional_kwargs.__raw_response = rawResponse;
263
+ if (delta.audio) additional_kwargs.audio = {
264
+ ...delta.audio,
265
+ index: rawResponse.choices[0].index
266
+ };
267
+ const response_metadata = {
268
+ model_provider: "openai",
269
+ usage: { ...rawResponse.usage }
270
+ };
271
+ if (role === "user") return new HumanMessageChunk({
272
+ content,
273
+ response_metadata
274
+ });
275
+ else if (role === "assistant") {
276
+ const toolCallChunks = [];
277
+ if (Array.isArray(delta.tool_calls)) for (const rawToolCall of delta.tool_calls) toolCallChunks.push({
278
+ name: rawToolCall.function?.name,
279
+ args: rawToolCall.function?.arguments,
280
+ id: rawToolCall.id,
281
+ index: rawToolCall.index,
282
+ type: "tool_call_chunk"
283
+ });
284
+ return new AIMessageChunk({
285
+ content,
286
+ tool_call_chunks: toolCallChunks,
287
+ additional_kwargs,
288
+ id: rawResponse.id,
289
+ response_metadata
290
+ });
291
+ } else if (role === "system") return new SystemMessageChunk({
292
+ content,
293
+ response_metadata
294
+ });
295
+ else if (role === "developer") return new SystemMessageChunk({
296
+ content,
297
+ response_metadata,
298
+ additional_kwargs: { __openai_role__: "developer" }
299
+ });
300
+ else if (role === "function") return new FunctionMessageChunk({
301
+ content,
302
+ additional_kwargs,
303
+ name: delta.name,
304
+ response_metadata
305
+ });
306
+ else if (role === "tool") return new ToolMessageChunk({
307
+ content,
308
+ additional_kwargs,
309
+ tool_call_id: delta.tool_call_id,
310
+ response_metadata
311
+ });
312
+ else return new ChatMessageChunk({
313
+ content,
314
+ role,
315
+ response_metadata
316
+ });
317
+ };
318
+ /**
319
+ * Converts a standard LangChain content block to an OpenAI Completions API content part.
320
+ *
321
+ * This converter transforms LangChain's standardized content blocks (image, audio, file)
322
+ * into the format expected by OpenAI's Chat Completions API. It handles various content
323
+ * types including images (URL or base64), audio (base64), and files (data or file ID).
324
+ *
325
+ * @param block - The standard content block to convert. Can be an image, audio, or file block.
326
+ *
327
+ * @returns An OpenAI Chat Completions content part object, or undefined if the block
328
+ * cannot be converted (e.g., missing required data).
329
+ *
330
+ * @example
331
+ * Image with URL:
332
+ * ```typescript
333
+ * const block = { type: "image", url: "https://example.com/image.jpg" };
334
+ * const part = convertStandardContentBlockToCompletionsContentPart(block);
335
+ * // Returns: { type: "image_url", image_url: { url: "https://example.com/image.jpg" } }
336
+ * ```
337
+ *
338
+ * @example
339
+ * Image with base64 data:
340
+ * ```typescript
341
+ * const block = { type: "image", data: "iVBORw0KGgo...", mimeType: "image/png" };
342
+ * const part = convertStandardContentBlockToCompletionsContentPart(block);
343
+ * // Returns: { type: "image_url", image_url: { url: "data:image/png;base64,iVBORw0KGgo..." } }
344
+ * ```
345
+ */
346
+ const convertStandardContentBlockToCompletionsContentPart = (block) => {
347
+ if (block.type === "image") {
348
+ if (block.url) return {
349
+ type: "image_url",
350
+ image_url: { url: block.url }
351
+ };
352
+ else if (block.data) return {
353
+ type: "image_url",
354
+ image_url: { url: `data:${block.mimeType};base64,${block.data}` }
355
+ };
356
+ }
357
+ if (block.type === "audio") {
358
+ if (block.data) {
359
+ const format = iife(() => {
360
+ const [, format$1] = block.mimeType.split("/");
361
+ if (format$1 === "wav" || format$1 === "mp3") return format$1;
362
+ return "wav";
363
+ });
364
+ return {
365
+ type: "input_audio",
366
+ input_audio: {
367
+ data: block.data.toString(),
368
+ format
369
+ }
370
+ };
371
+ }
372
+ }
373
+ if (block.type === "file") {
374
+ if (block.data) return {
375
+ type: "file",
376
+ file: { file_data: block.data.toString() }
377
+ };
378
+ if (block.fileId) return {
379
+ type: "file",
380
+ file: { file_id: block.fileId }
381
+ };
382
+ }
383
+ return void 0;
384
+ };
385
+ /**
386
+ * Converts a LangChain BaseMessage with standard content blocks to an OpenAI Chat Completions API message parameter.
387
+ *
388
+ * This converter transforms LangChain's standardized message format (using contentBlocks) into the format
389
+ * expected by OpenAI's Chat Completions API. It handles role mapping, content filtering, and multi-modal
390
+ * content conversion for various message types.
391
+ *
392
+ * @remarks
393
+ * The converter performs the following transformations:
394
+ * - Maps LangChain message roles to OpenAI API roles (user, assistant, system, developer, tool, function)
395
+ * - For reasoning models, automatically converts "system" role to "developer" role
396
+ * - Filters content blocks based on message role (most roles only include text blocks)
397
+ * - For user messages, converts multi-modal content blocks (images, audio, files) to OpenAI format
398
+ * - Preserves tool call IDs for tool messages and function names for function messages
399
+ *
400
+ * Role-specific behavior:
401
+ * - **developer**: Returns only text content blocks (used for reasoning models)
402
+ * - **system**: Returns only text content blocks
403
+ * - **assistant**: Returns only text content blocks
404
+ * - **tool**: Returns only text content blocks with tool_call_id preserved
405
+ * - **function**: Returns text content blocks joined as a single string with function name
406
+ * - **user** (default): Returns multi-modal content including text, images, audio, and files
407
+ *
408
+ * @param params - Conversion parameters
409
+ * @param params.message - The LangChain BaseMessage to convert. Must have contentBlocks property
410
+ * containing an array of standard content blocks (text, image, audio, file, etc.)
411
+ * @param params.model - Optional model name. Used to determine if special role mapping is needed
412
+ * (e.g., "system" -> "developer" for reasoning models like o1)
413
+ *
414
+ * @returns An OpenAI ChatCompletionMessageParam object formatted for the Chat Completions API.
415
+ * The structure varies by role:
416
+ * - Developer/System/Assistant: `{ role, content: TextBlock[] }`
417
+ * - Tool: `{ role: "tool", tool_call_id, content: TextBlock[] }`
418
+ * - Function: `{ role: "function", name, content: string }`
419
+ * - User: `{ role: "user", content: Array<TextPart | ImagePart | AudioPart | FilePart> }`
420
+ *
421
+ * @example
422
+ * Simple text message:
423
+ * ```typescript
424
+ * const message = new HumanMessage({
425
+ * content: [{ type: "text", text: "Hello!" }]
426
+ * });
427
+ * const param = convertStandardContentMessageToCompletionsMessage({ message });
428
+ * // Returns: { role: "user", content: [{ type: "text", text: "Hello!" }] }
429
+ * ```
430
+ *
431
+ * @example
432
+ * Multi-modal user message with image:
433
+ * ```typescript
434
+ * const message = new HumanMessage({
435
+ * content: [
436
+ * { type: "text", text: "What's in this image?" },
437
+ * { type: "image", url: "https://example.com/image.jpg" }
438
+ * ]
439
+ * });
440
+ * const param = convertStandardContentMessageToCompletionsMessage({ message });
441
+ * // Returns: {
442
+ * // role: "user",
443
+ * // content: [
444
+ * // { type: "text", text: "What's in this image?" },
445
+ * // { type: "image_url", image_url: { url: "https://example.com/image.jpg" } }
446
+ * // ]
447
+ * // }
448
+ * ```
449
+ */
450
+ const convertStandardContentMessageToCompletionsMessage = ({ message, model }) => {
451
+ let role = messageToOpenAIRole(message);
452
+ if (role === "system" && isReasoningModel(model)) role = "developer";
453
+ if (role === "developer") return {
454
+ role: "developer",
455
+ content: message.contentBlocks.filter((block) => block.type === "text")
456
+ };
457
+ else if (role === "system") return {
458
+ role: "system",
459
+ content: message.contentBlocks.filter((block) => block.type === "text")
460
+ };
461
+ else if (role === "assistant") return {
462
+ role: "assistant",
463
+ content: message.contentBlocks.filter((block) => block.type === "text")
464
+ };
465
+ else if (role === "tool" && ToolMessage.isInstance(message)) return {
466
+ role: "tool",
467
+ tool_call_id: message.tool_call_id,
468
+ content: message.contentBlocks.filter((block) => block.type === "text")
469
+ };
470
+ else if (role === "function") return {
471
+ role: "function",
472
+ name: message.name ?? "",
473
+ content: message.contentBlocks.filter((block) => block.type === "text").join("")
474
+ };
475
+ function* iterateUserContent(blocks) {
476
+ for (const block of blocks) {
477
+ if (block.type === "text") yield {
478
+ type: "text",
479
+ text: block.text
480
+ };
481
+ const data = convertStandardContentBlockToCompletionsContentPart(block);
482
+ if (data) yield data;
483
+ }
484
+ }
485
+ return {
486
+ role: "user",
487
+ content: Array.from(iterateUserContent(message.contentBlocks))
488
+ };
489
+ };
490
+ /**
491
+ * Converts an array of LangChain BaseMessages to OpenAI Chat Completions API message parameters.
492
+ *
493
+ * This converter transforms LangChain's internal message representation into the format required
494
+ * by OpenAI's Chat Completions API. It handles various message types, roles, content formats,
495
+ * tool calls, function calls, audio messages, and special model-specific requirements.
496
+ *
497
+ * @remarks
498
+ * The converter performs several key transformations:
499
+ * - Maps LangChain message types to OpenAI roles (user, assistant, system, tool, function, developer)
500
+ * - Converts standard content blocks (v1 format) using a specialized converter
501
+ * - Handles multimodal content including text, images, audio, and data blocks
502
+ * - Preserves tool calls and function calls with proper formatting
503
+ * - Applies model-specific role mappings (e.g., "system" → "developer" for reasoning models)
504
+ * - Splits audio messages into separate message parameters when needed
505
+ *
506
+ * @param params - Conversion parameters
507
+ * @param params.messages - Array of LangChain BaseMessages to convert. Can include any message
508
+ * type: HumanMessage, AIMessage, SystemMessage, ToolMessage, FunctionMessage, etc.
509
+ * @param params.model - Optional model name used to determine if special role mapping is needed.
510
+ * For reasoning models (o1, o3, etc.), "system" role is converted to "developer" role.
511
+ *
512
+ * @returns Array of ChatCompletionMessageParam objects formatted for OpenAI's Chat Completions API.
513
+ * Some messages may be split into multiple parameters (e.g., audio messages).
514
+ *
515
+ * @example
516
+ * Basic message conversion:
517
+ * ```typescript
518
+ * const messages = [
519
+ * new HumanMessage("What's the weather like?"),
520
+ * new AIMessage("Let me check that for you.")
521
+ * ];
522
+ *
523
+ * const params = convertMessagesToCompletionsMessageParams({
524
+ * messages,
525
+ * model: "gpt-4"
526
+ * });
527
+ * // Returns:
528
+ * // [
529
+ * // { role: "user", content: "What's the weather like?" },
530
+ * // { role: "assistant", content: "Let me check that for you." }
531
+ * // ]
532
+ * ```
533
+ *
534
+ * @example
535
+ * Message with tool calls:
536
+ * ```typescript
537
+ * const messages = [
538
+ * new AIMessage({
539
+ * content: "",
540
+ * tool_calls: [{
541
+ * id: "call_123",
542
+ * name: "get_weather",
543
+ * args: { location: "San Francisco" }
544
+ * }]
545
+ * })
546
+ * ];
547
+ *
548
+ * const params = convertMessagesToCompletionsMessageParams({ messages });
549
+ * // Returns:
550
+ * // [{
551
+ * // role: "assistant",
552
+ * // content: "",
553
+ * // tool_calls: [{
554
+ * // id: "call_123",
555
+ * // type: "function",
556
+ * // function: { name: "get_weather", arguments: '{"location":"San Francisco"}' }
557
+ * // }]
558
+ * // }]
559
+ * ```
560
+ */
561
+ const convertMessagesToCompletionsMessageParams = ({ messages, model }) => {
562
+ return messages.flatMap((message) => {
563
+ if ("output_version" in message.response_metadata && message.response_metadata?.output_version === "v1") return convertStandardContentMessageToCompletionsMessage({ message });
564
+ let role = messageToOpenAIRole(message);
565
+ if (role === "system" && isReasoningModel(model)) role = "developer";
566
+ const content = typeof message.content === "string" ? message.content : message.content.map((m) => {
567
+ if (isDataContentBlock(m)) return convertToProviderContentBlock(m, completionsApiContentBlockConverter);
568
+ return m;
569
+ });
570
+ const completionParam = {
571
+ role,
572
+ content
573
+ };
574
+ if (message.name != null) completionParam.name = message.name;
575
+ if (message.additional_kwargs.function_call != null) {
576
+ completionParam.function_call = message.additional_kwargs.function_call;
577
+ completionParam.content = "";
578
+ }
579
+ if (AIMessage.isInstance(message) && !!message.tool_calls?.length) {
580
+ completionParam.tool_calls = message.tool_calls.map(convertLangChainToolCallToOpenAI);
581
+ completionParam.content = "";
582
+ } else {
583
+ if (message.additional_kwargs.tool_calls != null) completionParam.tool_calls = message.additional_kwargs.tool_calls;
584
+ if (ToolMessage.isInstance(message) && message.tool_call_id != null) completionParam.tool_call_id = message.tool_call_id;
585
+ }
586
+ if (message.additional_kwargs.audio && typeof message.additional_kwargs.audio === "object" && "id" in message.additional_kwargs.audio) {
587
+ const audioMessage = {
588
+ role: "assistant",
589
+ audio: { id: message.additional_kwargs.audio.id }
590
+ };
591
+ return [completionParam, audioMessage];
592
+ }
593
+ return completionParam;
594
+ });
595
+ };
596
+
597
+ //#endregion
598
+ export { completionsApiContentBlockConverter, convertCompletionsDeltaToBaseMessageChunk, convertCompletionsMessageToBaseMessage, convertMessagesToCompletionsMessageParams };
599
+ //# sourceMappingURL=completions.js.map