@langchain/openai 0.0.18 → 0.0.19

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.
@@ -742,21 +742,26 @@ class ChatOpenAI extends chat_models_1.BaseChatModel {
742
742
  llm = this.bind({
743
743
  response_format: { type: "json_object" },
744
744
  });
745
- outputParser = new output_parsers_1.JsonOutputParser();
745
+ if (isZodSchema(schema)) {
746
+ outputParser = output_parsers_1.StructuredOutputParser.fromZodSchema(schema);
747
+ }
748
+ else {
749
+ outputParser = new output_parsers_1.JsonOutputParser();
750
+ }
746
751
  }
747
752
  else {
748
753
  const functionName = name ?? "extract";
749
754
  // Is function calling
750
755
  if (isZodSchema(schema)) {
751
- const asZodSchema = (0, zod_to_json_schema_1.zodToJsonSchema)(schema);
756
+ const asJsonSchema = (0, zod_to_json_schema_1.zodToJsonSchema)(schema);
752
757
  llm = this.bind({
753
758
  tools: [
754
759
  {
755
760
  type: "function",
756
761
  function: {
757
762
  name: functionName,
758
- description: asZodSchema.description,
759
- parameters: asZodSchema,
763
+ description: asJsonSchema.description,
764
+ parameters: asJsonSchema,
760
765
  },
761
766
  },
762
767
  ],
@@ -770,6 +775,7 @@ class ChatOpenAI extends chat_models_1.BaseChatModel {
770
775
  outputParser = new openai_tools_1.JsonOutputKeyToolsParser({
771
776
  returnSingle: true,
772
777
  keyName: functionName,
778
+ zodSchema: schema,
773
779
  });
774
780
  }
775
781
  else {
@@ -819,7 +825,9 @@ class ChatOpenAI extends chat_models_1.BaseChatModel {
819
825
  }
820
826
  }
821
827
  exports.ChatOpenAI = ChatOpenAI;
822
- function isZodSchema(input) {
828
+ function isZodSchema(
829
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
830
+ input) {
823
831
  // Check for a characteristic method of Zod schemas
824
832
  return typeof input?.parse === "function";
825
833
  }
@@ -5,7 +5,7 @@ import { getEnvironmentVariable } from "@langchain/core/utils/env";
5
5
  import { BaseChatModel, } from "@langchain/core/language_models/chat_models";
6
6
  import { convertToOpenAITool } from "@langchain/core/utils/function_calling";
7
7
  import { RunnablePassthrough, RunnableSequence, } from "@langchain/core/runnables";
8
- import { JsonOutputParser } from "@langchain/core/output_parsers";
8
+ import { JsonOutputParser, StructuredOutputParser, } from "@langchain/core/output_parsers";
9
9
  import { JsonOutputKeyToolsParser } from "@langchain/core/output_parsers/openai_tools";
10
10
  import { zodToJsonSchema } from "zod-to-json-schema";
11
11
  import { getEndpoint } from "./utils/azure.js";
@@ -738,21 +738,26 @@ export class ChatOpenAI extends BaseChatModel {
738
738
  llm = this.bind({
739
739
  response_format: { type: "json_object" },
740
740
  });
741
- outputParser = new JsonOutputParser();
741
+ if (isZodSchema(schema)) {
742
+ outputParser = StructuredOutputParser.fromZodSchema(schema);
743
+ }
744
+ else {
745
+ outputParser = new JsonOutputParser();
746
+ }
742
747
  }
743
748
  else {
744
749
  const functionName = name ?? "extract";
745
750
  // Is function calling
746
751
  if (isZodSchema(schema)) {
747
- const asZodSchema = zodToJsonSchema(schema);
752
+ const asJsonSchema = zodToJsonSchema(schema);
748
753
  llm = this.bind({
749
754
  tools: [
750
755
  {
751
756
  type: "function",
752
757
  function: {
753
758
  name: functionName,
754
- description: asZodSchema.description,
755
- parameters: asZodSchema,
759
+ description: asJsonSchema.description,
760
+ parameters: asJsonSchema,
756
761
  },
757
762
  },
758
763
  ],
@@ -766,6 +771,7 @@ export class ChatOpenAI extends BaseChatModel {
766
771
  outputParser = new JsonOutputKeyToolsParser({
767
772
  returnSingle: true,
768
773
  keyName: functionName,
774
+ zodSchema: schema,
769
775
  });
770
776
  }
771
777
  else {
@@ -814,7 +820,9 @@ export class ChatOpenAI extends BaseChatModel {
814
820
  ]);
815
821
  }
816
822
  }
817
- function isZodSchema(input) {
823
+ function isZodSchema(
824
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
825
+ input) {
818
826
  // Check for a characteristic method of Zod schemas
819
827
  return typeof input?.parse === "function";
820
828
  }
package/dist/index.cjs CHANGED
@@ -26,3 +26,4 @@ __exportStar(require("./embeddings.cjs"), exports);
26
26
  __exportStar(require("./types.cjs"), exports);
27
27
  __exportStar(require("./utils/openai.cjs"), exports);
28
28
  __exportStar(require("./utils/azure.cjs"), exports);
29
+ __exportStar(require("./tools/index.cjs"), exports);
package/dist/index.d.ts CHANGED
@@ -7,3 +7,4 @@ export * from "./embeddings.js";
7
7
  export * from "./types.js";
8
8
  export * from "./utils/openai.js";
9
9
  export * from "./utils/azure.js";
10
+ export * from "./tools/index.js";
package/dist/index.js CHANGED
@@ -7,3 +7,4 @@ export * from "./embeddings.js";
7
7
  export * from "./types.js";
8
8
  export * from "./utils/openai.js";
9
9
  export * from "./utils/azure.js";
10
+ export * from "./tools/index.js";
@@ -1,25 +1,10 @@
1
- import { test, jest, expect, describe } from "@jest/globals";
1
+ import { test, jest, expect } from "@jest/globals";
2
2
  import { ChatMessage, HumanMessage, SystemMessage, } from "@langchain/core/messages";
3
3
  import { ChatPromptValue } from "@langchain/core/prompt_values";
4
4
  import { PromptTemplate, ChatPromptTemplate, HumanMessagePromptTemplate, SystemMessagePromptTemplate, } from "@langchain/core/prompts";
5
5
  import { CallbackManager } from "@langchain/core/callbacks/manager";
6
6
  import { InMemoryCache } from "@langchain/core/caches";
7
- import { z } from "zod";
8
- import { zodToJsonSchema } from "zod-to-json-schema";
9
7
  import { ChatOpenAI } from "../chat_models.js";
10
- test("Test ChatOpenAI", async () => {
11
- const chat = new ChatOpenAI({ modelName: "gpt-3.5-turbo", maxTokens: 10 });
12
- const message = new HumanMessage("Hello!");
13
- const res = await chat.call([message]);
14
- console.log({ res });
15
- });
16
- test("Test ChatOpenAI with SystemChatMessage", async () => {
17
- const chat = new ChatOpenAI({ modelName: "gpt-3.5-turbo", maxTokens: 10 });
18
- const system_message = new SystemMessage("You are to chat with a user.");
19
- const message = new HumanMessage("Hello!");
20
- const res = await chat.call([system_message, message]);
21
- console.log({ res });
22
- });
23
8
  test("Test ChatOpenAI Generate", async () => {
24
9
  const chat = new ChatOpenAI({
25
10
  modelName: "gpt-3.5-turbo",
@@ -65,7 +50,7 @@ test("Test ChatOpenAI tokenUsage", async () => {
65
50
  }),
66
51
  });
67
52
  const message = new HumanMessage("Hello");
68
- const res = await model.call([message]);
53
+ const res = await model.invoke([message]);
69
54
  console.log({ res });
70
55
  expect(tokenUsage.promptTokens).toBeGreaterThan(0);
71
56
  });
@@ -108,7 +93,7 @@ test("Test ChatOpenAI in streaming mode", async () => {
108
93
  ],
109
94
  });
110
95
  const message = new HumanMessage("Hello!");
111
- const result = await model.call([message]);
96
+ const result = await model.invoke([message]);
112
97
  console.log(result);
113
98
  expect(nrNewTokens > 0).toBe(true);
114
99
  expect(result.content).toBe(streamedCompletion);
@@ -175,25 +160,27 @@ test("OpenAI Chat, docs, prompt templates", async () => {
175
160
  }, 5000);
176
161
  test("Test OpenAI with stop", async () => {
177
162
  const model = new ChatOpenAI({ maxTokens: 5 });
178
- const res = await model.call([new HumanMessage("Print hello world")], ["world"]);
163
+ const res = await model.invoke([new HumanMessage("Print hello world")], {
164
+ stop: ["world"],
165
+ });
179
166
  console.log({ res });
180
167
  });
181
168
  test("Test OpenAI with stop in object", async () => {
182
169
  const model = new ChatOpenAI({ maxTokens: 5 });
183
- const res = await model.call([new HumanMessage("Print hello world")], {
170
+ const res = await model.invoke([new HumanMessage("Print hello world")], {
184
171
  stop: ["world"],
185
172
  });
186
173
  console.log({ res });
187
174
  });
188
175
  test("Test OpenAI with timeout in call options", async () => {
189
- const model = new ChatOpenAI({ maxTokens: 5 });
190
- await expect(() => model.call([new HumanMessage("Print hello world")], {
176
+ const model = new ChatOpenAI({ maxTokens: 5, maxRetries: 0 });
177
+ await expect(() => model.invoke([new HumanMessage("Print hello world")], {
191
178
  options: { timeout: 10 },
192
179
  })).rejects.toThrow();
193
180
  }, 5000);
194
181
  test("Test OpenAI with timeout in call options and node adapter", async () => {
195
- const model = new ChatOpenAI({ maxTokens: 5 });
196
- await expect(() => model.call([new HumanMessage("Print hello world")], {
182
+ const model = new ChatOpenAI({ maxTokens: 5, maxRetries: 0 });
183
+ await expect(() => model.invoke([new HumanMessage("Print hello world")], {
197
184
  options: { timeout: 10 },
198
185
  })).rejects.toThrow();
199
186
  }, 5000);
@@ -201,7 +188,7 @@ test("Test OpenAI with signal in call options", async () => {
201
188
  const model = new ChatOpenAI({ maxTokens: 5 });
202
189
  const controller = new AbortController();
203
190
  await expect(() => {
204
- const ret = model.call([new HumanMessage("Print hello world")], {
191
+ const ret = model.invoke([new HumanMessage("Print hello world")], {
205
192
  options: { signal: controller.signal },
206
193
  });
207
194
  controller.abort();
@@ -215,7 +202,7 @@ test("Test OpenAI with signal in call options and node adapter", async () => {
215
202
  });
216
203
  const controller = new AbortController();
217
204
  await expect(() => {
218
- const ret = model.call([new HumanMessage("Print hello world")], {
205
+ const ret = model.invoke([new HumanMessage("Print hello world")], {
219
206
  options: { signal: controller.signal },
220
207
  });
221
208
  controller.abort();
@@ -260,7 +247,7 @@ test("Test OpenAI with specific roles in ChatMessage", async () => {
260
247
  const chat = new ChatOpenAI({ modelName: "gpt-3.5-turbo", maxTokens: 10 });
261
248
  const system_message = new ChatMessage("You are to chat with a user.", "system");
262
249
  const user_message = new ChatMessage("Hello!", "user");
263
- const res = await chat.call([system_message, user_message]);
250
+ const res = await chat.invoke([system_message, user_message]);
264
251
  console.log({ res });
265
252
  });
266
253
  test("Test ChatOpenAI stream method", async () => {
@@ -305,6 +292,7 @@ test("Test ChatOpenAI stream method, timeout error thrown from SDK", async () =>
305
292
  maxTokens: 50,
306
293
  modelName: "gpt-3.5-turbo",
307
294
  timeout: 1,
295
+ maxRetries: 0,
308
296
  });
309
297
  const stream = await model.stream("How is your day going? Be extremely verbose.");
310
298
  for await (const chunk of stream) {
@@ -630,169 +618,3 @@ test("Test ChatOpenAI token usage reporting for streaming calls", async () => {
630
618
  expect(streamingTokenUsed).toEqual(nonStreamingTokenUsed);
631
619
  }
632
620
  });
633
- describe("ChatOpenAI withStructuredOutput", () => {
634
- test("withStructuredOutput zod schema function calling", async () => {
635
- const model = new ChatOpenAI({
636
- temperature: 0,
637
- modelName: "gpt-4-turbo-preview",
638
- });
639
- const calculatorSchema = z.object({
640
- operation: z.enum(["add", "subtract", "multiply", "divide"]),
641
- number1: z.number(),
642
- number2: z.number(),
643
- });
644
- const modelWithStructuredOutput = model.withStructuredOutput(calculatorSchema, {
645
- name: "calculator",
646
- });
647
- const prompt = ChatPromptTemplate.fromMessages([
648
- "system",
649
- "You are VERY bad at math and must always use a calculator.",
650
- "human",
651
- "Please help me!! What is 2 + 2?",
652
- ]);
653
- const chain = prompt.pipe(modelWithStructuredOutput);
654
- const result = await chain.invoke({});
655
- console.log(result);
656
- expect("operation" in result).toBe(true);
657
- expect("number1" in result).toBe(true);
658
- expect("number2" in result).toBe(true);
659
- });
660
- test("withStructuredOutput zod schema JSON mode", async () => {
661
- const model = new ChatOpenAI({
662
- temperature: 0,
663
- modelName: "gpt-4-turbo-preview",
664
- });
665
- const calculatorSchema = z.object({
666
- operation: z.enum(["add", "subtract", "multiply", "divide"]),
667
- number1: z.number(),
668
- number2: z.number(),
669
- });
670
- const modelWithStructuredOutput = model.withStructuredOutput(calculatorSchema, {
671
- name: "calculator",
672
- method: "jsonMode",
673
- });
674
- const prompt = ChatPromptTemplate.fromMessages([
675
- "system",
676
- `You are VERY bad at math and must always use a calculator.
677
- Respond with a JSON object containing three keys:
678
- 'operation': the type of operation to execute, either 'add', 'subtract', 'multiply' or 'divide',
679
- 'number1': the first number to operate on,
680
- 'number2': the second number to operate on.
681
- `,
682
- "human",
683
- "Please help me!! What is 2 + 2?",
684
- ]);
685
- const chain = prompt.pipe(modelWithStructuredOutput);
686
- const result = await chain.invoke({});
687
- console.log(result);
688
- expect("operation" in result).toBe(true);
689
- expect("number1" in result).toBe(true);
690
- expect("number2" in result).toBe(true);
691
- });
692
- test("withStructuredOutput JSON schema function calling", async () => {
693
- const model = new ChatOpenAI({
694
- temperature: 0,
695
- modelName: "gpt-4-turbo-preview",
696
- });
697
- const calculatorSchema = z.object({
698
- operation: z.enum(["add", "subtract", "multiply", "divide"]),
699
- number1: z.number(),
700
- number2: z.number(),
701
- });
702
- const modelWithStructuredOutput = model.withStructuredOutput({
703
- schema: zodToJsonSchema(calculatorSchema),
704
- name: "calculator",
705
- });
706
- const prompt = ChatPromptTemplate.fromMessages([
707
- "system",
708
- `You are VERY bad at math and must always use a calculator.`,
709
- "human",
710
- "Please help me!! What is 2 + 2?",
711
- ]);
712
- const chain = prompt.pipe(modelWithStructuredOutput);
713
- const result = await chain.invoke({});
714
- console.log(result);
715
- expect("operation" in result).toBe(true);
716
- expect("number1" in result).toBe(true);
717
- expect("number2" in result).toBe(true);
718
- });
719
- test("withStructuredOutput JSON schema JSON mode", async () => {
720
- const model = new ChatOpenAI({
721
- temperature: 0,
722
- modelName: "gpt-4-turbo-preview",
723
- });
724
- const calculatorSchema = z.object({
725
- operation: z.enum(["add", "subtract", "multiply", "divide"]),
726
- number1: z.number(),
727
- number2: z.number(),
728
- });
729
- const modelWithStructuredOutput = model.withStructuredOutput(zodToJsonSchema(calculatorSchema), {
730
- name: "calculator",
731
- method: "jsonMode",
732
- });
733
- const prompt = ChatPromptTemplate.fromMessages([
734
- "system",
735
- `You are VERY bad at math and must always use a calculator.
736
- Respond with a JSON object containing three keys:
737
- 'operation': the type of operation to execute, either 'add', 'subtract', 'multiply' or 'divide',
738
- 'number1': the first number to operate on,
739
- 'number2': the second number to operate on.
740
- `,
741
- "human",
742
- "Please help me!! What is 2 + 2?",
743
- ]);
744
- const chain = prompt.pipe(modelWithStructuredOutput);
745
- const result = await chain.invoke({});
746
- console.log(result);
747
- expect("operation" in result).toBe(true);
748
- expect("number1" in result).toBe(true);
749
- expect("number2" in result).toBe(true);
750
- });
751
- test("withStructuredOutput includeRaw true", async () => {
752
- const model = new ChatOpenAI({
753
- temperature: 0,
754
- modelName: "gpt-4-turbo-preview",
755
- });
756
- const calculatorSchema = z.object({
757
- operation: z.enum(["add", "subtract", "multiply", "divide"]),
758
- number1: z.number(),
759
- number2: z.number(),
760
- });
761
- const modelWithStructuredOutput = model.withStructuredOutput(calculatorSchema, {
762
- name: "calculator",
763
- includeRaw: true,
764
- });
765
- const prompt = ChatPromptTemplate.fromMessages([
766
- "system",
767
- "You are VERY bad at math and must always use a calculator.",
768
- "human",
769
- "Please help me!! What is 2 + 2?",
770
- ]);
771
- const chain = prompt.pipe(modelWithStructuredOutput);
772
- const result = await chain.invoke({});
773
- console.log(result);
774
- expect("parsed" in result).toBe(true);
775
- // Need to make TS happy :)
776
- if (!("parsed" in result)) {
777
- throw new Error("parsed not in result");
778
- }
779
- const { parsed } = result;
780
- expect("operation" in parsed).toBe(true);
781
- expect("number1" in parsed).toBe(true);
782
- expect("number2" in parsed).toBe(true);
783
- expect("raw" in result).toBe(true);
784
- // Need to make TS happy :)
785
- if (!("raw" in result)) {
786
- throw new Error("raw not in result");
787
- }
788
- const { raw } = result;
789
- expect(raw.additional_kwargs.tool_calls?.length).toBeGreaterThan(0);
790
- expect(raw.additional_kwargs.tool_calls?.[0].function.name).toBe("calculator");
791
- expect("operation" in
792
- JSON.parse(raw.additional_kwargs.tool_calls?.[0].function.arguments ?? "")).toBe(true);
793
- expect("number1" in
794
- JSON.parse(raw.additional_kwargs.tool_calls?.[0].function.arguments ?? "")).toBe(true);
795
- expect("number2" in
796
- JSON.parse(raw.additional_kwargs.tool_calls?.[0].function.arguments ?? "")).toBe(true);
797
- });
798
- });
@@ -0,0 +1,168 @@
1
+ import { z } from "zod";
2
+ import { zodToJsonSchema } from "zod-to-json-schema";
3
+ import { ChatPromptTemplate } from "@langchain/core/prompts";
4
+ import { ChatOpenAI } from "../chat_models.js";
5
+ test("withStructuredOutput zod schema function calling", async () => {
6
+ const model = new ChatOpenAI({
7
+ temperature: 0,
8
+ modelName: "gpt-4-turbo-preview",
9
+ });
10
+ const calculatorSchema = z.object({
11
+ operation: z.enum(["add", "subtract", "multiply", "divide"]),
12
+ number1: z.number(),
13
+ number2: z.number(),
14
+ });
15
+ const modelWithStructuredOutput = model.withStructuredOutput(calculatorSchema, {
16
+ name: "calculator",
17
+ });
18
+ const prompt = ChatPromptTemplate.fromMessages([
19
+ "system",
20
+ "You are VERY bad at math and must always use a calculator.",
21
+ "human",
22
+ "Please help me!! What is 2 + 2?",
23
+ ]);
24
+ const chain = prompt.pipe(modelWithStructuredOutput);
25
+ const result = await chain.invoke({});
26
+ console.log(result);
27
+ expect("operation" in result).toBe(true);
28
+ expect("number1" in result).toBe(true);
29
+ expect("number2" in result).toBe(true);
30
+ });
31
+ test("withStructuredOutput zod schema JSON mode", async () => {
32
+ const model = new ChatOpenAI({
33
+ temperature: 0,
34
+ modelName: "gpt-4-turbo-preview",
35
+ });
36
+ const calculatorSchema = z.object({
37
+ operation: z.enum(["add", "subtract", "multiply", "divide"]),
38
+ number1: z.number(),
39
+ number2: z.number(),
40
+ });
41
+ const modelWithStructuredOutput = model.withStructuredOutput(calculatorSchema, {
42
+ name: "calculator",
43
+ method: "jsonMode",
44
+ });
45
+ const prompt = ChatPromptTemplate.fromMessages([
46
+ "system",
47
+ `You are VERY bad at math and must always use a calculator.
48
+ Respond with a JSON object containing three keys:
49
+ 'operation': the type of operation to execute, either 'add', 'subtract', 'multiply' or 'divide',
50
+ 'number1': the first number to operate on,
51
+ 'number2': the second number to operate on.
52
+ `,
53
+ "human",
54
+ "Please help me!! What is 2 + 2?",
55
+ ]);
56
+ const chain = prompt.pipe(modelWithStructuredOutput);
57
+ const result = await chain.invoke({});
58
+ console.log(result);
59
+ expect("operation" in result).toBe(true);
60
+ expect("number1" in result).toBe(true);
61
+ expect("number2" in result).toBe(true);
62
+ });
63
+ test("withStructuredOutput JSON schema function calling", async () => {
64
+ const model = new ChatOpenAI({
65
+ temperature: 0,
66
+ modelName: "gpt-4-turbo-preview",
67
+ });
68
+ const calculatorSchema = z.object({
69
+ operation: z.enum(["add", "subtract", "multiply", "divide"]),
70
+ number1: z.number(),
71
+ number2: z.number(),
72
+ });
73
+ const modelWithStructuredOutput = model.withStructuredOutput({
74
+ schema: zodToJsonSchema(calculatorSchema),
75
+ name: "calculator",
76
+ });
77
+ const prompt = ChatPromptTemplate.fromMessages([
78
+ "system",
79
+ `You are VERY bad at math and must always use a calculator.`,
80
+ "human",
81
+ "Please help me!! What is 2 + 2?",
82
+ ]);
83
+ const chain = prompt.pipe(modelWithStructuredOutput);
84
+ const result = await chain.invoke({});
85
+ console.log(result);
86
+ expect("operation" in result).toBe(true);
87
+ expect("number1" in result).toBe(true);
88
+ expect("number2" in result).toBe(true);
89
+ });
90
+ test("withStructuredOutput JSON schema JSON mode", async () => {
91
+ const model = new ChatOpenAI({
92
+ temperature: 0,
93
+ modelName: "gpt-4-turbo-preview",
94
+ });
95
+ const calculatorSchema = z.object({
96
+ operation: z.enum(["add", "subtract", "multiply", "divide"]),
97
+ number1: z.number(),
98
+ number2: z.number(),
99
+ });
100
+ const modelWithStructuredOutput = model.withStructuredOutput(zodToJsonSchema(calculatorSchema), {
101
+ name: "calculator",
102
+ method: "jsonMode",
103
+ });
104
+ const prompt = ChatPromptTemplate.fromMessages([
105
+ "system",
106
+ `You are VERY bad at math and must always use a calculator.
107
+ Respond with a JSON object containing three keys:
108
+ 'operation': the type of operation to execute, either 'add', 'subtract', 'multiply' or 'divide',
109
+ 'number1': the first number to operate on,
110
+ 'number2': the second number to operate on.
111
+ `,
112
+ "human",
113
+ "Please help me!! What is 2 + 2?",
114
+ ]);
115
+ const chain = prompt.pipe(modelWithStructuredOutput);
116
+ const result = await chain.invoke({});
117
+ console.log(result);
118
+ expect("operation" in result).toBe(true);
119
+ expect("number1" in result).toBe(true);
120
+ expect("number2" in result).toBe(true);
121
+ });
122
+ test("withStructuredOutput includeRaw true", async () => {
123
+ const model = new ChatOpenAI({
124
+ temperature: 0,
125
+ modelName: "gpt-4-turbo-preview",
126
+ });
127
+ const calculatorSchema = z.object({
128
+ operation: z.enum(["add", "subtract", "multiply", "divide"]),
129
+ number1: z.number(),
130
+ number2: z.number(),
131
+ });
132
+ const modelWithStructuredOutput = model.withStructuredOutput(calculatorSchema, {
133
+ name: "calculator",
134
+ includeRaw: true,
135
+ });
136
+ const prompt = ChatPromptTemplate.fromMessages([
137
+ "system",
138
+ "You are VERY bad at math and must always use a calculator.",
139
+ "human",
140
+ "Please help me!! What is 2 + 2?",
141
+ ]);
142
+ const chain = prompt.pipe(modelWithStructuredOutput);
143
+ const result = await chain.invoke({});
144
+ console.log(result);
145
+ expect("parsed" in result).toBe(true);
146
+ // Need to make TS happy :)
147
+ if (!("parsed" in result)) {
148
+ throw new Error("parsed not in result");
149
+ }
150
+ const { parsed } = result;
151
+ expect("operation" in parsed).toBe(true);
152
+ expect("number1" in parsed).toBe(true);
153
+ expect("number2" in parsed).toBe(true);
154
+ expect("raw" in result).toBe(true);
155
+ // Need to make TS happy :)
156
+ if (!("raw" in result)) {
157
+ throw new Error("raw not in result");
158
+ }
159
+ const { raw } = result;
160
+ expect(raw.additional_kwargs.tool_calls?.length).toBeGreaterThan(0);
161
+ expect(raw.additional_kwargs.tool_calls?.[0].function.name).toBe("calculator");
162
+ expect("operation" in
163
+ JSON.parse(raw.additional_kwargs.tool_calls?.[0].function.arguments ?? "")).toBe(true);
164
+ expect("number1" in
165
+ JSON.parse(raw.additional_kwargs.tool_calls?.[0].function.arguments ?? "")).toBe(true);
166
+ expect("number2" in
167
+ JSON.parse(raw.additional_kwargs.tool_calls?.[0].function.arguments ?? "")).toBe(true);
168
+ });
@@ -32,6 +32,7 @@ test("Test timeout error thrown from SDK", async () => {
32
32
  await expect(async () => {
33
33
  const model = new OpenAIEmbeddings({
34
34
  timeout: 1,
35
+ maxRetries: 0,
35
36
  });
36
37
  await model.embedDocuments([
37
38
  "Hello world",
@@ -30,6 +30,7 @@ test("Test OpenAI with stop in object", async () => {
30
30
  test("Test OpenAI with timeout in call options", async () => {
31
31
  const model = new OpenAI({
32
32
  maxTokens: 5,
33
+ maxRetries: 0,
33
34
  modelName: "gpt-3.5-turbo-instruct",
34
35
  });
35
36
  await expect(() => model.call("Print hello world", {
@@ -39,6 +40,7 @@ test("Test OpenAI with timeout in call options", async () => {
39
40
  test("Test OpenAI with timeout in call options and node adapter", async () => {
40
41
  const model = new OpenAI({
41
42
  maxTokens: 5,
43
+ maxRetries: 0,
42
44
  modelName: "gpt-3.5-turbo-instruct",
43
45
  });
44
46
  await expect(() => model.call("Print hello world", {
@@ -230,6 +232,7 @@ test("Test OpenAI stream method with abort", async () => {
230
232
  await expect(async () => {
231
233
  const model = new OpenAI({
232
234
  maxTokens: 250,
235
+ maxRetries: 0,
233
236
  modelName: "gpt-3.5-turbo-instruct",
234
237
  });
235
238
  const stream = await model.stream("How is your day going? Be extremely verbose.", {
@@ -0,0 +1,124 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DallEAPIWrapper = void 0;
4
+ const env_1 = require("@langchain/core/utils/env");
5
+ const openai_1 = require("openai");
6
+ const tools_1 = require("@langchain/core/tools");
7
+ /**
8
+ * A tool for generating images with Open AIs Dall-E 2 or 3 API.
9
+ */
10
+ class DallEAPIWrapper extends tools_1.Tool {
11
+ static lc_name() {
12
+ return "DallEAPIWrapper";
13
+ }
14
+ constructor(fields) {
15
+ super(fields);
16
+ Object.defineProperty(this, "name", {
17
+ enumerable: true,
18
+ configurable: true,
19
+ writable: true,
20
+ value: "dalle_api_wrapper"
21
+ });
22
+ Object.defineProperty(this, "description", {
23
+ enumerable: true,
24
+ configurable: true,
25
+ writable: true,
26
+ value: "A wrapper around OpenAI DALL-E API. Useful for when you need to generate images from a text description. Input should be an image description."
27
+ });
28
+ Object.defineProperty(this, "client", {
29
+ enumerable: true,
30
+ configurable: true,
31
+ writable: true,
32
+ value: void 0
33
+ });
34
+ Object.defineProperty(this, "modelName", {
35
+ enumerable: true,
36
+ configurable: true,
37
+ writable: true,
38
+ value: "dall-e-3"
39
+ });
40
+ Object.defineProperty(this, "style", {
41
+ enumerable: true,
42
+ configurable: true,
43
+ writable: true,
44
+ value: "vivid"
45
+ });
46
+ Object.defineProperty(this, "quality", {
47
+ enumerable: true,
48
+ configurable: true,
49
+ writable: true,
50
+ value: "standard"
51
+ });
52
+ Object.defineProperty(this, "n", {
53
+ enumerable: true,
54
+ configurable: true,
55
+ writable: true,
56
+ value: 1
57
+ });
58
+ Object.defineProperty(this, "size", {
59
+ enumerable: true,
60
+ configurable: true,
61
+ writable: true,
62
+ value: "1024x1024"
63
+ });
64
+ Object.defineProperty(this, "responseFormat", {
65
+ enumerable: true,
66
+ configurable: true,
67
+ writable: true,
68
+ value: "url"
69
+ });
70
+ Object.defineProperty(this, "user", {
71
+ enumerable: true,
72
+ configurable: true,
73
+ writable: true,
74
+ value: void 0
75
+ });
76
+ const openAIApiKey = fields?.openAIApiKey ?? (0, env_1.getEnvironmentVariable)("OPENAI_API_KEY");
77
+ const organization = fields?.organization ?? (0, env_1.getEnvironmentVariable)("OPENAI_ORGANIZATION");
78
+ const clientConfig = {
79
+ apiKey: openAIApiKey,
80
+ organization,
81
+ dangerouslyAllowBrowser: true,
82
+ };
83
+ this.client = new openai_1.OpenAI(clientConfig);
84
+ this.modelName = fields?.modelName ?? this.modelName;
85
+ this.style = fields?.style ?? this.style;
86
+ this.quality = fields?.quality ?? this.quality;
87
+ this.n = fields?.n ?? this.n;
88
+ this.size = fields?.size ?? this.size;
89
+ this.responseFormat = fields?.responseFormat ?? this.responseFormat;
90
+ this.user = fields?.user;
91
+ }
92
+ /** @ignore */
93
+ async _call(input) {
94
+ const response = await this.client.images.generate({
95
+ model: this.modelName,
96
+ prompt: input,
97
+ n: this.n,
98
+ size: this.size,
99
+ response_format: this.responseFormat,
100
+ style: this.style,
101
+ quality: this.quality,
102
+ user: this.user,
103
+ });
104
+ let data = "";
105
+ if (this.responseFormat === "url") {
106
+ [data] = response.data
107
+ .map((item) => item.url)
108
+ .filter((url) => url !== "undefined");
109
+ }
110
+ else {
111
+ [data] = response.data
112
+ .map((item) => item.b64_json)
113
+ .filter((b64_json) => b64_json !== "undefined");
114
+ }
115
+ return data;
116
+ }
117
+ }
118
+ exports.DallEAPIWrapper = DallEAPIWrapper;
119
+ Object.defineProperty(DallEAPIWrapper, "toolName", {
120
+ enumerable: true,
121
+ configurable: true,
122
+ writable: true,
123
+ value: "dalle_api_wrapper"
124
+ });
@@ -0,0 +1,79 @@
1
+ import { OpenAI as OpenAIClient } from "openai";
2
+ import { Tool, ToolParams } from "@langchain/core/tools";
3
+ /**
4
+ * An interface for the Dall-E API Wrapper.
5
+ */
6
+ export interface DallEAPIWrapperParams extends ToolParams {
7
+ /**
8
+ * The OpenAI API key
9
+ */
10
+ openAIApiKey?: string;
11
+ /**
12
+ * The model to use.
13
+ * @params "dall-e-2" | "dall-e-3"
14
+ * @default "dall-e-3"
15
+ */
16
+ modelName?: string;
17
+ /**
18
+ * The style of the generated images. Must be one of vivid or natural.
19
+ * Vivid causes the model to lean towards generating hyper-real and dramatic images.
20
+ * Natural causes the model to produce more natural, less hyper-real looking images.
21
+ * @default "vivid"
22
+ */
23
+ style?: "natural" | "vivid";
24
+ /**
25
+ * The quality of the image that will be generated. ‘hd’ creates images with finer
26
+ * details and greater consistency across the image.
27
+ * @default "standard"
28
+ */
29
+ quality?: "standard" | "hd";
30
+ /**
31
+ * The number of images to generate.
32
+ * Must be between 1 and 10.
33
+ * For dall-e-3, only `n: 1` is supported.
34
+ * @default 1
35
+ */
36
+ n?: number;
37
+ /**
38
+ * The size of the generated images.
39
+ * Must be one of 256x256, 512x512, or 1024x1024 for DALL·E-2 models.
40
+ * Must be one of 1024x1024, 1792x1024, or 1024x1792 for DALL·E-3 models.
41
+ * @default "1024x1024"
42
+ */
43
+ size?: "256x256" | "512x512" | "1024x1024" | "1792x1024" | "1024x1792";
44
+ /**
45
+ * The format in which the generated images are returned.
46
+ * Must be one of "url" or "b64_json".
47
+ * @default "url"
48
+ */
49
+ responseFormat?: "url" | "b64_json";
50
+ /**
51
+ * A unique identifier representing your end-user, which will help
52
+ * OpenAI to monitor and detect abuse.
53
+ */
54
+ user?: string;
55
+ /**
56
+ * The organization to use
57
+ */
58
+ organization?: string;
59
+ }
60
+ /**
61
+ * A tool for generating images with Open AIs Dall-E 2 or 3 API.
62
+ */
63
+ export declare class DallEAPIWrapper extends Tool {
64
+ static lc_name(): string;
65
+ name: string;
66
+ description: string;
67
+ protected client: OpenAIClient;
68
+ static readonly toolName = "dalle_api_wrapper";
69
+ private modelName;
70
+ private style;
71
+ private quality;
72
+ private n;
73
+ private size;
74
+ private responseFormat;
75
+ private user?;
76
+ constructor(fields?: DallEAPIWrapperParams);
77
+ /** @ignore */
78
+ _call(input: string): Promise<string>;
79
+ }
@@ -0,0 +1,120 @@
1
+ import { getEnvironmentVariable } from "@langchain/core/utils/env";
2
+ import { OpenAI as OpenAIClient } from "openai";
3
+ import { Tool } from "@langchain/core/tools";
4
+ /**
5
+ * A tool for generating images with Open AIs Dall-E 2 or 3 API.
6
+ */
7
+ export class DallEAPIWrapper extends Tool {
8
+ static lc_name() {
9
+ return "DallEAPIWrapper";
10
+ }
11
+ constructor(fields) {
12
+ super(fields);
13
+ Object.defineProperty(this, "name", {
14
+ enumerable: true,
15
+ configurable: true,
16
+ writable: true,
17
+ value: "dalle_api_wrapper"
18
+ });
19
+ Object.defineProperty(this, "description", {
20
+ enumerable: true,
21
+ configurable: true,
22
+ writable: true,
23
+ value: "A wrapper around OpenAI DALL-E API. Useful for when you need to generate images from a text description. Input should be an image description."
24
+ });
25
+ Object.defineProperty(this, "client", {
26
+ enumerable: true,
27
+ configurable: true,
28
+ writable: true,
29
+ value: void 0
30
+ });
31
+ Object.defineProperty(this, "modelName", {
32
+ enumerable: true,
33
+ configurable: true,
34
+ writable: true,
35
+ value: "dall-e-3"
36
+ });
37
+ Object.defineProperty(this, "style", {
38
+ enumerable: true,
39
+ configurable: true,
40
+ writable: true,
41
+ value: "vivid"
42
+ });
43
+ Object.defineProperty(this, "quality", {
44
+ enumerable: true,
45
+ configurable: true,
46
+ writable: true,
47
+ value: "standard"
48
+ });
49
+ Object.defineProperty(this, "n", {
50
+ enumerable: true,
51
+ configurable: true,
52
+ writable: true,
53
+ value: 1
54
+ });
55
+ Object.defineProperty(this, "size", {
56
+ enumerable: true,
57
+ configurable: true,
58
+ writable: true,
59
+ value: "1024x1024"
60
+ });
61
+ Object.defineProperty(this, "responseFormat", {
62
+ enumerable: true,
63
+ configurable: true,
64
+ writable: true,
65
+ value: "url"
66
+ });
67
+ Object.defineProperty(this, "user", {
68
+ enumerable: true,
69
+ configurable: true,
70
+ writable: true,
71
+ value: void 0
72
+ });
73
+ const openAIApiKey = fields?.openAIApiKey ?? getEnvironmentVariable("OPENAI_API_KEY");
74
+ const organization = fields?.organization ?? getEnvironmentVariable("OPENAI_ORGANIZATION");
75
+ const clientConfig = {
76
+ apiKey: openAIApiKey,
77
+ organization,
78
+ dangerouslyAllowBrowser: true,
79
+ };
80
+ this.client = new OpenAIClient(clientConfig);
81
+ this.modelName = fields?.modelName ?? this.modelName;
82
+ this.style = fields?.style ?? this.style;
83
+ this.quality = fields?.quality ?? this.quality;
84
+ this.n = fields?.n ?? this.n;
85
+ this.size = fields?.size ?? this.size;
86
+ this.responseFormat = fields?.responseFormat ?? this.responseFormat;
87
+ this.user = fields?.user;
88
+ }
89
+ /** @ignore */
90
+ async _call(input) {
91
+ const response = await this.client.images.generate({
92
+ model: this.modelName,
93
+ prompt: input,
94
+ n: this.n,
95
+ size: this.size,
96
+ response_format: this.responseFormat,
97
+ style: this.style,
98
+ quality: this.quality,
99
+ user: this.user,
100
+ });
101
+ let data = "";
102
+ if (this.responseFormat === "url") {
103
+ [data] = response.data
104
+ .map((item) => item.url)
105
+ .filter((url) => url !== "undefined");
106
+ }
107
+ else {
108
+ [data] = response.data
109
+ .map((item) => item.b64_json)
110
+ .filter((b64_json) => b64_json !== "undefined");
111
+ }
112
+ return data;
113
+ }
114
+ }
115
+ Object.defineProperty(DallEAPIWrapper, "toolName", {
116
+ enumerable: true,
117
+ configurable: true,
118
+ writable: true,
119
+ value: "dalle_api_wrapper"
120
+ });
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./dalle.cjs"), exports);
@@ -0,0 +1 @@
1
+ export * from "./dalle.js";
@@ -0,0 +1 @@
1
+ export * from "./dalle.js";
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,16 @@
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
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@langchain/openai",
3
- "version": "0.0.18",
3
+ "version": "0.0.19",
4
4
  "description": "OpenAI integrations for LangChain.js",
5
5
  "type": "module",
6
6
  "engines": {
@@ -39,7 +39,7 @@
39
39
  "author": "LangChain",
40
40
  "license": "MIT",
41
41
  "dependencies": {
42
- "@langchain/core": "~0.1.41",
42
+ "@langchain/core": "~0.1.44",
43
43
  "js-tiktoken": "^1.0.7",
44
44
  "openai": "^4.26.0",
45
45
  "zod": "^3.22.4",