@langchain/openai 0.4.4 → 0.4.5-rc.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chat_models.cjs +442 -1
- package/dist/chat_models.d.ts +46 -5
- package/dist/chat_models.js +442 -1
- package/dist/types.d.ts +1 -1
- package/package.json +2 -2
package/dist/chat_models.cjs
CHANGED
|
@@ -97,6 +97,283 @@ function _convertMessagesToOpenAIParams(messages, model) {
|
|
|
97
97
|
});
|
|
98
98
|
}
|
|
99
99
|
exports._convertMessagesToOpenAIParams = _convertMessagesToOpenAIParams;
|
|
100
|
+
const _FUNCTION_CALL_IDS_MAP_KEY = "__openai_function_call_ids__";
|
|
101
|
+
function _convertMessagesToOpenAIResponsesParams(messages, model) {
|
|
102
|
+
return messages.flatMap((lcMsg) => {
|
|
103
|
+
let role = messageToOpenAIRole(lcMsg);
|
|
104
|
+
if (role === "system" && isReasoningModel(model))
|
|
105
|
+
role = "developer";
|
|
106
|
+
if (role === "function") {
|
|
107
|
+
throw new Error("Function messages are not supported in Responses API");
|
|
108
|
+
}
|
|
109
|
+
if (role === "tool") {
|
|
110
|
+
const toolMessage = lcMsg;
|
|
111
|
+
return {
|
|
112
|
+
type: "function_call_output",
|
|
113
|
+
call_id: toolMessage.tool_call_id,
|
|
114
|
+
id: toolMessage.id,
|
|
115
|
+
output: typeof toolMessage.content !== "string"
|
|
116
|
+
? JSON.stringify(toolMessage.content)
|
|
117
|
+
: toolMessage.content,
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
if (role === "assistant") {
|
|
121
|
+
const functionCallIds =
|
|
122
|
+
// eslint-disable-next-line @typescript-eslint/no-use-before-define
|
|
123
|
+
lcMsg.additional_kwargs[_FUNCTION_CALL_IDS_MAP_KEY];
|
|
124
|
+
if ((0, messages_1.isAIMessage)(lcMsg) && !!lcMsg.tool_calls?.length) {
|
|
125
|
+
return lcMsg.tool_calls.map((toolCall) => ({
|
|
126
|
+
type: "function_call",
|
|
127
|
+
name: toolCall.name,
|
|
128
|
+
arguments: JSON.stringify(toolCall.args),
|
|
129
|
+
call_id: toolCall.id,
|
|
130
|
+
id: functionCallIds[toolCall.id],
|
|
131
|
+
}));
|
|
132
|
+
}
|
|
133
|
+
if (lcMsg.additional_kwargs.tool_calls != null) {
|
|
134
|
+
return lcMsg.additional_kwargs.tool_calls.map((toolCall) => ({
|
|
135
|
+
type: "function_call",
|
|
136
|
+
name: toolCall.function.name,
|
|
137
|
+
call_id: toolCall.id,
|
|
138
|
+
id: functionCallIds[toolCall.id],
|
|
139
|
+
arguments: toolCall.function.arguments,
|
|
140
|
+
}));
|
|
141
|
+
}
|
|
142
|
+
let { content } = lcMsg;
|
|
143
|
+
if (lcMsg.additional_kwargs.refusal != null) {
|
|
144
|
+
if (typeof content === "string") {
|
|
145
|
+
content = [{ type: "output_text", text: content, annotations: [] }];
|
|
146
|
+
}
|
|
147
|
+
content = [
|
|
148
|
+
...content,
|
|
149
|
+
{ type: "refusal", refusal: lcMsg.additional_kwargs.refusal },
|
|
150
|
+
];
|
|
151
|
+
}
|
|
152
|
+
return {
|
|
153
|
+
type: "message",
|
|
154
|
+
role: "assistant",
|
|
155
|
+
content: typeof content === "string"
|
|
156
|
+
? content
|
|
157
|
+
: content.flatMap((item) => {
|
|
158
|
+
if (item.type === "text") {
|
|
159
|
+
return {
|
|
160
|
+
type: "output_text",
|
|
161
|
+
text: item.text,
|
|
162
|
+
// @ts-expect-error TODO: add types for `annotations`
|
|
163
|
+
annotations: item.annotations ?? [],
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
if (item.type === "output_text" || item.type === "refusal") {
|
|
167
|
+
return item;
|
|
168
|
+
}
|
|
169
|
+
return [];
|
|
170
|
+
}),
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
if (role === "user") {
|
|
174
|
+
return {
|
|
175
|
+
type: "message",
|
|
176
|
+
role: "user",
|
|
177
|
+
content: typeof lcMsg.content === "string"
|
|
178
|
+
? lcMsg.content
|
|
179
|
+
: lcMsg.content.flatMap((item) => {
|
|
180
|
+
if (item.type === "text") {
|
|
181
|
+
return { type: "input_text", text: item.text };
|
|
182
|
+
}
|
|
183
|
+
if (item.type === "image_url") {
|
|
184
|
+
const image_url = typeof item.image_url === "string"
|
|
185
|
+
? item.image_url
|
|
186
|
+
: item.image_url.url;
|
|
187
|
+
const detail = typeof item.image_url === "string"
|
|
188
|
+
? "auto"
|
|
189
|
+
: item.image_url.detail;
|
|
190
|
+
return { type: "input_image", image_url, detail };
|
|
191
|
+
}
|
|
192
|
+
if (item.type === "input_text" ||
|
|
193
|
+
item.type === "input_image" ||
|
|
194
|
+
item.type === "input_file") {
|
|
195
|
+
return item;
|
|
196
|
+
}
|
|
197
|
+
return [];
|
|
198
|
+
}),
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
return [];
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
function _convertOpenAIResponsesMessageToBaseMessage(response) {
|
|
205
|
+
if (response.error) {
|
|
206
|
+
// TODO: add support for `addLangChainErrorFields`
|
|
207
|
+
const error = new Error(response.error.message);
|
|
208
|
+
error.name = response.error.code;
|
|
209
|
+
throw error;
|
|
210
|
+
}
|
|
211
|
+
const content = [];
|
|
212
|
+
const tool_calls = [];
|
|
213
|
+
const invalid_tool_calls = [];
|
|
214
|
+
const response_metadata = {
|
|
215
|
+
model: response.model,
|
|
216
|
+
created_at: response.created_at,
|
|
217
|
+
id: response.id,
|
|
218
|
+
incomplete_details: response.incomplete_details,
|
|
219
|
+
metadata: response.metadata,
|
|
220
|
+
object: response.object,
|
|
221
|
+
status: response.status,
|
|
222
|
+
user: response.user,
|
|
223
|
+
// for compatibility with chat completion calls.
|
|
224
|
+
model_name: response.model,
|
|
225
|
+
};
|
|
226
|
+
const additional_kwargs = {};
|
|
227
|
+
for (const item of response.output) {
|
|
228
|
+
if (item.type === "message") {
|
|
229
|
+
// TODO: how to handle refusals?
|
|
230
|
+
content.push(...item.content.map((part) => {
|
|
231
|
+
if (part.type === "output_text") {
|
|
232
|
+
return {
|
|
233
|
+
type: "text",
|
|
234
|
+
text: part.text,
|
|
235
|
+
annotations: part.annotations,
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
return part;
|
|
239
|
+
}));
|
|
240
|
+
}
|
|
241
|
+
else if (item.type === "function_call") {
|
|
242
|
+
const fnAdapter = {
|
|
243
|
+
function: { name: item.name, arguments: item.arguments },
|
|
244
|
+
id: item.call_id,
|
|
245
|
+
};
|
|
246
|
+
try {
|
|
247
|
+
tool_calls.push((0, openai_tools_1.parseToolCall)(fnAdapter, { returnId: true }));
|
|
248
|
+
}
|
|
249
|
+
catch (e) {
|
|
250
|
+
let errMessage;
|
|
251
|
+
if (typeof e === "object" &&
|
|
252
|
+
e != null &&
|
|
253
|
+
"message" in e &&
|
|
254
|
+
typeof e.message === "string") {
|
|
255
|
+
errMessage = e.message;
|
|
256
|
+
}
|
|
257
|
+
invalid_tool_calls.push((0, openai_tools_1.makeInvalidToolCall)(fnAdapter, errMessage));
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
else if (item.type === "reasoning") {
|
|
261
|
+
additional_kwargs.reasoning = item;
|
|
262
|
+
}
|
|
263
|
+
else {
|
|
264
|
+
additional_kwargs.tool_outputs ??= [];
|
|
265
|
+
additional_kwargs.tool_outputs.push(item);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
return new messages_1.AIMessage({
|
|
269
|
+
id: response.id,
|
|
270
|
+
content,
|
|
271
|
+
tool_calls,
|
|
272
|
+
invalid_tool_calls,
|
|
273
|
+
usage_metadata: response.usage,
|
|
274
|
+
additional_kwargs,
|
|
275
|
+
response_metadata,
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
function _convertOpenAIResponsesDeltaToBaseMessageChunk(chunk) {
|
|
279
|
+
const content = [];
|
|
280
|
+
let generationInfo = {};
|
|
281
|
+
let usage_metadata;
|
|
282
|
+
const tool_call_chunks = [];
|
|
283
|
+
const response_metadata = {};
|
|
284
|
+
const additional_kwargs = {};
|
|
285
|
+
let id;
|
|
286
|
+
if (chunk.type === "response.output_text.delta") {
|
|
287
|
+
content.push({
|
|
288
|
+
type: "text",
|
|
289
|
+
text: chunk.delta,
|
|
290
|
+
index: chunk.content_index,
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
else if (chunk.type === "response.output_text.annotation.added") {
|
|
294
|
+
content.push({
|
|
295
|
+
type: "text",
|
|
296
|
+
text: "",
|
|
297
|
+
annotations: [chunk.annotation],
|
|
298
|
+
index: chunk.content_index,
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
else if (chunk.type === "response.output_item.added" &&
|
|
302
|
+
chunk.item.type === "message") {
|
|
303
|
+
id = chunk.item.id;
|
|
304
|
+
}
|
|
305
|
+
else if (chunk.type === "response.output_item.added" &&
|
|
306
|
+
chunk.item.type === "function_call") {
|
|
307
|
+
tool_call_chunks.push({
|
|
308
|
+
type: "tool_call_chunk",
|
|
309
|
+
name: chunk.item.name,
|
|
310
|
+
args: chunk.item.arguments,
|
|
311
|
+
id: chunk.item.id,
|
|
312
|
+
index: chunk.output_index,
|
|
313
|
+
});
|
|
314
|
+
additional_kwargs[_FUNCTION_CALL_IDS_MAP_KEY] = {
|
|
315
|
+
[chunk.item.call_id]: chunk.item.id,
|
|
316
|
+
};
|
|
317
|
+
}
|
|
318
|
+
else if (chunk.type === "response.output_item.done" &&
|
|
319
|
+
(chunk.item.type === "web_search_call" ||
|
|
320
|
+
chunk.item.type === "file_search_call")) {
|
|
321
|
+
additional_kwargs.tool_outputs = [chunk.item];
|
|
322
|
+
}
|
|
323
|
+
else if (chunk.type === "response.created") {
|
|
324
|
+
response_metadata.id = chunk.response.id;
|
|
325
|
+
response_metadata.model_name = chunk.response.model;
|
|
326
|
+
response_metadata.model = chunk.response.model;
|
|
327
|
+
}
|
|
328
|
+
else if (chunk.type === "response.completed") {
|
|
329
|
+
usage_metadata = chunk.response.usage;
|
|
330
|
+
}
|
|
331
|
+
else if (chunk.type === "response.function_call_arguments.delta") {
|
|
332
|
+
tool_call_chunks.push({
|
|
333
|
+
type: "tool_call_chunk",
|
|
334
|
+
args: chunk.delta,
|
|
335
|
+
index: chunk.output_index,
|
|
336
|
+
});
|
|
337
|
+
}
|
|
338
|
+
else if (chunk.type === "response.web_search_call.completed" ||
|
|
339
|
+
chunk.type === "response.file_search_call.completed") {
|
|
340
|
+
generationInfo = {
|
|
341
|
+
tool_outputs: {
|
|
342
|
+
id: chunk.item_id,
|
|
343
|
+
type: chunk.type.replace("response.", "").replace(".completed", ""),
|
|
344
|
+
status: "completed",
|
|
345
|
+
},
|
|
346
|
+
};
|
|
347
|
+
}
|
|
348
|
+
else if (chunk.type === "response.refusal.done") {
|
|
349
|
+
additional_kwargs.refusal = chunk.refusal;
|
|
350
|
+
}
|
|
351
|
+
else {
|
|
352
|
+
return null;
|
|
353
|
+
}
|
|
354
|
+
return new outputs_1.ChatGenerationChunk({
|
|
355
|
+
// Legacy reasons, `onLLMNewToken` should pulls this out
|
|
356
|
+
text: content.map((part) => part.text).join(""),
|
|
357
|
+
message: new messages_1.AIMessageChunk({
|
|
358
|
+
id,
|
|
359
|
+
content,
|
|
360
|
+
tool_call_chunks,
|
|
361
|
+
usage_metadata,
|
|
362
|
+
additional_kwargs,
|
|
363
|
+
response_metadata,
|
|
364
|
+
}),
|
|
365
|
+
generationInfo,
|
|
366
|
+
});
|
|
367
|
+
}
|
|
368
|
+
function isBuiltInTool(tool) {
|
|
369
|
+
return "type" in tool && tool.type !== "function";
|
|
370
|
+
}
|
|
371
|
+
function isBuiltInToolChoice(tool_choice) {
|
|
372
|
+
return (tool_choice != null &&
|
|
373
|
+
typeof tool_choice === "object" &&
|
|
374
|
+
"type" in tool_choice &&
|
|
375
|
+
tool_choice.type !== "function");
|
|
376
|
+
}
|
|
100
377
|
function _convertChatOpenAIToolTypeToOpenAITool(tool, fields) {
|
|
101
378
|
if ((0, base_1.isOpenAITool)(tool)) {
|
|
102
379
|
if (fields?.strict !== undefined) {
|
|
@@ -904,6 +1181,12 @@ class ChatOpenAI extends chat_models_1.BaseChatModel {
|
|
|
904
1181
|
writable: true,
|
|
905
1182
|
value: void 0
|
|
906
1183
|
});
|
|
1184
|
+
Object.defineProperty(this, "useResponsesApi", {
|
|
1185
|
+
enumerable: true,
|
|
1186
|
+
configurable: true,
|
|
1187
|
+
writable: true,
|
|
1188
|
+
value: false
|
|
1189
|
+
});
|
|
907
1190
|
this.openAIApiKey =
|
|
908
1191
|
fields?.apiKey ??
|
|
909
1192
|
fields?.openAIApiKey ??
|
|
@@ -933,6 +1216,7 @@ class ChatOpenAI extends chat_models_1.BaseChatModel {
|
|
|
933
1216
|
this.modalities = fields?.modalities;
|
|
934
1217
|
this.reasoningEffort = fields?.reasoningEffort;
|
|
935
1218
|
this.maxTokens = fields?.maxCompletionTokens ?? fields?.maxTokens;
|
|
1219
|
+
this.useResponsesApi = fields?.useResponsesApi ?? this.useResponsesApi;
|
|
936
1220
|
if (this.model === "o1") {
|
|
937
1221
|
this.disableStreaming = true;
|
|
938
1222
|
}
|
|
@@ -970,7 +1254,9 @@ class ChatOpenAI extends chat_models_1.BaseChatModel {
|
|
|
970
1254
|
strict = this.supportsStrictToolCalling;
|
|
971
1255
|
}
|
|
972
1256
|
return this.bind({
|
|
973
|
-
tools: tools.map((tool) =>
|
|
1257
|
+
tools: tools.map((tool) => isBuiltInTool(tool)
|
|
1258
|
+
? tool
|
|
1259
|
+
: _convertChatOpenAIToolTypeToOpenAITool(tool, { strict })),
|
|
974
1260
|
...kwargs,
|
|
975
1261
|
});
|
|
976
1262
|
}
|
|
@@ -996,6 +1282,80 @@ class ChatOpenAI extends chat_models_1.BaseChatModel {
|
|
|
996
1282
|
else if (this.supportsStrictToolCalling !== undefined) {
|
|
997
1283
|
strict = this.supportsStrictToolCalling;
|
|
998
1284
|
}
|
|
1285
|
+
if (this._useResponseApi(options)) {
|
|
1286
|
+
const params = {
|
|
1287
|
+
model: this.model,
|
|
1288
|
+
temperature: this.temperature,
|
|
1289
|
+
top_p: this.topP,
|
|
1290
|
+
user: this.user,
|
|
1291
|
+
// if include_usage is set or streamUsage then stream must be set to true.
|
|
1292
|
+
stream: this.streaming,
|
|
1293
|
+
previous_response_id: options?.previous_response_id,
|
|
1294
|
+
truncation: options?.truncation,
|
|
1295
|
+
include: options?.include,
|
|
1296
|
+
tools: options?.tools?.length
|
|
1297
|
+
? options.tools
|
|
1298
|
+
.map((tool) => {
|
|
1299
|
+
if (isBuiltInTool(tool)) {
|
|
1300
|
+
return tool;
|
|
1301
|
+
}
|
|
1302
|
+
else if ((0, base_1.isOpenAITool)(tool)) {
|
|
1303
|
+
return {
|
|
1304
|
+
type: "function",
|
|
1305
|
+
name: tool.function.name,
|
|
1306
|
+
parameters: tool.function.parameters,
|
|
1307
|
+
description: tool.function.description,
|
|
1308
|
+
strict,
|
|
1309
|
+
};
|
|
1310
|
+
}
|
|
1311
|
+
else {
|
|
1312
|
+
return null;
|
|
1313
|
+
}
|
|
1314
|
+
})
|
|
1315
|
+
.filter((tool) => tool !== null)
|
|
1316
|
+
: undefined,
|
|
1317
|
+
tool_choice: isBuiltInToolChoice(options?.tool_choice)
|
|
1318
|
+
? options?.tool_choice
|
|
1319
|
+
: (() => {
|
|
1320
|
+
const formatted = (0, openai_js_1.formatToOpenAIToolChoice)(options?.tool_choice);
|
|
1321
|
+
if (typeof formatted === "object" && "type" in formatted) {
|
|
1322
|
+
return { type: "function", name: formatted.function.name };
|
|
1323
|
+
}
|
|
1324
|
+
else {
|
|
1325
|
+
return undefined;
|
|
1326
|
+
}
|
|
1327
|
+
})(),
|
|
1328
|
+
text: (() => {
|
|
1329
|
+
if (options?.text)
|
|
1330
|
+
return options.text;
|
|
1331
|
+
const format = this.createResponseFormat(options?.response_format);
|
|
1332
|
+
if (format?.type === "json_schema") {
|
|
1333
|
+
if (format.json_schema.schema != null) {
|
|
1334
|
+
return {
|
|
1335
|
+
format: {
|
|
1336
|
+
type: "json_schema",
|
|
1337
|
+
schema: format.json_schema.schema,
|
|
1338
|
+
description: format.json_schema.description,
|
|
1339
|
+
name: format.json_schema.name,
|
|
1340
|
+
strict: format.json_schema.strict,
|
|
1341
|
+
},
|
|
1342
|
+
};
|
|
1343
|
+
}
|
|
1344
|
+
return undefined;
|
|
1345
|
+
}
|
|
1346
|
+
return { format };
|
|
1347
|
+
})(),
|
|
1348
|
+
parallel_tool_calls: options?.parallel_tool_calls,
|
|
1349
|
+
max_output_tokens: this.maxTokens === -1 ? undefined : this.maxTokens,
|
|
1350
|
+
...this.modelKwargs,
|
|
1351
|
+
};
|
|
1352
|
+
const reasoningEffort = options?.reasoning_effort ?? this.reasoningEffort;
|
|
1353
|
+
if (reasoningEffort !== undefined) {
|
|
1354
|
+
params.reasoning = { effort: reasoningEffort };
|
|
1355
|
+
}
|
|
1356
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1357
|
+
return params;
|
|
1358
|
+
}
|
|
999
1359
|
let streamOptionsConfig = {};
|
|
1000
1360
|
if (options?.stream_options !== undefined) {
|
|
1001
1361
|
streamOptionsConfig = { stream_options: options.stream_options };
|
|
@@ -1049,6 +1409,7 @@ class ChatOpenAI extends chat_models_1.BaseChatModel {
|
|
|
1049
1409
|
else {
|
|
1050
1410
|
params.max_tokens = this.maxTokens === -1 ? undefined : this.maxTokens;
|
|
1051
1411
|
}
|
|
1412
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1052
1413
|
return params;
|
|
1053
1414
|
}
|
|
1054
1415
|
_convertOpenAIChatCompletionMessageToBaseMessage(message, rawResponse) {
|
|
@@ -1192,6 +1553,20 @@ class ChatOpenAI extends chat_models_1.BaseChatModel {
|
|
|
1192
1553
|
};
|
|
1193
1554
|
}
|
|
1194
1555
|
async *_streamResponseChunks(messages, options, runManager) {
|
|
1556
|
+
if (this._useResponseApi(options)) {
|
|
1557
|
+
const streamIterable = await this.responseApiWithRetry({
|
|
1558
|
+
...this.invocationParams(options, { streaming: true }),
|
|
1559
|
+
input: _convertMessagesToOpenAIResponsesParams(messages, this.model),
|
|
1560
|
+
stream: true,
|
|
1561
|
+
}, options);
|
|
1562
|
+
for await (const data of streamIterable) {
|
|
1563
|
+
const chunk = _convertOpenAIResponsesDeltaToBaseMessageChunk(data);
|
|
1564
|
+
if (chunk == null)
|
|
1565
|
+
continue;
|
|
1566
|
+
yield chunk;
|
|
1567
|
+
}
|
|
1568
|
+
return;
|
|
1569
|
+
}
|
|
1195
1570
|
const messagesMapped = _convertMessagesToOpenAIParams(messages, this.model);
|
|
1196
1571
|
const params = {
|
|
1197
1572
|
...this.invocationParams(options, {
|
|
@@ -1296,7 +1671,60 @@ class ChatOpenAI extends chat_models_1.BaseChatModel {
|
|
|
1296
1671
|
return this._identifyingParams();
|
|
1297
1672
|
}
|
|
1298
1673
|
/** @ignore */
|
|
1674
|
+
async _responseApiGenerate(messages, options, runManager) {
|
|
1675
|
+
const invocationParams = this.invocationParams(options);
|
|
1676
|
+
if (invocationParams.stream) {
|
|
1677
|
+
const stream = this._streamResponseChunks(messages, options, runManager);
|
|
1678
|
+
let finalChunk;
|
|
1679
|
+
for await (const chunk of stream) {
|
|
1680
|
+
chunk.message.response_metadata = {
|
|
1681
|
+
...chunk.generationInfo,
|
|
1682
|
+
...chunk.message.response_metadata,
|
|
1683
|
+
};
|
|
1684
|
+
finalChunk = finalChunk?.concat(chunk) ?? chunk;
|
|
1685
|
+
}
|
|
1686
|
+
return {
|
|
1687
|
+
generations: finalChunk ? [finalChunk] : [],
|
|
1688
|
+
llmOutput: {
|
|
1689
|
+
estimatedTokenUsage: finalChunk?.message
|
|
1690
|
+
?.usage_metadata,
|
|
1691
|
+
},
|
|
1692
|
+
};
|
|
1693
|
+
}
|
|
1694
|
+
const input = _convertMessagesToOpenAIResponsesParams(messages, this.model);
|
|
1695
|
+
const data = await this.responseApiWithRetry({ input, ...invocationParams }, { signal: options?.signal, ...options?.options });
|
|
1696
|
+
return {
|
|
1697
|
+
generations: [
|
|
1698
|
+
{
|
|
1699
|
+
text: data.output_text,
|
|
1700
|
+
message: _convertOpenAIResponsesMessageToBaseMessage(data),
|
|
1701
|
+
},
|
|
1702
|
+
],
|
|
1703
|
+
llmOutput: {
|
|
1704
|
+
id: data.id,
|
|
1705
|
+
estimatedTokenUsage: data.usage
|
|
1706
|
+
? {
|
|
1707
|
+
promptTokens: data.usage.input_tokens,
|
|
1708
|
+
completionTokens: data.usage.output_tokens,
|
|
1709
|
+
totalTokens: data.usage.total_tokens,
|
|
1710
|
+
}
|
|
1711
|
+
: undefined,
|
|
1712
|
+
},
|
|
1713
|
+
};
|
|
1714
|
+
}
|
|
1715
|
+
_useResponseApi(options) {
|
|
1716
|
+
const usesBuiltInTools = options?.tools?.some(isBuiltInTool);
|
|
1717
|
+
const hasResponsesOnlyKwargs = options?.previous_response_id != null ||
|
|
1718
|
+
options?.text != null ||
|
|
1719
|
+
options?.truncation != null ||
|
|
1720
|
+
options?.include != null;
|
|
1721
|
+
return this.useResponsesApi || usesBuiltInTools || hasResponsesOnlyKwargs;
|
|
1722
|
+
}
|
|
1723
|
+
/** @ignore */
|
|
1299
1724
|
async _generate(messages, options, runManager) {
|
|
1725
|
+
if (this._useResponseApi(options)) {
|
|
1726
|
+
return this._responseApiGenerate(messages, options, runManager);
|
|
1727
|
+
}
|
|
1300
1728
|
const usageMetadata = {};
|
|
1301
1729
|
const params = this.invocationParams(options);
|
|
1302
1730
|
const messagesMapped = _convertMessagesToOpenAIParams(messages, this.model);
|
|
@@ -1534,6 +1962,19 @@ class ChatOpenAI extends chat_models_1.BaseChatModel {
|
|
|
1534
1962
|
}
|
|
1535
1963
|
});
|
|
1536
1964
|
}
|
|
1965
|
+
async responseApiWithRetry(request, options) {
|
|
1966
|
+
return this.caller.call(async () => {
|
|
1967
|
+
const requestOptions = this._getClientOptions(options);
|
|
1968
|
+
try {
|
|
1969
|
+
const res = await this.client.responses.create(request, requestOptions);
|
|
1970
|
+
return res;
|
|
1971
|
+
}
|
|
1972
|
+
catch (e) {
|
|
1973
|
+
const error = (0, openai_js_1.wrapOpenAIClientError)(e);
|
|
1974
|
+
throw error;
|
|
1975
|
+
}
|
|
1976
|
+
});
|
|
1977
|
+
}
|
|
1537
1978
|
/**
|
|
1538
1979
|
* Call the beta chat completions parse endpoint. This should only be called if
|
|
1539
1980
|
* response_format is set to "json_object".
|
package/dist/chat_models.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { type ClientOptions, OpenAI as OpenAIClient } from "openai";
|
|
|
2
2
|
import { CallbackManagerForLLMRun } from "@langchain/core/callbacks/manager";
|
|
3
3
|
import { AIMessageChunk, type BaseMessage, ChatMessageChunk, FunctionMessageChunk, HumanMessageChunk, SystemMessageChunk, ToolMessageChunk } from "@langchain/core/messages";
|
|
4
4
|
import { ChatGenerationChunk, type ChatResult } from "@langchain/core/outputs";
|
|
5
|
-
import { BaseChatModel, BindToolsInput, LangSmithParams, type BaseChatModelParams } from "@langchain/core/language_models/chat_models";
|
|
5
|
+
import { BaseChatModel, type BindToolsInput, type LangSmithParams, type BaseChatModelParams } from "@langchain/core/language_models/chat_models";
|
|
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";
|
|
@@ -21,7 +21,21 @@ type OpenAIRoleEnum = "system" | "developer" | "assistant" | "user" | "function"
|
|
|
21
21
|
type OpenAICompletionParam = OpenAIClient.Chat.Completions.ChatCompletionMessageParam;
|
|
22
22
|
export declare function messageToOpenAIRole(message: BaseMessage): OpenAIRoleEnum;
|
|
23
23
|
export declare function _convertMessagesToOpenAIParams(messages: BaseMessage[], model?: string): OpenAICompletionParam[];
|
|
24
|
-
type
|
|
24
|
+
type ExcludeController<T> = T extends {
|
|
25
|
+
controller: unknown;
|
|
26
|
+
} ? never : T;
|
|
27
|
+
type ExcludeNonController<T> = T extends {
|
|
28
|
+
controller: unknown;
|
|
29
|
+
} ? T : never;
|
|
30
|
+
type ResponsesCreate = OpenAIClient.Responses["create"];
|
|
31
|
+
type ResponsesCreateParams = Parameters<OpenAIClient.Responses["create"]>[0];
|
|
32
|
+
type ResponsesTool = Exclude<ResponsesCreateParams["tools"], undefined>[number];
|
|
33
|
+
type ResponsesToolChoice = Exclude<ResponsesCreateParams["tool_choice"], undefined>;
|
|
34
|
+
type ResponsesCreateInvoke = ExcludeController<Awaited<ReturnType<ResponsesCreate>>>;
|
|
35
|
+
type ResponsesCreateStream = ExcludeNonController<Awaited<ReturnType<ResponsesCreate>>>;
|
|
36
|
+
type ResponseInvocationParams = Omit<ResponsesCreateParams, "input">;
|
|
37
|
+
type ChatCompletionInvocationParams = Omit<OpenAIClient.Chat.ChatCompletionCreateParams, "messages">;
|
|
38
|
+
type ChatOpenAIToolType = BindToolsInput | OpenAIClient.ChatCompletionTool | ResponsesTool;
|
|
25
39
|
export interface ChatOpenAIStructuredOutputMethodOptions<IncludeRaw extends boolean> extends StructuredOutputMethodOptions<IncludeRaw> {
|
|
26
40
|
/**
|
|
27
41
|
* strict: If `true` and `method` = "function_calling", model output is
|
|
@@ -41,7 +55,7 @@ export interface ChatOpenAIStructuredOutputMethodOptions<IncludeRaw extends bool
|
|
|
41
55
|
}
|
|
42
56
|
export interface ChatOpenAICallOptions extends OpenAICallOptions, BaseFunctionCallOptions {
|
|
43
57
|
tools?: ChatOpenAIToolType[];
|
|
44
|
-
tool_choice?: OpenAIToolChoice;
|
|
58
|
+
tool_choice?: OpenAIToolChoice | ResponsesToolChoice;
|
|
45
59
|
promptIndex?: number;
|
|
46
60
|
response_format?: ChatOpenAIResponseFormat;
|
|
47
61
|
seed?: number;
|
|
@@ -105,9 +119,28 @@ export interface ChatOpenAICallOptions extends OpenAICallOptions, BaseFunctionCa
|
|
|
105
119
|
* Reducing reasoning effort can result in faster responses and fewer tokens used on reasoning in a response.
|
|
106
120
|
*/
|
|
107
121
|
reasoning_effort?: OpenAIClient.Chat.ChatCompletionReasoningEffort;
|
|
122
|
+
/**
|
|
123
|
+
* Configuration options for a text response from the model. Can be plain text or
|
|
124
|
+
* structured JSON data.
|
|
125
|
+
*/
|
|
126
|
+
text?: ResponsesCreateParams["text"];
|
|
127
|
+
/**
|
|
128
|
+
* The truncation strategy to use for the model response.
|
|
129
|
+
*/
|
|
130
|
+
truncation?: ResponsesCreateParams["truncation"];
|
|
131
|
+
/**
|
|
132
|
+
* Specify additional output data to include in the model response.
|
|
133
|
+
*/
|
|
134
|
+
include?: ResponsesCreateParams["include"];
|
|
135
|
+
/**
|
|
136
|
+
* The unique ID of the previous response to the model. Use this to create
|
|
137
|
+
* multi-turn conversations.
|
|
138
|
+
*/
|
|
139
|
+
previous_response_id?: ResponsesCreateParams["previous_response_id"];
|
|
108
140
|
}
|
|
109
141
|
export interface ChatOpenAIFields extends Partial<OpenAIChatInput>, BaseChatModelParams {
|
|
110
142
|
configuration?: ClientOptions;
|
|
143
|
+
useResponsesApi?: boolean;
|
|
111
144
|
}
|
|
112
145
|
/**
|
|
113
146
|
* OpenAI chat model integration.
|
|
@@ -689,6 +722,7 @@ export declare class ChatOpenAI<CallOptions extends ChatOpenAICallOptions = Chat
|
|
|
689
722
|
audio?: OpenAIClient.Chat.ChatCompletionAudioParam;
|
|
690
723
|
modalities?: Array<OpenAIClient.Chat.ChatCompletionModality>;
|
|
691
724
|
reasoningEffort?: OpenAIClient.Chat.ChatCompletionReasoningEffort;
|
|
725
|
+
useResponsesApi: boolean;
|
|
692
726
|
constructor(fields?: ChatOpenAIFields);
|
|
693
727
|
getLsParams(options: this["ParsedCallOptions"]): LangSmithParams;
|
|
694
728
|
bindTools(tools: ChatOpenAIToolType[], kwargs?: Partial<CallOptions>): Runnable<BaseLanguageModelInput, AIMessageChunk, CallOptions>;
|
|
@@ -696,9 +730,9 @@ export declare class ChatOpenAI<CallOptions extends ChatOpenAICallOptions = Chat
|
|
|
696
730
|
/**
|
|
697
731
|
* Get the parameters used to invoke the model
|
|
698
732
|
*/
|
|
699
|
-
invocationParams(options?: this["ParsedCallOptions"], extra?: {
|
|
733
|
+
invocationParams<Type extends "responses" | "completion" = "completion">(options?: this["ParsedCallOptions"], extra?: {
|
|
700
734
|
streaming?: boolean;
|
|
701
|
-
}):
|
|
735
|
+
}): Type extends "responses" ? ResponseInvocationParams : ChatCompletionInvocationParams;
|
|
702
736
|
protected _convertOpenAIChatCompletionMessageToBaseMessage(message: OpenAIClient.Chat.Completions.ChatCompletionMessage, rawResponse: OpenAIClient.Chat.Completions.ChatCompletion): BaseMessage;
|
|
703
737
|
protected _convertOpenAIDeltaToBaseMessageChunk(delta: Record<string, any>, rawResponse: OpenAIClient.Chat.Completions.ChatCompletionChunk, defaultRole?: OpenAIRoleEnum): AIMessageChunk | HumanMessageChunk | SystemMessageChunk | FunctionMessageChunk | ToolMessageChunk | ChatMessageChunk;
|
|
704
738
|
/** @ignore */
|
|
@@ -714,6 +748,9 @@ export declare class ChatOpenAI<CallOptions extends ChatOpenAICallOptions = Chat
|
|
|
714
748
|
model_name: string;
|
|
715
749
|
} & ClientOptions;
|
|
716
750
|
/** @ignore */
|
|
751
|
+
_responseApiGenerate(messages: BaseMessage[], options: this["ParsedCallOptions"], runManager?: CallbackManagerForLLMRun): Promise<ChatResult>;
|
|
752
|
+
protected _useResponseApi(options: this["ParsedCallOptions"] | undefined): boolean;
|
|
753
|
+
/** @ignore */
|
|
717
754
|
_generate(messages: BaseMessage[], options: this["ParsedCallOptions"], runManager?: CallbackManagerForLLMRun): Promise<ChatResult>;
|
|
718
755
|
/**
|
|
719
756
|
* Estimate the number of tokens a prompt will use.
|
|
@@ -736,6 +773,10 @@ export declare class ChatOpenAI<CallOptions extends ChatOpenAICallOptions = Chat
|
|
|
736
773
|
*/
|
|
737
774
|
completionWithRetry(request: OpenAIClient.Chat.ChatCompletionCreateParamsStreaming, options?: OpenAICoreRequestOptions): Promise<AsyncIterable<OpenAIClient.Chat.Completions.ChatCompletionChunk>>;
|
|
738
775
|
completionWithRetry(request: OpenAIClient.Chat.ChatCompletionCreateParamsNonStreaming, options?: OpenAICoreRequestOptions): Promise<OpenAIClient.Chat.Completions.ChatCompletion>;
|
|
776
|
+
protected responseApiWithRetry(request: ResponsesCreateParams & {
|
|
777
|
+
stream: true;
|
|
778
|
+
}, options?: OpenAICoreRequestOptions): Promise<ResponsesCreateStream>;
|
|
779
|
+
protected responseApiWithRetry(request: ResponsesCreateParams, options?: OpenAICoreRequestOptions): Promise<ResponsesCreateInvoke>;
|
|
739
780
|
/**
|
|
740
781
|
* Call the beta chat completions parse endpoint. This should only be called if
|
|
741
782
|
* response_format is set to "json_object".
|
package/dist/chat_models.js
CHANGED
|
@@ -92,6 +92,283 @@ export function _convertMessagesToOpenAIParams(messages, model) {
|
|
|
92
92
|
return completionParam;
|
|
93
93
|
});
|
|
94
94
|
}
|
|
95
|
+
const _FUNCTION_CALL_IDS_MAP_KEY = "__openai_function_call_ids__";
|
|
96
|
+
function _convertMessagesToOpenAIResponsesParams(messages, model) {
|
|
97
|
+
return messages.flatMap((lcMsg) => {
|
|
98
|
+
let role = messageToOpenAIRole(lcMsg);
|
|
99
|
+
if (role === "system" && isReasoningModel(model))
|
|
100
|
+
role = "developer";
|
|
101
|
+
if (role === "function") {
|
|
102
|
+
throw new Error("Function messages are not supported in Responses API");
|
|
103
|
+
}
|
|
104
|
+
if (role === "tool") {
|
|
105
|
+
const toolMessage = lcMsg;
|
|
106
|
+
return {
|
|
107
|
+
type: "function_call_output",
|
|
108
|
+
call_id: toolMessage.tool_call_id,
|
|
109
|
+
id: toolMessage.id,
|
|
110
|
+
output: typeof toolMessage.content !== "string"
|
|
111
|
+
? JSON.stringify(toolMessage.content)
|
|
112
|
+
: toolMessage.content,
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
if (role === "assistant") {
|
|
116
|
+
const functionCallIds =
|
|
117
|
+
// eslint-disable-next-line @typescript-eslint/no-use-before-define
|
|
118
|
+
lcMsg.additional_kwargs[_FUNCTION_CALL_IDS_MAP_KEY];
|
|
119
|
+
if (isAIMessage(lcMsg) && !!lcMsg.tool_calls?.length) {
|
|
120
|
+
return lcMsg.tool_calls.map((toolCall) => ({
|
|
121
|
+
type: "function_call",
|
|
122
|
+
name: toolCall.name,
|
|
123
|
+
arguments: JSON.stringify(toolCall.args),
|
|
124
|
+
call_id: toolCall.id,
|
|
125
|
+
id: functionCallIds[toolCall.id],
|
|
126
|
+
}));
|
|
127
|
+
}
|
|
128
|
+
if (lcMsg.additional_kwargs.tool_calls != null) {
|
|
129
|
+
return lcMsg.additional_kwargs.tool_calls.map((toolCall) => ({
|
|
130
|
+
type: "function_call",
|
|
131
|
+
name: toolCall.function.name,
|
|
132
|
+
call_id: toolCall.id,
|
|
133
|
+
id: functionCallIds[toolCall.id],
|
|
134
|
+
arguments: toolCall.function.arguments,
|
|
135
|
+
}));
|
|
136
|
+
}
|
|
137
|
+
let { content } = lcMsg;
|
|
138
|
+
if (lcMsg.additional_kwargs.refusal != null) {
|
|
139
|
+
if (typeof content === "string") {
|
|
140
|
+
content = [{ type: "output_text", text: content, annotations: [] }];
|
|
141
|
+
}
|
|
142
|
+
content = [
|
|
143
|
+
...content,
|
|
144
|
+
{ type: "refusal", refusal: lcMsg.additional_kwargs.refusal },
|
|
145
|
+
];
|
|
146
|
+
}
|
|
147
|
+
return {
|
|
148
|
+
type: "message",
|
|
149
|
+
role: "assistant",
|
|
150
|
+
content: typeof content === "string"
|
|
151
|
+
? content
|
|
152
|
+
: content.flatMap((item) => {
|
|
153
|
+
if (item.type === "text") {
|
|
154
|
+
return {
|
|
155
|
+
type: "output_text",
|
|
156
|
+
text: item.text,
|
|
157
|
+
// @ts-expect-error TODO: add types for `annotations`
|
|
158
|
+
annotations: item.annotations ?? [],
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
if (item.type === "output_text" || item.type === "refusal") {
|
|
162
|
+
return item;
|
|
163
|
+
}
|
|
164
|
+
return [];
|
|
165
|
+
}),
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
if (role === "user") {
|
|
169
|
+
return {
|
|
170
|
+
type: "message",
|
|
171
|
+
role: "user",
|
|
172
|
+
content: typeof lcMsg.content === "string"
|
|
173
|
+
? lcMsg.content
|
|
174
|
+
: lcMsg.content.flatMap((item) => {
|
|
175
|
+
if (item.type === "text") {
|
|
176
|
+
return { type: "input_text", text: item.text };
|
|
177
|
+
}
|
|
178
|
+
if (item.type === "image_url") {
|
|
179
|
+
const image_url = typeof item.image_url === "string"
|
|
180
|
+
? item.image_url
|
|
181
|
+
: item.image_url.url;
|
|
182
|
+
const detail = typeof item.image_url === "string"
|
|
183
|
+
? "auto"
|
|
184
|
+
: item.image_url.detail;
|
|
185
|
+
return { type: "input_image", image_url, detail };
|
|
186
|
+
}
|
|
187
|
+
if (item.type === "input_text" ||
|
|
188
|
+
item.type === "input_image" ||
|
|
189
|
+
item.type === "input_file") {
|
|
190
|
+
return item;
|
|
191
|
+
}
|
|
192
|
+
return [];
|
|
193
|
+
}),
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
return [];
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
function _convertOpenAIResponsesMessageToBaseMessage(response) {
|
|
200
|
+
if (response.error) {
|
|
201
|
+
// TODO: add support for `addLangChainErrorFields`
|
|
202
|
+
const error = new Error(response.error.message);
|
|
203
|
+
error.name = response.error.code;
|
|
204
|
+
throw error;
|
|
205
|
+
}
|
|
206
|
+
const content = [];
|
|
207
|
+
const tool_calls = [];
|
|
208
|
+
const invalid_tool_calls = [];
|
|
209
|
+
const response_metadata = {
|
|
210
|
+
model: response.model,
|
|
211
|
+
created_at: response.created_at,
|
|
212
|
+
id: response.id,
|
|
213
|
+
incomplete_details: response.incomplete_details,
|
|
214
|
+
metadata: response.metadata,
|
|
215
|
+
object: response.object,
|
|
216
|
+
status: response.status,
|
|
217
|
+
user: response.user,
|
|
218
|
+
// for compatibility with chat completion calls.
|
|
219
|
+
model_name: response.model,
|
|
220
|
+
};
|
|
221
|
+
const additional_kwargs = {};
|
|
222
|
+
for (const item of response.output) {
|
|
223
|
+
if (item.type === "message") {
|
|
224
|
+
// TODO: how to handle refusals?
|
|
225
|
+
content.push(...item.content.map((part) => {
|
|
226
|
+
if (part.type === "output_text") {
|
|
227
|
+
return {
|
|
228
|
+
type: "text",
|
|
229
|
+
text: part.text,
|
|
230
|
+
annotations: part.annotations,
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
return part;
|
|
234
|
+
}));
|
|
235
|
+
}
|
|
236
|
+
else if (item.type === "function_call") {
|
|
237
|
+
const fnAdapter = {
|
|
238
|
+
function: { name: item.name, arguments: item.arguments },
|
|
239
|
+
id: item.call_id,
|
|
240
|
+
};
|
|
241
|
+
try {
|
|
242
|
+
tool_calls.push(parseToolCall(fnAdapter, { returnId: true }));
|
|
243
|
+
}
|
|
244
|
+
catch (e) {
|
|
245
|
+
let errMessage;
|
|
246
|
+
if (typeof e === "object" &&
|
|
247
|
+
e != null &&
|
|
248
|
+
"message" in e &&
|
|
249
|
+
typeof e.message === "string") {
|
|
250
|
+
errMessage = e.message;
|
|
251
|
+
}
|
|
252
|
+
invalid_tool_calls.push(makeInvalidToolCall(fnAdapter, errMessage));
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
else if (item.type === "reasoning") {
|
|
256
|
+
additional_kwargs.reasoning = item;
|
|
257
|
+
}
|
|
258
|
+
else {
|
|
259
|
+
additional_kwargs.tool_outputs ??= [];
|
|
260
|
+
additional_kwargs.tool_outputs.push(item);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
return new AIMessage({
|
|
264
|
+
id: response.id,
|
|
265
|
+
content,
|
|
266
|
+
tool_calls,
|
|
267
|
+
invalid_tool_calls,
|
|
268
|
+
usage_metadata: response.usage,
|
|
269
|
+
additional_kwargs,
|
|
270
|
+
response_metadata,
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
function _convertOpenAIResponsesDeltaToBaseMessageChunk(chunk) {
|
|
274
|
+
const content = [];
|
|
275
|
+
let generationInfo = {};
|
|
276
|
+
let usage_metadata;
|
|
277
|
+
const tool_call_chunks = [];
|
|
278
|
+
const response_metadata = {};
|
|
279
|
+
const additional_kwargs = {};
|
|
280
|
+
let id;
|
|
281
|
+
if (chunk.type === "response.output_text.delta") {
|
|
282
|
+
content.push({
|
|
283
|
+
type: "text",
|
|
284
|
+
text: chunk.delta,
|
|
285
|
+
index: chunk.content_index,
|
|
286
|
+
});
|
|
287
|
+
}
|
|
288
|
+
else if (chunk.type === "response.output_text.annotation.added") {
|
|
289
|
+
content.push({
|
|
290
|
+
type: "text",
|
|
291
|
+
text: "",
|
|
292
|
+
annotations: [chunk.annotation],
|
|
293
|
+
index: chunk.content_index,
|
|
294
|
+
});
|
|
295
|
+
}
|
|
296
|
+
else if (chunk.type === "response.output_item.added" &&
|
|
297
|
+
chunk.item.type === "message") {
|
|
298
|
+
id = chunk.item.id;
|
|
299
|
+
}
|
|
300
|
+
else if (chunk.type === "response.output_item.added" &&
|
|
301
|
+
chunk.item.type === "function_call") {
|
|
302
|
+
tool_call_chunks.push({
|
|
303
|
+
type: "tool_call_chunk",
|
|
304
|
+
name: chunk.item.name,
|
|
305
|
+
args: chunk.item.arguments,
|
|
306
|
+
id: chunk.item.id,
|
|
307
|
+
index: chunk.output_index,
|
|
308
|
+
});
|
|
309
|
+
additional_kwargs[_FUNCTION_CALL_IDS_MAP_KEY] = {
|
|
310
|
+
[chunk.item.call_id]: chunk.item.id,
|
|
311
|
+
};
|
|
312
|
+
}
|
|
313
|
+
else if (chunk.type === "response.output_item.done" &&
|
|
314
|
+
(chunk.item.type === "web_search_call" ||
|
|
315
|
+
chunk.item.type === "file_search_call")) {
|
|
316
|
+
additional_kwargs.tool_outputs = [chunk.item];
|
|
317
|
+
}
|
|
318
|
+
else if (chunk.type === "response.created") {
|
|
319
|
+
response_metadata.id = chunk.response.id;
|
|
320
|
+
response_metadata.model_name = chunk.response.model;
|
|
321
|
+
response_metadata.model = chunk.response.model;
|
|
322
|
+
}
|
|
323
|
+
else if (chunk.type === "response.completed") {
|
|
324
|
+
usage_metadata = chunk.response.usage;
|
|
325
|
+
}
|
|
326
|
+
else if (chunk.type === "response.function_call_arguments.delta") {
|
|
327
|
+
tool_call_chunks.push({
|
|
328
|
+
type: "tool_call_chunk",
|
|
329
|
+
args: chunk.delta,
|
|
330
|
+
index: chunk.output_index,
|
|
331
|
+
});
|
|
332
|
+
}
|
|
333
|
+
else if (chunk.type === "response.web_search_call.completed" ||
|
|
334
|
+
chunk.type === "response.file_search_call.completed") {
|
|
335
|
+
generationInfo = {
|
|
336
|
+
tool_outputs: {
|
|
337
|
+
id: chunk.item_id,
|
|
338
|
+
type: chunk.type.replace("response.", "").replace(".completed", ""),
|
|
339
|
+
status: "completed",
|
|
340
|
+
},
|
|
341
|
+
};
|
|
342
|
+
}
|
|
343
|
+
else if (chunk.type === "response.refusal.done") {
|
|
344
|
+
additional_kwargs.refusal = chunk.refusal;
|
|
345
|
+
}
|
|
346
|
+
else {
|
|
347
|
+
return null;
|
|
348
|
+
}
|
|
349
|
+
return new ChatGenerationChunk({
|
|
350
|
+
// Legacy reasons, `onLLMNewToken` should pulls this out
|
|
351
|
+
text: content.map((part) => part.text).join(""),
|
|
352
|
+
message: new AIMessageChunk({
|
|
353
|
+
id,
|
|
354
|
+
content,
|
|
355
|
+
tool_call_chunks,
|
|
356
|
+
usage_metadata,
|
|
357
|
+
additional_kwargs,
|
|
358
|
+
response_metadata,
|
|
359
|
+
}),
|
|
360
|
+
generationInfo,
|
|
361
|
+
});
|
|
362
|
+
}
|
|
363
|
+
function isBuiltInTool(tool) {
|
|
364
|
+
return "type" in tool && tool.type !== "function";
|
|
365
|
+
}
|
|
366
|
+
function isBuiltInToolChoice(tool_choice) {
|
|
367
|
+
return (tool_choice != null &&
|
|
368
|
+
typeof tool_choice === "object" &&
|
|
369
|
+
"type" in tool_choice &&
|
|
370
|
+
tool_choice.type !== "function");
|
|
371
|
+
}
|
|
95
372
|
function _convertChatOpenAIToolTypeToOpenAITool(tool, fields) {
|
|
96
373
|
if (isOpenAITool(tool)) {
|
|
97
374
|
if (fields?.strict !== undefined) {
|
|
@@ -899,6 +1176,12 @@ export class ChatOpenAI extends BaseChatModel {
|
|
|
899
1176
|
writable: true,
|
|
900
1177
|
value: void 0
|
|
901
1178
|
});
|
|
1179
|
+
Object.defineProperty(this, "useResponsesApi", {
|
|
1180
|
+
enumerable: true,
|
|
1181
|
+
configurable: true,
|
|
1182
|
+
writable: true,
|
|
1183
|
+
value: false
|
|
1184
|
+
});
|
|
902
1185
|
this.openAIApiKey =
|
|
903
1186
|
fields?.apiKey ??
|
|
904
1187
|
fields?.openAIApiKey ??
|
|
@@ -928,6 +1211,7 @@ export class ChatOpenAI extends BaseChatModel {
|
|
|
928
1211
|
this.modalities = fields?.modalities;
|
|
929
1212
|
this.reasoningEffort = fields?.reasoningEffort;
|
|
930
1213
|
this.maxTokens = fields?.maxCompletionTokens ?? fields?.maxTokens;
|
|
1214
|
+
this.useResponsesApi = fields?.useResponsesApi ?? this.useResponsesApi;
|
|
931
1215
|
if (this.model === "o1") {
|
|
932
1216
|
this.disableStreaming = true;
|
|
933
1217
|
}
|
|
@@ -965,7 +1249,9 @@ export class ChatOpenAI extends BaseChatModel {
|
|
|
965
1249
|
strict = this.supportsStrictToolCalling;
|
|
966
1250
|
}
|
|
967
1251
|
return this.bind({
|
|
968
|
-
tools: tools.map((tool) =>
|
|
1252
|
+
tools: tools.map((tool) => isBuiltInTool(tool)
|
|
1253
|
+
? tool
|
|
1254
|
+
: _convertChatOpenAIToolTypeToOpenAITool(tool, { strict })),
|
|
969
1255
|
...kwargs,
|
|
970
1256
|
});
|
|
971
1257
|
}
|
|
@@ -991,6 +1277,80 @@ export class ChatOpenAI extends BaseChatModel {
|
|
|
991
1277
|
else if (this.supportsStrictToolCalling !== undefined) {
|
|
992
1278
|
strict = this.supportsStrictToolCalling;
|
|
993
1279
|
}
|
|
1280
|
+
if (this._useResponseApi(options)) {
|
|
1281
|
+
const params = {
|
|
1282
|
+
model: this.model,
|
|
1283
|
+
temperature: this.temperature,
|
|
1284
|
+
top_p: this.topP,
|
|
1285
|
+
user: this.user,
|
|
1286
|
+
// if include_usage is set or streamUsage then stream must be set to true.
|
|
1287
|
+
stream: this.streaming,
|
|
1288
|
+
previous_response_id: options?.previous_response_id,
|
|
1289
|
+
truncation: options?.truncation,
|
|
1290
|
+
include: options?.include,
|
|
1291
|
+
tools: options?.tools?.length
|
|
1292
|
+
? options.tools
|
|
1293
|
+
.map((tool) => {
|
|
1294
|
+
if (isBuiltInTool(tool)) {
|
|
1295
|
+
return tool;
|
|
1296
|
+
}
|
|
1297
|
+
else if (isOpenAITool(tool)) {
|
|
1298
|
+
return {
|
|
1299
|
+
type: "function",
|
|
1300
|
+
name: tool.function.name,
|
|
1301
|
+
parameters: tool.function.parameters,
|
|
1302
|
+
description: tool.function.description,
|
|
1303
|
+
strict,
|
|
1304
|
+
};
|
|
1305
|
+
}
|
|
1306
|
+
else {
|
|
1307
|
+
return null;
|
|
1308
|
+
}
|
|
1309
|
+
})
|
|
1310
|
+
.filter((tool) => tool !== null)
|
|
1311
|
+
: undefined,
|
|
1312
|
+
tool_choice: isBuiltInToolChoice(options?.tool_choice)
|
|
1313
|
+
? options?.tool_choice
|
|
1314
|
+
: (() => {
|
|
1315
|
+
const formatted = formatToOpenAIToolChoice(options?.tool_choice);
|
|
1316
|
+
if (typeof formatted === "object" && "type" in formatted) {
|
|
1317
|
+
return { type: "function", name: formatted.function.name };
|
|
1318
|
+
}
|
|
1319
|
+
else {
|
|
1320
|
+
return undefined;
|
|
1321
|
+
}
|
|
1322
|
+
})(),
|
|
1323
|
+
text: (() => {
|
|
1324
|
+
if (options?.text)
|
|
1325
|
+
return options.text;
|
|
1326
|
+
const format = this.createResponseFormat(options?.response_format);
|
|
1327
|
+
if (format?.type === "json_schema") {
|
|
1328
|
+
if (format.json_schema.schema != null) {
|
|
1329
|
+
return {
|
|
1330
|
+
format: {
|
|
1331
|
+
type: "json_schema",
|
|
1332
|
+
schema: format.json_schema.schema,
|
|
1333
|
+
description: format.json_schema.description,
|
|
1334
|
+
name: format.json_schema.name,
|
|
1335
|
+
strict: format.json_schema.strict,
|
|
1336
|
+
},
|
|
1337
|
+
};
|
|
1338
|
+
}
|
|
1339
|
+
return undefined;
|
|
1340
|
+
}
|
|
1341
|
+
return { format };
|
|
1342
|
+
})(),
|
|
1343
|
+
parallel_tool_calls: options?.parallel_tool_calls,
|
|
1344
|
+
max_output_tokens: this.maxTokens === -1 ? undefined : this.maxTokens,
|
|
1345
|
+
...this.modelKwargs,
|
|
1346
|
+
};
|
|
1347
|
+
const reasoningEffort = options?.reasoning_effort ?? this.reasoningEffort;
|
|
1348
|
+
if (reasoningEffort !== undefined) {
|
|
1349
|
+
params.reasoning = { effort: reasoningEffort };
|
|
1350
|
+
}
|
|
1351
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1352
|
+
return params;
|
|
1353
|
+
}
|
|
994
1354
|
let streamOptionsConfig = {};
|
|
995
1355
|
if (options?.stream_options !== undefined) {
|
|
996
1356
|
streamOptionsConfig = { stream_options: options.stream_options };
|
|
@@ -1044,6 +1404,7 @@ export class ChatOpenAI extends BaseChatModel {
|
|
|
1044
1404
|
else {
|
|
1045
1405
|
params.max_tokens = this.maxTokens === -1 ? undefined : this.maxTokens;
|
|
1046
1406
|
}
|
|
1407
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1047
1408
|
return params;
|
|
1048
1409
|
}
|
|
1049
1410
|
_convertOpenAIChatCompletionMessageToBaseMessage(message, rawResponse) {
|
|
@@ -1187,6 +1548,20 @@ export class ChatOpenAI extends BaseChatModel {
|
|
|
1187
1548
|
};
|
|
1188
1549
|
}
|
|
1189
1550
|
async *_streamResponseChunks(messages, options, runManager) {
|
|
1551
|
+
if (this._useResponseApi(options)) {
|
|
1552
|
+
const streamIterable = await this.responseApiWithRetry({
|
|
1553
|
+
...this.invocationParams(options, { streaming: true }),
|
|
1554
|
+
input: _convertMessagesToOpenAIResponsesParams(messages, this.model),
|
|
1555
|
+
stream: true,
|
|
1556
|
+
}, options);
|
|
1557
|
+
for await (const data of streamIterable) {
|
|
1558
|
+
const chunk = _convertOpenAIResponsesDeltaToBaseMessageChunk(data);
|
|
1559
|
+
if (chunk == null)
|
|
1560
|
+
continue;
|
|
1561
|
+
yield chunk;
|
|
1562
|
+
}
|
|
1563
|
+
return;
|
|
1564
|
+
}
|
|
1190
1565
|
const messagesMapped = _convertMessagesToOpenAIParams(messages, this.model);
|
|
1191
1566
|
const params = {
|
|
1192
1567
|
...this.invocationParams(options, {
|
|
@@ -1291,7 +1666,60 @@ export class ChatOpenAI extends BaseChatModel {
|
|
|
1291
1666
|
return this._identifyingParams();
|
|
1292
1667
|
}
|
|
1293
1668
|
/** @ignore */
|
|
1669
|
+
async _responseApiGenerate(messages, options, runManager) {
|
|
1670
|
+
const invocationParams = this.invocationParams(options);
|
|
1671
|
+
if (invocationParams.stream) {
|
|
1672
|
+
const stream = this._streamResponseChunks(messages, options, runManager);
|
|
1673
|
+
let finalChunk;
|
|
1674
|
+
for await (const chunk of stream) {
|
|
1675
|
+
chunk.message.response_metadata = {
|
|
1676
|
+
...chunk.generationInfo,
|
|
1677
|
+
...chunk.message.response_metadata,
|
|
1678
|
+
};
|
|
1679
|
+
finalChunk = finalChunk?.concat(chunk) ?? chunk;
|
|
1680
|
+
}
|
|
1681
|
+
return {
|
|
1682
|
+
generations: finalChunk ? [finalChunk] : [],
|
|
1683
|
+
llmOutput: {
|
|
1684
|
+
estimatedTokenUsage: finalChunk?.message
|
|
1685
|
+
?.usage_metadata,
|
|
1686
|
+
},
|
|
1687
|
+
};
|
|
1688
|
+
}
|
|
1689
|
+
const input = _convertMessagesToOpenAIResponsesParams(messages, this.model);
|
|
1690
|
+
const data = await this.responseApiWithRetry({ input, ...invocationParams }, { signal: options?.signal, ...options?.options });
|
|
1691
|
+
return {
|
|
1692
|
+
generations: [
|
|
1693
|
+
{
|
|
1694
|
+
text: data.output_text,
|
|
1695
|
+
message: _convertOpenAIResponsesMessageToBaseMessage(data),
|
|
1696
|
+
},
|
|
1697
|
+
],
|
|
1698
|
+
llmOutput: {
|
|
1699
|
+
id: data.id,
|
|
1700
|
+
estimatedTokenUsage: data.usage
|
|
1701
|
+
? {
|
|
1702
|
+
promptTokens: data.usage.input_tokens,
|
|
1703
|
+
completionTokens: data.usage.output_tokens,
|
|
1704
|
+
totalTokens: data.usage.total_tokens,
|
|
1705
|
+
}
|
|
1706
|
+
: undefined,
|
|
1707
|
+
},
|
|
1708
|
+
};
|
|
1709
|
+
}
|
|
1710
|
+
_useResponseApi(options) {
|
|
1711
|
+
const usesBuiltInTools = options?.tools?.some(isBuiltInTool);
|
|
1712
|
+
const hasResponsesOnlyKwargs = options?.previous_response_id != null ||
|
|
1713
|
+
options?.text != null ||
|
|
1714
|
+
options?.truncation != null ||
|
|
1715
|
+
options?.include != null;
|
|
1716
|
+
return this.useResponsesApi || usesBuiltInTools || hasResponsesOnlyKwargs;
|
|
1717
|
+
}
|
|
1718
|
+
/** @ignore */
|
|
1294
1719
|
async _generate(messages, options, runManager) {
|
|
1720
|
+
if (this._useResponseApi(options)) {
|
|
1721
|
+
return this._responseApiGenerate(messages, options, runManager);
|
|
1722
|
+
}
|
|
1295
1723
|
const usageMetadata = {};
|
|
1296
1724
|
const params = this.invocationParams(options);
|
|
1297
1725
|
const messagesMapped = _convertMessagesToOpenAIParams(messages, this.model);
|
|
@@ -1529,6 +1957,19 @@ export class ChatOpenAI extends BaseChatModel {
|
|
|
1529
1957
|
}
|
|
1530
1958
|
});
|
|
1531
1959
|
}
|
|
1960
|
+
async responseApiWithRetry(request, options) {
|
|
1961
|
+
return this.caller.call(async () => {
|
|
1962
|
+
const requestOptions = this._getClientOptions(options);
|
|
1963
|
+
try {
|
|
1964
|
+
const res = await this.client.responses.create(request, requestOptions);
|
|
1965
|
+
return res;
|
|
1966
|
+
}
|
|
1967
|
+
catch (e) {
|
|
1968
|
+
const error = wrapOpenAIClientError(e);
|
|
1969
|
+
throw error;
|
|
1970
|
+
}
|
|
1971
|
+
});
|
|
1972
|
+
}
|
|
1532
1973
|
/**
|
|
1533
1974
|
* Call the beta chat completions parse endpoint. This should only be called if
|
|
1534
1975
|
* response_format is set to "json_object".
|
package/dist/types.d.ts
CHANGED
|
@@ -146,7 +146,7 @@ export interface OpenAIChatInput extends OpenAIBaseInput {
|
|
|
146
146
|
* Constrains effort on reasoning for reasoning models. Currently supported values are low, medium, and high.
|
|
147
147
|
* Reducing reasoning effort can result in faster responses and fewer tokens used on reasoning in a response.
|
|
148
148
|
*/
|
|
149
|
-
reasoningEffort?: OpenAIClient.ChatCompletionReasoningEffort;
|
|
149
|
+
reasoningEffort?: OpenAIClient.Chat.ChatCompletionReasoningEffort;
|
|
150
150
|
}
|
|
151
151
|
export declare interface AzureOpenAIInput {
|
|
152
152
|
/**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@langchain/openai",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.5-rc.0",
|
|
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.87.3",
|
|
40
40
|
"zod": "^3.22.4",
|
|
41
41
|
"zod-to-json-schema": "^3.22.3"
|
|
42
42
|
},
|