Skip to content

Commit

Permalink
Make default withStructuredOutput method for OpenAI json_schema
Browse files Browse the repository at this point in the history
  • Loading branch information
jacoblee93 committed Dec 28, 2024
1 parent 4549863 commit f6d5056
Show file tree
Hide file tree
Showing 8 changed files with 79 additions and 52 deletions.
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 { test, expect, describe, it } from "@jest/globals";
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 @@ test("withStructuredOutput with o1", async () => {
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 @@ test("withStructuredOutput zod schema streaming", async () => {
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 streaming", async () => {
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 @@ Respond with a JSON object containing three keys:
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 @@ test("withStructuredOutput JSON schema function calling", async () => {
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 OpenAI function definition function calling", async (
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 @@ Respond with a JSON object containing three keys:
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 @@ test("withStructuredOutput JSON schema", async () => {
number1: { type: "number" },
number2: { type: "number" },
},
required: ["operation", "number1", "number2"],
additionalProperties: false,
};
const modelWithStructuredOutput = model.withStructuredOutput(jsonSchema);

Expand All @@ -278,7 +315,7 @@ Respond with a JSON object containing three keys:
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 @@ test("withStructuredOutput includeRaw true", async () => {
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

0 comments on commit f6d5056

Please sign in to comment.