@langchain/openai 0.5.6 → 0.5.8

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.
@@ -50,6 +50,132 @@ function messageToOpenAIRole(message) {
50
50
  }
51
51
  }
52
52
  exports.messageToOpenAIRole = messageToOpenAIRole;
53
+ const completionsApiContentBlockConverter = {
54
+ providerName: "ChatOpenAI",
55
+ fromStandardTextBlock(block) {
56
+ return { type: "text", text: block.text };
57
+ },
58
+ fromStandardImageBlock(block) {
59
+ if (block.source_type === "url") {
60
+ return {
61
+ type: "image_url",
62
+ image_url: {
63
+ url: block.url,
64
+ ...(block.metadata?.detail
65
+ ? { detail: block.metadata.detail }
66
+ : {}),
67
+ },
68
+ };
69
+ }
70
+ if (block.source_type === "base64") {
71
+ const url = `data:${block.mime_type ?? ""};base64,${block.data}`;
72
+ return {
73
+ type: "image_url",
74
+ image_url: {
75
+ url,
76
+ ...(block.metadata?.detail
77
+ ? { detail: block.metadata.detail }
78
+ : {}),
79
+ },
80
+ };
81
+ }
82
+ throw new Error(`Image content blocks with source_type ${block.source_type} are not supported for ChatOpenAI`);
83
+ },
84
+ fromStandardAudioBlock(block) {
85
+ if (block.source_type === "url") {
86
+ const data = (0, messages_1.parseBase64DataUrl)({ dataUrl: block.url });
87
+ if (!data) {
88
+ throw new Error(`URL audio blocks with source_type ${block.source_type} must be formatted as a data URL for ChatOpenAI`);
89
+ }
90
+ const rawMimeType = data.mime_type || block.mime_type || "";
91
+ let mimeType;
92
+ try {
93
+ mimeType = (0, messages_1.parseMimeType)(rawMimeType);
94
+ }
95
+ catch {
96
+ throw new Error(`Audio blocks with source_type ${block.source_type} must have mime type of audio/wav or audio/mp3`);
97
+ }
98
+ if (mimeType.type !== "audio" ||
99
+ (mimeType.subtype !== "wav" && mimeType.subtype !== "mp3")) {
100
+ throw new Error(`Audio blocks with source_type ${block.source_type} must have mime type of audio/wav or audio/mp3`);
101
+ }
102
+ return {
103
+ type: "input_audio",
104
+ input_audio: {
105
+ format: mimeType.subtype,
106
+ data: data.data,
107
+ },
108
+ };
109
+ }
110
+ if (block.source_type === "base64") {
111
+ let mimeType;
112
+ try {
113
+ mimeType = (0, messages_1.parseMimeType)(block.mime_type ?? "");
114
+ }
115
+ catch {
116
+ throw new Error(`Audio blocks with source_type ${block.source_type} must have mime type of audio/wav or audio/mp3`);
117
+ }
118
+ if (mimeType.type !== "audio" ||
119
+ (mimeType.subtype !== "wav" && mimeType.subtype !== "mp3")) {
120
+ throw new Error(`Audio blocks with source_type ${block.source_type} must have mime type of audio/wav or audio/mp3`);
121
+ }
122
+ return {
123
+ type: "input_audio",
124
+ input_audio: {
125
+ format: mimeType.subtype,
126
+ data: block.data,
127
+ },
128
+ };
129
+ }
130
+ throw new Error(`Audio content blocks with source_type ${block.source_type} are not supported for ChatOpenAI`);
131
+ },
132
+ fromStandardFileBlock(block) {
133
+ if (block.source_type === "url") {
134
+ const data = (0, messages_1.parseBase64DataUrl)({ dataUrl: block.url });
135
+ if (!data) {
136
+ throw new Error(`URL file blocks with source_type ${block.source_type} must be formatted as a data URL for ChatOpenAI`);
137
+ }
138
+ return {
139
+ type: "file",
140
+ file: {
141
+ file_data: block.url,
142
+ ...(block.metadata?.filename || block.metadata?.name
143
+ ? {
144
+ filename: (block.metadata?.filename ||
145
+ block.metadata?.name),
146
+ }
147
+ : {}),
148
+ },
149
+ };
150
+ }
151
+ if (block.source_type === "base64") {
152
+ return {
153
+ type: "file",
154
+ file: {
155
+ file_data: `data:${block.mime_type ?? ""};base64,${block.data}`,
156
+ ...(block.metadata?.filename ||
157
+ block.metadata?.name ||
158
+ block.metadata?.title
159
+ ? {
160
+ filename: (block.metadata?.filename ||
161
+ block.metadata?.name ||
162
+ block.metadata?.title),
163
+ }
164
+ : {}),
165
+ },
166
+ };
167
+ }
168
+ if (block.source_type === "id") {
169
+ return {
170
+ type: "file",
171
+ file: {
172
+ file_id: block.id,
173
+ },
174
+ };
175
+ }
176
+ throw new Error(`File content blocks with source_type ${block.source_type} are not supported for ChatOpenAI`);
177
+ },
178
+ };
53
179
  // Used in LangSmith, export is important here
54
180
  function _convertMessagesToOpenAIParams(messages, model) {
55
181
  // TODO: Function messages do not support array content, fix cast
@@ -58,10 +184,18 @@ function _convertMessagesToOpenAIParams(messages, model) {
58
184
  if (role === "system" && isReasoningModel(model)) {
59
185
  role = "developer";
60
186
  }
187
+ const content = typeof message.content === "string"
188
+ ? message.content
189
+ : message.content.map((m) => {
190
+ if ((0, messages_1.isDataContentBlock)(m)) {
191
+ return (0, messages_1.convertToProviderContentBlock)(m, completionsApiContentBlockConverter);
192
+ }
193
+ return m;
194
+ });
61
195
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
62
196
  const completionParam = {
63
197
  role,
64
- content: message.content,
198
+ content,
65
199
  };
66
200
  if (message.name != null) {
67
201
  completionParam.name = message.name;
@@ -98,8 +232,32 @@ function _convertMessagesToOpenAIParams(messages, model) {
98
232
  }
99
233
  exports._convertMessagesToOpenAIParams = _convertMessagesToOpenAIParams;
100
234
  const _FUNCTION_CALL_IDS_MAP_KEY = "__openai_function_call_ids__";
101
- function _convertMessagesToOpenAIResponsesParams(messages, model) {
102
- 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) => {
103
261
  let role = messageToOpenAIRole(lcMsg);
104
262
  if (role === "system" && isReasoningModel(model))
105
263
  role = "developer";
@@ -149,15 +307,25 @@ function _convertMessagesToOpenAIResponsesParams(messages, model) {
149
307
  };
150
308
  }
151
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
152
319
  const input = [];
153
320
  // reasoning items
154
- if (lcMsg.additional_kwargs.reasoning != null) {
321
+ if (!zdrEnabled && lcMsg.additional_kwargs.reasoning != null) {
155
322
  const isReasoningItem = (item) => typeof item === "object" &&
156
323
  item != null &&
157
324
  "type" in item &&
158
325
  item.type === "reasoning";
159
326
  if (isReasoningItem(lcMsg.additional_kwargs.reasoning)) {
160
- input.push(lcMsg.additional_kwargs.reasoning);
327
+ const reasoningItem = _convertReasoningSummaryToOpenAIResponsesParams(lcMsg.additional_kwargs.reasoning);
328
+ input.push(reasoningItem);
161
329
  }
162
330
  }
163
331
  // ai content
@@ -174,6 +342,7 @@ function _convertMessagesToOpenAIResponsesParams(messages, model) {
174
342
  input.push({
175
343
  type: "message",
176
344
  role: "assistant",
345
+ ...(lcMsg.id && !zdrEnabled ? { id: lcMsg.id } : {}),
177
346
  content: typeof content === "string"
178
347
  ? content
179
348
  : content.flatMap((item) => {
@@ -201,7 +370,7 @@ function _convertMessagesToOpenAIResponsesParams(messages, model) {
201
370
  name: toolCall.name,
202
371
  arguments: JSON.stringify(toolCall.args),
203
372
  call_id: toolCall.id,
204
- id: functionCallIds?.[toolCall.id],
373
+ ...(zdrEnabled ? { id: functionCallIds?.[toolCall.id] } : {}),
205
374
  })));
206
375
  }
207
376
  else if (lcMsg.additional_kwargs.tool_calls != null) {
@@ -209,21 +378,17 @@ function _convertMessagesToOpenAIResponsesParams(messages, model) {
209
378
  type: "function_call",
210
379
  name: toolCall.function.name,
211
380
  call_id: toolCall.id,
212
- id: functionCallIds?.[toolCall.id],
381
+ ...(zdrEnabled ? { id: functionCallIds?.[toolCall.id] } : {}),
213
382
  arguments: toolCall.function.arguments,
214
383
  })));
215
384
  }
216
385
  const toolOutputs = lcMsg.response_metadata.output?.length
217
386
  ? lcMsg.response_metadata.output
218
387
  : lcMsg.additional_kwargs.tool_outputs;
388
+ let computerCalls = [];
219
389
  if (toolOutputs != null) {
220
390
  const castToolOutputs = toolOutputs;
221
- const reasoningCalls = castToolOutputs?.filter((item) => item.type === "reasoning");
222
- const computerCalls = castToolOutputs?.filter((item) => item.type === "computer_call");
223
- // NOTE: Reasoning outputs must be passed to the model BEFORE computer calls.
224
- if (reasoningCalls.length > 0 && computerCalls.length > 0) {
225
- input.push(...reasoningCalls);
226
- }
391
+ computerCalls = castToolOutputs?.filter((item) => item.type === "computer_call");
227
392
  if (computerCalls.length > 0)
228
393
  input.push(...computerCalls);
229
394
  }
@@ -232,6 +397,9 @@ function _convertMessagesToOpenAIResponsesParams(messages, model) {
232
397
  const content = typeof lcMsg.content === "string"
233
398
  ? lcMsg.content
234
399
  : lcMsg.content.flatMap((item) => {
400
+ if ((0, messages_1.isDataContentBlock)(item)) {
401
+ return (0, messages_1.convertToProviderContentBlock)(item, completionsApiContentBlockConverter);
402
+ }
235
403
  if (item.type === "text") {
236
404
  return { type: "input_text", text: item.text };
237
405
  }
@@ -424,6 +592,39 @@ function _convertOpenAIResponsesDeltaToBaseMessageChunk(chunk) {
424
592
  else if (chunk.type === "response.refusal.done") {
425
593
  additional_kwargs.refusal = chunk.refusal;
426
594
  }
595
+ else if (chunk.type === "response.output_item.added" &&
596
+ "item" in chunk &&
597
+ chunk.item.type === "reasoning") {
598
+ const summary = chunk
599
+ .item.summary
600
+ ? chunk.item.summary.map((s, index) => ({
601
+ ...s,
602
+ index,
603
+ }))
604
+ : undefined;
605
+ additional_kwargs.reasoning = {
606
+ // We only capture ID in the first chunk or else the concatenated result of all chunks will
607
+ // have an ID field that is repeated once per chunk. There is special handling for the `type`
608
+ // field that prevents this, however.
609
+ id: chunk.item.id,
610
+ type: chunk.item.type,
611
+ ...(summary ? { summary } : {}),
612
+ };
613
+ }
614
+ else if (chunk.type === "response.reasoning_summary_part.added") {
615
+ additional_kwargs.reasoning = {
616
+ type: "reasoning",
617
+ summary: [{ ...chunk.part, index: chunk.summary_index }],
618
+ };
619
+ }
620
+ else if (chunk.type === "response.reasoning_summary_text.delta") {
621
+ additional_kwargs.reasoning = {
622
+ type: "reasoning",
623
+ summary: [
624
+ { text: chunk.delta, type: "summary_text", index: chunk.summary_index },
625
+ ],
626
+ };
627
+ }
427
628
  else {
428
629
  return null;
429
630
  }
@@ -466,9 +667,7 @@ function _convertChatOpenAIToolTypeToOpenAITool(tool, fields) {
466
667
  return (0, tools_js_1._convertToOpenAITool)(tool, fields);
467
668
  }
468
669
  function isReasoningModel(model) {
469
- return (model?.startsWith("o1") ||
470
- model?.startsWith("o3") ||
471
- model?.startsWith("o4"));
670
+ return model && /^o\d/.test(model);
472
671
  }
473
672
  /**
474
673
  * OpenAI chat model integration.
@@ -1076,6 +1275,9 @@ class ChatOpenAI extends chat_models_1.BaseChatModel {
1076
1275
  "tags",
1077
1276
  "metadata",
1078
1277
  "disableStreaming",
1278
+ "useResponsesApi",
1279
+ "zdrEnabled",
1280
+ "reasoning",
1079
1281
  ];
1080
1282
  }
1081
1283
  constructor(fields) {
@@ -1253,18 +1455,51 @@ class ChatOpenAI extends chat_models_1.BaseChatModel {
1253
1455
  writable: true,
1254
1456
  value: void 0
1255
1457
  });
1458
+ /**
1459
+ * @deprecated Use {@link reasoning} object instead.
1460
+ */
1256
1461
  Object.defineProperty(this, "reasoningEffort", {
1257
1462
  enumerable: true,
1258
1463
  configurable: true,
1259
1464
  writable: true,
1260
1465
  value: void 0
1261
1466
  });
1467
+ /**
1468
+ * Options for reasoning models.
1469
+ */
1470
+ Object.defineProperty(this, "reasoning", {
1471
+ enumerable: true,
1472
+ configurable: true,
1473
+ writable: true,
1474
+ value: void 0
1475
+ });
1476
+ /**
1477
+ * Whether to use the responses API for all requests. If `false` the responses API will be used
1478
+ * only when required in order to fulfill the request.
1479
+ */
1262
1480
  Object.defineProperty(this, "useResponsesApi", {
1263
1481
  enumerable: true,
1264
1482
  configurable: true,
1265
1483
  writable: true,
1266
1484
  value: false
1267
1485
  });
1486
+ /**
1487
+ * Must be set to `true` in tenancies with Zero Data Retention. Setting to `true` will disable
1488
+ * output storage in the Responses API, but this DOES NOT enable Zero Data Retention in your
1489
+ * OpenAI organization or project. This must be configured directly with OpenAI.
1490
+ *
1491
+ * See:
1492
+ * https://help.openai.com/en/articles/10503543-data-residency-for-the-openai-api
1493
+ * https://platform.openai.com/docs/api-reference/responses/create#responses-create-store
1494
+ *
1495
+ * @default false
1496
+ */
1497
+ Object.defineProperty(this, "zdrEnabled", {
1498
+ enumerable: true,
1499
+ configurable: true,
1500
+ writable: true,
1501
+ value: void 0
1502
+ });
1268
1503
  this.openAIApiKey =
1269
1504
  fields?.apiKey ??
1270
1505
  fields?.openAIApiKey ??
@@ -1292,7 +1527,12 @@ class ChatOpenAI extends chat_models_1.BaseChatModel {
1292
1527
  this.__includeRawResponse = fields?.__includeRawResponse;
1293
1528
  this.audio = fields?.audio;
1294
1529
  this.modalities = fields?.modalities;
1295
- this.reasoningEffort = fields?.reasoningEffort;
1530
+ this.reasoningEffort = fields?.reasoningEffort ?? fields?.reasoning?.effort;
1531
+ this.reasoning =
1532
+ fields?.reasoning ??
1533
+ (fields?.reasoningEffort
1534
+ ? { effort: fields.reasoningEffort }
1535
+ : undefined);
1296
1536
  this.maxTokens = fields?.maxCompletionTokens ?? fields?.maxTokens;
1297
1537
  this.useResponsesApi = fields?.useResponsesApi ?? this.useResponsesApi;
1298
1538
  this.disableStreaming = fields?.disableStreaming ?? this.disableStreaming;
@@ -1315,6 +1555,7 @@ class ChatOpenAI extends chat_models_1.BaseChatModel {
1315
1555
  if (fields?.supportsStrictToolCalling !== undefined) {
1316
1556
  this.supportsStrictToolCalling = fields.supportsStrictToolCalling;
1317
1557
  }
1558
+ this.zdrEnabled = fields?.zdrEnabled ?? false;
1318
1559
  }
1319
1560
  getLsParams(options) {
1320
1561
  const params = this.invocationParams(options);
@@ -1353,6 +1594,35 @@ class ChatOpenAI extends chat_models_1.BaseChatModel {
1353
1594
  }
1354
1595
  return resFormat;
1355
1596
  }
1597
+ getReasoningParams(options) {
1598
+ if (!isReasoningModel(this.model)) {
1599
+ return;
1600
+ }
1601
+ let reasoning;
1602
+ // apply reasoning options in reverse order of priority without else blocks so that highest priority overrides lowest priority
1603
+ if (this.reasoningEffort !== undefined) {
1604
+ reasoning = { effort: this.reasoningEffort };
1605
+ }
1606
+ if (this.reasoning !== undefined) {
1607
+ reasoning = {
1608
+ ...reasoning,
1609
+ ...this.reasoning,
1610
+ };
1611
+ }
1612
+ if (options?.reasoning_effort !== undefined) {
1613
+ reasoning = {
1614
+ ...reasoning,
1615
+ effort: options.reasoning_effort,
1616
+ };
1617
+ }
1618
+ if (options?.reasoning !== undefined) {
1619
+ reasoning = {
1620
+ ...reasoning,
1621
+ ...options.reasoning,
1622
+ };
1623
+ }
1624
+ return reasoning;
1625
+ }
1356
1626
  /**
1357
1627
  * Get the parameters used to invoke the model
1358
1628
  */
@@ -1429,11 +1699,12 @@ class ChatOpenAI extends chat_models_1.BaseChatModel {
1429
1699
  })(),
1430
1700
  parallel_tool_calls: options?.parallel_tool_calls,
1431
1701
  max_output_tokens: this.maxTokens === -1 ? undefined : this.maxTokens,
1702
+ ...(this.zdrEnabled ? { store: false } : {}),
1432
1703
  ...this.modelKwargs,
1433
1704
  };
1434
- const reasoningEffort = options?.reasoning_effort ?? this.reasoningEffort;
1435
- if (reasoningEffort !== undefined) {
1436
- params.reasoning = { effort: reasoningEffort };
1705
+ const reasoning = this.getReasoningParams(options);
1706
+ if (reasoning !== undefined) {
1707
+ params.reasoning = reasoning;
1437
1708
  }
1438
1709
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
1439
1710
  return params;
@@ -1480,9 +1751,9 @@ class ChatOpenAI extends chat_models_1.BaseChatModel {
1480
1751
  if (options?.prediction !== undefined) {
1481
1752
  params.prediction = options.prediction;
1482
1753
  }
1483
- const reasoningEffort = options?.reasoning_effort ?? this.reasoningEffort;
1484
- if (reasoningEffort !== undefined) {
1485
- params.reasoning_effort = reasoningEffort;
1754
+ const reasoning = this.getReasoningParams(options);
1755
+ if (reasoning !== undefined && reasoning.effort !== undefined) {
1756
+ params.reasoning_effort = reasoning.effort;
1486
1757
  }
1487
1758
  if (isReasoningModel(params.model)) {
1488
1759
  params.max_completion_tokens =
@@ -1636,10 +1907,17 @@ class ChatOpenAI extends chat_models_1.BaseChatModel {
1636
1907
  }
1637
1908
  async *_streamResponseChunks(messages, options, runManager) {
1638
1909
  if (this._useResponseApi(options)) {
1910
+ const lastAIMessage = messages.filter((m) => (0, messages_1.isAIMessage)(m)).pop();
1911
+ const lastAIMessageId = lastAIMessage?.response_metadata?.id;
1639
1912
  const streamIterable = await this.responseApiWithRetry({
1640
1913
  ...this.invocationParams(options, { streaming: true }),
1641
- input: _convertMessagesToOpenAIResponsesParams(messages, this.model),
1914
+ input: _convertMessagesToOpenAIResponsesParams(messages, this.model, this.zdrEnabled),
1642
1915
  stream: true,
1916
+ ...(lastAIMessageId &&
1917
+ lastAIMessageId.startsWith("resp_") &&
1918
+ !this.zdrEnabled
1919
+ ? { previous_response_id: lastAIMessageId }
1920
+ : {}),
1643
1921
  }, options);
1644
1922
  for await (const data of streamIterable) {
1645
1923
  const chunk = _convertOpenAIResponsesDeltaToBaseMessageChunk(data);
@@ -1773,8 +2051,18 @@ class ChatOpenAI extends chat_models_1.BaseChatModel {
1773
2051
  },
1774
2052
  };
1775
2053
  }
1776
- const input = _convertMessagesToOpenAIResponsesParams(messages, this.model);
1777
- const data = await this.responseApiWithRetry({ input, ...invocationParams }, { signal: options?.signal, ...options?.options });
2054
+ const lastAIMessage = messages.filter((m) => (0, messages_1.isAIMessage)(m)).pop();
2055
+ const lastAIMessageId = lastAIMessage?.response_metadata?.id;
2056
+ const input = _convertMessagesToOpenAIResponsesParams(messages, this.model, this.zdrEnabled);
2057
+ const data = await this.responseApiWithRetry({
2058
+ input,
2059
+ ...invocationParams,
2060
+ ...(lastAIMessageId &&
2061
+ lastAIMessageId.startsWith("resp_") &&
2062
+ !this.zdrEnabled
2063
+ ? { previous_response_id: lastAIMessageId }
2064
+ : {}),
2065
+ }, { signal: options?.signal, ...options?.options });
1778
2066
  return {
1779
2067
  generations: [
1780
2068
  {
@@ -1794,12 +2082,23 @@ class ChatOpenAI extends chat_models_1.BaseChatModel {
1794
2082
  },
1795
2083
  };
1796
2084
  }
2085
+ /**
2086
+ * Determines whether the responses API should be used for the given request.
2087
+ *
2088
+ * @internal
2089
+ *
2090
+ * @param options The parsed call options for the request.
2091
+ * @returns `true` if the responses API should be used, either because it is explicitly enabled,
2092
+ * or because the request requires it.
2093
+ */
1797
2094
  _useResponseApi(options) {
1798
2095
  const usesBuiltInTools = options?.tools?.some(isBuiltInTool);
1799
2096
  const hasResponsesOnlyKwargs = options?.previous_response_id != null ||
1800
2097
  options?.text != null ||
1801
2098
  options?.truncation != null ||
1802
- options?.include != null;
2099
+ options?.include != null ||
2100
+ options?.reasoning?.summary != null ||
2101
+ this.reasoning?.summary != null;
1803
2102
  return this.useResponsesApi || usesBuiltInTools || hasResponsesOnlyKwargs;
1804
2103
  }
1805
2104
  /** @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>;