@langchain/openai 0.2.4 → 0.2.6

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,6 +6,7 @@ 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 base_1 = require("@langchain/core/language_models/base");
9
10
  const function_calling_1 = require("@langchain/core/utils/function_calling");
10
11
  const runnables_1 = require("@langchain/core/runnables");
11
12
  const output_parsers_1 = require("@langchain/core/output_parsers");
@@ -184,6 +185,21 @@ function convertMessagesToOpenAIParams(messages) {
184
185
  return completionParam;
185
186
  });
186
187
  }
188
+ function _convertChatOpenAIToolTypeToOpenAITool(tool, fields) {
189
+ if ((0, base_1.isOpenAITool)(tool)) {
190
+ if (fields?.strict !== undefined) {
191
+ return {
192
+ ...tool,
193
+ function: {
194
+ ...tool.function,
195
+ strict: fields.strict,
196
+ },
197
+ };
198
+ }
199
+ return tool;
200
+ }
201
+ return (0, function_calling_1.convertToOpenAITool)(tool, fields);
202
+ }
187
203
  /**
188
204
  * Wrapper around OpenAI large language models that use the Chat endpoint.
189
205
  *
@@ -443,6 +459,16 @@ class ChatOpenAI extends chat_models_1.BaseChatModel {
443
459
  writable: true,
444
460
  value: void 0
445
461
  });
462
+ /**
463
+ * Whether the model supports the `strict` argument when passing in tools.
464
+ * If `undefined` the `strict` argument will not be passed to OpenAI.
465
+ */
466
+ Object.defineProperty(this, "supportsStrictToolCalling", {
467
+ enumerable: true,
468
+ configurable: true,
469
+ writable: true,
470
+ value: void 0
471
+ });
446
472
  this.openAIApiKey =
447
473
  fields?.apiKey ??
448
474
  fields?.openAIApiKey ??
@@ -515,6 +541,11 @@ class ChatOpenAI extends chat_models_1.BaseChatModel {
515
541
  ...configuration,
516
542
  ...fields?.configuration,
517
543
  };
544
+ // If `supportsStrictToolCalling` is explicitly set, use that value.
545
+ // Else leave undefined so it's not passed to OpenAI.
546
+ if (fields?.supportsStrictToolCalling !== undefined) {
547
+ this.supportsStrictToolCalling = fields.supportsStrictToolCalling;
548
+ }
518
549
  }
519
550
  getLsParams(options) {
520
551
  const params = this.invocationParams(options);
@@ -528,8 +559,15 @@ class ChatOpenAI extends chat_models_1.BaseChatModel {
528
559
  };
529
560
  }
530
561
  bindTools(tools, kwargs) {
562
+ let strict;
563
+ if (kwargs?.strict !== undefined) {
564
+ strict = kwargs.strict;
565
+ }
566
+ else if (this.supportsStrictToolCalling !== undefined) {
567
+ strict = this.supportsStrictToolCalling;
568
+ }
531
569
  return this.bind({
532
- tools: tools.map(function_calling_1.convertToOpenAITool),
570
+ tools: tools.map((tool) => _convertChatOpenAIToolTypeToOpenAITool(tool, { strict })),
533
571
  ...kwargs,
534
572
  });
535
573
  }
@@ -537,9 +575,12 @@ class ChatOpenAI extends chat_models_1.BaseChatModel {
537
575
  * Get the parameters used to invoke the model
538
576
  */
539
577
  invocationParams(options, extra) {
540
- function isStructuredToolArray(tools) {
541
- return (tools !== undefined &&
542
- tools.every((tool) => Array.isArray(tool.lc_namespace)));
578
+ let strict;
579
+ if (options?.strict !== undefined) {
580
+ strict = options.strict;
581
+ }
582
+ else if (this.supportsStrictToolCalling !== undefined) {
583
+ strict = this.supportsStrictToolCalling;
543
584
  }
544
585
  let streamOptionsConfig = {};
545
586
  if (options?.stream_options !== undefined) {
@@ -565,9 +606,9 @@ class ChatOpenAI extends chat_models_1.BaseChatModel {
565
606
  stream: this.streaming,
566
607
  functions: options?.functions,
567
608
  function_call: options?.function_call,
568
- tools: isStructuredToolArray(options?.tools)
569
- ? options?.tools.map(function_calling_1.convertToOpenAITool)
570
- : options?.tools,
609
+ tools: options?.tools?.length
610
+ ? options.tools.map((tool) => _convertChatOpenAIToolTypeToOpenAITool(tool, { strict }))
611
+ : undefined,
571
612
  tool_choice: (0, openai_js_1.formatToOpenAIToolChoice)(options?.tool_choice),
572
613
  response_format: options?.response_format,
573
614
  seed: options?.seed,
@@ -928,6 +969,9 @@ class ChatOpenAI extends chat_models_1.BaseChatModel {
928
969
  }
929
970
  let llm;
930
971
  let outputParser;
972
+ if (config?.strict !== undefined && method === "jsonMode") {
973
+ throw new Error("Argument `strict` is only supported for `method` = 'function_calling'");
974
+ }
931
975
  if (method === "jsonMode") {
932
976
  llm = this.bind({
933
977
  response_format: { type: "json_object" },
@@ -961,6 +1005,8 @@ class ChatOpenAI extends chat_models_1.BaseChatModel {
961
1005
  name: functionName,
962
1006
  },
963
1007
  },
1008
+ // Do not pass `strict` argument to OpenAI if `config.strict` is undefined
1009
+ ...(config?.strict !== undefined ? { strict: config.strict } : {}),
964
1010
  });
965
1011
  outputParser = new openai_tools_1.JsonOutputKeyToolsParser({
966
1012
  returnSingle: true,
@@ -997,6 +1043,8 @@ class ChatOpenAI extends chat_models_1.BaseChatModel {
997
1043
  name: functionName,
998
1044
  },
999
1045
  },
1046
+ // Do not pass `strict` argument to OpenAI if `config.strict` is undefined
1047
+ ...(config?.strict !== undefined ? { strict: config.strict } : {}),
1000
1048
  });
1001
1049
  outputParser = new openai_tools_1.JsonOutputKeyToolsParser({
1002
1050
  returnSingle: true,
@@ -2,11 +2,10 @@ import { type ClientOptions, OpenAI as OpenAIClient } from "openai";
2
2
  import { CallbackManagerForLLMRun } from "@langchain/core/callbacks/manager";
3
3
  import { AIMessageChunk, type BaseMessage } from "@langchain/core/messages";
4
4
  import { ChatGenerationChunk, type ChatResult } from "@langchain/core/outputs";
5
- import { type StructuredToolInterface } from "@langchain/core/tools";
6
- import { BaseChatModel, LangSmithParams, type BaseChatModelParams } from "@langchain/core/language_models/chat_models";
7
- import type { BaseFunctionCallOptions, BaseLanguageModelInput, StructuredOutputMethodOptions, StructuredOutputMethodParams } from "@langchain/core/language_models/base";
5
+ import { BaseChatModel, BindToolsInput, LangSmithParams, type BaseChatModelParams } from "@langchain/core/language_models/chat_models";
6
+ import { type BaseFunctionCallOptions, type BaseLanguageModelInput, type StructuredOutputMethodOptions, type StructuredOutputMethodParams } from "@langchain/core/language_models/base";
8
7
  import { z } from "zod";
9
- import { Runnable, RunnableToolLike } from "@langchain/core/runnables";
8
+ import { Runnable } from "@langchain/core/runnables";
10
9
  import type { AzureOpenAIInput, OpenAICallOptions, OpenAIChatInput, OpenAICoreRequestOptions, LegacyOpenAIInput } from "./types.js";
11
10
  import { OpenAIToolChoice } from "./utils/openai.js";
12
11
  export type { AzureOpenAIInput, OpenAICallOptions, OpenAIChatInput };
@@ -20,8 +19,26 @@ interface OpenAILLMOutput {
20
19
  }
21
20
  type OpenAIRoleEnum = "system" | "assistant" | "user" | "function" | "tool";
22
21
  export declare function messageToOpenAIRole(message: BaseMessage): OpenAIRoleEnum;
22
+ type ChatOpenAIToolType = BindToolsInput | OpenAIClient.ChatCompletionTool;
23
+ export interface ChatOpenAIStructuredOutputMethodOptions<IncludeRaw extends boolean> extends StructuredOutputMethodOptions<IncludeRaw> {
24
+ /**
25
+ * strict: If `true` and `method` = "function_calling", model output is
26
+ * guaranteed to exactly match the schema. If `true`, the input schema
27
+ * will also be validated according to
28
+ * https://platform.openai.com/docs/guides/structured-outputs/supported-schemas.
29
+ * If `false`, input schema will not be validated and model output will not
30
+ * be validated.
31
+ * If `undefined`, `strict` argument will not be passed to the model.
32
+ *
33
+ * @version 0.2.6
34
+ * @note Planned breaking change in version `0.3.0`:
35
+ * `strict` will default to `true` when `method` is
36
+ * "function_calling" as of version `0.3.0`.
37
+ */
38
+ strict?: boolean;
39
+ }
23
40
  export interface ChatOpenAICallOptions extends OpenAICallOptions, BaseFunctionCallOptions {
24
- tools?: StructuredToolInterface[] | OpenAIClient.ChatCompletionTool[];
41
+ tools?: ChatOpenAIToolType[];
25
42
  tool_choice?: OpenAIToolChoice;
26
43
  promptIndex?: number;
27
44
  response_format?: {
@@ -45,6 +62,23 @@ export interface ChatOpenAICallOptions extends OpenAICallOptions, BaseFunctionCa
45
62
  * call multiple tools in one response.
46
63
  */
47
64
  parallel_tool_calls?: boolean;
65
+ /**
66
+ * If `true`, model output is guaranteed to exactly match the JSON Schema
67
+ * provided in the tool definition. If `true`, the input schema will also be
68
+ * validated according to
69
+ * https://platform.openai.com/docs/guides/structured-outputs/supported-schemas.
70
+ *
71
+ * If `false`, input schema will not be validated and model output will not
72
+ * be validated.
73
+ *
74
+ * If `undefined`, `strict` argument will not be passed to the model.
75
+ *
76
+ * @version 0.2.6
77
+ */
78
+ strict?: boolean;
79
+ }
80
+ export interface ChatOpenAIFields extends Partial<OpenAIChatInput>, Partial<AzureOpenAIInput>, BaseChatModelParams {
81
+ configuration?: ClientOptions & LegacyOpenAIInput;
48
82
  }
49
83
  /**
50
84
  * Wrapper around OpenAI large language models that use the Chat endpoint.
@@ -117,13 +151,16 @@ export declare class ChatOpenAI<CallOptions extends ChatOpenAICallOptions = Chat
117
151
  __includeRawResponse?: boolean;
118
152
  protected client: OpenAIClient;
119
153
  protected clientConfig: ClientOptions;
120
- constructor(fields?: Partial<OpenAIChatInput> & Partial<AzureOpenAIInput> & BaseChatModelParams & {
121
- configuration?: ClientOptions & LegacyOpenAIInput;
122
- },
154
+ /**
155
+ * Whether the model supports the `strict` argument when passing in tools.
156
+ * If `undefined` the `strict` argument will not be passed to OpenAI.
157
+ */
158
+ supportsStrictToolCalling?: boolean;
159
+ constructor(fields?: ChatOpenAIFields,
123
160
  /** @deprecated */
124
161
  configuration?: ClientOptions & LegacyOpenAIInput);
125
162
  getLsParams(options: this["ParsedCallOptions"]): LangSmithParams;
126
- bindTools(tools: (Record<string, unknown> | StructuredToolInterface | RunnableToolLike)[], kwargs?: Partial<CallOptions>): Runnable<BaseLanguageModelInput, AIMessageChunk, CallOptions>;
163
+ bindTools(tools: ChatOpenAIToolType[], kwargs?: Partial<CallOptions>): Runnable<BaseLanguageModelInput, AIMessageChunk, CallOptions>;
127
164
  /**
128
165
  * Get the parameters used to invoke the model
129
166
  */
@@ -169,8 +206,8 @@ export declare class ChatOpenAI<CallOptions extends ChatOpenAICallOptions = Chat
169
206
  _llmType(): string;
170
207
  /** @ignore */
171
208
  _combineLLMOutput(...llmOutputs: OpenAILLMOutput[]): OpenAILLMOutput;
172
- withStructuredOutput<RunOutput extends Record<string, any> = Record<string, any>>(outputSchema: StructuredOutputMethodParams<RunOutput, false> | z.ZodType<RunOutput> | Record<string, any>, config?: StructuredOutputMethodOptions<false>): Runnable<BaseLanguageModelInput, RunOutput>;
173
- withStructuredOutput<RunOutput extends Record<string, any> = Record<string, any>>(outputSchema: StructuredOutputMethodParams<RunOutput, true> | z.ZodType<RunOutput> | Record<string, any>, config?: StructuredOutputMethodOptions<true>): Runnable<BaseLanguageModelInput, {
209
+ withStructuredOutput<RunOutput extends Record<string, any> = Record<string, any>>(outputSchema: StructuredOutputMethodParams<RunOutput, false> | z.ZodType<RunOutput> | Record<string, any>, config?: ChatOpenAIStructuredOutputMethodOptions<false>): Runnable<BaseLanguageModelInput, RunOutput>;
210
+ withStructuredOutput<RunOutput extends Record<string, any> = Record<string, any>>(outputSchema: StructuredOutputMethodParams<RunOutput, true> | z.ZodType<RunOutput> | Record<string, any>, config?: ChatOpenAIStructuredOutputMethodOptions<true>): Runnable<BaseLanguageModelInput, {
174
211
  raw: BaseMessage;
175
212
  parsed: RunOutput;
176
213
  }>;
@@ -3,6 +3,7 @@ import { AIMessage, AIMessageChunk, ChatMessage, ChatMessageChunk, FunctionMessa
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";
6
+ import { isOpenAITool, } from "@langchain/core/language_models/base";
6
7
  import { convertToOpenAITool } from "@langchain/core/utils/function_calling";
7
8
  import { RunnablePassthrough, RunnableSequence, } from "@langchain/core/runnables";
8
9
  import { JsonOutputParser, StructuredOutputParser, } from "@langchain/core/output_parsers";
@@ -180,6 +181,21 @@ function convertMessagesToOpenAIParams(messages) {
180
181
  return completionParam;
181
182
  });
182
183
  }
184
+ function _convertChatOpenAIToolTypeToOpenAITool(tool, fields) {
185
+ if (isOpenAITool(tool)) {
186
+ if (fields?.strict !== undefined) {
187
+ return {
188
+ ...tool,
189
+ function: {
190
+ ...tool.function,
191
+ strict: fields.strict,
192
+ },
193
+ };
194
+ }
195
+ return tool;
196
+ }
197
+ return convertToOpenAITool(tool, fields);
198
+ }
183
199
  /**
184
200
  * Wrapper around OpenAI large language models that use the Chat endpoint.
185
201
  *
@@ -439,6 +455,16 @@ export class ChatOpenAI extends BaseChatModel {
439
455
  writable: true,
440
456
  value: void 0
441
457
  });
458
+ /**
459
+ * Whether the model supports the `strict` argument when passing in tools.
460
+ * If `undefined` the `strict` argument will not be passed to OpenAI.
461
+ */
462
+ Object.defineProperty(this, "supportsStrictToolCalling", {
463
+ enumerable: true,
464
+ configurable: true,
465
+ writable: true,
466
+ value: void 0
467
+ });
442
468
  this.openAIApiKey =
443
469
  fields?.apiKey ??
444
470
  fields?.openAIApiKey ??
@@ -511,6 +537,11 @@ export class ChatOpenAI extends BaseChatModel {
511
537
  ...configuration,
512
538
  ...fields?.configuration,
513
539
  };
540
+ // If `supportsStrictToolCalling` is explicitly set, use that value.
541
+ // Else leave undefined so it's not passed to OpenAI.
542
+ if (fields?.supportsStrictToolCalling !== undefined) {
543
+ this.supportsStrictToolCalling = fields.supportsStrictToolCalling;
544
+ }
514
545
  }
515
546
  getLsParams(options) {
516
547
  const params = this.invocationParams(options);
@@ -524,8 +555,15 @@ export class ChatOpenAI extends BaseChatModel {
524
555
  };
525
556
  }
526
557
  bindTools(tools, kwargs) {
558
+ let strict;
559
+ if (kwargs?.strict !== undefined) {
560
+ strict = kwargs.strict;
561
+ }
562
+ else if (this.supportsStrictToolCalling !== undefined) {
563
+ strict = this.supportsStrictToolCalling;
564
+ }
527
565
  return this.bind({
528
- tools: tools.map(convertToOpenAITool),
566
+ tools: tools.map((tool) => _convertChatOpenAIToolTypeToOpenAITool(tool, { strict })),
529
567
  ...kwargs,
530
568
  });
531
569
  }
@@ -533,9 +571,12 @@ export class ChatOpenAI extends BaseChatModel {
533
571
  * Get the parameters used to invoke the model
534
572
  */
535
573
  invocationParams(options, extra) {
536
- function isStructuredToolArray(tools) {
537
- return (tools !== undefined &&
538
- tools.every((tool) => Array.isArray(tool.lc_namespace)));
574
+ let strict;
575
+ if (options?.strict !== undefined) {
576
+ strict = options.strict;
577
+ }
578
+ else if (this.supportsStrictToolCalling !== undefined) {
579
+ strict = this.supportsStrictToolCalling;
539
580
  }
540
581
  let streamOptionsConfig = {};
541
582
  if (options?.stream_options !== undefined) {
@@ -561,9 +602,9 @@ export class ChatOpenAI extends BaseChatModel {
561
602
  stream: this.streaming,
562
603
  functions: options?.functions,
563
604
  function_call: options?.function_call,
564
- tools: isStructuredToolArray(options?.tools)
565
- ? options?.tools.map(convertToOpenAITool)
566
- : options?.tools,
605
+ tools: options?.tools?.length
606
+ ? options.tools.map((tool) => _convertChatOpenAIToolTypeToOpenAITool(tool, { strict }))
607
+ : undefined,
567
608
  tool_choice: formatToOpenAIToolChoice(options?.tool_choice),
568
609
  response_format: options?.response_format,
569
610
  seed: options?.seed,
@@ -924,6 +965,9 @@ export class ChatOpenAI extends BaseChatModel {
924
965
  }
925
966
  let llm;
926
967
  let outputParser;
968
+ if (config?.strict !== undefined && method === "jsonMode") {
969
+ throw new Error("Argument `strict` is only supported for `method` = 'function_calling'");
970
+ }
927
971
  if (method === "jsonMode") {
928
972
  llm = this.bind({
929
973
  response_format: { type: "json_object" },
@@ -957,6 +1001,8 @@ export class ChatOpenAI extends BaseChatModel {
957
1001
  name: functionName,
958
1002
  },
959
1003
  },
1004
+ // Do not pass `strict` argument to OpenAI if `config.strict` is undefined
1005
+ ...(config?.strict !== undefined ? { strict: config.strict } : {}),
960
1006
  });
961
1007
  outputParser = new JsonOutputKeyToolsParser({
962
1008
  returnSingle: true,
@@ -993,6 +1039,8 @@ export class ChatOpenAI extends BaseChatModel {
993
1039
  name: functionName,
994
1040
  },
995
1041
  },
1042
+ // Do not pass `strict` argument to OpenAI if `config.strict` is undefined
1043
+ ...(config?.strict !== undefined ? { strict: config.strict } : {}),
996
1044
  });
997
1045
  outputParser = new JsonOutputKeyToolsParser({
998
1046
  returnSingle: true,
package/dist/legacy.cjs CHANGED
@@ -253,7 +253,7 @@ class OpenAIChat extends llms_1.LLM {
253
253
  this.organization =
254
254
  fields?.configuration?.organization ??
255
255
  (0, env_1.getEnvironmentVariable)("OPENAI_ORGANIZATION");
256
- this.modelName = fields?.modelName ?? this.modelName;
256
+ this.modelName = fields?.model ?? fields?.modelName ?? this.modelName;
257
257
  this.prefixMessages = fields?.prefixMessages ?? this.prefixMessages;
258
258
  this.modelKwargs = fields?.modelKwargs ?? {};
259
259
  this.timeout = fields?.timeout;
package/dist/legacy.js CHANGED
@@ -250,7 +250,7 @@ export class OpenAIChat extends LLM {
250
250
  this.organization =
251
251
  fields?.configuration?.organization ??
252
252
  getEnvironmentVariable("OPENAI_ORGANIZATION");
253
- this.modelName = fields?.modelName ?? this.modelName;
253
+ this.modelName = fields?.model ?? fields?.modelName ?? this.modelName;
254
254
  this.prefixMessages = fields?.prefixMessages ?? this.prefixMessages;
255
255
  this.modelKwargs = fields?.modelKwargs ?? {};
256
256
  this.timeout = fields?.timeout;
package/dist/llms.cjs CHANGED
@@ -75,6 +75,15 @@ class OpenAI extends llms_1.BaseLLM {
75
75
  let model = fields?.model ?? fields?.modelName;
76
76
  if ((model?.startsWith("gpt-3.5-turbo") || model?.startsWith("gpt-4")) &&
77
77
  !model?.includes("-instruct")) {
78
+ console.warn([
79
+ `Your chosen OpenAI model, "${model}", is a chat model and not a text-in/text-out LLM.`,
80
+ `Passing it into the "OpenAI" class is deprecated and only permitted for backwards-compatibility. You may experience odd behavior.`,
81
+ `Please use the "ChatOpenAI" class instead.`,
82
+ "",
83
+ `See this page for more information:`,
84
+ "|",
85
+ `└> https://js.langchain.com/v0.2/docs/integrations/chat/openai`,
86
+ ].join("\n"));
78
87
  // eslint-disable-next-line no-constructor-return
79
88
  return new legacy_js_1.OpenAIChat(fields, configuration);
80
89
  }
package/dist/llms.js CHANGED
@@ -72,6 +72,15 @@ export class OpenAI extends BaseLLM {
72
72
  let model = fields?.model ?? fields?.modelName;
73
73
  if ((model?.startsWith("gpt-3.5-turbo") || model?.startsWith("gpt-4")) &&
74
74
  !model?.includes("-instruct")) {
75
+ console.warn([
76
+ `Your chosen OpenAI model, "${model}", is a chat model and not a text-in/text-out LLM.`,
77
+ `Passing it into the "OpenAI" class is deprecated and only permitted for backwards-compatibility. You may experience odd behavior.`,
78
+ `Please use the "ChatOpenAI" class instead.`,
79
+ "",
80
+ `See this page for more information:`,
81
+ "|",
82
+ `└> https://js.langchain.com/v0.2/docs/integrations/chat/openai`,
83
+ ].join("\n"));
75
84
  // eslint-disable-next-line no-constructor-return
76
85
  return new OpenAIChat(fields, configuration);
77
86
  }
@@ -100,18 +100,75 @@ class DallEAPIWrapper extends tools_1.Tool {
100
100
  fields?.dallEResponseFormat ?? this.dallEResponseFormat;
101
101
  this.user = fields?.user;
102
102
  }
103
+ /**
104
+ * Processes the API response if multiple images are generated.
105
+ * Returns a list of MessageContentImageUrl objects. If the response
106
+ * format is `url`, then the `image_url` field will contain the URL.
107
+ * If it is `b64_json`, then the `image_url` field will contain an object
108
+ * with a `url` field with the base64 encoded image.
109
+ *
110
+ * @param {OpenAIClient.Images.ImagesResponse[]} response The API response
111
+ * @returns {MessageContentImageUrl[]}
112
+ */
113
+ processMultipleGeneratedUrls(response) {
114
+ if (this.dallEResponseFormat === "url") {
115
+ return response.flatMap((res) => {
116
+ const imageUrlContent = res.data
117
+ .flatMap((item) => {
118
+ if (!item.url)
119
+ return [];
120
+ return {
121
+ type: "image_url",
122
+ image_url: item.url,
123
+ };
124
+ })
125
+ .filter((item) => item !== undefined &&
126
+ item.type === "image_url" &&
127
+ typeof item.image_url === "string" &&
128
+ item.image_url !== undefined);
129
+ return imageUrlContent;
130
+ });
131
+ }
132
+ else {
133
+ return response.flatMap((res) => {
134
+ const b64Content = res.data
135
+ .flatMap((item) => {
136
+ if (!item.b64_json)
137
+ return [];
138
+ return {
139
+ type: "image_url",
140
+ image_url: {
141
+ url: item.b64_json,
142
+ },
143
+ };
144
+ })
145
+ .filter((item) => item !== undefined &&
146
+ item.type === "image_url" &&
147
+ typeof item.image_url === "object" &&
148
+ "url" in item.image_url &&
149
+ typeof item.image_url.url === "string" &&
150
+ item.image_url.url !== undefined);
151
+ return b64Content;
152
+ });
153
+ }
154
+ }
103
155
  /** @ignore */
104
156
  async _call(input) {
105
- const response = await this.client.images.generate({
157
+ const generateImageFields = {
106
158
  model: this.model,
107
159
  prompt: input,
108
- n: this.n,
160
+ n: 1,
109
161
  size: this.size,
110
162
  response_format: this.dallEResponseFormat,
111
163
  style: this.style,
112
164
  quality: this.quality,
113
165
  user: this.user,
114
- });
166
+ };
167
+ if (this.n > 1) {
168
+ const results = await Promise.all(Array.from({ length: this.n }).map(() => this.client.images.generate(generateImageFields)));
169
+ return this.processMultipleGeneratedUrls(results);
170
+ }
171
+ const response = await this.client.images.generate(generateImageFields);
115
172
  let data = "";
116
173
  if (this.dallEResponseFormat === "url") {
117
174
  [data] = response.data
@@ -1,5 +1,6 @@
1
1
  import { OpenAI as OpenAIClient } from "openai";
2
2
  import { Tool, ToolParams } from "@langchain/core/tools";
3
+ import { MessageContentComplex } from "@langchain/core/messages";
3
4
  /**
4
5
  * An interface for the Dall-E API Wrapper.
5
6
  */
@@ -90,6 +91,17 @@ export declare class DallEAPIWrapper extends Tool {
90
91
  private dallEResponseFormat;
91
92
  private user?;
92
93
  constructor(fields?: DallEAPIWrapperParams);
94
+ /**
95
+ * Processes the API response if multiple images are generated.
96
+ * Returns a list of MessageContentImageUrl objects. If the response
97
+ * format is `url`, then the `image_url` field will contain the URL.
98
+ * If it is `b64_json`, then the `image_url` field will contain an object
99
+ * with a `url` field with the base64 encoded image.
100
+ *
101
+ * @param {OpenAIClient.Images.ImagesResponse[]} response The API response
102
+ * @returns {MessageContentImageUrl[]}
103
+ */
104
+ private processMultipleGeneratedUrls;
93
105
  /** @ignore */
94
- _call(input: string): Promise<string>;
106
+ _call(input: string): Promise<string | MessageContentComplex[]>;
95
107
  }
@@ -97,18 +97,75 @@ export class DallEAPIWrapper extends Tool {
97
97
  fields?.dallEResponseFormat ?? this.dallEResponseFormat;
98
98
  this.user = fields?.user;
99
99
  }
100
+ /**
101
+ * Processes the API response if multiple images are generated.
102
+ * Returns a list of MessageContentImageUrl objects. If the response
103
+ * format is `url`, then the `image_url` field will contain the URL.
104
+ * If it is `b64_json`, then the `image_url` field will contain an object
105
+ * with a `url` field with the base64 encoded image.
106
+ *
107
+ * @param {OpenAIClient.Images.ImagesResponse[]} response The API response
108
+ * @returns {MessageContentImageUrl[]}
109
+ */
110
+ processMultipleGeneratedUrls(response) {
111
+ if (this.dallEResponseFormat === "url") {
112
+ return response.flatMap((res) => {
113
+ const imageUrlContent = res.data
114
+ .flatMap((item) => {
115
+ if (!item.url)
116
+ return [];
117
+ return {
118
+ type: "image_url",
119
+ image_url: item.url,
120
+ };
121
+ })
122
+ .filter((item) => item !== undefined &&
123
+ item.type === "image_url" &&
124
+ typeof item.image_url === "string" &&
125
+ item.image_url !== undefined);
126
+ return imageUrlContent;
127
+ });
128
+ }
129
+ else {
130
+ return response.flatMap((res) => {
131
+ const b64Content = res.data
132
+ .flatMap((item) => {
133
+ if (!item.b64_json)
134
+ return [];
135
+ return {
136
+ type: "image_url",
137
+ image_url: {
138
+ url: item.b64_json,
139
+ },
140
+ };
141
+ })
142
+ .filter((item) => item !== undefined &&
143
+ item.type === "image_url" &&
144
+ typeof item.image_url === "object" &&
145
+ "url" in item.image_url &&
146
+ typeof item.image_url.url === "string" &&
147
+ item.image_url.url !== undefined);
148
+ return b64Content;
149
+ });
150
+ }
151
+ }
100
152
  /** @ignore */
101
153
  async _call(input) {
102
- const response = await this.client.images.generate({
154
+ const generateImageFields = {
103
155
  model: this.model,
104
156
  prompt: input,
105
- n: this.n,
157
+ n: 1,
106
158
  size: this.size,
107
159
  response_format: this.dallEResponseFormat,
108
160
  style: this.style,
109
161
  quality: this.quality,
110
162
  user: this.user,
111
- });
163
+ };
164
+ if (this.n > 1) {
165
+ const results = await Promise.all(Array.from({ length: this.n }).map(() => this.client.images.generate(generateImageFields)));
166
+ return this.processMultipleGeneratedUrls(results);
167
+ }
168
+ const response = await this.client.images.generate(generateImageFields);
112
169
  let data = "";
113
170
  if (this.dallEResponseFormat === "url") {
114
171
  [data] = response.data
package/dist/types.d.ts CHANGED
@@ -121,6 +121,11 @@ export interface OpenAIChatInput extends OpenAIBaseInput {
121
121
  * Currently in experimental beta.
122
122
  */
123
123
  __includeRawResponse?: boolean;
124
+ /**
125
+ * Whether the model supports the `strict` argument when passing in tools.
126
+ * If `undefined` the `strict` argument will not be passed to OpenAI.
127
+ */
128
+ supportsStrictToolCalling?: boolean;
124
129
  }
125
130
  export declare interface AzureOpenAIInput {
126
131
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@langchain/openai",
3
- "version": "0.2.4",
3
+ "version": "0.2.6",
4
4
  "description": "OpenAI integrations for LangChain.js",
5
5
  "type": "module",
6
6
  "engines": {
@@ -15,7 +15,7 @@
15
15
  "homepage": "https://github.com/langchain-ai/langchainjs/tree/main/libs/langchain-openai/",
16
16
  "scripts": {
17
17
  "build": "yarn turbo:command build:internal --filter=@langchain/openai",
18
- "build:internal": "yarn lc-build:v2 --create-entrypoints --pre --tree-shaking",
18
+ "build:internal": "yarn lc_build_v2 --create-entrypoints --pre --tree-shaking",
19
19
  "lint:eslint": "NODE_OPTIONS=--max-old-space-size=4096 eslint --cache --ext .ts,.js src/",
20
20
  "lint:dpdm": "dpdm --exit-code circular:1 --no-warning --no-tree src/*.ts src/**/*.ts",
21
21
  "lint": "yarn lint:eslint && yarn lint:dpdm",
@@ -35,16 +35,16 @@
35
35
  "author": "LangChain",
36
36
  "license": "MIT",
37
37
  "dependencies": {
38
- "@langchain/core": ">=0.2.16 <0.3.0",
38
+ "@langchain/core": ">=0.2.21 <0.3.0",
39
39
  "js-tiktoken": "^1.0.12",
40
- "openai": "^4.49.1",
40
+ "openai": "^4.55.0",
41
41
  "zod": "^3.22.4",
42
42
  "zod-to-json-schema": "^3.22.3"
43
43
  },
44
44
  "devDependencies": {
45
- "@azure/identity": "^4.2.0",
45
+ "@azure/identity": "^4.2.1",
46
46
  "@jest/globals": "^29.5.0",
47
- "@langchain/scripts": "~0.0.14",
47
+ "@langchain/scripts": "~0.0.20",
48
48
  "@langchain/standard-tests": "0.0.0",
49
49
  "@swc/core": "^1.3.90",
50
50
  "@swc/jest": "^0.2.29",
@@ -59,7 +59,7 @@
59
59
  "jest": "^29.5.0",
60
60
  "jest-environment-node": "^29.6.4",
61
61
  "prettier": "^2.8.3",
62
- "release-it": "^15.10.1",
62
+ "release-it": "^17.6.0",
63
63
  "rimraf": "^5.0.1",
64
64
  "ts-jest": "^29.1.0",
65
65
  "typescript": "~5.1.6"
@@ -1 +0,0 @@
1
- export {};
@@ -1,16 +0,0 @@
1
- import { test, expect } from "@jest/globals";
2
- import { DallEAPIWrapper } from "../dalle.js";
3
- test.skip("Dalle can generate images", async () => {
4
- const dalle = new DallEAPIWrapper();
5
- const res = await dalle.invoke("A painting of a cat");
6
- expect(res).toBeDefined();
7
- expect(res).toContain("https://");
8
- });
9
- test.skip("Dalle can generate images with base 64 response format", async () => {
10
- const dalle = new DallEAPIWrapper({
11
- responseFormat: "b64_json",
12
- });
13
- const res = await dalle.invoke("A painting of a cat");
14
- expect(res).toBeDefined();
15
- expect(res).not.toContain("https://");
16
- });