Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(openai): Make default withStructuredOutput method for OpenAI json_schema #7439

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/core_docs/docs/how_to/graph_constructing.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@
"\n",
"const model = new ChatOpenAI({\n",
" temperature: 0,\n",
" model: \"gpt-4-turbo-preview\",\n",
" model: \"gpt-4o-mini\",\n",
"});\n",
"\n",
"const llmGraphTransformer = new LLMGraphTransformer({\n",
Expand Down
4 changes: 2 additions & 2 deletions docs/core_docs/docs/how_to/query_high_cardinality.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -392,7 +392,7 @@
"metadata": {},
"source": [
"```{=mdx}\n",
"<ChatModelTabs customVarName=\"llmLong\" openaiParams={`{ model: \"gpt-4-turbo-preview\" }`} />\n",
"<ChatModelTabs customVarName=\"llmLong\" openaiParams={`{ model: \"gpt-4o-mini\" }`} />\n",
"```"
]
},
Expand Down Expand Up @@ -635,4 +635,4 @@
},
"nbformat": 4,
"nbformat_minor": 5
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { ChatOpenAI } from "@langchain/openai";

const model = new ChatOpenAI({
temperature: 0,
model: "gpt-4-turbo-preview",
model: "gpt-4o-mini",
});

const calculatorSchema = {
Expand Down
2 changes: 1 addition & 1 deletion examples/src/models/chat/integration_openai_wsa_zod.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { z } from "zod";

const model = new ChatOpenAI({
temperature: 0,
model: "gpt-4-turbo-preview",
model: "gpt-4o-mini",
});

const calculatorSchema = z.object({
Expand Down
2 changes: 1 addition & 1 deletion examples/src/tools/duckduckgo_search_agent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ const prompt = await pull<ChatPromptTemplate>(
"hwchase17/openai-functions-agent"
);
const llm = new ChatOpenAI({
model: "gpt-4-turbo-preview",
model: "gpt-4o-mini",
temperature: 0,
});
const agent = await createOpenAIFunctionsAgent({
Expand Down
38 changes: 20 additions & 18 deletions libs/langchain-openai/src/chat_models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2035,24 +2035,7 @@ export class ChatOpenAI<
} else {
outputParser = new JsonOutputParser<RunOutput>();
}
} else if (method === "jsonSchema") {
llm = this.bind({
response_format: {
type: "json_schema",
json_schema: {
name: name ?? "extract",
description: schema.description,
schema,
strict: config?.strict,
},
},
} as Partial<CallOptions>);
if (isZodSchema(schema)) {
outputParser = StructuredOutputParser.fromZodSchema(schema);
} else {
outputParser = new JsonOutputParser<RunOutput>();
}
} else {
} else if (method === "tool_calling") {
let functionName = name ?? "extract";
// Is function calling
if (isZodSchema(schema)) {
Expand Down Expand Up @@ -2120,6 +2103,25 @@ export class ChatOpenAI<
keyName: functionName,
});
}
} else {
let finalSchema = schema;
if (!isZodSchema(schema)) {
if (schema.parameters !== undefined) {
finalSchema = schema.parameters;
}
}
llm = this.bind({
response_format: {
type: "json_schema",
json_schema: {
name: name ?? "extract",
description: schema.description,
schema: finalSchema,
strict: config?.strict ?? true,
},
},
} as Partial<CallOptions>);
outputParser = new JsonOutputParser<RunOutput>();
}

if (!includeRaw) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ class ChatOpenAIStandardIntegrationTests extends ChatModelIntegrationTests<
chatModelHasStructuredOutput: true,
supportsParallelToolCalls: true,
constructorArgs: {
model: "gpt-3.5-turbo",
model: "gpt-4o-mini",
},
});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@
import { concat } from "@langchain/core/utils/stream";
import { ChatOpenAI } from "../chat_models.js";

test("withStructuredOutput zod schema function calling", async () => {
test("withStructuredOutput with zod schema", async () => {
const model = new ChatOpenAI({
temperature: 0,
modelName: "gpt-4-turbo-preview",
modelName: "gpt-4o-mini",
});

const calculatorSchema = z.object({
Expand Down Expand Up @@ -65,10 +65,44 @@
expect("number2" in result).toBe(true);
});

test.only("withStructuredOutput with optional properties", async () => {

Check failure on line 68 in libs/langchain-openai/src/tests/chat_models_structured_output.int.test.ts

View workflow job for this annotation

GitHub Actions / Check linting

Unexpected focused test
const model = new ChatOpenAI({
model: "o1",
});

const calculatorSchema = z.object({
operation: z.enum(["add", "subtract", "multiply", "divide"]),
number1: z.number(),
number2: z.number(),
number3: z.nullable(z.number()),
});
const modelWithStructuredOutput = model.withStructuredOutput(
calculatorSchema,
{
name: "calculator",
}
);

const prompt = ChatPromptTemplate.fromMessages([
[
"developer",
"You are VERY bad at math and must always use a calculator. Do not supply any additional numbers.",
],
["human", "Please help me!! What is 2 + 2?"],
]);
const chain = prompt.pipe(modelWithStructuredOutput);
const result = await chain.invoke({});
console.log(result);
expect("operation" in result).toBe(true);
expect("number1" in result).toBe(true);
expect("number2" in result).toBe(true);
expect(result.number3).toBe(null);
});

test("withStructuredOutput zod schema streaming", async () => {
const model = new ChatOpenAI({
temperature: 0,
modelName: "gpt-4-turbo-preview",
modelName: "gpt-4o-mini",
});

const calculatorSchema = z.object({
Expand All @@ -91,6 +125,7 @@
const stream = await chain.stream({});
const chunks = [];
for await (const chunk of stream) {
console.log(chunk);
chunks.push(chunk);
}
expect(chunks.length).toBeGreaterThan(1);
Expand All @@ -103,7 +138,7 @@
test("withStructuredOutput zod schema JSON mode", async () => {
const model = new ChatOpenAI({
temperature: 0,
modelName: "gpt-4-turbo-preview",
modelName: "gpt-4o-mini",
});

const calculatorSchema = z.object({
Expand Down Expand Up @@ -139,10 +174,10 @@
expect("number2" in result).toBe(true);
});

test("withStructuredOutput JSON schema function calling", async () => {
test("withStructuredOutput with JSON schema", async () => {
const model = new ChatOpenAI({
temperature: 0,
modelName: "gpt-4-turbo-preview",
modelName: "gpt-4o-mini",
});

const calculatorSchema = z.object({
Expand All @@ -167,10 +202,10 @@
expect("number2" in result).toBe(true);
});

test("withStructuredOutput OpenAI function definition function calling", async () => {
test("withStructuredOutput OpenAI with function definition", async () => {
const model = new ChatOpenAI({
temperature: 0,
modelName: "gpt-4-turbo-preview",
modelName: "gpt-4o-mini",
});

const calculatorSchema = z.object({
Expand Down Expand Up @@ -198,7 +233,7 @@
test("withStructuredOutput JSON schema JSON mode", async () => {
const model = new ChatOpenAI({
temperature: 0,
modelName: "gpt-4-turbo-preview",
modelName: "gpt-4o-mini",
});

const calculatorSchema = z.object({
Expand Down Expand Up @@ -237,7 +272,7 @@
test("withStructuredOutput JSON schema", async () => {
const model = new ChatOpenAI({
temperature: 0,
modelName: "gpt-4-turbo-preview",
modelName: "gpt-4o-mini",
});

const jsonSchema = {
Expand All @@ -252,6 +287,8 @@
number1: { type: "number" },
number2: { type: "number" },
},
required: ["operation", "number1", "number2"],
additionalProperties: false,
};
const modelWithStructuredOutput = model.withStructuredOutput(jsonSchema);

Expand All @@ -278,7 +315,7 @@
test("withStructuredOutput includeRaw true", async () => {
const model = new ChatOpenAI({
temperature: 0,
modelName: "gpt-4-turbo-preview",
modelName: "gpt-4o-mini",
});

const calculatorSchema = z.object({
Expand Down Expand Up @@ -318,22 +355,10 @@
throw new Error("raw not in result");
}
const { raw } = result as { raw: AIMessage };
expect(raw.additional_kwargs.tool_calls?.length).toBeGreaterThan(0);
expect(raw.additional_kwargs.tool_calls?.[0].function.name).toBe(
"calculator"
);
expect(
"operation" in
JSON.parse(raw.additional_kwargs.tool_calls?.[0].function.arguments ?? "")
).toBe(true);
expect(
"number1" in
JSON.parse(raw.additional_kwargs.tool_calls?.[0].function.arguments ?? "")
).toBe(true);
expect(
"number2" in
JSON.parse(raw.additional_kwargs.tool_calls?.[0].function.arguments ?? "")
).toBe(true);
console.log(raw);
expect("operation" in JSON.parse(raw.content as string)).toBe(true);
expect("number1" in JSON.parse(raw.content as string)).toBe(true);
expect("number2" in JSON.parse(raw.content as string)).toBe(true);
});

test("parallelToolCalls param", async () => {
Expand Down
Loading