Skip to content

Commit 91a18e7

Browse files
authored
feat: add support for structured output with zod schema. (#1749)
1 parent d1c1f99 commit 91a18e7

File tree

20 files changed

+148
-17
lines changed

20 files changed

+148
-17
lines changed

.changeset/mighty-eagles-wink.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
---
2+
"@llamaindex/huggingface": minor
3+
"@llamaindex/anthropic": minor
4+
"@llamaindex/mistral": minor
5+
"@llamaindex/google": minor
6+
"@llamaindex/ollama": minor
7+
"@llamaindex/openai": minor
8+
"@llamaindex/core": minor
9+
"@llamaindex/examples": minor
10+
---
11+
12+
Added support for structured output in the chat api of openai and ollama
13+
Added structured output parameter in the provider

e2e/fixtures/llm/openai.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ export class OpenAI implements LLM {
4242
contextWindow: 2048,
4343
tokenizer: undefined,
4444
isFunctionCallingModel: true,
45+
structuredOutput: false,
4546
};
4647
}
4748

examples/jsonExtract.ts

Lines changed: 39 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,53 @@
11
import { OpenAI } from "@llamaindex/openai";
2+
import { z } from "zod";
23

34
// Example using OpenAI's chat API to extract JSON from a sales call transcript
45
// using json_mode see https://platform.openai.com/docs/guides/text-generation/json-mode for more details
56

67
const transcript =
78
"[Phone rings]\n\nJohn: Hello, this is John.\n\nSarah: Hi John, this is Sarah from XYZ Company. I'm calling to discuss our new product, the XYZ Widget, and see if it might be a good fit for your business.\n\nJohn: Hi Sarah, thanks for reaching out. I'm definitely interested in learning more about the XYZ Widget. Can you give me a quick overview of what it does?\n\nSarah: Of course! The XYZ Widget is a cutting-edge tool that helps businesses streamline their workflow and improve productivity. It's designed to automate repetitive tasks and provide real-time data analytics to help you make informed decisions.\n\nJohn: That sounds really interesting. I can see how that could benefit our team. Do you have any case studies or success stories from other companies who have used the XYZ Widget?\n\nSarah: Absolutely, we have several case studies that I can share with you. I'll send those over along with some additional information about the product. I'd also love to schedule a demo for you and your team to see the XYZ Widget in action.\n\nJohn: That would be great. I'll make sure to review the case studies and then we can set up a time for the demo. In the meantime, are there any specific action items or next steps we should take?\n\nSarah: Yes, I'll send over the information and then follow up with you to schedule the demo. In the meantime, feel free to reach out if you have any questions or need further information.\n\nJohn: Sounds good, I appreciate your help Sarah. I'm looking forward to learning more about the XYZ Widget and seeing how it can benefit our business.\n\nSarah: Thank you, John. I'll be in touch soon. Have a great day!\n\nJohn: You too, bye.";
89

10+
const exampleSchema = z.object({
11+
summary: z.string(),
12+
products: z.array(z.string()),
13+
rep_name: z.string(),
14+
prospect_name: z.string(),
15+
action_items: z.array(z.string()),
16+
});
17+
18+
const example = {
19+
summary:
20+
"High-level summary of the call transcript. Should not exceed 3 sentences.",
21+
products: ["product 1", "product 2"],
22+
rep_name: "Name of the sales rep",
23+
prospect_name: "Name of the prospect",
24+
action_items: ["action item 1", "action item 2"],
25+
};
26+
927
async function main() {
1028
const llm = new OpenAI({
11-
model: "gpt-4-1106-preview",
12-
additionalChatOptions: { response_format: { type: "json_object" } },
29+
model: "gpt-4o",
1330
});
1431

15-
const example = {
16-
summary:
17-
"High-level summary of the call transcript. Should not exceed 3 sentences.",
18-
products: ["product 1", "product 2"],
19-
rep_name: "Name of the sales rep",
20-
prospect_name: "Name of the prospect",
21-
action_items: ["action item 1", "action item 2"],
22-
};
23-
32+
//response format as zod schema
2433
const response = await llm.chat({
34+
messages: [
35+
{
36+
role: "system",
37+
content: `You are an expert assistant for summarizing and extracting insights from sales call transcripts.`,
38+
},
39+
{
40+
role: "user",
41+
content: `Here is the transcript: \n------\n${transcript}\n------`,
42+
},
43+
],
44+
responseFormat: exampleSchema,
45+
});
46+
47+
console.log(response.message.content);
48+
49+
//response format as json_object
50+
const response2 = await llm.chat({
2551
messages: [
2652
{
2753
role: "system",
@@ -34,9 +60,10 @@ async function main() {
3460
content: `Here is the transcript: \n------\n${transcript}\n------`,
3561
},
3662
],
63+
responseFormat: { type: "json_object" },
3764
});
3865

39-
console.log(response.message.content);
66+
console.log(response2.message.content);
4067
}
4168

4269
main().catch(console.error);

packages/community/src/llm/bedrock/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -381,6 +381,7 @@ export class Bedrock extends ToolCallLLM<BedrockAdditionalChatOptions> {
381381
maxTokens: this.maxTokens,
382382
contextWindow: BEDROCK_FOUNDATION_LLMS[this.model] ?? 128000,
383383
tokenizer: undefined,
384+
structuredOutput: false,
384385
};
385386
}
386387

packages/core/src/llms/base.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,12 @@ export abstract class BaseLLM<
2828
async complete(
2929
params: LLMCompletionParamsStreaming | LLMCompletionParamsNonStreaming,
3030
): Promise<CompletionResponse | AsyncIterable<CompletionResponse>> {
31-
const { prompt, stream } = params;
31+
const { prompt, stream, responseFormat } = params;
3232
if (stream) {
3333
const stream = await this.chat({
3434
messages: [{ content: prompt, role: "user" }],
3535
stream: true,
36+
...(responseFormat ? { responseFormat } : {}),
3637
});
3738
return streamConverter(stream, (chunk) => {
3839
return {
@@ -41,9 +42,12 @@ export abstract class BaseLLM<
4142
};
4243
});
4344
}
45+
4446
const chatResponse = await this.chat({
4547
messages: [{ content: prompt, role: "user" }],
48+
...(responseFormat ? { responseFormat } : {}),
4649
});
50+
4751
return {
4852
text: extractText(chatResponse.message.content),
4953
raw: chatResponse.raw,

packages/core/src/llms/type.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import type { Tokenizers } from "@llamaindex/env/tokenizers";
22
import type { JSONSchemaType } from "ajv";
3+
import { z } from "zod";
34
import type { JSONObject, JSONValue } from "../global";
45

56
/**
@@ -106,6 +107,7 @@ export type LLMMetadata = {
106107
maxTokens?: number | undefined;
107108
contextWindow: number;
108109
tokenizer: Tokenizers | undefined;
110+
structuredOutput: boolean;
109111
};
110112

111113
export interface LLMChatParamsBase<
@@ -115,6 +117,7 @@ export interface LLMChatParamsBase<
115117
messages: ChatMessage<AdditionalMessageOptions>[];
116118
additionalChatOptions?: AdditionalChatOptions;
117119
tools?: BaseTool[];
120+
responseFormat?: z.ZodType | object;
118121
}
119122

120123
export interface LLMChatParamsStreaming<
@@ -133,6 +136,7 @@ export interface LLMChatParamsNonStreaming<
133136

134137
export interface LLMCompletionParamsBase {
135138
prompt: MessageContent;
139+
responseFormat?: z.ZodType | object;
136140
}
137141

138142
export interface LLMCompletionParamsStreaming extends LLMCompletionParamsBase {

packages/core/src/utils/mock.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ export class MockLLM extends ToolCallLLM {
3535
topP: 0.5,
3636
contextWindow: 1024,
3737
tokenizer: undefined,
38+
structuredOutput: false,
3839
};
3940
}
4041

packages/providers/anthropic/src/llm.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,7 @@ export class Anthropic extends ToolCallLLM<
191191
].contextWindow
192192
: 200000,
193193
tokenizer: undefined,
194+
structuredOutput: false,
194195
};
195196
}
196197

packages/providers/google/src/base.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,7 @@ export class Gemini extends ToolCallLLM<GeminiAdditionalChatOptions> {
241241
maxTokens: this.maxTokens,
242242
contextWindow: GEMINI_MODEL_INFO_MAP[this.model].contextWindow,
243243
tokenizer: undefined,
244+
structuredOutput: false,
244245
};
245246
}
246247

packages/providers/huggingface/src/llm.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ export class HuggingFaceLLM extends BaseLLM {
5757
maxTokens: this.maxTokens,
5858
contextWindow: this.contextWindow,
5959
tokenizer: undefined,
60+
structuredOutput: false,
6061
};
6162
}
6263

0 commit comments

Comments
 (0)