@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,889 @@
1
+ import { iife, isReasoningModel, messageToOpenAIRole } from "../utils/misc.js";
2
+ import { isCustomToolCall, parseCustomToolCall } from "../utils/tools.js";
3
+ import { completionsApiContentBlockConverter } from "./completions.js";
4
+ import { makeInvalidToolCall, parseToolCall } from "@langchain/core/output_parsers/openai_tools";
5
+ import { AIMessage, AIMessageChunk, convertToProviderContentBlock, isDataContentBlock } from "@langchain/core/messages";
6
+ import { ChatGenerationChunk } from "@langchain/core/outputs";
7
+
8
+ //#region src/converters/responses.ts
9
+ const _FUNCTION_CALL_IDS_MAP_KEY = "__openai_function_call_ids__";
10
+ /**
11
+ * Converts OpenAI Responses API usage statistics to LangChain's UsageMetadata format.
12
+ *
13
+ * This converter transforms token usage information from OpenAI's Responses API into
14
+ * the standardized UsageMetadata format used throughout LangChain. It handles both
15
+ * basic token counts and detailed token breakdowns including cached tokens and
16
+ * reasoning tokens.
17
+ *
18
+ * @param usage - The usage statistics object from OpenAI's Responses API containing
19
+ * token counts and optional detailed breakdowns.
20
+ *
21
+ * @returns A UsageMetadata object containing:
22
+ * - `input_tokens`: Total number of tokens in the input/prompt (defaults to 0 if not provided)
23
+ * - `output_tokens`: Total number of tokens in the model's output (defaults to 0 if not provided)
24
+ * - `total_tokens`: Combined total of input and output tokens (defaults to 0 if not provided)
25
+ * - `input_token_details`: Object containing detailed input token information:
26
+ * - `cache_read`: Number of tokens read from cache (only included if available)
27
+ * - `output_token_details`: Object containing detailed output token information:
28
+ * - `reasoning`: Number of tokens used for reasoning (only included if available)
29
+ *
30
+ * @example
31
+ * ```typescript
32
+ * const usage = {
33
+ * input_tokens: 100,
34
+ * output_tokens: 50,
35
+ * total_tokens: 150,
36
+ * input_tokens_details: { cached_tokens: 20 },
37
+ * output_tokens_details: { reasoning_tokens: 10 }
38
+ * };
39
+ *
40
+ * const metadata = convertResponsesUsageToUsageMetadata(usage);
41
+ * // Returns:
42
+ * // {
43
+ * // input_tokens: 100,
44
+ * // output_tokens: 50,
45
+ * // total_tokens: 150,
46
+ * // input_token_details: { cache_read: 20 },
47
+ * // output_token_details: { reasoning: 10 }
48
+ * // }
49
+ * ```
50
+ *
51
+ * @remarks
52
+ * - The function safely handles undefined or null values by using optional chaining
53
+ * and nullish coalescing operators
54
+ * - Detailed token information (cache_read, reasoning) is only included in the result
55
+ * if the corresponding values are present in the input
56
+ * - Token counts default to 0 if not provided in the usage object
57
+ * - This converter is specifically designed for OpenAI's Responses API format and
58
+ * may differ from other OpenAI API endpoints
59
+ */
60
+ const convertResponsesUsageToUsageMetadata = (usage) => {
61
+ const inputTokenDetails = { ...usage?.input_tokens_details?.cached_tokens != null && { cache_read: usage?.input_tokens_details?.cached_tokens } };
62
+ const outputTokenDetails = { ...usage?.output_tokens_details?.reasoning_tokens != null && { reasoning: usage?.output_tokens_details?.reasoning_tokens } };
63
+ return {
64
+ input_tokens: usage?.input_tokens ?? 0,
65
+ output_tokens: usage?.output_tokens ?? 0,
66
+ total_tokens: usage?.total_tokens ?? 0,
67
+ input_token_details: inputTokenDetails,
68
+ output_token_details: outputTokenDetails
69
+ };
70
+ };
71
+ /**
72
+ * Converts an OpenAI Responses API response to a LangChain AIMessage.
73
+ *
74
+ * This converter processes the output from OpenAI's Responses API (both `create` and `parse` methods)
75
+ * and transforms it into a LangChain AIMessage object with all relevant metadata, tool calls, and content.
76
+ *
77
+ * @param response - The response object from OpenAI's Responses API. Can be either:
78
+ * - ResponsesCreateInvoke: Result from `responses.create()`
79
+ * - ResponsesParseInvoke: Result from `responses.parse()`
80
+ *
81
+ * @returns An AIMessage containing:
82
+ * - `id`: The message ID from the response output
83
+ * - `content`: Array of message content blocks (text, images, etc.)
84
+ * - `tool_calls`: Array of successfully parsed tool calls
85
+ * - `invalid_tool_calls`: Array of tool calls that failed to parse
86
+ * - `usage_metadata`: Token usage information converted to LangChain format
87
+ * - `additional_kwargs`: Extra data including:
88
+ * - `refusal`: Refusal text if the model refused to respond
89
+ * - `reasoning`: Reasoning output for reasoning models
90
+ * - `tool_outputs`: Results from built-in tools (web search, file search, etc.)
91
+ * - `parsed`: Parsed structured output when using json_schema format
92
+ * - Function call ID mappings for tracking
93
+ * - `response_metadata`: Metadata about the response including model, timestamps, status, etc.
94
+ *
95
+ * @throws Error if the response contains an error object. The error message and code are extracted
96
+ * from the response.error field.
97
+ *
98
+ * @example
99
+ * ```typescript
100
+ * const response = await client.responses.create({
101
+ * model: "gpt-4",
102
+ * input: [{ type: "message", content: "Hello" }]
103
+ * });
104
+ * const message = convertResponsesMessageToAIMessage(response);
105
+ * console.log(message.content); // Message content
106
+ * console.log(message.tool_calls); // Any tool calls made
107
+ * ```
108
+ *
109
+ * @remarks
110
+ * The converter handles multiple output item types:
111
+ * - `message`: Text and structured content from the model
112
+ * - `function_call`: Tool/function calls that need to be executed
113
+ * - `reasoning`: Reasoning traces from reasoning models (o1, o3, etc.)
114
+ * - `custom_tool_call`: Custom tool invocations
115
+ * - Built-in tool outputs: web_search, file_search, code_interpreter, etc.
116
+ *
117
+ * Tool calls are parsed and validated. Invalid tool calls (malformed JSON, etc.) are captured
118
+ * in the `invalid_tool_calls` array rather than throwing errors.
119
+ */
120
+ const convertResponsesMessageToAIMessage = (response) => {
121
+ if (response.error) {
122
+ const error = new Error(response.error.message);
123
+ error.name = response.error.code;
124
+ throw error;
125
+ }
126
+ let messageId;
127
+ const content = [];
128
+ const tool_calls = [];
129
+ const invalid_tool_calls = [];
130
+ const response_metadata = {
131
+ model_provider: "openai",
132
+ model: response.model,
133
+ created_at: response.created_at,
134
+ id: response.id,
135
+ incomplete_details: response.incomplete_details,
136
+ metadata: response.metadata,
137
+ object: response.object,
138
+ status: response.status,
139
+ user: response.user,
140
+ service_tier: response.service_tier,
141
+ model_name: response.model
142
+ };
143
+ const additional_kwargs = {};
144
+ for (const item of response.output) if (item.type === "message") {
145
+ messageId = item.id;
146
+ content.push(...item.content.flatMap((part) => {
147
+ if (part.type === "output_text") {
148
+ if ("parsed" in part && part.parsed != null) additional_kwargs.parsed = part.parsed;
149
+ return {
150
+ type: "text",
151
+ text: part.text,
152
+ annotations: part.annotations
153
+ };
154
+ }
155
+ if (part.type === "refusal") {
156
+ additional_kwargs.refusal = part.refusal;
157
+ return [];
158
+ }
159
+ return part;
160
+ }));
161
+ } else if (item.type === "function_call") {
162
+ const fnAdapter = {
163
+ function: {
164
+ name: item.name,
165
+ arguments: item.arguments
166
+ },
167
+ id: item.call_id
168
+ };
169
+ try {
170
+ tool_calls.push(parseToolCall(fnAdapter, { returnId: true }));
171
+ } catch (e) {
172
+ let errMessage;
173
+ if (typeof e === "object" && e != null && "message" in e && typeof e.message === "string") errMessage = e.message;
174
+ invalid_tool_calls.push(makeInvalidToolCall(fnAdapter, errMessage));
175
+ }
176
+ additional_kwargs[_FUNCTION_CALL_IDS_MAP_KEY] ??= {};
177
+ if (item.id) additional_kwargs[_FUNCTION_CALL_IDS_MAP_KEY][item.call_id] = item.id;
178
+ } else if (item.type === "reasoning") additional_kwargs.reasoning = item;
179
+ else if (item.type === "custom_tool_call") {
180
+ const parsed = parseCustomToolCall(item);
181
+ if (parsed) tool_calls.push(parsed);
182
+ else invalid_tool_calls.push(makeInvalidToolCall(item, "Malformed custom tool call"));
183
+ } else {
184
+ additional_kwargs.tool_outputs ??= [];
185
+ additional_kwargs.tool_outputs.push(item);
186
+ }
187
+ return new AIMessage({
188
+ id: messageId,
189
+ content,
190
+ tool_calls,
191
+ invalid_tool_calls,
192
+ usage_metadata: convertResponsesUsageToUsageMetadata(response.usage),
193
+ additional_kwargs,
194
+ response_metadata
195
+ });
196
+ };
197
+ /**
198
+ * Converts a LangChain ChatOpenAI reasoning summary to an OpenAI Responses API reasoning item.
199
+ *
200
+ * This converter transforms reasoning summaries that have been accumulated during streaming
201
+ * (where summary parts may arrive in multiple chunks with the same index) into the final
202
+ * consolidated format expected by OpenAI's Responses API. It combines summary parts that
203
+ * share the same index and removes the index field from the final output.
204
+ *
205
+ * @param reasoning - A ChatOpenAI reasoning summary object containing:
206
+ * - `id`: The reasoning item ID
207
+ * - `type`: The type of reasoning (typically "reasoning")
208
+ * - `summary`: Array of summary parts, each with:
209
+ * - `text`: The summary text content
210
+ * - `type`: The summary type (e.g., "summary_text")
211
+ * - `index`: The index used to group related summary parts during streaming
212
+ *
213
+ * @returns An OpenAI Responses API ResponseReasoningItem with:
214
+ * - All properties from the input reasoning object
215
+ * - `summary`: Consolidated array of summary objects with:
216
+ * - `text`: Combined text from all parts with the same index
217
+ * - `type`: The summary type
218
+ * - No `index` field (removed after consolidation)
219
+ *
220
+ * @example
221
+ * ```typescript
222
+ * // Input: Reasoning summary with multiple parts at the same index
223
+ * const reasoning = {
224
+ * id: "reasoning_123",
225
+ * type: "reasoning",
226
+ * summary: [
227
+ * { text: "First ", type: "summary_text", index: 0 },
228
+ * { text: "part", type: "summary_text", index: 0 },
229
+ * { text: "Second part", type: "summary_text", index: 1 }
230
+ * ]
231
+ * };
232
+ *
233
+ * const result = convertReasoningSummaryToResponsesReasoningItem(reasoning);
234
+ * // Returns:
235
+ * // {
236
+ * // id: "reasoning_123",
237
+ * // type: "reasoning",
238
+ * // summary: [
239
+ * // { text: "First part", type: "summary_text" },
240
+ * // { text: "Second part", type: "summary_text" }
241
+ * // ]
242
+ * // }
243
+ * ```
244
+ *
245
+ * @remarks
246
+ * - This converter is primarily used when reconstructing complete reasoning items from
247
+ * streaming chunks, where summary parts may arrive incrementally with index markers
248
+ * - Summary parts with the same index are concatenated in the order they appear
249
+ * - If the reasoning summary contains only one part, no reduction is performed
250
+ * - The index field is used internally during streaming to track which summary parts
251
+ * belong together, but is removed from the final output as it's not part of the
252
+ * OpenAI Responses API schema
253
+ * - This is the inverse operation of the streaming accumulation that happens in
254
+ * `convertResponsesDeltaToChatGenerationChunk`
255
+ */
256
+ const convertReasoningSummaryToResponsesReasoningItem = (reasoning) => {
257
+ const summary = (reasoning.summary.length > 1 ? reasoning.summary.reduce((acc, curr) => {
258
+ const last = acc[acc.length - 1];
259
+ if (last.index === curr.index) last.text += curr.text;
260
+ else acc.push(curr);
261
+ return acc;
262
+ }, [{ ...reasoning.summary[0] }]) : reasoning.summary).map((s) => Object.fromEntries(Object.entries(s).filter(([k]) => k !== "index")));
263
+ return {
264
+ ...reasoning,
265
+ summary
266
+ };
267
+ };
268
+ /**
269
+ * Converts OpenAI Responses API stream events to LangChain ChatGenerationChunk objects.
270
+ *
271
+ * This converter processes streaming events from OpenAI's Responses API and transforms them
272
+ * into LangChain ChatGenerationChunk objects that can be used in streaming chat applications.
273
+ * It handles various event types including text deltas, tool calls, reasoning, and metadata updates.
274
+ *
275
+ * @param event - A streaming event from OpenAI's Responses API
276
+ *
277
+ * @returns A ChatGenerationChunk containing:
278
+ * - `text`: Concatenated text content from all text parts in the event
279
+ * - `message`: An AIMessageChunk with:
280
+ * - `id`: Message ID (set when a message output item is added)
281
+ * - `content`: Array of content blocks (text with optional annotations)
282
+ * - `tool_call_chunks`: Incremental tool call data (name, args, id)
283
+ * - `usage_metadata`: Token usage information (only in completion events)
284
+ * - `additional_kwargs`: Extra data including:
285
+ * - `refusal`: Refusal text if the model refused to respond
286
+ * - `reasoning`: Reasoning output for reasoning models (id, type, summary)
287
+ * - `tool_outputs`: Results from built-in tools (web search, file search, etc.)
288
+ * - `parsed`: Parsed structured output when using json_schema format
289
+ * - Function call ID mappings for tracking
290
+ * - `response_metadata`: Metadata about the response (model, id, etc.)
291
+ * - `generationInfo`: Additional generation information (e.g., tool output status)
292
+ *
293
+ * Returns `null` for events that don't produce meaningful chunks:
294
+ * - Partial image generation events (to avoid storing all partial images in history)
295
+ * - Unrecognized event types
296
+ *
297
+ * @example
298
+ * ```typescript
299
+ * const stream = await client.responses.create({
300
+ * model: "gpt-4",
301
+ * input: [{ type: "message", content: "Hello" }],
302
+ * stream: true
303
+ * });
304
+ *
305
+ * for await (const event of stream) {
306
+ * const chunk = convertResponsesDeltaToChatGenerationChunk(event);
307
+ * if (chunk) {
308
+ * console.log(chunk.text); // Incremental text
309
+ * console.log(chunk.message.tool_call_chunks); // Tool call updates
310
+ * }
311
+ * }
312
+ * ```
313
+ *
314
+ * @remarks
315
+ * - Text content is accumulated in an array with index tracking for proper ordering
316
+ * - Tool call chunks include incremental arguments that need to be concatenated by the consumer
317
+ * - Reasoning summaries are built incrementally across multiple events
318
+ * - Function call IDs are tracked in `additional_kwargs` to map call_id to item id
319
+ * - The `text` field is provided for legacy compatibility with `onLLMNewToken` callbacks
320
+ * - Usage metadata is only available in `response.completed` events
321
+ * - Partial images are intentionally ignored to prevent memory bloat in conversation history
322
+ */
323
+ const convertResponsesDeltaToChatGenerationChunk = (event) => {
324
+ const content = [];
325
+ let generationInfo = {};
326
+ let usage_metadata;
327
+ const tool_call_chunks = [];
328
+ const response_metadata = { model_provider: "openai" };
329
+ const additional_kwargs = {};
330
+ let id;
331
+ if (event.type === "response.output_text.delta") content.push({
332
+ type: "text",
333
+ text: event.delta,
334
+ index: event.content_index
335
+ });
336
+ else if (event.type === "response.output_text.annotation.added") content.push({
337
+ type: "text",
338
+ text: "",
339
+ annotations: [event.annotation],
340
+ index: event.content_index
341
+ });
342
+ else if (event.type === "response.output_item.added" && event.item.type === "message") id = event.item.id;
343
+ else if (event.type === "response.output_item.added" && event.item.type === "function_call") {
344
+ tool_call_chunks.push({
345
+ type: "tool_call_chunk",
346
+ name: event.item.name,
347
+ args: event.item.arguments,
348
+ id: event.item.call_id,
349
+ index: event.output_index
350
+ });
351
+ additional_kwargs[_FUNCTION_CALL_IDS_MAP_KEY] = { [event.item.call_id]: event.item.id };
352
+ } else if (event.type === "response.output_item.done" && [
353
+ "web_search_call",
354
+ "file_search_call",
355
+ "computer_call",
356
+ "code_interpreter_call",
357
+ "mcp_call",
358
+ "mcp_list_tools",
359
+ "mcp_approval_request",
360
+ "image_generation_call",
361
+ "custom_tool_call"
362
+ ].includes(event.item.type)) additional_kwargs.tool_outputs = [event.item];
363
+ else if (event.type === "response.created") {
364
+ response_metadata.id = event.response.id;
365
+ response_metadata.model_name = event.response.model;
366
+ response_metadata.model = event.response.model;
367
+ } else if (event.type === "response.completed") {
368
+ const msg = convertResponsesMessageToAIMessage(event.response);
369
+ usage_metadata = convertResponsesUsageToUsageMetadata(event.response.usage);
370
+ if (event.response.text?.format?.type === "json_schema") additional_kwargs.parsed ??= JSON.parse(msg.text);
371
+ for (const [key, value] of Object.entries(event.response)) if (key !== "id") response_metadata[key] = value;
372
+ } else if (event.type === "response.function_call_arguments.delta" || event.type === "response.custom_tool_call_input.delta") tool_call_chunks.push({
373
+ type: "tool_call_chunk",
374
+ args: event.delta,
375
+ index: event.output_index
376
+ });
377
+ else if (event.type === "response.web_search_call.completed" || event.type === "response.file_search_call.completed") generationInfo = { tool_outputs: {
378
+ id: event.item_id,
379
+ type: event.type.replace("response.", "").replace(".completed", ""),
380
+ status: "completed"
381
+ } };
382
+ else if (event.type === "response.refusal.done") additional_kwargs.refusal = event.refusal;
383
+ else if (event.type === "response.output_item.added" && "item" in event && event.item.type === "reasoning") {
384
+ const summary = event.item.summary ? event.item.summary.map((s, index) => ({
385
+ ...s,
386
+ index
387
+ })) : void 0;
388
+ additional_kwargs.reasoning = {
389
+ id: event.item.id,
390
+ type: event.item.type,
391
+ ...summary ? { summary } : {}
392
+ };
393
+ } else if (event.type === "response.reasoning_summary_part.added") additional_kwargs.reasoning = {
394
+ type: "reasoning",
395
+ summary: [{
396
+ ...event.part,
397
+ index: event.summary_index
398
+ }]
399
+ };
400
+ else if (event.type === "response.reasoning_summary_text.delta") additional_kwargs.reasoning = {
401
+ type: "reasoning",
402
+ summary: [{
403
+ text: event.delta,
404
+ type: "summary_text",
405
+ index: event.summary_index
406
+ }]
407
+ };
408
+ else if (event.type === "response.image_generation_call.partial_image") return null;
409
+ else return null;
410
+ return new ChatGenerationChunk({
411
+ text: content.map((part) => part.text).join(""),
412
+ message: new AIMessageChunk({
413
+ id,
414
+ content,
415
+ tool_call_chunks,
416
+ usage_metadata,
417
+ additional_kwargs,
418
+ response_metadata
419
+ }),
420
+ generationInfo
421
+ });
422
+ };
423
+ /**
424
+ * Converts a single LangChain BaseMessage to OpenAI Responses API input format.
425
+ *
426
+ * This converter transforms a LangChain message into one or more ResponseInputItem objects
427
+ * that can be used with OpenAI's Responses API. It handles complex message structures including
428
+ * tool calls, reasoning blocks, multimodal content, and various content block types.
429
+ *
430
+ * @param message - The LangChain BaseMessage to convert. Can be any message type including
431
+ * HumanMessage, AIMessage, SystemMessage, ToolMessage, etc.
432
+ *
433
+ * @returns An array of ResponseInputItem objects.
434
+ *
435
+ * @example
436
+ * Basic text message conversion:
437
+ * ```typescript
438
+ * const message = new HumanMessage("Hello, how are you?");
439
+ * const items = convertStandardContentMessageToResponsesInput(message);
440
+ * // Returns: [{ type: "message", role: "user", content: [{ type: "input_text", text: "Hello, how are you?" }] }]
441
+ * ```
442
+ *
443
+ * @example
444
+ * AI message with tool calls:
445
+ * ```typescript
446
+ * const message = new AIMessage({
447
+ * content: "I'll check the weather for you.",
448
+ * tool_calls: [{
449
+ * id: "call_123",
450
+ * name: "get_weather",
451
+ * args: { location: "San Francisco" }
452
+ * }]
453
+ * });
454
+ * const items = convertStandardContentMessageToResponsesInput(message);
455
+ * // Returns:
456
+ * // [
457
+ * // { type: "message", role: "assistant", content: [{ type: "input_text", text: "I'll check the weather for you." }] },
458
+ * // { type: "function_call", call_id: "call_123", name: "get_weather", arguments: '{"location":"San Francisco"}' }
459
+ * // ]
460
+ * ```
461
+ */
462
+ const convertStandardContentMessageToResponsesInput = (message) => {
463
+ const isResponsesMessage = AIMessage.isInstance(message) && message.response_metadata?.model_provider === "openai";
464
+ function* iterateItems() {
465
+ const messageRole = iife(() => {
466
+ try {
467
+ const role = messageToOpenAIRole(message);
468
+ if (role === "system" || role === "developer" || role === "assistant" || role === "user") return role;
469
+ return "assistant";
470
+ } catch {
471
+ return "assistant";
472
+ }
473
+ });
474
+ let currentMessage = void 0;
475
+ const functionCallIdsWithBlocks = /* @__PURE__ */ new Set();
476
+ const serverFunctionCallIdsWithBlocks = /* @__PURE__ */ new Set();
477
+ const pendingFunctionChunks = /* @__PURE__ */ new Map();
478
+ const pendingServerFunctionChunks = /* @__PURE__ */ new Map();
479
+ function* flushMessage() {
480
+ if (!currentMessage) return;
481
+ const content = currentMessage.content;
482
+ if (typeof content === "string" && content.length > 0 || Array.isArray(content) && content.length > 0) yield currentMessage;
483
+ currentMessage = void 0;
484
+ }
485
+ const pushMessageContent = (content) => {
486
+ if (!currentMessage) currentMessage = {
487
+ type: "message",
488
+ role: messageRole,
489
+ content: []
490
+ };
491
+ if (typeof currentMessage.content === "string") currentMessage.content = currentMessage.content.length > 0 ? [{
492
+ type: "input_text",
493
+ text: currentMessage.content
494
+ }, ...content] : [...content];
495
+ else currentMessage.content.push(...content);
496
+ };
497
+ const toJsonString = (value) => {
498
+ if (typeof value === "string") return value;
499
+ try {
500
+ return JSON.stringify(value ?? {});
501
+ } catch {
502
+ return "{}";
503
+ }
504
+ };
505
+ const resolveImageItem = (block) => {
506
+ const detail = iife(() => {
507
+ const raw = block.metadata?.detail;
508
+ if (raw === "low" || raw === "high" || raw === "auto") return raw;
509
+ return "auto";
510
+ });
511
+ if (block.fileId) return {
512
+ type: "input_image",
513
+ detail,
514
+ file_id: block.fileId
515
+ };
516
+ if (block.url) return {
517
+ type: "input_image",
518
+ detail,
519
+ image_url: block.url
520
+ };
521
+ if (block.data) {
522
+ const base64Data = typeof block.data === "string" ? block.data : Buffer.from(block.data).toString("base64");
523
+ const mimeType = block.mimeType ?? "image/png";
524
+ return {
525
+ type: "input_image",
526
+ detail,
527
+ image_url: `data:${mimeType};base64,${base64Data}`
528
+ };
529
+ }
530
+ return void 0;
531
+ };
532
+ const resolveFileItem = (block) => {
533
+ const filename = block.metadata?.filename ?? block.metadata?.name ?? block.metadata?.title;
534
+ if (block.fileId && typeof filename === "string") return {
535
+ type: "input_file",
536
+ file_id: block.fileId,
537
+ ...filename ? { filename } : {}
538
+ };
539
+ if (block.url && typeof filename === "string") return {
540
+ type: "input_file",
541
+ file_url: block.url,
542
+ ...filename ? { filename } : {}
543
+ };
544
+ if (block.data && typeof filename === "string") {
545
+ const encoded = typeof block.data === "string" ? block.data : Buffer.from(block.data).toString("base64");
546
+ const mimeType = block.mimeType ?? "application/octet-stream";
547
+ return {
548
+ type: "input_file",
549
+ file_data: `data:${mimeType};base64,${encoded}`,
550
+ ...filename ? { filename } : {}
551
+ };
552
+ }
553
+ return void 0;
554
+ };
555
+ const convertReasoningBlock = (block) => {
556
+ const summaryEntries = iife(() => {
557
+ if (Array.isArray(block.summary)) {
558
+ const candidate = block.summary;
559
+ const mapped = candidate?.map((item) => item?.text).filter((text) => typeof text === "string") ?? [];
560
+ if (mapped.length > 0) return mapped;
561
+ }
562
+ return block.reasoning ? [block.reasoning] : [];
563
+ });
564
+ const summary = summaryEntries.length > 0 ? summaryEntries.map((text) => ({
565
+ type: "summary_text",
566
+ text
567
+ })) : [{
568
+ type: "summary_text",
569
+ text: ""
570
+ }];
571
+ const reasoningItem = {
572
+ type: "reasoning",
573
+ id: block.id ?? "",
574
+ summary
575
+ };
576
+ if (block.reasoning) reasoningItem.content = [{
577
+ type: "reasoning_text",
578
+ text: block.reasoning
579
+ }];
580
+ return reasoningItem;
581
+ };
582
+ const convertFunctionCall = (block) => ({
583
+ type: "function_call",
584
+ name: block.name ?? "",
585
+ call_id: block.id ?? "",
586
+ arguments: toJsonString(block.args)
587
+ });
588
+ const convertFunctionCallOutput = (block) => {
589
+ const output = toJsonString(block.output);
590
+ const status = block.status === "success" ? "completed" : block.status === "error" ? "incomplete" : void 0;
591
+ return {
592
+ type: "function_call_output",
593
+ call_id: block.toolCallId ?? "",
594
+ output,
595
+ ...status ? { status } : {}
596
+ };
597
+ };
598
+ for (const block of message.contentBlocks) if (block.type === "text") pushMessageContent([{
599
+ type: "input_text",
600
+ text: block.text
601
+ }]);
602
+ else if (block.type === "invalid_tool_call") {} else if (block.type === "reasoning") {
603
+ yield* flushMessage();
604
+ yield convertReasoningBlock(block);
605
+ } else if (block.type === "tool_call") {
606
+ yield* flushMessage();
607
+ const id = block.id ?? "";
608
+ if (id) {
609
+ functionCallIdsWithBlocks.add(id);
610
+ pendingFunctionChunks.delete(id);
611
+ }
612
+ yield convertFunctionCall(block);
613
+ } else if (block.type === "tool_call_chunk") {
614
+ if (block.id) {
615
+ const existing = pendingFunctionChunks.get(block.id) ?? {
616
+ name: block.name,
617
+ args: []
618
+ };
619
+ if (block.name) existing.name = block.name;
620
+ if (block.args) existing.args.push(block.args);
621
+ pendingFunctionChunks.set(block.id, existing);
622
+ }
623
+ } else if (block.type === "server_tool_call") {
624
+ yield* flushMessage();
625
+ const id = block.id ?? "";
626
+ if (id) {
627
+ serverFunctionCallIdsWithBlocks.add(id);
628
+ pendingServerFunctionChunks.delete(id);
629
+ }
630
+ yield convertFunctionCall(block);
631
+ } else if (block.type === "server_tool_call_chunk") {
632
+ if (block.id) {
633
+ const existing = pendingServerFunctionChunks.get(block.id) ?? {
634
+ name: block.name,
635
+ args: []
636
+ };
637
+ if (block.name) existing.name = block.name;
638
+ if (block.args) existing.args.push(block.args);
639
+ pendingServerFunctionChunks.set(block.id, existing);
640
+ }
641
+ } else if (block.type === "server_tool_call_result") {
642
+ yield* flushMessage();
643
+ yield convertFunctionCallOutput(block);
644
+ } else if (block.type === "audio") {} else if (block.type === "file") {
645
+ const fileItem = resolveFileItem(block);
646
+ if (fileItem) pushMessageContent([fileItem]);
647
+ } else if (block.type === "image") {
648
+ const imageItem = resolveImageItem(block);
649
+ if (imageItem) pushMessageContent([imageItem]);
650
+ } else if (block.type === "video") {
651
+ const videoItem = resolveFileItem(block);
652
+ if (videoItem) pushMessageContent([videoItem]);
653
+ } else if (block.type === "text-plain") {
654
+ if (block.text) pushMessageContent([{
655
+ type: "input_text",
656
+ text: block.text
657
+ }]);
658
+ } else if (block.type === "non_standard" && isResponsesMessage) {
659
+ yield* flushMessage();
660
+ yield block.value;
661
+ }
662
+ yield* flushMessage();
663
+ for (const [id, chunk] of pendingFunctionChunks) {
664
+ if (!id || functionCallIdsWithBlocks.has(id)) continue;
665
+ const args = chunk.args.join("");
666
+ if (!chunk.name && !args) continue;
667
+ yield {
668
+ type: "function_call",
669
+ call_id: id,
670
+ name: chunk.name ?? "",
671
+ arguments: args
672
+ };
673
+ }
674
+ for (const [id, chunk] of pendingServerFunctionChunks) {
675
+ if (!id || serverFunctionCallIdsWithBlocks.has(id)) continue;
676
+ const args = chunk.args.join("");
677
+ if (!chunk.name && !args) continue;
678
+ yield {
679
+ type: "function_call",
680
+ call_id: id,
681
+ name: chunk.name ?? "",
682
+ arguments: args
683
+ };
684
+ }
685
+ }
686
+ return Array.from(iterateItems());
687
+ };
688
+ /**
689
+ * - MCP (Model Context Protocol) approval responses
690
+ * - Zero Data Retention (ZDR) mode handling
691
+ *
692
+ * @param params - Conversion parameters
693
+ * @param params.messages - Array of LangChain BaseMessages to convert
694
+ * @param params.zdrEnabled - Whether Zero Data Retention mode is enabled. When true, certain
695
+ * metadata like message IDs and function call IDs are omitted from the output
696
+ * @param params.model - The model name being used. Used to determine if special role mapping
697
+ * is needed (e.g., "system" -> "developer" for reasoning models)
698
+ *
699
+ * @returns Array of ResponsesInputItem objects formatted for the OpenAI Responses API
700
+ *
701
+ * @throws {Error} When a function message is encountered (not supported)
702
+ * @throws {Error} When computer call output format is invalid
703
+ *
704
+ * @example
705
+ * ```typescript
706
+ * const messages = [
707
+ * new HumanMessage("Hello"),
708
+ * new AIMessage({ content: "Hi there!", tool_calls: [...] })
709
+ * ];
710
+ *
711
+ * const input = convertMessagesToResponsesInput({
712
+ * messages,
713
+ * zdrEnabled: false,
714
+ * model: "gpt-4"
715
+ * });
716
+ * ```
717
+ */
718
+ const convertMessagesToResponsesInput = ({ messages, zdrEnabled, model }) => {
719
+ return messages.flatMap((lcMsg) => {
720
+ const responseMetadata = lcMsg.response_metadata;
721
+ if (responseMetadata?.output_version === "v1") return convertStandardContentMessageToResponsesInput(lcMsg);
722
+ const additional_kwargs = lcMsg.additional_kwargs;
723
+ let role = messageToOpenAIRole(lcMsg);
724
+ if (role === "system" && isReasoningModel(model)) role = "developer";
725
+ if (role === "function") throw new Error("Function messages are not supported in Responses API");
726
+ if (role === "tool") {
727
+ const toolMessage = lcMsg;
728
+ if (additional_kwargs?.type === "computer_call_output") {
729
+ const output = (() => {
730
+ if (typeof toolMessage.content === "string") return {
731
+ type: "computer_screenshot",
732
+ image_url: toolMessage.content
733
+ };
734
+ if (Array.isArray(toolMessage.content)) {
735
+ const oaiScreenshot = toolMessage.content.find((i) => i.type === "computer_screenshot");
736
+ if (oaiScreenshot) return oaiScreenshot;
737
+ const lcImage = toolMessage.content.find((i) => i.type === "image_url");
738
+ if (lcImage) return {
739
+ type: "computer_screenshot",
740
+ image_url: typeof lcImage.image_url === "string" ? lcImage.image_url : lcImage.image_url.url
741
+ };
742
+ }
743
+ throw new Error("Invalid computer call output");
744
+ })();
745
+ return {
746
+ type: "computer_call_output",
747
+ output,
748
+ call_id: toolMessage.tool_call_id
749
+ };
750
+ }
751
+ if (toolMessage.additional_kwargs?.customTool) return {
752
+ type: "custom_tool_call_output",
753
+ call_id: toolMessage.tool_call_id,
754
+ output: toolMessage.content
755
+ };
756
+ return {
757
+ type: "function_call_output",
758
+ call_id: toolMessage.tool_call_id,
759
+ id: toolMessage.id?.startsWith("fc_") ? toolMessage.id : void 0,
760
+ output: typeof toolMessage.content !== "string" ? JSON.stringify(toolMessage.content) : toolMessage.content
761
+ };
762
+ }
763
+ if (role === "assistant") {
764
+ if (!zdrEnabled && responseMetadata?.output != null && Array.isArray(responseMetadata?.output) && responseMetadata?.output.length > 0 && responseMetadata?.output.every((item) => "type" in item)) return responseMetadata?.output;
765
+ const input = [];
766
+ if (additional_kwargs?.reasoning && !zdrEnabled) {
767
+ const reasoningItem = convertReasoningSummaryToResponsesReasoningItem(additional_kwargs.reasoning);
768
+ input.push(reasoningItem);
769
+ }
770
+ let { content } = lcMsg;
771
+ if (additional_kwargs?.refusal) {
772
+ if (typeof content === "string") content = [{
773
+ type: "output_text",
774
+ text: content,
775
+ annotations: []
776
+ }];
777
+ content = [...content, {
778
+ type: "refusal",
779
+ refusal: additional_kwargs.refusal
780
+ }];
781
+ }
782
+ if (typeof content === "string" || content.length > 0) input.push({
783
+ type: "message",
784
+ role: "assistant",
785
+ ...lcMsg.id && !zdrEnabled && lcMsg.id.startsWith("msg_") ? { id: lcMsg.id } : {},
786
+ content: iife(() => {
787
+ if (typeof content === "string") return content;
788
+ return content.flatMap((item) => {
789
+ if (item.type === "text") return {
790
+ type: "output_text",
791
+ text: item.text,
792
+ annotations: item.annotations ?? []
793
+ };
794
+ if (item.type === "output_text" || item.type === "refusal") return item;
795
+ return [];
796
+ });
797
+ })
798
+ });
799
+ const functionCallIds = additional_kwargs?.[_FUNCTION_CALL_IDS_MAP_KEY];
800
+ if (AIMessage.isInstance(lcMsg) && !!lcMsg.tool_calls?.length) input.push(...lcMsg.tool_calls.map((toolCall) => {
801
+ if (isCustomToolCall(toolCall)) return {
802
+ type: "custom_tool_call",
803
+ id: toolCall.call_id,
804
+ call_id: toolCall.id ?? "",
805
+ input: toolCall.args.input,
806
+ name: toolCall.name
807
+ };
808
+ return {
809
+ type: "function_call",
810
+ name: toolCall.name,
811
+ arguments: JSON.stringify(toolCall.args),
812
+ call_id: toolCall.id,
813
+ ...!zdrEnabled ? { id: functionCallIds?.[toolCall.id] } : {}
814
+ };
815
+ }));
816
+ else if (additional_kwargs?.tool_calls) input.push(...additional_kwargs.tool_calls.map((toolCall) => ({
817
+ type: "function_call",
818
+ name: toolCall.function.name,
819
+ call_id: toolCall.id,
820
+ arguments: toolCall.function.arguments,
821
+ ...!zdrEnabled ? { id: functionCallIds?.[toolCall.id] } : {}
822
+ })));
823
+ const toolOutputs = (responseMetadata?.output)?.length ? responseMetadata?.output : additional_kwargs.tool_outputs;
824
+ const fallthroughCallTypes = [
825
+ "computer_call",
826
+ "mcp_call",
827
+ "code_interpreter_call",
828
+ "image_generation_call"
829
+ ];
830
+ if (toolOutputs != null) {
831
+ const castToolOutputs = toolOutputs;
832
+ const fallthroughCalls = castToolOutputs?.filter((item) => fallthroughCallTypes.includes(item.type));
833
+ if (fallthroughCalls.length > 0) input.push(...fallthroughCalls);
834
+ }
835
+ return input;
836
+ }
837
+ if (role === "user" || role === "system" || role === "developer") {
838
+ if (typeof lcMsg.content === "string") return {
839
+ type: "message",
840
+ role,
841
+ content: lcMsg.content
842
+ };
843
+ const messages$1 = [];
844
+ const content = lcMsg.content.flatMap((item) => {
845
+ if (item.type === "mcp_approval_response") messages$1.push({
846
+ type: "mcp_approval_response",
847
+ approval_request_id: item.approval_request_id,
848
+ approve: item.approve
849
+ });
850
+ if (isDataContentBlock(item)) return convertToProviderContentBlock(item, completionsApiContentBlockConverter);
851
+ if (item.type === "text") return {
852
+ type: "input_text",
853
+ text: item.text
854
+ };
855
+ if (item.type === "image_url") {
856
+ const imageUrl = iife(() => {
857
+ if (typeof item.image_url === "string") return item.image_url;
858
+ else if (typeof item.image_url === "object" && item.image_url !== null && "url" in item.image_url) return item.image_url.url;
859
+ return void 0;
860
+ });
861
+ const detail = iife(() => {
862
+ if (typeof item.image_url === "string") return "auto";
863
+ else if (typeof item.image_url === "object" && item.image_url !== null && "detail" in item.image_url) return item.image_url.detail;
864
+ return void 0;
865
+ });
866
+ return {
867
+ type: "input_image",
868
+ image_url: imageUrl,
869
+ detail
870
+ };
871
+ }
872
+ if (item.type === "input_text" || item.type === "input_image" || item.type === "input_file") return item;
873
+ return [];
874
+ });
875
+ if (content.length > 0) messages$1.push({
876
+ type: "message",
877
+ role,
878
+ content
879
+ });
880
+ return messages$1;
881
+ }
882
+ console.warn(`Unsupported role found when converting to OpenAI Responses API: ${role}`);
883
+ return [];
884
+ });
885
+ };
886
+
887
+ //#endregion
888
+ export { convertMessagesToResponsesInput, convertResponsesDeltaToChatGenerationChunk, convertResponsesMessageToAIMessage };
889
+ //# sourceMappingURL=responses.js.map