@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.
- package/dist/chat_models.cjs +326 -27
- package/dist/chat_models.d.ts +61 -3
- package/dist/chat_models.js +327 -28
- package/dist/tools/dalle.cjs +13 -10
- package/dist/tools/dalle.js +13 -10
- package/dist/types.d.ts +29 -0
- package/package.json +3 -3
package/dist/chat_models.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { OpenAI as OpenAIClient } from "openai";
|
|
2
|
-
import { AIMessage, AIMessageChunk, ChatMessage, ChatMessageChunk, FunctionMessageChunk, HumanMessageChunk, SystemMessageChunk, ToolMessageChunk, isAIMessage, } from "@langchain/core/messages";
|
|
2
|
+
import { AIMessage, AIMessageChunk, ChatMessage, ChatMessageChunk, FunctionMessageChunk, HumanMessageChunk, SystemMessageChunk, ToolMessageChunk, isAIMessage, parseBase64DataUrl, parseMimeType, convertToProviderContentBlock, isDataContentBlock, } from "@langchain/core/messages";
|
|
3
3
|
import { ChatGenerationChunk, } from "@langchain/core/outputs";
|
|
4
4
|
import { getEnvironmentVariable } from "@langchain/core/utils/env";
|
|
5
5
|
import { BaseChatModel, } from "@langchain/core/language_models/chat_models";
|
|
@@ -46,6 +46,132 @@ export function messageToOpenAIRole(message) {
|
|
|
46
46
|
throw new Error(`Unknown message type: ${type}`);
|
|
47
47
|
}
|
|
48
48
|
}
|
|
49
|
+
const completionsApiContentBlockConverter = {
|
|
50
|
+
providerName: "ChatOpenAI",
|
|
51
|
+
fromStandardTextBlock(block) {
|
|
52
|
+
return { type: "text", text: block.text };
|
|
53
|
+
},
|
|
54
|
+
fromStandardImageBlock(block) {
|
|
55
|
+
if (block.source_type === "url") {
|
|
56
|
+
return {
|
|
57
|
+
type: "image_url",
|
|
58
|
+
image_url: {
|
|
59
|
+
url: block.url,
|
|
60
|
+
...(block.metadata?.detail
|
|
61
|
+
? { detail: block.metadata.detail }
|
|
62
|
+
: {}),
|
|
63
|
+
},
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
if (block.source_type === "base64") {
|
|
67
|
+
const url = `data:${block.mime_type ?? ""};base64,${block.data}`;
|
|
68
|
+
return {
|
|
69
|
+
type: "image_url",
|
|
70
|
+
image_url: {
|
|
71
|
+
url,
|
|
72
|
+
...(block.metadata?.detail
|
|
73
|
+
? { detail: block.metadata.detail }
|
|
74
|
+
: {}),
|
|
75
|
+
},
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
throw new Error(`Image content blocks with source_type ${block.source_type} are not supported for ChatOpenAI`);
|
|
79
|
+
},
|
|
80
|
+
fromStandardAudioBlock(block) {
|
|
81
|
+
if (block.source_type === "url") {
|
|
82
|
+
const data = parseBase64DataUrl({ dataUrl: block.url });
|
|
83
|
+
if (!data) {
|
|
84
|
+
throw new Error(`URL audio blocks with source_type ${block.source_type} must be formatted as a data URL for ChatOpenAI`);
|
|
85
|
+
}
|
|
86
|
+
const rawMimeType = data.mime_type || block.mime_type || "";
|
|
87
|
+
let mimeType;
|
|
88
|
+
try {
|
|
89
|
+
mimeType = parseMimeType(rawMimeType);
|
|
90
|
+
}
|
|
91
|
+
catch {
|
|
92
|
+
throw new Error(`Audio blocks with source_type ${block.source_type} must have mime type of audio/wav or audio/mp3`);
|
|
93
|
+
}
|
|
94
|
+
if (mimeType.type !== "audio" ||
|
|
95
|
+
(mimeType.subtype !== "wav" && mimeType.subtype !== "mp3")) {
|
|
96
|
+
throw new Error(`Audio blocks with source_type ${block.source_type} must have mime type of audio/wav or audio/mp3`);
|
|
97
|
+
}
|
|
98
|
+
return {
|
|
99
|
+
type: "input_audio",
|
|
100
|
+
input_audio: {
|
|
101
|
+
format: mimeType.subtype,
|
|
102
|
+
data: data.data,
|
|
103
|
+
},
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
if (block.source_type === "base64") {
|
|
107
|
+
let mimeType;
|
|
108
|
+
try {
|
|
109
|
+
mimeType = parseMimeType(block.mime_type ?? "");
|
|
110
|
+
}
|
|
111
|
+
catch {
|
|
112
|
+
throw new Error(`Audio blocks with source_type ${block.source_type} must have mime type of audio/wav or audio/mp3`);
|
|
113
|
+
}
|
|
114
|
+
if (mimeType.type !== "audio" ||
|
|
115
|
+
(mimeType.subtype !== "wav" && mimeType.subtype !== "mp3")) {
|
|
116
|
+
throw new Error(`Audio blocks with source_type ${block.source_type} must have mime type of audio/wav or audio/mp3`);
|
|
117
|
+
}
|
|
118
|
+
return {
|
|
119
|
+
type: "input_audio",
|
|
120
|
+
input_audio: {
|
|
121
|
+
format: mimeType.subtype,
|
|
122
|
+
data: block.data,
|
|
123
|
+
},
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
throw new Error(`Audio content blocks with source_type ${block.source_type} are not supported for ChatOpenAI`);
|
|
127
|
+
},
|
|
128
|
+
fromStandardFileBlock(block) {
|
|
129
|
+
if (block.source_type === "url") {
|
|
130
|
+
const data = parseBase64DataUrl({ dataUrl: block.url });
|
|
131
|
+
if (!data) {
|
|
132
|
+
throw new Error(`URL file blocks with source_type ${block.source_type} must be formatted as a data URL for ChatOpenAI`);
|
|
133
|
+
}
|
|
134
|
+
return {
|
|
135
|
+
type: "file",
|
|
136
|
+
file: {
|
|
137
|
+
file_data: block.url,
|
|
138
|
+
...(block.metadata?.filename || block.metadata?.name
|
|
139
|
+
? {
|
|
140
|
+
filename: (block.metadata?.filename ||
|
|
141
|
+
block.metadata?.name),
|
|
142
|
+
}
|
|
143
|
+
: {}),
|
|
144
|
+
},
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
if (block.source_type === "base64") {
|
|
148
|
+
return {
|
|
149
|
+
type: "file",
|
|
150
|
+
file: {
|
|
151
|
+
file_data: `data:${block.mime_type ?? ""};base64,${block.data}`,
|
|
152
|
+
...(block.metadata?.filename ||
|
|
153
|
+
block.metadata?.name ||
|
|
154
|
+
block.metadata?.title
|
|
155
|
+
? {
|
|
156
|
+
filename: (block.metadata?.filename ||
|
|
157
|
+
block.metadata?.name ||
|
|
158
|
+
block.metadata?.title),
|
|
159
|
+
}
|
|
160
|
+
: {}),
|
|
161
|
+
},
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
if (block.source_type === "id") {
|
|
165
|
+
return {
|
|
166
|
+
type: "file",
|
|
167
|
+
file: {
|
|
168
|
+
file_id: block.id,
|
|
169
|
+
},
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
throw new Error(`File content blocks with source_type ${block.source_type} are not supported for ChatOpenAI`);
|
|
173
|
+
},
|
|
174
|
+
};
|
|
49
175
|
// Used in LangSmith, export is important here
|
|
50
176
|
export function _convertMessagesToOpenAIParams(messages, model) {
|
|
51
177
|
// TODO: Function messages do not support array content, fix cast
|
|
@@ -54,10 +180,18 @@ export function _convertMessagesToOpenAIParams(messages, model) {
|
|
|
54
180
|
if (role === "system" && isReasoningModel(model)) {
|
|
55
181
|
role = "developer";
|
|
56
182
|
}
|
|
183
|
+
const content = typeof message.content === "string"
|
|
184
|
+
? message.content
|
|
185
|
+
: message.content.map((m) => {
|
|
186
|
+
if (isDataContentBlock(m)) {
|
|
187
|
+
return convertToProviderContentBlock(m, completionsApiContentBlockConverter);
|
|
188
|
+
}
|
|
189
|
+
return m;
|
|
190
|
+
});
|
|
57
191
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
58
192
|
const completionParam = {
|
|
59
193
|
role,
|
|
60
|
-
content
|
|
194
|
+
content,
|
|
61
195
|
};
|
|
62
196
|
if (message.name != null) {
|
|
63
197
|
completionParam.name = message.name;
|
|
@@ -93,8 +227,32 @@ export function _convertMessagesToOpenAIParams(messages, model) {
|
|
|
93
227
|
});
|
|
94
228
|
}
|
|
95
229
|
const _FUNCTION_CALL_IDS_MAP_KEY = "__openai_function_call_ids__";
|
|
96
|
-
function
|
|
97
|
-
|
|
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) => {
|
|
98
256
|
let role = messageToOpenAIRole(lcMsg);
|
|
99
257
|
if (role === "system" && isReasoningModel(model))
|
|
100
258
|
role = "developer";
|
|
@@ -144,15 +302,25 @@ function _convertMessagesToOpenAIResponsesParams(messages, model) {
|
|
|
144
302
|
};
|
|
145
303
|
}
|
|
146
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
|
|
147
314
|
const input = [];
|
|
148
315
|
// reasoning items
|
|
149
|
-
if (lcMsg.additional_kwargs.reasoning != null) {
|
|
316
|
+
if (!zdrEnabled && lcMsg.additional_kwargs.reasoning != null) {
|
|
150
317
|
const isReasoningItem = (item) => typeof item === "object" &&
|
|
151
318
|
item != null &&
|
|
152
319
|
"type" in item &&
|
|
153
320
|
item.type === "reasoning";
|
|
154
321
|
if (isReasoningItem(lcMsg.additional_kwargs.reasoning)) {
|
|
155
|
-
|
|
322
|
+
const reasoningItem = _convertReasoningSummaryToOpenAIResponsesParams(lcMsg.additional_kwargs.reasoning);
|
|
323
|
+
input.push(reasoningItem);
|
|
156
324
|
}
|
|
157
325
|
}
|
|
158
326
|
// ai content
|
|
@@ -169,6 +337,7 @@ function _convertMessagesToOpenAIResponsesParams(messages, model) {
|
|
|
169
337
|
input.push({
|
|
170
338
|
type: "message",
|
|
171
339
|
role: "assistant",
|
|
340
|
+
...(lcMsg.id && !zdrEnabled ? { id: lcMsg.id } : {}),
|
|
172
341
|
content: typeof content === "string"
|
|
173
342
|
? content
|
|
174
343
|
: content.flatMap((item) => {
|
|
@@ -196,7 +365,7 @@ function _convertMessagesToOpenAIResponsesParams(messages, model) {
|
|
|
196
365
|
name: toolCall.name,
|
|
197
366
|
arguments: JSON.stringify(toolCall.args),
|
|
198
367
|
call_id: toolCall.id,
|
|
199
|
-
id: functionCallIds?.[toolCall.id],
|
|
368
|
+
...(zdrEnabled ? { id: functionCallIds?.[toolCall.id] } : {}),
|
|
200
369
|
})));
|
|
201
370
|
}
|
|
202
371
|
else if (lcMsg.additional_kwargs.tool_calls != null) {
|
|
@@ -204,21 +373,17 @@ function _convertMessagesToOpenAIResponsesParams(messages, model) {
|
|
|
204
373
|
type: "function_call",
|
|
205
374
|
name: toolCall.function.name,
|
|
206
375
|
call_id: toolCall.id,
|
|
207
|
-
id: functionCallIds?.[toolCall.id],
|
|
376
|
+
...(zdrEnabled ? { id: functionCallIds?.[toolCall.id] } : {}),
|
|
208
377
|
arguments: toolCall.function.arguments,
|
|
209
378
|
})));
|
|
210
379
|
}
|
|
211
380
|
const toolOutputs = lcMsg.response_metadata.output?.length
|
|
212
381
|
? lcMsg.response_metadata.output
|
|
213
382
|
: lcMsg.additional_kwargs.tool_outputs;
|
|
383
|
+
let computerCalls = [];
|
|
214
384
|
if (toolOutputs != null) {
|
|
215
385
|
const castToolOutputs = toolOutputs;
|
|
216
|
-
|
|
217
|
-
const computerCalls = castToolOutputs?.filter((item) => item.type === "computer_call");
|
|
218
|
-
// NOTE: Reasoning outputs must be passed to the model BEFORE computer calls.
|
|
219
|
-
if (reasoningCalls.length > 0 && computerCalls.length > 0) {
|
|
220
|
-
input.push(...reasoningCalls);
|
|
221
|
-
}
|
|
386
|
+
computerCalls = castToolOutputs?.filter((item) => item.type === "computer_call");
|
|
222
387
|
if (computerCalls.length > 0)
|
|
223
388
|
input.push(...computerCalls);
|
|
224
389
|
}
|
|
@@ -227,6 +392,9 @@ function _convertMessagesToOpenAIResponsesParams(messages, model) {
|
|
|
227
392
|
const content = typeof lcMsg.content === "string"
|
|
228
393
|
? lcMsg.content
|
|
229
394
|
: lcMsg.content.flatMap((item) => {
|
|
395
|
+
if (isDataContentBlock(item)) {
|
|
396
|
+
return convertToProviderContentBlock(item, completionsApiContentBlockConverter);
|
|
397
|
+
}
|
|
230
398
|
if (item.type === "text") {
|
|
231
399
|
return { type: "input_text", text: item.text };
|
|
232
400
|
}
|
|
@@ -419,6 +587,39 @@ function _convertOpenAIResponsesDeltaToBaseMessageChunk(chunk) {
|
|
|
419
587
|
else if (chunk.type === "response.refusal.done") {
|
|
420
588
|
additional_kwargs.refusal = chunk.refusal;
|
|
421
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
|
+
}
|
|
422
623
|
else {
|
|
423
624
|
return null;
|
|
424
625
|
}
|
|
@@ -461,9 +662,7 @@ function _convertChatOpenAIToolTypeToOpenAITool(tool, fields) {
|
|
|
461
662
|
return _convertToOpenAITool(tool, fields);
|
|
462
663
|
}
|
|
463
664
|
function isReasoningModel(model) {
|
|
464
|
-
return (model
|
|
465
|
-
model?.startsWith("o3") ||
|
|
466
|
-
model?.startsWith("o4"));
|
|
665
|
+
return model && /^o\d/.test(model);
|
|
467
666
|
}
|
|
468
667
|
/**
|
|
469
668
|
* OpenAI chat model integration.
|
|
@@ -1071,6 +1270,9 @@ export class ChatOpenAI extends BaseChatModel {
|
|
|
1071
1270
|
"tags",
|
|
1072
1271
|
"metadata",
|
|
1073
1272
|
"disableStreaming",
|
|
1273
|
+
"useResponsesApi",
|
|
1274
|
+
"zdrEnabled",
|
|
1275
|
+
"reasoning",
|
|
1074
1276
|
];
|
|
1075
1277
|
}
|
|
1076
1278
|
constructor(fields) {
|
|
@@ -1248,18 +1450,51 @@ export class ChatOpenAI extends BaseChatModel {
|
|
|
1248
1450
|
writable: true,
|
|
1249
1451
|
value: void 0
|
|
1250
1452
|
});
|
|
1453
|
+
/**
|
|
1454
|
+
* @deprecated Use {@link reasoning} object instead.
|
|
1455
|
+
*/
|
|
1251
1456
|
Object.defineProperty(this, "reasoningEffort", {
|
|
1252
1457
|
enumerable: true,
|
|
1253
1458
|
configurable: true,
|
|
1254
1459
|
writable: true,
|
|
1255
1460
|
value: void 0
|
|
1256
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
|
+
*/
|
|
1257
1475
|
Object.defineProperty(this, "useResponsesApi", {
|
|
1258
1476
|
enumerable: true,
|
|
1259
1477
|
configurable: true,
|
|
1260
1478
|
writable: true,
|
|
1261
1479
|
value: false
|
|
1262
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
|
+
});
|
|
1263
1498
|
this.openAIApiKey =
|
|
1264
1499
|
fields?.apiKey ??
|
|
1265
1500
|
fields?.openAIApiKey ??
|
|
@@ -1287,7 +1522,12 @@ export class ChatOpenAI extends BaseChatModel {
|
|
|
1287
1522
|
this.__includeRawResponse = fields?.__includeRawResponse;
|
|
1288
1523
|
this.audio = fields?.audio;
|
|
1289
1524
|
this.modalities = fields?.modalities;
|
|
1290
|
-
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);
|
|
1291
1531
|
this.maxTokens = fields?.maxCompletionTokens ?? fields?.maxTokens;
|
|
1292
1532
|
this.useResponsesApi = fields?.useResponsesApi ?? this.useResponsesApi;
|
|
1293
1533
|
this.disableStreaming = fields?.disableStreaming ?? this.disableStreaming;
|
|
@@ -1310,6 +1550,7 @@ export class ChatOpenAI extends BaseChatModel {
|
|
|
1310
1550
|
if (fields?.supportsStrictToolCalling !== undefined) {
|
|
1311
1551
|
this.supportsStrictToolCalling = fields.supportsStrictToolCalling;
|
|
1312
1552
|
}
|
|
1553
|
+
this.zdrEnabled = fields?.zdrEnabled ?? false;
|
|
1313
1554
|
}
|
|
1314
1555
|
getLsParams(options) {
|
|
1315
1556
|
const params = this.invocationParams(options);
|
|
@@ -1348,6 +1589,35 @@ export class ChatOpenAI extends BaseChatModel {
|
|
|
1348
1589
|
}
|
|
1349
1590
|
return resFormat;
|
|
1350
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
|
+
}
|
|
1351
1621
|
/**
|
|
1352
1622
|
* Get the parameters used to invoke the model
|
|
1353
1623
|
*/
|
|
@@ -1424,11 +1694,12 @@ export class ChatOpenAI extends BaseChatModel {
|
|
|
1424
1694
|
})(),
|
|
1425
1695
|
parallel_tool_calls: options?.parallel_tool_calls,
|
|
1426
1696
|
max_output_tokens: this.maxTokens === -1 ? undefined : this.maxTokens,
|
|
1697
|
+
...(this.zdrEnabled ? { store: false } : {}),
|
|
1427
1698
|
...this.modelKwargs,
|
|
1428
1699
|
};
|
|
1429
|
-
const
|
|
1430
|
-
if (
|
|
1431
|
-
params.reasoning =
|
|
1700
|
+
const reasoning = this.getReasoningParams(options);
|
|
1701
|
+
if (reasoning !== undefined) {
|
|
1702
|
+
params.reasoning = reasoning;
|
|
1432
1703
|
}
|
|
1433
1704
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1434
1705
|
return params;
|
|
@@ -1475,9 +1746,9 @@ export class ChatOpenAI extends BaseChatModel {
|
|
|
1475
1746
|
if (options?.prediction !== undefined) {
|
|
1476
1747
|
params.prediction = options.prediction;
|
|
1477
1748
|
}
|
|
1478
|
-
const
|
|
1479
|
-
if (
|
|
1480
|
-
params.reasoning_effort =
|
|
1749
|
+
const reasoning = this.getReasoningParams(options);
|
|
1750
|
+
if (reasoning !== undefined && reasoning.effort !== undefined) {
|
|
1751
|
+
params.reasoning_effort = reasoning.effort;
|
|
1481
1752
|
}
|
|
1482
1753
|
if (isReasoningModel(params.model)) {
|
|
1483
1754
|
params.max_completion_tokens =
|
|
@@ -1631,10 +1902,17 @@ export class ChatOpenAI extends BaseChatModel {
|
|
|
1631
1902
|
}
|
|
1632
1903
|
async *_streamResponseChunks(messages, options, runManager) {
|
|
1633
1904
|
if (this._useResponseApi(options)) {
|
|
1905
|
+
const lastAIMessage = messages.filter((m) => isAIMessage(m)).pop();
|
|
1906
|
+
const lastAIMessageId = lastAIMessage?.response_metadata?.id;
|
|
1634
1907
|
const streamIterable = await this.responseApiWithRetry({
|
|
1635
1908
|
...this.invocationParams(options, { streaming: true }),
|
|
1636
|
-
input: _convertMessagesToOpenAIResponsesParams(messages, this.model),
|
|
1909
|
+
input: _convertMessagesToOpenAIResponsesParams(messages, this.model, this.zdrEnabled),
|
|
1637
1910
|
stream: true,
|
|
1911
|
+
...(lastAIMessageId &&
|
|
1912
|
+
lastAIMessageId.startsWith("resp_") &&
|
|
1913
|
+
!this.zdrEnabled
|
|
1914
|
+
? { previous_response_id: lastAIMessageId }
|
|
1915
|
+
: {}),
|
|
1638
1916
|
}, options);
|
|
1639
1917
|
for await (const data of streamIterable) {
|
|
1640
1918
|
const chunk = _convertOpenAIResponsesDeltaToBaseMessageChunk(data);
|
|
@@ -1768,8 +2046,18 @@ export class ChatOpenAI extends BaseChatModel {
|
|
|
1768
2046
|
},
|
|
1769
2047
|
};
|
|
1770
2048
|
}
|
|
1771
|
-
const
|
|
1772
|
-
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 });
|
|
1773
2061
|
return {
|
|
1774
2062
|
generations: [
|
|
1775
2063
|
{
|
|
@@ -1789,12 +2077,23 @@ export class ChatOpenAI extends BaseChatModel {
|
|
|
1789
2077
|
},
|
|
1790
2078
|
};
|
|
1791
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
|
+
*/
|
|
1792
2089
|
_useResponseApi(options) {
|
|
1793
2090
|
const usesBuiltInTools = options?.tools?.some(isBuiltInTool);
|
|
1794
2091
|
const hasResponsesOnlyKwargs = options?.previous_response_id != null ||
|
|
1795
2092
|
options?.text != null ||
|
|
1796
2093
|
options?.truncation != null ||
|
|
1797
|
-
options?.include != null
|
|
2094
|
+
options?.include != null ||
|
|
2095
|
+
options?.reasoning?.summary != null ||
|
|
2096
|
+
this.reasoning?.summary != null;
|
|
1798
2097
|
return this.useResponsesApi || usesBuiltInTools || hasResponsesOnlyKwargs;
|
|
1799
2098
|
}
|
|
1800
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
|
}
|