@langchain/openai 0.5.7 → 0.5.9

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.
@@ -232,8 +232,32 @@ function _convertMessagesToOpenAIParams(messages, model) {
232
232
  }
233
233
  exports._convertMessagesToOpenAIParams = _convertMessagesToOpenAIParams;
234
234
  const _FUNCTION_CALL_IDS_MAP_KEY = "__openai_function_call_ids__";
235
- function _convertMessagesToOpenAIResponsesParams(messages, model) {
236
- return messages.flatMap((lcMsg) => {
235
+ function _convertReasoningSummaryToOpenAIResponsesParams(reasoning) {
236
+ // combine summary parts that have the the same index and then remove the indexes
237
+ const summary = (reasoning.summary.length > 1
238
+ ? reasoning.summary.reduce((acc, curr) => {
239
+ const last = acc.at(-1);
240
+ if (last.index === curr.index) {
241
+ last.text += curr.text;
242
+ }
243
+ else {
244
+ acc.push(curr);
245
+ }
246
+ return acc;
247
+ }, [{ ...reasoning.summary[0] }])
248
+ : reasoning.summary).map((s) => Object.fromEntries(Object.entries(s).filter(([k]) => k !== "index")));
249
+ return {
250
+ ...reasoning,
251
+ summary,
252
+ };
253
+ }
254
+ function _convertMessagesToOpenAIResponsesParams(messages, model, zdrEnabled) {
255
+ const lastAIMessage = messages.filter((m) => (0, messages_1.isAIMessage)(m)).pop();
256
+ const lastAIMessageId = lastAIMessage?.response_metadata?.id;
257
+ const newMessages = lastAIMessageId && lastAIMessageId.startsWith("resp_") && !zdrEnabled
258
+ ? messages.slice(messages.indexOf(lastAIMessage) + 1)
259
+ : messages;
260
+ return newMessages.flatMap((lcMsg) => {
237
261
  let role = messageToOpenAIRole(lcMsg);
238
262
  if (role === "system" && isReasoningModel(model))
239
263
  role = "developer";
@@ -283,15 +307,25 @@ function _convertMessagesToOpenAIResponsesParams(messages, model) {
283
307
  };
284
308
  }
285
309
  if (role === "assistant") {
310
+ // if we have the original response items, just reuse them
311
+ if (!zdrEnabled &&
312
+ lcMsg.response_metadata.output != null &&
313
+ Array.isArray(lcMsg.response_metadata.output) &&
314
+ lcMsg.response_metadata.output.length > 0 &&
315
+ lcMsg.response_metadata.output.every((item) => "type" in item)) {
316
+ return lcMsg.response_metadata.output;
317
+ }
318
+ // otherwise, try to reconstruct the response from what we have
286
319
  const input = [];
287
320
  // reasoning items
288
- if (lcMsg.additional_kwargs.reasoning != null) {
321
+ if (!zdrEnabled && lcMsg.additional_kwargs.reasoning != null) {
289
322
  const isReasoningItem = (item) => typeof item === "object" &&
290
323
  item != null &&
291
324
  "type" in item &&
292
325
  item.type === "reasoning";
293
326
  if (isReasoningItem(lcMsg.additional_kwargs.reasoning)) {
294
- input.push(lcMsg.additional_kwargs.reasoning);
327
+ const reasoningItem = _convertReasoningSummaryToOpenAIResponsesParams(lcMsg.additional_kwargs.reasoning);
328
+ input.push(reasoningItem);
295
329
  }
296
330
  }
297
331
  // ai content
@@ -308,6 +342,7 @@ function _convertMessagesToOpenAIResponsesParams(messages, model) {
308
342
  input.push({
309
343
  type: "message",
310
344
  role: "assistant",
345
+ ...(lcMsg.id && !zdrEnabled ? { id: lcMsg.id } : {}),
311
346
  content: typeof content === "string"
312
347
  ? content
313
348
  : content.flatMap((item) => {
@@ -335,7 +370,7 @@ function _convertMessagesToOpenAIResponsesParams(messages, model) {
335
370
  name: toolCall.name,
336
371
  arguments: JSON.stringify(toolCall.args),
337
372
  call_id: toolCall.id,
338
- id: functionCallIds?.[toolCall.id],
373
+ ...(zdrEnabled ? { id: functionCallIds?.[toolCall.id] } : {}),
339
374
  })));
340
375
  }
341
376
  else if (lcMsg.additional_kwargs.tool_calls != null) {
@@ -343,21 +378,17 @@ function _convertMessagesToOpenAIResponsesParams(messages, model) {
343
378
  type: "function_call",
344
379
  name: toolCall.function.name,
345
380
  call_id: toolCall.id,
346
- id: functionCallIds?.[toolCall.id],
381
+ ...(zdrEnabled ? { id: functionCallIds?.[toolCall.id] } : {}),
347
382
  arguments: toolCall.function.arguments,
348
383
  })));
349
384
  }
350
385
  const toolOutputs = lcMsg.response_metadata.output?.length
351
386
  ? lcMsg.response_metadata.output
352
387
  : lcMsg.additional_kwargs.tool_outputs;
388
+ let computerCalls = [];
353
389
  if (toolOutputs != null) {
354
390
  const castToolOutputs = toolOutputs;
355
- const reasoningCalls = castToolOutputs?.filter((item) => item.type === "reasoning");
356
- const computerCalls = castToolOutputs?.filter((item) => item.type === "computer_call");
357
- // NOTE: Reasoning outputs must be passed to the model BEFORE computer calls.
358
- if (reasoningCalls.length > 0 && computerCalls.length > 0) {
359
- input.push(...reasoningCalls);
360
- }
391
+ computerCalls = castToolOutputs?.filter((item) => item.type === "computer_call");
361
392
  if (computerCalls.length > 0)
362
393
  input.push(...computerCalls);
363
394
  }
@@ -402,6 +433,7 @@ function _convertOpenAIResponsesMessageToBaseMessage(response) {
402
433
  error.name = response.error.code;
403
434
  throw error;
404
435
  }
436
+ let messageId;
405
437
  const content = [];
406
438
  const tool_calls = [];
407
439
  const invalid_tool_calls = [];
@@ -420,6 +452,7 @@ function _convertOpenAIResponsesMessageToBaseMessage(response) {
420
452
  const additional_kwargs = {};
421
453
  for (const item of response.output) {
422
454
  if (item.type === "message") {
455
+ messageId = item.id;
423
456
  content.push(...item.content.flatMap((part) => {
424
457
  if (part.type === "output_text") {
425
458
  if ("parsed" in part && part.parsed != null) {
@@ -470,7 +503,7 @@ function _convertOpenAIResponsesMessageToBaseMessage(response) {
470
503
  }
471
504
  }
472
505
  return new messages_1.AIMessage({
473
- id: response.id,
506
+ id: messageId,
474
507
  content,
475
508
  tool_calls,
476
509
  invalid_tool_calls,
@@ -561,6 +594,39 @@ function _convertOpenAIResponsesDeltaToBaseMessageChunk(chunk) {
561
594
  else if (chunk.type === "response.refusal.done") {
562
595
  additional_kwargs.refusal = chunk.refusal;
563
596
  }
597
+ else if (chunk.type === "response.output_item.added" &&
598
+ "item" in chunk &&
599
+ chunk.item.type === "reasoning") {
600
+ const summary = chunk
601
+ .item.summary
602
+ ? chunk.item.summary.map((s, index) => ({
603
+ ...s,
604
+ index,
605
+ }))
606
+ : undefined;
607
+ additional_kwargs.reasoning = {
608
+ // We only capture ID in the first chunk or else the concatenated result of all chunks will
609
+ // have an ID field that is repeated once per chunk. There is special handling for the `type`
610
+ // field that prevents this, however.
611
+ id: chunk.item.id,
612
+ type: chunk.item.type,
613
+ ...(summary ? { summary } : {}),
614
+ };
615
+ }
616
+ else if (chunk.type === "response.reasoning_summary_part.added") {
617
+ additional_kwargs.reasoning = {
618
+ type: "reasoning",
619
+ summary: [{ ...chunk.part, index: chunk.summary_index }],
620
+ };
621
+ }
622
+ else if (chunk.type === "response.reasoning_summary_text.delta") {
623
+ additional_kwargs.reasoning = {
624
+ type: "reasoning",
625
+ summary: [
626
+ { text: chunk.delta, type: "summary_text", index: chunk.summary_index },
627
+ ],
628
+ };
629
+ }
564
630
  else {
565
631
  return null;
566
632
  }
@@ -603,9 +669,7 @@ function _convertChatOpenAIToolTypeToOpenAITool(tool, fields) {
603
669
  return (0, tools_js_1._convertToOpenAITool)(tool, fields);
604
670
  }
605
671
  function isReasoningModel(model) {
606
- return (model?.startsWith("o1") ||
607
- model?.startsWith("o3") ||
608
- model?.startsWith("o4"));
672
+ return model && /^o\d/.test(model);
609
673
  }
610
674
  /**
611
675
  * OpenAI chat model integration.
@@ -1213,6 +1277,9 @@ class ChatOpenAI extends chat_models_1.BaseChatModel {
1213
1277
  "tags",
1214
1278
  "metadata",
1215
1279
  "disableStreaming",
1280
+ "useResponsesApi",
1281
+ "zdrEnabled",
1282
+ "reasoning",
1216
1283
  ];
1217
1284
  }
1218
1285
  constructor(fields) {
@@ -1390,18 +1457,51 @@ class ChatOpenAI extends chat_models_1.BaseChatModel {
1390
1457
  writable: true,
1391
1458
  value: void 0
1392
1459
  });
1460
+ /**
1461
+ * @deprecated Use {@link reasoning} object instead.
1462
+ */
1393
1463
  Object.defineProperty(this, "reasoningEffort", {
1394
1464
  enumerable: true,
1395
1465
  configurable: true,
1396
1466
  writable: true,
1397
1467
  value: void 0
1398
1468
  });
1469
+ /**
1470
+ * Options for reasoning models.
1471
+ */
1472
+ Object.defineProperty(this, "reasoning", {
1473
+ enumerable: true,
1474
+ configurable: true,
1475
+ writable: true,
1476
+ value: void 0
1477
+ });
1478
+ /**
1479
+ * Whether to use the responses API for all requests. If `false` the responses API will be used
1480
+ * only when required in order to fulfill the request.
1481
+ */
1399
1482
  Object.defineProperty(this, "useResponsesApi", {
1400
1483
  enumerable: true,
1401
1484
  configurable: true,
1402
1485
  writable: true,
1403
1486
  value: false
1404
1487
  });
1488
+ /**
1489
+ * Must be set to `true` in tenancies with Zero Data Retention. Setting to `true` will disable
1490
+ * output storage in the Responses API, but this DOES NOT enable Zero Data Retention in your
1491
+ * OpenAI organization or project. This must be configured directly with OpenAI.
1492
+ *
1493
+ * See:
1494
+ * https://help.openai.com/en/articles/10503543-data-residency-for-the-openai-api
1495
+ * https://platform.openai.com/docs/api-reference/responses/create#responses-create-store
1496
+ *
1497
+ * @default false
1498
+ */
1499
+ Object.defineProperty(this, "zdrEnabled", {
1500
+ enumerable: true,
1501
+ configurable: true,
1502
+ writable: true,
1503
+ value: void 0
1504
+ });
1405
1505
  this.openAIApiKey =
1406
1506
  fields?.apiKey ??
1407
1507
  fields?.openAIApiKey ??
@@ -1429,7 +1529,12 @@ class ChatOpenAI extends chat_models_1.BaseChatModel {
1429
1529
  this.__includeRawResponse = fields?.__includeRawResponse;
1430
1530
  this.audio = fields?.audio;
1431
1531
  this.modalities = fields?.modalities;
1432
- this.reasoningEffort = fields?.reasoningEffort;
1532
+ this.reasoningEffort = fields?.reasoningEffort ?? fields?.reasoning?.effort;
1533
+ this.reasoning =
1534
+ fields?.reasoning ??
1535
+ (fields?.reasoningEffort
1536
+ ? { effort: fields.reasoningEffort }
1537
+ : undefined);
1433
1538
  this.maxTokens = fields?.maxCompletionTokens ?? fields?.maxTokens;
1434
1539
  this.useResponsesApi = fields?.useResponsesApi ?? this.useResponsesApi;
1435
1540
  this.disableStreaming = fields?.disableStreaming ?? this.disableStreaming;
@@ -1452,6 +1557,7 @@ class ChatOpenAI extends chat_models_1.BaseChatModel {
1452
1557
  if (fields?.supportsStrictToolCalling !== undefined) {
1453
1558
  this.supportsStrictToolCalling = fields.supportsStrictToolCalling;
1454
1559
  }
1560
+ this.zdrEnabled = fields?.zdrEnabled ?? false;
1455
1561
  }
1456
1562
  getLsParams(options) {
1457
1563
  const params = this.invocationParams(options);
@@ -1490,6 +1596,35 @@ class ChatOpenAI extends chat_models_1.BaseChatModel {
1490
1596
  }
1491
1597
  return resFormat;
1492
1598
  }
1599
+ getReasoningParams(options) {
1600
+ if (!isReasoningModel(this.model)) {
1601
+ return;
1602
+ }
1603
+ let reasoning;
1604
+ // apply reasoning options in reverse order of priority without else blocks so that highest priority overrides lowest priority
1605
+ if (this.reasoningEffort !== undefined) {
1606
+ reasoning = { effort: this.reasoningEffort };
1607
+ }
1608
+ if (this.reasoning !== undefined) {
1609
+ reasoning = {
1610
+ ...reasoning,
1611
+ ...this.reasoning,
1612
+ };
1613
+ }
1614
+ if (options?.reasoning_effort !== undefined) {
1615
+ reasoning = {
1616
+ ...reasoning,
1617
+ effort: options.reasoning_effort,
1618
+ };
1619
+ }
1620
+ if (options?.reasoning !== undefined) {
1621
+ reasoning = {
1622
+ ...reasoning,
1623
+ ...options.reasoning,
1624
+ };
1625
+ }
1626
+ return reasoning;
1627
+ }
1493
1628
  /**
1494
1629
  * Get the parameters used to invoke the model
1495
1630
  */
@@ -1566,11 +1701,12 @@ class ChatOpenAI extends chat_models_1.BaseChatModel {
1566
1701
  })(),
1567
1702
  parallel_tool_calls: options?.parallel_tool_calls,
1568
1703
  max_output_tokens: this.maxTokens === -1 ? undefined : this.maxTokens,
1704
+ ...(this.zdrEnabled ? { store: false } : {}),
1569
1705
  ...this.modelKwargs,
1570
1706
  };
1571
- const reasoningEffort = options?.reasoning_effort ?? this.reasoningEffort;
1572
- if (reasoningEffort !== undefined) {
1573
- params.reasoning = { effort: reasoningEffort };
1707
+ const reasoning = this.getReasoningParams(options);
1708
+ if (reasoning !== undefined) {
1709
+ params.reasoning = reasoning;
1574
1710
  }
1575
1711
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
1576
1712
  return params;
@@ -1617,9 +1753,9 @@ class ChatOpenAI extends chat_models_1.BaseChatModel {
1617
1753
  if (options?.prediction !== undefined) {
1618
1754
  params.prediction = options.prediction;
1619
1755
  }
1620
- const reasoningEffort = options?.reasoning_effort ?? this.reasoningEffort;
1621
- if (reasoningEffort !== undefined) {
1622
- params.reasoning_effort = reasoningEffort;
1756
+ const reasoning = this.getReasoningParams(options);
1757
+ if (reasoning !== undefined && reasoning.effort !== undefined) {
1758
+ params.reasoning_effort = reasoning.effort;
1623
1759
  }
1624
1760
  if (isReasoningModel(params.model)) {
1625
1761
  params.max_completion_tokens =
@@ -1773,10 +1909,17 @@ class ChatOpenAI extends chat_models_1.BaseChatModel {
1773
1909
  }
1774
1910
  async *_streamResponseChunks(messages, options, runManager) {
1775
1911
  if (this._useResponseApi(options)) {
1912
+ const lastAIMessage = messages.filter((m) => (0, messages_1.isAIMessage)(m)).pop();
1913
+ const lastAIMessageId = lastAIMessage?.response_metadata?.id;
1776
1914
  const streamIterable = await this.responseApiWithRetry({
1777
1915
  ...this.invocationParams(options, { streaming: true }),
1778
- input: _convertMessagesToOpenAIResponsesParams(messages, this.model),
1916
+ input: _convertMessagesToOpenAIResponsesParams(messages, this.model, this.zdrEnabled),
1779
1917
  stream: true,
1918
+ ...(lastAIMessageId &&
1919
+ lastAIMessageId.startsWith("resp_") &&
1920
+ !this.zdrEnabled
1921
+ ? { previous_response_id: lastAIMessageId }
1922
+ : {}),
1780
1923
  }, options);
1781
1924
  for await (const data of streamIterable) {
1782
1925
  const chunk = _convertOpenAIResponsesDeltaToBaseMessageChunk(data);
@@ -1910,8 +2053,18 @@ class ChatOpenAI extends chat_models_1.BaseChatModel {
1910
2053
  },
1911
2054
  };
1912
2055
  }
1913
- const input = _convertMessagesToOpenAIResponsesParams(messages, this.model);
1914
- const data = await this.responseApiWithRetry({ input, ...invocationParams }, { signal: options?.signal, ...options?.options });
2056
+ const lastAIMessage = messages.filter((m) => (0, messages_1.isAIMessage)(m)).pop();
2057
+ const lastAIMessageId = lastAIMessage?.response_metadata?.id;
2058
+ const input = _convertMessagesToOpenAIResponsesParams(messages, this.model, this.zdrEnabled);
2059
+ const data = await this.responseApiWithRetry({
2060
+ input,
2061
+ ...invocationParams,
2062
+ ...(lastAIMessageId &&
2063
+ lastAIMessageId.startsWith("resp_") &&
2064
+ !this.zdrEnabled
2065
+ ? { previous_response_id: lastAIMessageId }
2066
+ : {}),
2067
+ }, { signal: options?.signal, ...options?.options });
1915
2068
  return {
1916
2069
  generations: [
1917
2070
  {
@@ -1931,12 +2084,23 @@ class ChatOpenAI extends chat_models_1.BaseChatModel {
1931
2084
  },
1932
2085
  };
1933
2086
  }
2087
+ /**
2088
+ * Determines whether the responses API should be used for the given request.
2089
+ *
2090
+ * @internal
2091
+ *
2092
+ * @param options The parsed call options for the request.
2093
+ * @returns `true` if the responses API should be used, either because it is explicitly enabled,
2094
+ * or because the request requires it.
2095
+ */
1934
2096
  _useResponseApi(options) {
1935
2097
  const usesBuiltInTools = options?.tools?.some(isBuiltInTool);
1936
2098
  const hasResponsesOnlyKwargs = options?.previous_response_id != null ||
1937
2099
  options?.text != null ||
1938
2100
  options?.truncation != null ||
1939
- options?.include != null;
2101
+ options?.include != null ||
2102
+ options?.reasoning?.summary != null ||
2103
+ this.reasoning?.summary != null;
1940
2104
  return this.useResponsesApi || usesBuiltInTools || hasResponsesOnlyKwargs;
1941
2105
  }
1942
2106
  /** @ignore */
@@ -6,7 +6,7 @@ import { BaseChatModel, type BindToolsInput, type LangSmithParams, type BaseChat
6
6
  import { type BaseFunctionCallOptions, type BaseLanguageModelInput, type StructuredOutputMethodOptions } from "@langchain/core/language_models/base";
7
7
  import { z } from "zod";
8
8
  import { Runnable } from "@langchain/core/runnables";
9
- import type { OpenAICallOptions, OpenAIChatInput, OpenAICoreRequestOptions, ChatOpenAIResponseFormat } from "./types.js";
9
+ import { type OpenAICallOptions, type OpenAIChatInput, type OpenAICoreRequestOptions, type ChatOpenAIResponseFormat } from "./types.js";
10
10
  import { OpenAIToolChoice } from "./utils/openai.js";
11
11
  export type { OpenAICallOptions, OpenAIChatInput };
12
12
  interface TokenUsage {
@@ -117,29 +117,54 @@ export interface ChatOpenAICallOptions extends OpenAICallOptions, BaseFunctionCa
117
117
  /**
118
118
  * Constrains effort on reasoning for reasoning models. Currently supported values are low, medium, and high.
119
119
  * Reducing reasoning effort can result in faster responses and fewer tokens used on reasoning in a response.
120
+ *
121
+ * @deprecated Use {@link reasoning} object instead.
120
122
  */
121
123
  reasoning_effort?: OpenAIClient.Chat.ChatCompletionReasoningEffort;
124
+ /**
125
+ * Options for reasoning models.
126
+ *
127
+ * Note that some options, like reasoning summaries, are only available when using the responses
128
+ * API. If these options are set, the responses API will be used to fulfill the request.
129
+ *
130
+ * These options will be ignored when not using a reasoning model.
131
+ */
132
+ reasoning?: OpenAIClient.Reasoning;
122
133
  /**
123
134
  * Configuration options for a text response from the model. Can be plain text or
124
135
  * structured JSON data.
136
+ *
137
+ * If set, the Responses API will be used to fulfill the request.
125
138
  */
126
139
  text?: ResponsesCreateParams["text"];
127
140
  /**
128
141
  * The truncation strategy to use for the model response.
142
+ *
143
+ * If set, the Responses API will be used to fulfill the request.
129
144
  */
130
145
  truncation?: ResponsesCreateParams["truncation"];
131
146
  /**
132
147
  * Specify additional output data to include in the model response.
148
+ *
149
+ * If set, the Responses API will be used to fulfill the request.
133
150
  */
134
151
  include?: ResponsesCreateParams["include"];
135
152
  /**
136
- * The unique ID of the previous response to the model. Use this to create
137
- * multi-turn conversations.
153
+ * The unique ID of the previous response to the model. Use this to create multi-turn
154
+ * conversations. Will be set automatically if {@link ChatOpenAI.zdrEnabled} is `false`, provided
155
+ * that AIMessages included in the request have a `response_metadata.id` property. Ignored unless
156
+ * {@link ChatOpenAI.useResponsesApi} is `true`.
157
+ *
158
+ * If set, the Responses API will be used to fulfill the request.
138
159
  */
139
160
  previous_response_id?: ResponsesCreateParams["previous_response_id"];
140
161
  }
141
162
  export interface ChatOpenAIFields extends Partial<OpenAIChatInput>, BaseChatModelParams {
142
163
  configuration?: ClientOptions;
164
+ /**
165
+ * Whether to use the responses API for all requests. If `false` the responses API will be used
166
+ * only when required in order to fulfill the request.
167
+ */
143
168
  useResponsesApi?: boolean;
144
169
  }
145
170
  /**
@@ -721,12 +746,36 @@ export declare class ChatOpenAI<CallOptions extends ChatOpenAICallOptions = Chat
721
746
  supportsStrictToolCalling?: boolean;
722
747
  audio?: OpenAIClient.Chat.ChatCompletionAudioParam;
723
748
  modalities?: Array<OpenAIClient.Chat.ChatCompletionModality>;
749
+ /**
750
+ * @deprecated Use {@link reasoning} object instead.
751
+ */
724
752
  reasoningEffort?: OpenAIClient.Chat.ChatCompletionReasoningEffort;
753
+ /**
754
+ * Options for reasoning models.
755
+ */
756
+ reasoning?: OpenAIClient.Reasoning;
757
+ /**
758
+ * Whether to use the responses API for all requests. If `false` the responses API will be used
759
+ * only when required in order to fulfill the request.
760
+ */
725
761
  useResponsesApi: boolean;
762
+ /**
763
+ * Must be set to `true` in tenancies with Zero Data Retention. Setting to `true` will disable
764
+ * output storage in the Responses API, but this DOES NOT enable Zero Data Retention in your
765
+ * OpenAI organization or project. This must be configured directly with OpenAI.
766
+ *
767
+ * See:
768
+ * https://help.openai.com/en/articles/10503543-data-residency-for-the-openai-api
769
+ * https://platform.openai.com/docs/api-reference/responses/create#responses-create-store
770
+ *
771
+ * @default false
772
+ */
773
+ zdrEnabled?: boolean | undefined;
726
774
  constructor(fields?: ChatOpenAIFields);
727
775
  getLsParams(options: this["ParsedCallOptions"]): LangSmithParams;
728
776
  bindTools(tools: ChatOpenAIToolType[], kwargs?: Partial<CallOptions>): Runnable<BaseLanguageModelInput, AIMessageChunk, CallOptions>;
729
777
  private createResponseFormat;
778
+ private getReasoningParams;
730
779
  /**
731
780
  * Get the parameters used to invoke the model
732
781
  */
@@ -749,6 +798,15 @@ export declare class ChatOpenAI<CallOptions extends ChatOpenAICallOptions = Chat
749
798
  } & ClientOptions;
750
799
  /** @ignore */
751
800
  _responseApiGenerate(messages: BaseMessage[], options: this["ParsedCallOptions"], runManager?: CallbackManagerForLLMRun): Promise<ChatResult>;
801
+ /**
802
+ * Determines whether the responses API should be used for the given request.
803
+ *
804
+ * @internal
805
+ *
806
+ * @param options The parsed call options for the request.
807
+ * @returns `true` if the responses API should be used, either because it is explicitly enabled,
808
+ * or because the request requires it.
809
+ */
752
810
  protected _useResponseApi(options: this["ParsedCallOptions"] | undefined): boolean;
753
811
  /** @ignore */
754
812
  _generate(messages: BaseMessage[], options: this["ParsedCallOptions"], runManager?: CallbackManagerForLLMRun): Promise<ChatResult>;
@@ -227,8 +227,32 @@ export function _convertMessagesToOpenAIParams(messages, model) {
227
227
  });
228
228
  }
229
229
  const _FUNCTION_CALL_IDS_MAP_KEY = "__openai_function_call_ids__";
230
- function _convertMessagesToOpenAIResponsesParams(messages, model) {
231
- return messages.flatMap((lcMsg) => {
230
+ function _convertReasoningSummaryToOpenAIResponsesParams(reasoning) {
231
+ // combine summary parts that have the the same index and then remove the indexes
232
+ const summary = (reasoning.summary.length > 1
233
+ ? reasoning.summary.reduce((acc, curr) => {
234
+ const last = acc.at(-1);
235
+ if (last.index === curr.index) {
236
+ last.text += curr.text;
237
+ }
238
+ else {
239
+ acc.push(curr);
240
+ }
241
+ return acc;
242
+ }, [{ ...reasoning.summary[0] }])
243
+ : reasoning.summary).map((s) => Object.fromEntries(Object.entries(s).filter(([k]) => k !== "index")));
244
+ return {
245
+ ...reasoning,
246
+ summary,
247
+ };
248
+ }
249
+ function _convertMessagesToOpenAIResponsesParams(messages, model, zdrEnabled) {
250
+ const lastAIMessage = messages.filter((m) => isAIMessage(m)).pop();
251
+ const lastAIMessageId = lastAIMessage?.response_metadata?.id;
252
+ const newMessages = lastAIMessageId && lastAIMessageId.startsWith("resp_") && !zdrEnabled
253
+ ? messages.slice(messages.indexOf(lastAIMessage) + 1)
254
+ : messages;
255
+ return newMessages.flatMap((lcMsg) => {
232
256
  let role = messageToOpenAIRole(lcMsg);
233
257
  if (role === "system" && isReasoningModel(model))
234
258
  role = "developer";
@@ -278,15 +302,25 @@ function _convertMessagesToOpenAIResponsesParams(messages, model) {
278
302
  };
279
303
  }
280
304
  if (role === "assistant") {
305
+ // if we have the original response items, just reuse them
306
+ if (!zdrEnabled &&
307
+ lcMsg.response_metadata.output != null &&
308
+ Array.isArray(lcMsg.response_metadata.output) &&
309
+ lcMsg.response_metadata.output.length > 0 &&
310
+ lcMsg.response_metadata.output.every((item) => "type" in item)) {
311
+ return lcMsg.response_metadata.output;
312
+ }
313
+ // otherwise, try to reconstruct the response from what we have
281
314
  const input = [];
282
315
  // reasoning items
283
- if (lcMsg.additional_kwargs.reasoning != null) {
316
+ if (!zdrEnabled && lcMsg.additional_kwargs.reasoning != null) {
284
317
  const isReasoningItem = (item) => typeof item === "object" &&
285
318
  item != null &&
286
319
  "type" in item &&
287
320
  item.type === "reasoning";
288
321
  if (isReasoningItem(lcMsg.additional_kwargs.reasoning)) {
289
- input.push(lcMsg.additional_kwargs.reasoning);
322
+ const reasoningItem = _convertReasoningSummaryToOpenAIResponsesParams(lcMsg.additional_kwargs.reasoning);
323
+ input.push(reasoningItem);
290
324
  }
291
325
  }
292
326
  // ai content
@@ -303,6 +337,7 @@ function _convertMessagesToOpenAIResponsesParams(messages, model) {
303
337
  input.push({
304
338
  type: "message",
305
339
  role: "assistant",
340
+ ...(lcMsg.id && !zdrEnabled ? { id: lcMsg.id } : {}),
306
341
  content: typeof content === "string"
307
342
  ? content
308
343
  : content.flatMap((item) => {
@@ -330,7 +365,7 @@ function _convertMessagesToOpenAIResponsesParams(messages, model) {
330
365
  name: toolCall.name,
331
366
  arguments: JSON.stringify(toolCall.args),
332
367
  call_id: toolCall.id,
333
- id: functionCallIds?.[toolCall.id],
368
+ ...(zdrEnabled ? { id: functionCallIds?.[toolCall.id] } : {}),
334
369
  })));
335
370
  }
336
371
  else if (lcMsg.additional_kwargs.tool_calls != null) {
@@ -338,21 +373,17 @@ function _convertMessagesToOpenAIResponsesParams(messages, model) {
338
373
  type: "function_call",
339
374
  name: toolCall.function.name,
340
375
  call_id: toolCall.id,
341
- id: functionCallIds?.[toolCall.id],
376
+ ...(zdrEnabled ? { id: functionCallIds?.[toolCall.id] } : {}),
342
377
  arguments: toolCall.function.arguments,
343
378
  })));
344
379
  }
345
380
  const toolOutputs = lcMsg.response_metadata.output?.length
346
381
  ? lcMsg.response_metadata.output
347
382
  : lcMsg.additional_kwargs.tool_outputs;
383
+ let computerCalls = [];
348
384
  if (toolOutputs != null) {
349
385
  const castToolOutputs = toolOutputs;
350
- const reasoningCalls = castToolOutputs?.filter((item) => item.type === "reasoning");
351
- const computerCalls = castToolOutputs?.filter((item) => item.type === "computer_call");
352
- // NOTE: Reasoning outputs must be passed to the model BEFORE computer calls.
353
- if (reasoningCalls.length > 0 && computerCalls.length > 0) {
354
- input.push(...reasoningCalls);
355
- }
386
+ computerCalls = castToolOutputs?.filter((item) => item.type === "computer_call");
356
387
  if (computerCalls.length > 0)
357
388
  input.push(...computerCalls);
358
389
  }
@@ -397,6 +428,7 @@ function _convertOpenAIResponsesMessageToBaseMessage(response) {
397
428
  error.name = response.error.code;
398
429
  throw error;
399
430
  }
431
+ let messageId;
400
432
  const content = [];
401
433
  const tool_calls = [];
402
434
  const invalid_tool_calls = [];
@@ -415,6 +447,7 @@ function _convertOpenAIResponsesMessageToBaseMessage(response) {
415
447
  const additional_kwargs = {};
416
448
  for (const item of response.output) {
417
449
  if (item.type === "message") {
450
+ messageId = item.id;
418
451
  content.push(...item.content.flatMap((part) => {
419
452
  if (part.type === "output_text") {
420
453
  if ("parsed" in part && part.parsed != null) {
@@ -465,7 +498,7 @@ function _convertOpenAIResponsesMessageToBaseMessage(response) {
465
498
  }
466
499
  }
467
500
  return new AIMessage({
468
- id: response.id,
501
+ id: messageId,
469
502
  content,
470
503
  tool_calls,
471
504
  invalid_tool_calls,
@@ -556,6 +589,39 @@ function _convertOpenAIResponsesDeltaToBaseMessageChunk(chunk) {
556
589
  else if (chunk.type === "response.refusal.done") {
557
590
  additional_kwargs.refusal = chunk.refusal;
558
591
  }
592
+ else if (chunk.type === "response.output_item.added" &&
593
+ "item" in chunk &&
594
+ chunk.item.type === "reasoning") {
595
+ const summary = chunk
596
+ .item.summary
597
+ ? chunk.item.summary.map((s, index) => ({
598
+ ...s,
599
+ index,
600
+ }))
601
+ : undefined;
602
+ additional_kwargs.reasoning = {
603
+ // We only capture ID in the first chunk or else the concatenated result of all chunks will
604
+ // have an ID field that is repeated once per chunk. There is special handling for the `type`
605
+ // field that prevents this, however.
606
+ id: chunk.item.id,
607
+ type: chunk.item.type,
608
+ ...(summary ? { summary } : {}),
609
+ };
610
+ }
611
+ else if (chunk.type === "response.reasoning_summary_part.added") {
612
+ additional_kwargs.reasoning = {
613
+ type: "reasoning",
614
+ summary: [{ ...chunk.part, index: chunk.summary_index }],
615
+ };
616
+ }
617
+ else if (chunk.type === "response.reasoning_summary_text.delta") {
618
+ additional_kwargs.reasoning = {
619
+ type: "reasoning",
620
+ summary: [
621
+ { text: chunk.delta, type: "summary_text", index: chunk.summary_index },
622
+ ],
623
+ };
624
+ }
559
625
  else {
560
626
  return null;
561
627
  }
@@ -598,9 +664,7 @@ function _convertChatOpenAIToolTypeToOpenAITool(tool, fields) {
598
664
  return _convertToOpenAITool(tool, fields);
599
665
  }
600
666
  function isReasoningModel(model) {
601
- return (model?.startsWith("o1") ||
602
- model?.startsWith("o3") ||
603
- model?.startsWith("o4"));
667
+ return model && /^o\d/.test(model);
604
668
  }
605
669
  /**
606
670
  * OpenAI chat model integration.
@@ -1208,6 +1272,9 @@ export class ChatOpenAI extends BaseChatModel {
1208
1272
  "tags",
1209
1273
  "metadata",
1210
1274
  "disableStreaming",
1275
+ "useResponsesApi",
1276
+ "zdrEnabled",
1277
+ "reasoning",
1211
1278
  ];
1212
1279
  }
1213
1280
  constructor(fields) {
@@ -1385,18 +1452,51 @@ export class ChatOpenAI extends BaseChatModel {
1385
1452
  writable: true,
1386
1453
  value: void 0
1387
1454
  });
1455
+ /**
1456
+ * @deprecated Use {@link reasoning} object instead.
1457
+ */
1388
1458
  Object.defineProperty(this, "reasoningEffort", {
1389
1459
  enumerable: true,
1390
1460
  configurable: true,
1391
1461
  writable: true,
1392
1462
  value: void 0
1393
1463
  });
1464
+ /**
1465
+ * Options for reasoning models.
1466
+ */
1467
+ Object.defineProperty(this, "reasoning", {
1468
+ enumerable: true,
1469
+ configurable: true,
1470
+ writable: true,
1471
+ value: void 0
1472
+ });
1473
+ /**
1474
+ * Whether to use the responses API for all requests. If `false` the responses API will be used
1475
+ * only when required in order to fulfill the request.
1476
+ */
1394
1477
  Object.defineProperty(this, "useResponsesApi", {
1395
1478
  enumerable: true,
1396
1479
  configurable: true,
1397
1480
  writable: true,
1398
1481
  value: false
1399
1482
  });
1483
+ /**
1484
+ * Must be set to `true` in tenancies with Zero Data Retention. Setting to `true` will disable
1485
+ * output storage in the Responses API, but this DOES NOT enable Zero Data Retention in your
1486
+ * OpenAI organization or project. This must be configured directly with OpenAI.
1487
+ *
1488
+ * See:
1489
+ * https://help.openai.com/en/articles/10503543-data-residency-for-the-openai-api
1490
+ * https://platform.openai.com/docs/api-reference/responses/create#responses-create-store
1491
+ *
1492
+ * @default false
1493
+ */
1494
+ Object.defineProperty(this, "zdrEnabled", {
1495
+ enumerable: true,
1496
+ configurable: true,
1497
+ writable: true,
1498
+ value: void 0
1499
+ });
1400
1500
  this.openAIApiKey =
1401
1501
  fields?.apiKey ??
1402
1502
  fields?.openAIApiKey ??
@@ -1424,7 +1524,12 @@ export class ChatOpenAI extends BaseChatModel {
1424
1524
  this.__includeRawResponse = fields?.__includeRawResponse;
1425
1525
  this.audio = fields?.audio;
1426
1526
  this.modalities = fields?.modalities;
1427
- this.reasoningEffort = fields?.reasoningEffort;
1527
+ this.reasoningEffort = fields?.reasoningEffort ?? fields?.reasoning?.effort;
1528
+ this.reasoning =
1529
+ fields?.reasoning ??
1530
+ (fields?.reasoningEffort
1531
+ ? { effort: fields.reasoningEffort }
1532
+ : undefined);
1428
1533
  this.maxTokens = fields?.maxCompletionTokens ?? fields?.maxTokens;
1429
1534
  this.useResponsesApi = fields?.useResponsesApi ?? this.useResponsesApi;
1430
1535
  this.disableStreaming = fields?.disableStreaming ?? this.disableStreaming;
@@ -1447,6 +1552,7 @@ export class ChatOpenAI extends BaseChatModel {
1447
1552
  if (fields?.supportsStrictToolCalling !== undefined) {
1448
1553
  this.supportsStrictToolCalling = fields.supportsStrictToolCalling;
1449
1554
  }
1555
+ this.zdrEnabled = fields?.zdrEnabled ?? false;
1450
1556
  }
1451
1557
  getLsParams(options) {
1452
1558
  const params = this.invocationParams(options);
@@ -1485,6 +1591,35 @@ export class ChatOpenAI extends BaseChatModel {
1485
1591
  }
1486
1592
  return resFormat;
1487
1593
  }
1594
+ getReasoningParams(options) {
1595
+ if (!isReasoningModel(this.model)) {
1596
+ return;
1597
+ }
1598
+ let reasoning;
1599
+ // apply reasoning options in reverse order of priority without else blocks so that highest priority overrides lowest priority
1600
+ if (this.reasoningEffort !== undefined) {
1601
+ reasoning = { effort: this.reasoningEffort };
1602
+ }
1603
+ if (this.reasoning !== undefined) {
1604
+ reasoning = {
1605
+ ...reasoning,
1606
+ ...this.reasoning,
1607
+ };
1608
+ }
1609
+ if (options?.reasoning_effort !== undefined) {
1610
+ reasoning = {
1611
+ ...reasoning,
1612
+ effort: options.reasoning_effort,
1613
+ };
1614
+ }
1615
+ if (options?.reasoning !== undefined) {
1616
+ reasoning = {
1617
+ ...reasoning,
1618
+ ...options.reasoning,
1619
+ };
1620
+ }
1621
+ return reasoning;
1622
+ }
1488
1623
  /**
1489
1624
  * Get the parameters used to invoke the model
1490
1625
  */
@@ -1561,11 +1696,12 @@ export class ChatOpenAI extends BaseChatModel {
1561
1696
  })(),
1562
1697
  parallel_tool_calls: options?.parallel_tool_calls,
1563
1698
  max_output_tokens: this.maxTokens === -1 ? undefined : this.maxTokens,
1699
+ ...(this.zdrEnabled ? { store: false } : {}),
1564
1700
  ...this.modelKwargs,
1565
1701
  };
1566
- const reasoningEffort = options?.reasoning_effort ?? this.reasoningEffort;
1567
- if (reasoningEffort !== undefined) {
1568
- params.reasoning = { effort: reasoningEffort };
1702
+ const reasoning = this.getReasoningParams(options);
1703
+ if (reasoning !== undefined) {
1704
+ params.reasoning = reasoning;
1569
1705
  }
1570
1706
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
1571
1707
  return params;
@@ -1612,9 +1748,9 @@ export class ChatOpenAI extends BaseChatModel {
1612
1748
  if (options?.prediction !== undefined) {
1613
1749
  params.prediction = options.prediction;
1614
1750
  }
1615
- const reasoningEffort = options?.reasoning_effort ?? this.reasoningEffort;
1616
- if (reasoningEffort !== undefined) {
1617
- params.reasoning_effort = reasoningEffort;
1751
+ const reasoning = this.getReasoningParams(options);
1752
+ if (reasoning !== undefined && reasoning.effort !== undefined) {
1753
+ params.reasoning_effort = reasoning.effort;
1618
1754
  }
1619
1755
  if (isReasoningModel(params.model)) {
1620
1756
  params.max_completion_tokens =
@@ -1768,10 +1904,17 @@ export class ChatOpenAI extends BaseChatModel {
1768
1904
  }
1769
1905
  async *_streamResponseChunks(messages, options, runManager) {
1770
1906
  if (this._useResponseApi(options)) {
1907
+ const lastAIMessage = messages.filter((m) => isAIMessage(m)).pop();
1908
+ const lastAIMessageId = lastAIMessage?.response_metadata?.id;
1771
1909
  const streamIterable = await this.responseApiWithRetry({
1772
1910
  ...this.invocationParams(options, { streaming: true }),
1773
- input: _convertMessagesToOpenAIResponsesParams(messages, this.model),
1911
+ input: _convertMessagesToOpenAIResponsesParams(messages, this.model, this.zdrEnabled),
1774
1912
  stream: true,
1913
+ ...(lastAIMessageId &&
1914
+ lastAIMessageId.startsWith("resp_") &&
1915
+ !this.zdrEnabled
1916
+ ? { previous_response_id: lastAIMessageId }
1917
+ : {}),
1775
1918
  }, options);
1776
1919
  for await (const data of streamIterable) {
1777
1920
  const chunk = _convertOpenAIResponsesDeltaToBaseMessageChunk(data);
@@ -1905,8 +2048,18 @@ export class ChatOpenAI extends BaseChatModel {
1905
2048
  },
1906
2049
  };
1907
2050
  }
1908
- const input = _convertMessagesToOpenAIResponsesParams(messages, this.model);
1909
- const data = await this.responseApiWithRetry({ input, ...invocationParams }, { signal: options?.signal, ...options?.options });
2051
+ const lastAIMessage = messages.filter((m) => isAIMessage(m)).pop();
2052
+ const lastAIMessageId = lastAIMessage?.response_metadata?.id;
2053
+ const input = _convertMessagesToOpenAIResponsesParams(messages, this.model, this.zdrEnabled);
2054
+ const data = await this.responseApiWithRetry({
2055
+ input,
2056
+ ...invocationParams,
2057
+ ...(lastAIMessageId &&
2058
+ lastAIMessageId.startsWith("resp_") &&
2059
+ !this.zdrEnabled
2060
+ ? { previous_response_id: lastAIMessageId }
2061
+ : {}),
2062
+ }, { signal: options?.signal, ...options?.options });
1910
2063
  return {
1911
2064
  generations: [
1912
2065
  {
@@ -1926,12 +2079,23 @@ export class ChatOpenAI extends BaseChatModel {
1926
2079
  },
1927
2080
  };
1928
2081
  }
2082
+ /**
2083
+ * Determines whether the responses API should be used for the given request.
2084
+ *
2085
+ * @internal
2086
+ *
2087
+ * @param options The parsed call options for the request.
2088
+ * @returns `true` if the responses API should be used, either because it is explicitly enabled,
2089
+ * or because the request requires it.
2090
+ */
1929
2091
  _useResponseApi(options) {
1930
2092
  const usesBuiltInTools = options?.tools?.some(isBuiltInTool);
1931
2093
  const hasResponsesOnlyKwargs = options?.previous_response_id != null ||
1932
2094
  options?.text != null ||
1933
2095
  options?.truncation != null ||
1934
- options?.include != null;
2096
+ options?.include != null ||
2097
+ options?.reasoning?.summary != null ||
2098
+ this.reasoning?.summary != null;
1935
2099
  return this.useResponsesApi || usesBuiltInTools || hasResponsesOnlyKwargs;
1936
2100
  }
1937
2101
  /** @ignore */
@@ -115,7 +115,7 @@ class DallEAPIWrapper extends tools_1.Tool {
115
115
  if (this.dallEResponseFormat === "url") {
116
116
  return response.flatMap((res) => {
117
117
  const imageUrlContent = res.data
118
- .flatMap((item) => {
118
+ ?.flatMap((item) => {
119
119
  if (!item.url)
120
120
  return [];
121
121
  return {
@@ -126,14 +126,14 @@ class DallEAPIWrapper extends tools_1.Tool {
126
126
  .filter((item) => item !== undefined &&
127
127
  item.type === "image_url" &&
128
128
  typeof item.image_url === "string" &&
129
- item.image_url !== undefined);
129
+ item.image_url !== undefined) ?? [];
130
130
  return imageUrlContent;
131
131
  });
132
132
  }
133
133
  else {
134
134
  return response.flatMap((res) => {
135
135
  const b64Content = res.data
136
- .flatMap((item) => {
136
+ ?.flatMap((item) => {
137
137
  if (!item.b64_json)
138
138
  return [];
139
139
  return {
@@ -148,7 +148,7 @@ class DallEAPIWrapper extends tools_1.Tool {
148
148
  typeof item.image_url === "object" &&
149
149
  "url" in item.image_url &&
150
150
  typeof item.image_url.url === "string" &&
151
- item.image_url.url !== undefined);
151
+ item.image_url.url !== undefined) ?? [];
152
152
  return b64Content;
153
153
  });
154
154
  }
@@ -172,14 +172,17 @@ class DallEAPIWrapper extends tools_1.Tool {
172
172
  const response = await this.client.images.generate(generateImageFields);
173
173
  let data = "";
174
174
  if (this.dallEResponseFormat === "url") {
175
- [data] = response.data
176
- .map((item) => item.url)
177
- .filter((url) => url !== "undefined");
175
+ [data] =
176
+ response.data
177
+ ?.map((item) => item.url)
178
+ .filter((url) => url !== "undefined") ?? [];
178
179
  }
179
180
  else {
180
- [data] = response.data
181
- .map((item) => item.b64_json)
182
- .filter((b64_json) => b64_json !== "undefined");
181
+ [data] =
182
+ response.data
183
+ ?.map((item) => item.b64_json)
184
+ .filter((b64_json) => b64_json !== "undefined") ??
185
+ [];
183
186
  }
184
187
  return data;
185
188
  }
@@ -112,7 +112,7 @@ export class DallEAPIWrapper extends Tool {
112
112
  if (this.dallEResponseFormat === "url") {
113
113
  return response.flatMap((res) => {
114
114
  const imageUrlContent = res.data
115
- .flatMap((item) => {
115
+ ?.flatMap((item) => {
116
116
  if (!item.url)
117
117
  return [];
118
118
  return {
@@ -123,14 +123,14 @@ export class DallEAPIWrapper extends Tool {
123
123
  .filter((item) => item !== undefined &&
124
124
  item.type === "image_url" &&
125
125
  typeof item.image_url === "string" &&
126
- item.image_url !== undefined);
126
+ item.image_url !== undefined) ?? [];
127
127
  return imageUrlContent;
128
128
  });
129
129
  }
130
130
  else {
131
131
  return response.flatMap((res) => {
132
132
  const b64Content = res.data
133
- .flatMap((item) => {
133
+ ?.flatMap((item) => {
134
134
  if (!item.b64_json)
135
135
  return [];
136
136
  return {
@@ -145,7 +145,7 @@ export class DallEAPIWrapper extends Tool {
145
145
  typeof item.image_url === "object" &&
146
146
  "url" in item.image_url &&
147
147
  typeof item.image_url.url === "string" &&
148
- item.image_url.url !== undefined);
148
+ item.image_url.url !== undefined) ?? [];
149
149
  return b64Content;
150
150
  });
151
151
  }
@@ -169,14 +169,17 @@ export class DallEAPIWrapper extends Tool {
169
169
  const response = await this.client.images.generate(generateImageFields);
170
170
  let data = "";
171
171
  if (this.dallEResponseFormat === "url") {
172
- [data] = response.data
173
- .map((item) => item.url)
174
- .filter((url) => url !== "undefined");
172
+ [data] =
173
+ response.data
174
+ ?.map((item) => item.url)
175
+ .filter((url) => url !== "undefined") ?? [];
175
176
  }
176
177
  else {
177
- [data] = response.data
178
- .map((item) => item.b64_json)
179
- .filter((b64_json) => b64_json !== "undefined");
178
+ [data] =
179
+ response.data
180
+ ?.map((item) => item.b64_json)
181
+ .filter((b64_json) => b64_json !== "undefined") ??
182
+ [];
180
183
  }
181
184
  return data;
182
185
  }
package/dist/types.d.ts CHANGED
@@ -135,8 +135,24 @@ export interface OpenAIChatInput extends OpenAIBaseInput {
135
135
  /**
136
136
  * Constrains effort on reasoning for reasoning models. Currently supported values are low, medium, and high.
137
137
  * Reducing reasoning effort can result in faster responses and fewer tokens used on reasoning in a response.
138
+ *
139
+ * @deprecated Use the {@link reasoning} object instead.
138
140
  */
139
141
  reasoningEffort?: OpenAIClient.Chat.ChatCompletionReasoningEffort;
142
+ /**
143
+ * Options for reasoning models.
144
+ *
145
+ * Note that some options, like reasoning summaries, are only available when using the responses
146
+ * API. This option is ignored when not using a reasoning model.
147
+ */
148
+ reasoning?: OpenAIClient.Reasoning;
149
+ /**
150
+ * Should be set to `true` in tenancies with Zero Data Retention
151
+ * @see https://platform.openai.com/docs/guides/your-data
152
+ *
153
+ * @default false
154
+ */
155
+ zdrEnabled?: boolean;
140
156
  }
141
157
  export declare interface AzureOpenAIInput {
142
158
  /**
@@ -205,4 +221,17 @@ type ChatOpenAIResponseFormatJSONSchema = Omit<ResponseFormatJSONSchema, "json_s
205
221
  schema: Record<string, any> | z.ZodObject<any, any, any, any>;
206
222
  };
207
223
  };
224
+ /**
225
+ * The summary of a model's reasoning step.
226
+ */
227
+ export type ChatOpenAIReasoningSummary = Omit<OpenAIClient.Responses.ResponseReasoningItem, "summary"> & {
228
+ /**
229
+ * The summary of the reasoning step. The index field will be populated if the response was
230
+ * streamed. This allows LangChain to recompose the reasoning summary output correctly when the
231
+ * AIMessage is used as an input for future generation requests.
232
+ */
233
+ summary: Array<OpenAIClient.Responses.ResponseReasoningItem.Summary & {
234
+ index?: number;
235
+ }>;
236
+ };
208
237
  export type ChatOpenAIResponseFormat = ResponseFormatText | ResponseFormatJSONObject | ChatOpenAIResponseFormatJSONSchema;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@langchain/openai",
3
- "version": "0.5.7",
3
+ "version": "0.5.9",
4
4
  "description": "OpenAI integrations for LangChain.js",
5
5
  "type": "module",
6
6
  "engines": {
@@ -36,7 +36,7 @@
36
36
  "license": "MIT",
37
37
  "dependencies": {
38
38
  "js-tiktoken": "^1.0.12",
39
- "openai": "^4.93.0",
39
+ "openai": "^4.96.0",
40
40
  "zod": "^3.22.4",
41
41
  "zod-to-json-schema": "^3.22.3"
42
42
  },