@langchain/openai 0.5.7 → 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.
- package/dist/chat_models.cjs +188 -26
- package/dist/chat_models.d.ts +61 -3
- package/dist/chat_models.js +188 -26
- package/dist/tools/dalle.cjs +13 -10
- package/dist/tools/dalle.js +13 -10
- package/dist/types.d.ts +29 -0
- package/package.json +2 -2
package/dist/chat_models.cjs
CHANGED
|
@@ -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
|
|
236
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
}
|
|
@@ -561,6 +592,39 @@ function _convertOpenAIResponsesDeltaToBaseMessageChunk(chunk) {
|
|
|
561
592
|
else if (chunk.type === "response.refusal.done") {
|
|
562
593
|
additional_kwargs.refusal = chunk.refusal;
|
|
563
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
|
+
}
|
|
564
628
|
else {
|
|
565
629
|
return null;
|
|
566
630
|
}
|
|
@@ -603,9 +667,7 @@ function _convertChatOpenAIToolTypeToOpenAITool(tool, fields) {
|
|
|
603
667
|
return (0, tools_js_1._convertToOpenAITool)(tool, fields);
|
|
604
668
|
}
|
|
605
669
|
function isReasoningModel(model) {
|
|
606
|
-
return (model
|
|
607
|
-
model?.startsWith("o3") ||
|
|
608
|
-
model?.startsWith("o4"));
|
|
670
|
+
return model && /^o\d/.test(model);
|
|
609
671
|
}
|
|
610
672
|
/**
|
|
611
673
|
* OpenAI chat model integration.
|
|
@@ -1213,6 +1275,9 @@ class ChatOpenAI extends chat_models_1.BaseChatModel {
|
|
|
1213
1275
|
"tags",
|
|
1214
1276
|
"metadata",
|
|
1215
1277
|
"disableStreaming",
|
|
1278
|
+
"useResponsesApi",
|
|
1279
|
+
"zdrEnabled",
|
|
1280
|
+
"reasoning",
|
|
1216
1281
|
];
|
|
1217
1282
|
}
|
|
1218
1283
|
constructor(fields) {
|
|
@@ -1390,18 +1455,51 @@ class ChatOpenAI extends chat_models_1.BaseChatModel {
|
|
|
1390
1455
|
writable: true,
|
|
1391
1456
|
value: void 0
|
|
1392
1457
|
});
|
|
1458
|
+
/**
|
|
1459
|
+
* @deprecated Use {@link reasoning} object instead.
|
|
1460
|
+
*/
|
|
1393
1461
|
Object.defineProperty(this, "reasoningEffort", {
|
|
1394
1462
|
enumerable: true,
|
|
1395
1463
|
configurable: true,
|
|
1396
1464
|
writable: true,
|
|
1397
1465
|
value: void 0
|
|
1398
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
|
+
*/
|
|
1399
1480
|
Object.defineProperty(this, "useResponsesApi", {
|
|
1400
1481
|
enumerable: true,
|
|
1401
1482
|
configurable: true,
|
|
1402
1483
|
writable: true,
|
|
1403
1484
|
value: false
|
|
1404
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
|
+
});
|
|
1405
1503
|
this.openAIApiKey =
|
|
1406
1504
|
fields?.apiKey ??
|
|
1407
1505
|
fields?.openAIApiKey ??
|
|
@@ -1429,7 +1527,12 @@ class ChatOpenAI extends chat_models_1.BaseChatModel {
|
|
|
1429
1527
|
this.__includeRawResponse = fields?.__includeRawResponse;
|
|
1430
1528
|
this.audio = fields?.audio;
|
|
1431
1529
|
this.modalities = fields?.modalities;
|
|
1432
|
-
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);
|
|
1433
1536
|
this.maxTokens = fields?.maxCompletionTokens ?? fields?.maxTokens;
|
|
1434
1537
|
this.useResponsesApi = fields?.useResponsesApi ?? this.useResponsesApi;
|
|
1435
1538
|
this.disableStreaming = fields?.disableStreaming ?? this.disableStreaming;
|
|
@@ -1452,6 +1555,7 @@ class ChatOpenAI extends chat_models_1.BaseChatModel {
|
|
|
1452
1555
|
if (fields?.supportsStrictToolCalling !== undefined) {
|
|
1453
1556
|
this.supportsStrictToolCalling = fields.supportsStrictToolCalling;
|
|
1454
1557
|
}
|
|
1558
|
+
this.zdrEnabled = fields?.zdrEnabled ?? false;
|
|
1455
1559
|
}
|
|
1456
1560
|
getLsParams(options) {
|
|
1457
1561
|
const params = this.invocationParams(options);
|
|
@@ -1490,6 +1594,35 @@ class ChatOpenAI extends chat_models_1.BaseChatModel {
|
|
|
1490
1594
|
}
|
|
1491
1595
|
return resFormat;
|
|
1492
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
|
+
}
|
|
1493
1626
|
/**
|
|
1494
1627
|
* Get the parameters used to invoke the model
|
|
1495
1628
|
*/
|
|
@@ -1566,11 +1699,12 @@ class ChatOpenAI extends chat_models_1.BaseChatModel {
|
|
|
1566
1699
|
})(),
|
|
1567
1700
|
parallel_tool_calls: options?.parallel_tool_calls,
|
|
1568
1701
|
max_output_tokens: this.maxTokens === -1 ? undefined : this.maxTokens,
|
|
1702
|
+
...(this.zdrEnabled ? { store: false } : {}),
|
|
1569
1703
|
...this.modelKwargs,
|
|
1570
1704
|
};
|
|
1571
|
-
const
|
|
1572
|
-
if (
|
|
1573
|
-
params.reasoning =
|
|
1705
|
+
const reasoning = this.getReasoningParams(options);
|
|
1706
|
+
if (reasoning !== undefined) {
|
|
1707
|
+
params.reasoning = reasoning;
|
|
1574
1708
|
}
|
|
1575
1709
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1576
1710
|
return params;
|
|
@@ -1617,9 +1751,9 @@ class ChatOpenAI extends chat_models_1.BaseChatModel {
|
|
|
1617
1751
|
if (options?.prediction !== undefined) {
|
|
1618
1752
|
params.prediction = options.prediction;
|
|
1619
1753
|
}
|
|
1620
|
-
const
|
|
1621
|
-
if (
|
|
1622
|
-
params.reasoning_effort =
|
|
1754
|
+
const reasoning = this.getReasoningParams(options);
|
|
1755
|
+
if (reasoning !== undefined && reasoning.effort !== undefined) {
|
|
1756
|
+
params.reasoning_effort = reasoning.effort;
|
|
1623
1757
|
}
|
|
1624
1758
|
if (isReasoningModel(params.model)) {
|
|
1625
1759
|
params.max_completion_tokens =
|
|
@@ -1773,10 +1907,17 @@ class ChatOpenAI extends chat_models_1.BaseChatModel {
|
|
|
1773
1907
|
}
|
|
1774
1908
|
async *_streamResponseChunks(messages, options, runManager) {
|
|
1775
1909
|
if (this._useResponseApi(options)) {
|
|
1910
|
+
const lastAIMessage = messages.filter((m) => (0, messages_1.isAIMessage)(m)).pop();
|
|
1911
|
+
const lastAIMessageId = lastAIMessage?.response_metadata?.id;
|
|
1776
1912
|
const streamIterable = await this.responseApiWithRetry({
|
|
1777
1913
|
...this.invocationParams(options, { streaming: true }),
|
|
1778
|
-
input: _convertMessagesToOpenAIResponsesParams(messages, this.model),
|
|
1914
|
+
input: _convertMessagesToOpenAIResponsesParams(messages, this.model, this.zdrEnabled),
|
|
1779
1915
|
stream: true,
|
|
1916
|
+
...(lastAIMessageId &&
|
|
1917
|
+
lastAIMessageId.startsWith("resp_") &&
|
|
1918
|
+
!this.zdrEnabled
|
|
1919
|
+
? { previous_response_id: lastAIMessageId }
|
|
1920
|
+
: {}),
|
|
1780
1921
|
}, options);
|
|
1781
1922
|
for await (const data of streamIterable) {
|
|
1782
1923
|
const chunk = _convertOpenAIResponsesDeltaToBaseMessageChunk(data);
|
|
@@ -1910,8 +2051,18 @@ class ChatOpenAI extends chat_models_1.BaseChatModel {
|
|
|
1910
2051
|
},
|
|
1911
2052
|
};
|
|
1912
2053
|
}
|
|
1913
|
-
const
|
|
1914
|
-
const
|
|
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 });
|
|
1915
2066
|
return {
|
|
1916
2067
|
generations: [
|
|
1917
2068
|
{
|
|
@@ -1931,12 +2082,23 @@ class ChatOpenAI extends chat_models_1.BaseChatModel {
|
|
|
1931
2082
|
},
|
|
1932
2083
|
};
|
|
1933
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
|
+
*/
|
|
1934
2094
|
_useResponseApi(options) {
|
|
1935
2095
|
const usesBuiltInTools = options?.tools?.some(isBuiltInTool);
|
|
1936
2096
|
const hasResponsesOnlyKwargs = options?.previous_response_id != null ||
|
|
1937
2097
|
options?.text != null ||
|
|
1938
2098
|
options?.truncation != null ||
|
|
1939
|
-
options?.include != null
|
|
2099
|
+
options?.include != null ||
|
|
2100
|
+
options?.reasoning?.summary != null ||
|
|
2101
|
+
this.reasoning?.summary != null;
|
|
1940
2102
|
return this.useResponsesApi || usesBuiltInTools || hasResponsesOnlyKwargs;
|
|
1941
2103
|
}
|
|
1942
2104
|
/** @ignore */
|
package/dist/chat_models.d.ts
CHANGED
|
@@ -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
|
|
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
|
-
*
|
|
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>;
|
package/dist/chat_models.js
CHANGED
|
@@ -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
|
|
231
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
}
|
|
@@ -556,6 +587,39 @@ function _convertOpenAIResponsesDeltaToBaseMessageChunk(chunk) {
|
|
|
556
587
|
else if (chunk.type === "response.refusal.done") {
|
|
557
588
|
additional_kwargs.refusal = chunk.refusal;
|
|
558
589
|
}
|
|
590
|
+
else if (chunk.type === "response.output_item.added" &&
|
|
591
|
+
"item" in chunk &&
|
|
592
|
+
chunk.item.type === "reasoning") {
|
|
593
|
+
const summary = chunk
|
|
594
|
+
.item.summary
|
|
595
|
+
? chunk.item.summary.map((s, index) => ({
|
|
596
|
+
...s,
|
|
597
|
+
index,
|
|
598
|
+
}))
|
|
599
|
+
: undefined;
|
|
600
|
+
additional_kwargs.reasoning = {
|
|
601
|
+
// We only capture ID in the first chunk or else the concatenated result of all chunks will
|
|
602
|
+
// have an ID field that is repeated once per chunk. There is special handling for the `type`
|
|
603
|
+
// field that prevents this, however.
|
|
604
|
+
id: chunk.item.id,
|
|
605
|
+
type: chunk.item.type,
|
|
606
|
+
...(summary ? { summary } : {}),
|
|
607
|
+
};
|
|
608
|
+
}
|
|
609
|
+
else if (chunk.type === "response.reasoning_summary_part.added") {
|
|
610
|
+
additional_kwargs.reasoning = {
|
|
611
|
+
type: "reasoning",
|
|
612
|
+
summary: [{ ...chunk.part, index: chunk.summary_index }],
|
|
613
|
+
};
|
|
614
|
+
}
|
|
615
|
+
else if (chunk.type === "response.reasoning_summary_text.delta") {
|
|
616
|
+
additional_kwargs.reasoning = {
|
|
617
|
+
type: "reasoning",
|
|
618
|
+
summary: [
|
|
619
|
+
{ text: chunk.delta, type: "summary_text", index: chunk.summary_index },
|
|
620
|
+
],
|
|
621
|
+
};
|
|
622
|
+
}
|
|
559
623
|
else {
|
|
560
624
|
return null;
|
|
561
625
|
}
|
|
@@ -598,9 +662,7 @@ function _convertChatOpenAIToolTypeToOpenAITool(tool, fields) {
|
|
|
598
662
|
return _convertToOpenAITool(tool, fields);
|
|
599
663
|
}
|
|
600
664
|
function isReasoningModel(model) {
|
|
601
|
-
return (model
|
|
602
|
-
model?.startsWith("o3") ||
|
|
603
|
-
model?.startsWith("o4"));
|
|
665
|
+
return model && /^o\d/.test(model);
|
|
604
666
|
}
|
|
605
667
|
/**
|
|
606
668
|
* OpenAI chat model integration.
|
|
@@ -1208,6 +1270,9 @@ export class ChatOpenAI extends BaseChatModel {
|
|
|
1208
1270
|
"tags",
|
|
1209
1271
|
"metadata",
|
|
1210
1272
|
"disableStreaming",
|
|
1273
|
+
"useResponsesApi",
|
|
1274
|
+
"zdrEnabled",
|
|
1275
|
+
"reasoning",
|
|
1211
1276
|
];
|
|
1212
1277
|
}
|
|
1213
1278
|
constructor(fields) {
|
|
@@ -1385,18 +1450,51 @@ export class ChatOpenAI extends BaseChatModel {
|
|
|
1385
1450
|
writable: true,
|
|
1386
1451
|
value: void 0
|
|
1387
1452
|
});
|
|
1453
|
+
/**
|
|
1454
|
+
* @deprecated Use {@link reasoning} object instead.
|
|
1455
|
+
*/
|
|
1388
1456
|
Object.defineProperty(this, "reasoningEffort", {
|
|
1389
1457
|
enumerable: true,
|
|
1390
1458
|
configurable: true,
|
|
1391
1459
|
writable: true,
|
|
1392
1460
|
value: void 0
|
|
1393
1461
|
});
|
|
1462
|
+
/**
|
|
1463
|
+
* Options for reasoning models.
|
|
1464
|
+
*/
|
|
1465
|
+
Object.defineProperty(this, "reasoning", {
|
|
1466
|
+
enumerable: true,
|
|
1467
|
+
configurable: true,
|
|
1468
|
+
writable: true,
|
|
1469
|
+
value: void 0
|
|
1470
|
+
});
|
|
1471
|
+
/**
|
|
1472
|
+
* Whether to use the responses API for all requests. If `false` the responses API will be used
|
|
1473
|
+
* only when required in order to fulfill the request.
|
|
1474
|
+
*/
|
|
1394
1475
|
Object.defineProperty(this, "useResponsesApi", {
|
|
1395
1476
|
enumerable: true,
|
|
1396
1477
|
configurable: true,
|
|
1397
1478
|
writable: true,
|
|
1398
1479
|
value: false
|
|
1399
1480
|
});
|
|
1481
|
+
/**
|
|
1482
|
+
* Must be set to `true` in tenancies with Zero Data Retention. Setting to `true` will disable
|
|
1483
|
+
* output storage in the Responses API, but this DOES NOT enable Zero Data Retention in your
|
|
1484
|
+
* OpenAI organization or project. This must be configured directly with OpenAI.
|
|
1485
|
+
*
|
|
1486
|
+
* See:
|
|
1487
|
+
* https://help.openai.com/en/articles/10503543-data-residency-for-the-openai-api
|
|
1488
|
+
* https://platform.openai.com/docs/api-reference/responses/create#responses-create-store
|
|
1489
|
+
*
|
|
1490
|
+
* @default false
|
|
1491
|
+
*/
|
|
1492
|
+
Object.defineProperty(this, "zdrEnabled", {
|
|
1493
|
+
enumerable: true,
|
|
1494
|
+
configurable: true,
|
|
1495
|
+
writable: true,
|
|
1496
|
+
value: void 0
|
|
1497
|
+
});
|
|
1400
1498
|
this.openAIApiKey =
|
|
1401
1499
|
fields?.apiKey ??
|
|
1402
1500
|
fields?.openAIApiKey ??
|
|
@@ -1424,7 +1522,12 @@ export class ChatOpenAI extends BaseChatModel {
|
|
|
1424
1522
|
this.__includeRawResponse = fields?.__includeRawResponse;
|
|
1425
1523
|
this.audio = fields?.audio;
|
|
1426
1524
|
this.modalities = fields?.modalities;
|
|
1427
|
-
this.reasoningEffort = fields?.reasoningEffort;
|
|
1525
|
+
this.reasoningEffort = fields?.reasoningEffort ?? fields?.reasoning?.effort;
|
|
1526
|
+
this.reasoning =
|
|
1527
|
+
fields?.reasoning ??
|
|
1528
|
+
(fields?.reasoningEffort
|
|
1529
|
+
? { effort: fields.reasoningEffort }
|
|
1530
|
+
: undefined);
|
|
1428
1531
|
this.maxTokens = fields?.maxCompletionTokens ?? fields?.maxTokens;
|
|
1429
1532
|
this.useResponsesApi = fields?.useResponsesApi ?? this.useResponsesApi;
|
|
1430
1533
|
this.disableStreaming = fields?.disableStreaming ?? this.disableStreaming;
|
|
@@ -1447,6 +1550,7 @@ export class ChatOpenAI extends BaseChatModel {
|
|
|
1447
1550
|
if (fields?.supportsStrictToolCalling !== undefined) {
|
|
1448
1551
|
this.supportsStrictToolCalling = fields.supportsStrictToolCalling;
|
|
1449
1552
|
}
|
|
1553
|
+
this.zdrEnabled = fields?.zdrEnabled ?? false;
|
|
1450
1554
|
}
|
|
1451
1555
|
getLsParams(options) {
|
|
1452
1556
|
const params = this.invocationParams(options);
|
|
@@ -1485,6 +1589,35 @@ export class ChatOpenAI extends BaseChatModel {
|
|
|
1485
1589
|
}
|
|
1486
1590
|
return resFormat;
|
|
1487
1591
|
}
|
|
1592
|
+
getReasoningParams(options) {
|
|
1593
|
+
if (!isReasoningModel(this.model)) {
|
|
1594
|
+
return;
|
|
1595
|
+
}
|
|
1596
|
+
let reasoning;
|
|
1597
|
+
// apply reasoning options in reverse order of priority without else blocks so that highest priority overrides lowest priority
|
|
1598
|
+
if (this.reasoningEffort !== undefined) {
|
|
1599
|
+
reasoning = { effort: this.reasoningEffort };
|
|
1600
|
+
}
|
|
1601
|
+
if (this.reasoning !== undefined) {
|
|
1602
|
+
reasoning = {
|
|
1603
|
+
...reasoning,
|
|
1604
|
+
...this.reasoning,
|
|
1605
|
+
};
|
|
1606
|
+
}
|
|
1607
|
+
if (options?.reasoning_effort !== undefined) {
|
|
1608
|
+
reasoning = {
|
|
1609
|
+
...reasoning,
|
|
1610
|
+
effort: options.reasoning_effort,
|
|
1611
|
+
};
|
|
1612
|
+
}
|
|
1613
|
+
if (options?.reasoning !== undefined) {
|
|
1614
|
+
reasoning = {
|
|
1615
|
+
...reasoning,
|
|
1616
|
+
...options.reasoning,
|
|
1617
|
+
};
|
|
1618
|
+
}
|
|
1619
|
+
return reasoning;
|
|
1620
|
+
}
|
|
1488
1621
|
/**
|
|
1489
1622
|
* Get the parameters used to invoke the model
|
|
1490
1623
|
*/
|
|
@@ -1561,11 +1694,12 @@ export class ChatOpenAI extends BaseChatModel {
|
|
|
1561
1694
|
})(),
|
|
1562
1695
|
parallel_tool_calls: options?.parallel_tool_calls,
|
|
1563
1696
|
max_output_tokens: this.maxTokens === -1 ? undefined : this.maxTokens,
|
|
1697
|
+
...(this.zdrEnabled ? { store: false } : {}),
|
|
1564
1698
|
...this.modelKwargs,
|
|
1565
1699
|
};
|
|
1566
|
-
const
|
|
1567
|
-
if (
|
|
1568
|
-
params.reasoning =
|
|
1700
|
+
const reasoning = this.getReasoningParams(options);
|
|
1701
|
+
if (reasoning !== undefined) {
|
|
1702
|
+
params.reasoning = reasoning;
|
|
1569
1703
|
}
|
|
1570
1704
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1571
1705
|
return params;
|
|
@@ -1612,9 +1746,9 @@ export class ChatOpenAI extends BaseChatModel {
|
|
|
1612
1746
|
if (options?.prediction !== undefined) {
|
|
1613
1747
|
params.prediction = options.prediction;
|
|
1614
1748
|
}
|
|
1615
|
-
const
|
|
1616
|
-
if (
|
|
1617
|
-
params.reasoning_effort =
|
|
1749
|
+
const reasoning = this.getReasoningParams(options);
|
|
1750
|
+
if (reasoning !== undefined && reasoning.effort !== undefined) {
|
|
1751
|
+
params.reasoning_effort = reasoning.effort;
|
|
1618
1752
|
}
|
|
1619
1753
|
if (isReasoningModel(params.model)) {
|
|
1620
1754
|
params.max_completion_tokens =
|
|
@@ -1768,10 +1902,17 @@ export class ChatOpenAI extends BaseChatModel {
|
|
|
1768
1902
|
}
|
|
1769
1903
|
async *_streamResponseChunks(messages, options, runManager) {
|
|
1770
1904
|
if (this._useResponseApi(options)) {
|
|
1905
|
+
const lastAIMessage = messages.filter((m) => isAIMessage(m)).pop();
|
|
1906
|
+
const lastAIMessageId = lastAIMessage?.response_metadata?.id;
|
|
1771
1907
|
const streamIterable = await this.responseApiWithRetry({
|
|
1772
1908
|
...this.invocationParams(options, { streaming: true }),
|
|
1773
|
-
input: _convertMessagesToOpenAIResponsesParams(messages, this.model),
|
|
1909
|
+
input: _convertMessagesToOpenAIResponsesParams(messages, this.model, this.zdrEnabled),
|
|
1774
1910
|
stream: true,
|
|
1911
|
+
...(lastAIMessageId &&
|
|
1912
|
+
lastAIMessageId.startsWith("resp_") &&
|
|
1913
|
+
!this.zdrEnabled
|
|
1914
|
+
? { previous_response_id: lastAIMessageId }
|
|
1915
|
+
: {}),
|
|
1775
1916
|
}, options);
|
|
1776
1917
|
for await (const data of streamIterable) {
|
|
1777
1918
|
const chunk = _convertOpenAIResponsesDeltaToBaseMessageChunk(data);
|
|
@@ -1905,8 +2046,18 @@ export class ChatOpenAI extends BaseChatModel {
|
|
|
1905
2046
|
},
|
|
1906
2047
|
};
|
|
1907
2048
|
}
|
|
1908
|
-
const
|
|
1909
|
-
const
|
|
2049
|
+
const lastAIMessage = messages.filter((m) => isAIMessage(m)).pop();
|
|
2050
|
+
const lastAIMessageId = lastAIMessage?.response_metadata?.id;
|
|
2051
|
+
const input = _convertMessagesToOpenAIResponsesParams(messages, this.model, this.zdrEnabled);
|
|
2052
|
+
const data = await this.responseApiWithRetry({
|
|
2053
|
+
input,
|
|
2054
|
+
...invocationParams,
|
|
2055
|
+
...(lastAIMessageId &&
|
|
2056
|
+
lastAIMessageId.startsWith("resp_") &&
|
|
2057
|
+
!this.zdrEnabled
|
|
2058
|
+
? { previous_response_id: lastAIMessageId }
|
|
2059
|
+
: {}),
|
|
2060
|
+
}, { signal: options?.signal, ...options?.options });
|
|
1910
2061
|
return {
|
|
1911
2062
|
generations: [
|
|
1912
2063
|
{
|
|
@@ -1926,12 +2077,23 @@ export class ChatOpenAI extends BaseChatModel {
|
|
|
1926
2077
|
},
|
|
1927
2078
|
};
|
|
1928
2079
|
}
|
|
2080
|
+
/**
|
|
2081
|
+
* Determines whether the responses API should be used for the given request.
|
|
2082
|
+
*
|
|
2083
|
+
* @internal
|
|
2084
|
+
*
|
|
2085
|
+
* @param options The parsed call options for the request.
|
|
2086
|
+
* @returns `true` if the responses API should be used, either because it is explicitly enabled,
|
|
2087
|
+
* or because the request requires it.
|
|
2088
|
+
*/
|
|
1929
2089
|
_useResponseApi(options) {
|
|
1930
2090
|
const usesBuiltInTools = options?.tools?.some(isBuiltInTool);
|
|
1931
2091
|
const hasResponsesOnlyKwargs = options?.previous_response_id != null ||
|
|
1932
2092
|
options?.text != null ||
|
|
1933
2093
|
options?.truncation != null ||
|
|
1934
|
-
options?.include != null
|
|
2094
|
+
options?.include != null ||
|
|
2095
|
+
options?.reasoning?.summary != null ||
|
|
2096
|
+
this.reasoning?.summary != null;
|
|
1935
2097
|
return this.useResponsesApi || usesBuiltInTools || hasResponsesOnlyKwargs;
|
|
1936
2098
|
}
|
|
1937
2099
|
/** @ignore */
|
package/dist/tools/dalle.cjs
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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] =
|
|
176
|
-
.
|
|
177
|
-
|
|
175
|
+
[data] =
|
|
176
|
+
response.data
|
|
177
|
+
?.map((item) => item.url)
|
|
178
|
+
.filter((url) => url !== "undefined") ?? [];
|
|
178
179
|
}
|
|
179
180
|
else {
|
|
180
|
-
[data] =
|
|
181
|
-
.
|
|
182
|
-
|
|
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
|
}
|
package/dist/tools/dalle.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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] =
|
|
173
|
-
.
|
|
174
|
-
|
|
172
|
+
[data] =
|
|
173
|
+
response.data
|
|
174
|
+
?.map((item) => item.url)
|
|
175
|
+
.filter((url) => url !== "undefined") ?? [];
|
|
175
176
|
}
|
|
176
177
|
else {
|
|
177
|
-
[data] =
|
|
178
|
-
.
|
|
179
|
-
|
|
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.
|
|
3
|
+
"version": "0.5.8",
|
|
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.
|
|
39
|
+
"openai": "^4.96.0",
|
|
40
40
|
"zod": "^3.22.4",
|
|
41
41
|
"zod-to-json-schema": "^3.22.3"
|
|
42
42
|
},
|