@langchain/openai 0.2.5 → 0.2.7

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.
@@ -6,14 +6,16 @@ const messages_1 = require("@langchain/core/messages");
6
6
  const outputs_1 = require("@langchain/core/outputs");
7
7
  const env_1 = require("@langchain/core/utils/env");
8
8
  const chat_models_1 = require("@langchain/core/language_models/chat_models");
9
- const function_calling_1 = require("@langchain/core/utils/function_calling");
9
+ const base_1 = require("@langchain/core/language_models/base");
10
10
  const runnables_1 = require("@langchain/core/runnables");
11
11
  const output_parsers_1 = require("@langchain/core/output_parsers");
12
12
  const openai_tools_1 = require("@langchain/core/output_parsers/openai_tools");
13
13
  const zod_to_json_schema_1 = require("zod-to-json-schema");
14
+ const zod_1 = require("openai/helpers/zod");
14
15
  const azure_js_1 = require("./utils/azure.cjs");
15
16
  const openai_js_1 = require("./utils/openai.cjs");
16
17
  const openai_format_fndef_js_1 = require("./utils/openai-format-fndef.cjs");
18
+ const tools_js_1 = require("./utils/tools.cjs");
17
19
  function extractGenericMessageCustomRole(message) {
18
20
  if (message.role !== "system" &&
19
21
  message.role !== "assistant" &&
@@ -184,38 +186,434 @@ function convertMessagesToOpenAIParams(messages) {
184
186
  return completionParam;
185
187
  });
186
188
  }
189
+ function _convertChatOpenAIToolTypeToOpenAITool(tool, fields) {
190
+ if ((0, base_1.isOpenAITool)(tool)) {
191
+ if (fields?.strict !== undefined) {
192
+ return {
193
+ ...tool,
194
+ function: {
195
+ ...tool.function,
196
+ strict: fields.strict,
197
+ },
198
+ };
199
+ }
200
+ return tool;
201
+ }
202
+ return (0, tools_js_1._convertToOpenAITool)(tool, fields);
203
+ }
187
204
  /**
188
- * Wrapper around OpenAI large language models that use the Chat endpoint.
205
+ * OpenAI chat model integration.
206
+ *
207
+ * Setup:
208
+ * Install `@langchain/openai` and set an environment variable named `OPENAI_API_KEY`.
209
+ *
210
+ * ```bash
211
+ * npm install @langchain/openai
212
+ * export OPENAI_API_KEY="your-api-key"
213
+ * ```
214
+ *
215
+ * ## [Constructor args](https://api.js.langchain.com/classes/langchain_openai.ChatOpenAI.html#constructor)
216
+ *
217
+ * ## [Runtime args](https://api.js.langchain.com/interfaces/langchain_openai.ChatOpenAICallOptions.html)
218
+ *
219
+ * Runtime args can be passed as the second argument to any of the base runnable methods `.invoke`. `.stream`, `.batch`, etc.
220
+ * They can also be passed via `.bind`, or the second arg in `.bindTools`, like shown in the examples below:
221
+ *
222
+ * ```typescript
223
+ * // When calling `.bind`, call options should be passed via the first argument
224
+ * const llmWithArgsBound = llm.bind({
225
+ * stop: ["\n"],
226
+ * tools: [...],
227
+ * });
228
+ *
229
+ * // When calling `.bindTools`, call options should be passed via the second argument
230
+ * const llmWithTools = llm.bindTools(
231
+ * [...],
232
+ * {
233
+ * tool_choice: "auto",
234
+ * }
235
+ * );
236
+ * ```
237
+ *
238
+ * ## Examples
239
+ *
240
+ * <details open>
241
+ * <summary><strong>Instantiate</strong></summary>
242
+ *
243
+ * ```typescript
244
+ * import { ChatOpenAI } from '@langchain/openai';
245
+ *
246
+ * const llm = new ChatOpenAI({
247
+ * model: "gpt-4o",
248
+ * temperature: 0,
249
+ * maxTokens: undefined,
250
+ * timeout: undefined,
251
+ * maxRetries: 2,
252
+ * // apiKey: "...",
253
+ * // baseUrl: "...",
254
+ * // organization: "...",
255
+ * // other params...
256
+ * });
257
+ * ```
258
+ * </details>
259
+ *
260
+ * <br />
261
+ *
262
+ * <details>
263
+ * <summary><strong>Invoking</strong></summary>
264
+ *
265
+ * ```typescript
266
+ * const input = `Translate "I love programming" into French.`;
267
+ *
268
+ * // Models also accept a list of chat messages or a formatted prompt
269
+ * const result = await llm.invoke(input);
270
+ * console.log(result);
271
+ * ```
272
+ *
273
+ * ```txt
274
+ * AIMessage {
275
+ * "id": "chatcmpl-9u4Mpu44CbPjwYFkTbeoZgvzB00Tz",
276
+ * "content": "J'adore la programmation.",
277
+ * "response_metadata": {
278
+ * "tokenUsage": {
279
+ * "completionTokens": 5,
280
+ * "promptTokens": 28,
281
+ * "totalTokens": 33
282
+ * },
283
+ * "finish_reason": "stop",
284
+ * "system_fingerprint": "fp_3aa7262c27"
285
+ * },
286
+ * "usage_metadata": {
287
+ * "input_tokens": 28,
288
+ * "output_tokens": 5,
289
+ * "total_tokens": 33
290
+ * }
291
+ * }
292
+ * ```
293
+ * </details>
294
+ *
295
+ * <br />
296
+ *
297
+ * <details>
298
+ * <summary><strong>Streaming Chunks</strong></summary>
299
+ *
300
+ * ```typescript
301
+ * for await (const chunk of await llm.stream(input)) {
302
+ * console.log(chunk);
303
+ * }
304
+ * ```
305
+ *
306
+ * ```txt
307
+ * AIMessageChunk {
308
+ * "id": "chatcmpl-9u4NWB7yUeHCKdLr6jP3HpaOYHTqs",
309
+ * "content": ""
310
+ * }
311
+ * AIMessageChunk {
312
+ * "content": "J"
313
+ * }
314
+ * AIMessageChunk {
315
+ * "content": "'adore"
316
+ * }
317
+ * AIMessageChunk {
318
+ * "content": " la"
319
+ * }
320
+ * AIMessageChunk {
321
+ * "content": " programmation",,
322
+ * }
323
+ * AIMessageChunk {
324
+ * "content": ".",,
325
+ * }
326
+ * AIMessageChunk {
327
+ * "content": "",
328
+ * "response_metadata": {
329
+ * "finish_reason": "stop",
330
+ * "system_fingerprint": "fp_c9aa9c0491"
331
+ * },
332
+ * }
333
+ * AIMessageChunk {
334
+ * "content": "",
335
+ * "usage_metadata": {
336
+ * "input_tokens": 28,
337
+ * "output_tokens": 5,
338
+ * "total_tokens": 33
339
+ * }
340
+ * }
341
+ * ```
342
+ * </details>
343
+ *
344
+ * <br />
345
+ *
346
+ * <details>
347
+ * <summary><strong>Aggregate Streamed Chunks</strong></summary>
348
+ *
349
+ * ```typescript
350
+ * import { AIMessageChunk } from '@langchain/core/messages';
351
+ * import { concat } from '@langchain/core/utils/stream';
352
+ *
353
+ * const stream = await llm.stream(input);
354
+ * let full: AIMessageChunk | undefined;
355
+ * for await (const chunk of stream) {
356
+ * full = !full ? chunk : concat(full, chunk);
357
+ * }
358
+ * console.log(full);
359
+ * ```
360
+ *
361
+ * ```txt
362
+ * AIMessageChunk {
363
+ * "id": "chatcmpl-9u4PnX6Fy7OmK46DASy0bH6cxn5Xu",
364
+ * "content": "J'adore la programmation.",
365
+ * "response_metadata": {
366
+ * "prompt": 0,
367
+ * "completion": 0,
368
+ * "finish_reason": "stop",
369
+ * },
370
+ * "usage_metadata": {
371
+ * "input_tokens": 28,
372
+ * "output_tokens": 5,
373
+ * "total_tokens": 33
374
+ * }
375
+ * }
376
+ * ```
377
+ * </details>
378
+ *
379
+ * <br />
380
+ *
381
+ * <details>
382
+ * <summary><strong>Bind tools</strong></summary>
383
+ *
384
+ * ```typescript
385
+ * import { z } from 'zod';
386
+ *
387
+ * const GetWeather = {
388
+ * name: "GetWeather",
389
+ * description: "Get the current weather in a given location",
390
+ * schema: z.object({
391
+ * location: z.string().describe("The city and state, e.g. San Francisco, CA")
392
+ * }),
393
+ * }
394
+ *
395
+ * const GetPopulation = {
396
+ * name: "GetPopulation",
397
+ * description: "Get the current population in a given location",
398
+ * schema: z.object({
399
+ * location: z.string().describe("The city and state, e.g. San Francisco, CA")
400
+ * }),
401
+ * }
402
+ *
403
+ * const llmWithTools = llm.bindTools(
404
+ * [GetWeather, GetPopulation],
405
+ * {
406
+ * // strict: true // enforce tool args schema is respected
407
+ * }
408
+ * );
409
+ * const aiMsg = await llmWithTools.invoke(
410
+ * "Which city is hotter today and which is bigger: LA or NY?"
411
+ * );
412
+ * console.log(aiMsg.tool_calls);
413
+ * ```
414
+ *
415
+ * ```txt
416
+ * [
417
+ * {
418
+ * name: 'GetWeather',
419
+ * args: { location: 'Los Angeles, CA' },
420
+ * type: 'tool_call',
421
+ * id: 'call_uPU4FiFzoKAtMxfmPnfQL6UK'
422
+ * },
423
+ * {
424
+ * name: 'GetWeather',
425
+ * args: { location: 'New York, NY' },
426
+ * type: 'tool_call',
427
+ * id: 'call_UNkEwuQsHrGYqgDQuH9nPAtX'
428
+ * },
429
+ * {
430
+ * name: 'GetPopulation',
431
+ * args: { location: 'Los Angeles, CA' },
432
+ * type: 'tool_call',
433
+ * id: 'call_kL3OXxaq9OjIKqRTpvjaCH14'
434
+ * },
435
+ * {
436
+ * name: 'GetPopulation',
437
+ * args: { location: 'New York, NY' },
438
+ * type: 'tool_call',
439
+ * id: 'call_s9KQB1UWj45LLGaEnjz0179q'
440
+ * }
441
+ * ]
442
+ * ```
443
+ * </details>
444
+ *
445
+ * <br />
446
+ *
447
+ * <details>
448
+ * <summary><strong>Structured Output</strong></summary>
449
+ *
450
+ * ```typescript
451
+ * import { z } from 'zod';
452
+ *
453
+ * const Joke = z.object({
454
+ * setup: z.string().describe("The setup of the joke"),
455
+ * punchline: z.string().describe("The punchline to the joke"),
456
+ * rating: z.number().optional().describe("How funny the joke is, from 1 to 10")
457
+ * }).describe('Joke to tell user.');
458
+ *
459
+ * const structuredLlm = llm.withStructuredOutput(Joke);
460
+ * const jokeResult = await structuredLlm.invoke("Tell me a joke about cats", { name: "Joke" });
461
+ * console.log(jokeResult);
462
+ * ```
463
+ *
464
+ * ```txt
465
+ * {
466
+ * setup: 'Why was the cat sitting on the computer?',
467
+ * punchline: 'Because it wanted to keep an eye on the mouse!',
468
+ * rating: 7
469
+ * }
470
+ * ```
471
+ * </details>
472
+ *
473
+ * <br />
474
+ *
475
+ * <details>
476
+ * <summary><strong>JSON Object Response Format</strong></summary>
477
+ *
478
+ * ```typescript
479
+ * const jsonLlm = llm.bind({ response_format: { type: "json_object" } });
480
+ * const jsonLlmAiMsg = await jsonLlm.invoke(
481
+ * "Return a JSON object with key 'randomInts' and a value of 10 random ints in [0-99]"
482
+ * );
483
+ * console.log(jsonLlmAiMsg.content);
484
+ * ```
485
+ *
486
+ * ```txt
487
+ * {
488
+ * "randomInts": [23, 87, 45, 12, 78, 34, 56, 90, 11, 67]
489
+ * }
490
+ * ```
491
+ * </details>
189
492
  *
190
- * To use you should have the `OPENAI_API_KEY` environment variable set.
493
+ * <br />
191
494
  *
192
- * To use with Azure you should have the:
193
- * `AZURE_OPENAI_API_KEY`,
194
- * `AZURE_OPENAI_API_INSTANCE_NAME`,
195
- * `AZURE_OPENAI_API_DEPLOYMENT_NAME`
196
- * and `AZURE_OPENAI_API_VERSION` environment variables set.
197
- * `AZURE_OPENAI_BASE_PATH` is optional and will override `AZURE_OPENAI_API_INSTANCE_NAME` if you need to use a custom endpoint.
495
+ * <details>
496
+ * <summary><strong>Multimodal</strong></summary>
198
497
  *
199
- * @remarks
200
- * Any parameters that are valid to be passed to {@link
201
- * https://platform.openai.com/docs/api-reference/chat/create |
202
- * `openai.createChatCompletion`} can be passed through {@link modelKwargs}, even
203
- * if not explicitly available on this class.
204
- * @example
205
498
  * ```typescript
206
- * // Create a new instance of ChatOpenAI with specific temperature and model name settings
207
- * const model = new ChatOpenAI({
208
- * temperature: 0.9,
209
- * model: "ft:gpt-3.5-turbo-0613:{ORG_NAME}::{MODEL_ID}",
499
+ * import { HumanMessage } from '@langchain/core/messages';
500
+ *
501
+ * const imageUrl = "https://example.com/image.jpg";
502
+ * const imageData = await fetch(imageUrl).then(res => res.arrayBuffer());
503
+ * const base64Image = Buffer.from(imageData).toString('base64');
504
+ *
505
+ * const message = new HumanMessage({
506
+ * content: [
507
+ * { type: "text", text: "describe the weather in this image" },
508
+ * {
509
+ * type: "image_url",
510
+ * image_url: { url: `data:image/jpeg;base64,${base64Image}` },
511
+ * },
512
+ * ]
210
513
  * });
211
514
  *
212
- * // Invoke the model with a message and await the response
213
- * const message = await model.invoke("Hi there!");
515
+ * const imageDescriptionAiMsg = await llm.invoke([message]);
516
+ * console.log(imageDescriptionAiMsg.content);
517
+ * ```
518
+ *
519
+ * ```txt
520
+ * The weather in the image appears to be clear and sunny. The sky is mostly blue with a few scattered white clouds, indicating fair weather. The bright sunlight is casting shadows on the green, grassy hill, suggesting it is a pleasant day with good visibility. There are no signs of rain or stormy conditions.
521
+ * ```
522
+ * </details>
523
+ *
524
+ * <br />
525
+ *
526
+ * <details>
527
+ * <summary><strong>Usage Metadata</strong></summary>
528
+ *
529
+ * ```typescript
530
+ * const aiMsgForMetadata = await llm.invoke(input);
531
+ * console.log(aiMsgForMetadata.usage_metadata);
532
+ * ```
533
+ *
534
+ * ```txt
535
+ * { input_tokens: 28, output_tokens: 5, total_tokens: 33 }
536
+ * ```
537
+ * </details>
538
+ *
539
+ * <br />
540
+ *
541
+ * <details>
542
+ * <summary><strong>Logprobs</strong></summary>
543
+ *
544
+ * ```typescript
545
+ * const logprobsLlm = new ChatOpenAI({ logprobs: true });
546
+ * const aiMsgForLogprobs = await logprobsLlm.invoke(input);
547
+ * console.log(aiMsgForLogprobs.response_metadata.logprobs);
548
+ * ```
549
+ *
550
+ * ```txt
551
+ * {
552
+ * content: [
553
+ * {
554
+ * token: 'J',
555
+ * logprob: -0.000050616763,
556
+ * bytes: [Array],
557
+ * top_logprobs: []
558
+ * },
559
+ * {
560
+ * token: "'",
561
+ * logprob: -0.01868736,
562
+ * bytes: [Array],
563
+ * top_logprobs: []
564
+ * },
565
+ * {
566
+ * token: 'ad',
567
+ * logprob: -0.0000030545007,
568
+ * bytes: [Array],
569
+ * top_logprobs: []
570
+ * },
571
+ * { token: 'ore', logprob: 0, bytes: [Array], top_logprobs: [] },
572
+ * {
573
+ * token: ' la',
574
+ * logprob: -0.515404,
575
+ * bytes: [Array],
576
+ * top_logprobs: []
577
+ * },
578
+ * {
579
+ * token: ' programm',
580
+ * logprob: -0.0000118755715,
581
+ * bytes: [Array],
582
+ * top_logprobs: []
583
+ * },
584
+ * { token: 'ation', logprob: 0, bytes: [Array], top_logprobs: [] },
585
+ * {
586
+ * token: '.',
587
+ * logprob: -0.0000037697225,
588
+ * bytes: [Array],
589
+ * top_logprobs: []
590
+ * }
591
+ * ],
592
+ * refusal: null
593
+ * }
594
+ * ```
595
+ * </details>
596
+ *
597
+ * <br />
214
598
  *
215
- * // Log the response to the console
216
- * console.log(message);
599
+ * <details>
600
+ * <summary><strong>Response Metadata</strong></summary>
217
601
  *
602
+ * ```typescript
603
+ * const aiMsgForResponseMetadata = await llm.invoke(input);
604
+ * console.log(aiMsgForResponseMetadata.response_metadata);
218
605
  * ```
606
+ *
607
+ * ```txt
608
+ * {
609
+ * tokenUsage: { completionTokens: 5, promptTokens: 28, totalTokens: 33 },
610
+ * finish_reason: 'stop',
611
+ * system_fingerprint: 'fp_3aa7262c27'
612
+ * }
613
+ * ```
614
+ * </details>
615
+ *
616
+ * <br />
219
617
  */
220
618
  class ChatOpenAI extends chat_models_1.BaseChatModel {
221
619
  static lc_name() {
@@ -443,6 +841,16 @@ class ChatOpenAI extends chat_models_1.BaseChatModel {
443
841
  writable: true,
444
842
  value: void 0
445
843
  });
844
+ /**
845
+ * Whether the model supports the `strict` argument when passing in tools.
846
+ * If `undefined` the `strict` argument will not be passed to OpenAI.
847
+ */
848
+ Object.defineProperty(this, "supportsStrictToolCalling", {
849
+ enumerable: true,
850
+ configurable: true,
851
+ writable: true,
852
+ value: void 0
853
+ });
446
854
  this.openAIApiKey =
447
855
  fields?.apiKey ??
448
856
  fields?.openAIApiKey ??
@@ -515,6 +923,11 @@ class ChatOpenAI extends chat_models_1.BaseChatModel {
515
923
  ...configuration,
516
924
  ...fields?.configuration,
517
925
  };
926
+ // If `supportsStrictToolCalling` is explicitly set, use that value.
927
+ // Else leave undefined so it's not passed to OpenAI.
928
+ if (fields?.supportsStrictToolCalling !== undefined) {
929
+ this.supportsStrictToolCalling = fields.supportsStrictToolCalling;
930
+ }
518
931
  }
519
932
  getLsParams(options) {
520
933
  const params = this.invocationParams(options);
@@ -528,18 +941,39 @@ class ChatOpenAI extends chat_models_1.BaseChatModel {
528
941
  };
529
942
  }
530
943
  bindTools(tools, kwargs) {
944
+ let strict;
945
+ if (kwargs?.strict !== undefined) {
946
+ strict = kwargs.strict;
947
+ }
948
+ else if (this.supportsStrictToolCalling !== undefined) {
949
+ strict = this.supportsStrictToolCalling;
950
+ }
531
951
  return this.bind({
532
- tools: tools.map(function_calling_1.convertToOpenAITool),
952
+ tools: tools.map((tool) => _convertChatOpenAIToolTypeToOpenAITool(tool, { strict })),
533
953
  ...kwargs,
534
954
  });
535
955
  }
956
+ createResponseFormat(resFormat) {
957
+ if (resFormat &&
958
+ resFormat.type === "json_schema" &&
959
+ resFormat.json_schema.schema &&
960
+ isZodSchema(resFormat.json_schema.schema)) {
961
+ return (0, zod_1.zodResponseFormat)(resFormat.json_schema.schema, resFormat.json_schema.name, {
962
+ description: resFormat.json_schema.description,
963
+ });
964
+ }
965
+ return resFormat;
966
+ }
536
967
  /**
537
968
  * Get the parameters used to invoke the model
538
969
  */
539
970
  invocationParams(options, extra) {
540
- function isStructuredToolArray(tools) {
541
- return (tools !== undefined &&
542
- tools.every((tool) => Array.isArray(tool.lc_namespace)));
971
+ let strict;
972
+ if (options?.strict !== undefined) {
973
+ strict = options.strict;
974
+ }
975
+ else if (this.supportsStrictToolCalling !== undefined) {
976
+ strict = this.supportsStrictToolCalling;
543
977
  }
544
978
  let streamOptionsConfig = {};
545
979
  if (options?.stream_options !== undefined) {
@@ -565,11 +999,11 @@ class ChatOpenAI extends chat_models_1.BaseChatModel {
565
999
  stream: this.streaming,
566
1000
  functions: options?.functions,
567
1001
  function_call: options?.function_call,
568
- tools: isStructuredToolArray(options?.tools)
569
- ? options?.tools.map(function_calling_1.convertToOpenAITool)
570
- : options?.tools,
1002
+ tools: options?.tools?.length
1003
+ ? options.tools.map((tool) => _convertChatOpenAIToolTypeToOpenAITool(tool, { strict }))
1004
+ : undefined,
571
1005
  tool_choice: (0, openai_js_1.formatToOpenAIToolChoice)(options?.tool_choice),
572
- response_format: options?.response_format,
1006
+ response_format: this.createResponseFormat(options?.response_format),
573
1007
  seed: options?.seed,
574
1008
  ...streamOptionsConfig,
575
1009
  parallel_tool_calls: options?.parallel_tool_calls,
@@ -595,6 +1029,20 @@ class ChatOpenAI extends chat_models_1.BaseChatModel {
595
1029
  stream: true,
596
1030
  };
597
1031
  let defaultRole;
1032
+ if (params.response_format &&
1033
+ params.response_format.type === "json_schema") {
1034
+ console.warn(`OpenAI does not yet support streaming with "response_format" set to "json_schema". Falling back to non-streaming mode.`);
1035
+ const res = await this._generate(messages, options, runManager);
1036
+ const chunk = new outputs_1.ChatGenerationChunk({
1037
+ message: new messages_1.AIMessageChunk({
1038
+ ...res.generations[0].message,
1039
+ }),
1040
+ text: res.generations[0].text,
1041
+ generationInfo: res.generations[0].generationInfo,
1042
+ });
1043
+ yield chunk;
1044
+ return runManager?.handleLLMNewToken(res.generations[0].text ?? "", undefined, undefined, undefined, undefined, { chunk });
1045
+ }
598
1046
  const streamIterable = await this.completionWithRetry(params, options);
599
1047
  let usage;
600
1048
  for await (const data of streamIterable) {
@@ -698,14 +1146,28 @@ class ChatOpenAI extends chat_models_1.BaseChatModel {
698
1146
  return { generations, llmOutput: { estimatedTokenUsage: tokenUsage } };
699
1147
  }
700
1148
  else {
701
- const data = await this.completionWithRetry({
702
- ...params,
703
- stream: false,
704
- messages: messagesMapped,
705
- }, {
706
- signal: options?.signal,
707
- ...options?.options,
708
- });
1149
+ let data;
1150
+ if (options.response_format &&
1151
+ options.response_format.type === "json_schema") {
1152
+ data = await this.betaParsedCompletionWithRetry({
1153
+ ...params,
1154
+ stream: false,
1155
+ messages: messagesMapped,
1156
+ }, {
1157
+ signal: options?.signal,
1158
+ ...options?.options,
1159
+ });
1160
+ }
1161
+ else {
1162
+ data = await this.completionWithRetry({
1163
+ ...params,
1164
+ stream: false,
1165
+ messages: messagesMapped,
1166
+ }, {
1167
+ signal: options?.signal,
1168
+ ...options?.options,
1169
+ });
1170
+ }
709
1171
  const { completion_tokens: completionTokens, prompt_tokens: promptTokens, total_tokens: totalTokens, } = data?.usage ?? {};
710
1172
  if (completionTokens) {
711
1173
  tokenUsage.completionTokens =
@@ -850,6 +1312,25 @@ class ChatOpenAI extends chat_models_1.BaseChatModel {
850
1312
  }
851
1313
  });
852
1314
  }
1315
+ /**
1316
+ * Call the beta chat completions parse endpoint. This should only be called if
1317
+ * response_format is set to "json_object".
1318
+ * @param {OpenAIClient.Chat.ChatCompletionCreateParamsNonStreaming} request
1319
+ * @param {OpenAICoreRequestOptions | undefined} options
1320
+ */
1321
+ async betaParsedCompletionWithRetry(request, options) {
1322
+ const requestOptions = this._getClientOptions(options);
1323
+ return this.caller.call(async () => {
1324
+ try {
1325
+ const res = await this.client.beta.chat.completions.parse(request, requestOptions);
1326
+ return res;
1327
+ }
1328
+ catch (e) {
1329
+ const error = (0, openai_js_1.wrapOpenAIClientError)(e);
1330
+ throw error;
1331
+ }
1332
+ });
1333
+ }
853
1334
  _getClientOptions(options) {
854
1335
  if (!this.client) {
855
1336
  const openAIEndpointConfig = {
@@ -928,6 +1409,9 @@ class ChatOpenAI extends chat_models_1.BaseChatModel {
928
1409
  }
929
1410
  let llm;
930
1411
  let outputParser;
1412
+ if (config?.strict !== undefined && method === "jsonMode") {
1413
+ throw new Error("Argument `strict` is only supported for `method` = 'function_calling'");
1414
+ }
931
1415
  if (method === "jsonMode") {
932
1416
  llm = this.bind({
933
1417
  response_format: { type: "json_object" },
@@ -939,6 +1423,25 @@ class ChatOpenAI extends chat_models_1.BaseChatModel {
939
1423
  outputParser = new output_parsers_1.JsonOutputParser();
940
1424
  }
941
1425
  }
1426
+ else if (method === "jsonSchema") {
1427
+ llm = this.bind({
1428
+ response_format: {
1429
+ type: "json_schema",
1430
+ json_schema: {
1431
+ name: name ?? "extract",
1432
+ description: schema.description,
1433
+ schema,
1434
+ strict: config?.strict,
1435
+ },
1436
+ },
1437
+ });
1438
+ if (isZodSchema(schema)) {
1439
+ outputParser = output_parsers_1.StructuredOutputParser.fromZodSchema(schema);
1440
+ }
1441
+ else {
1442
+ outputParser = new output_parsers_1.JsonOutputParser();
1443
+ }
1444
+ }
942
1445
  else {
943
1446
  let functionName = name ?? "extract";
944
1447
  // Is function calling
@@ -961,6 +1464,8 @@ class ChatOpenAI extends chat_models_1.BaseChatModel {
961
1464
  name: functionName,
962
1465
  },
963
1466
  },
1467
+ // Do not pass `strict` argument to OpenAI if `config.strict` is undefined
1468
+ ...(config?.strict !== undefined ? { strict: config.strict } : {}),
964
1469
  });
965
1470
  outputParser = new openai_tools_1.JsonOutputKeyToolsParser({
966
1471
  returnSingle: true,
@@ -997,6 +1502,8 @@ class ChatOpenAI extends chat_models_1.BaseChatModel {
997
1502
  name: functionName,
998
1503
  },
999
1504
  },
1505
+ // Do not pass `strict` argument to OpenAI if `config.strict` is undefined
1506
+ ...(config?.strict !== undefined ? { strict: config.strict } : {}),
1000
1507
  });
1001
1508
  outputParser = new openai_tools_1.JsonOutputKeyToolsParser({
1002
1509
  returnSingle: true,