Compare commits

...

3 Commits

Author SHA1 Message Date
Clelia (Astra) Bertelli 2579cb6b96 chore: move to zod/v4 2025-07-08 21:38:35 +02:00
Clelia (Astra) Bertelli 28fb8247f8 ci: patching anthropic with changesets 2025-07-08 19:07:28 +02:00
Clelia (Astra) Bertelli 82e69bd5c4 feat: adding structured output support for anthropic 2025-07-08 16:51:13 +02:00
5 changed files with 159 additions and 79 deletions
+5
View File
@@ -0,0 +1,5 @@
---
"@llamaindex/anthropic": patch
---
Adding structured output support
+4 -3
View File
@@ -31,9 +31,9 @@
"test": "vitest run"
},
"devDependencies": {
"vitest": "^2.1.5",
"@llamaindex/core": "workspace:*",
"@llamaindex/env": "workspace:*"
"@llamaindex/env": "workspace:*",
"vitest": "^2.1.5"
},
"peerDependencies": {
"@llamaindex/core": "workspace:*",
@@ -41,6 +41,7 @@
},
"dependencies": {
"@anthropic-ai/sdk": "0.37.0",
"remeda": "^2.17.3"
"remeda": "^2.17.3",
"zod": "^3.25.67"
}
}
+63 -12
View File
@@ -30,6 +30,7 @@ import { ToolCallLLM } from "@llamaindex/core/llms";
import { extractText } from "@llamaindex/core/utils";
import { getEnv } from "@llamaindex/env";
import { isDeepEqual } from "remeda";
import * as z from "zod/v4";
export class AnthropicSession {
anthropic: SDKAnthropic;
@@ -203,7 +204,7 @@ export class Anthropic extends ToolCallLLM<
].contextWindow
: 200000,
tokenizer: undefined,
structuredOutput: false,
structuredOutput: true,
};
}
@@ -459,7 +460,7 @@ export class Anthropic extends ToolCallLLM<
| ChatResponse<AnthropicToolCallLLMMessageOptions>
| AsyncIterable<ChatResponseChunk<AnthropicToolCallLLMMessageOptions>>
> {
const { messages, stream, tools } = params;
const { messages, stream, tools, responseFormat } = params;
// Handle system messages
let systemPrompt: string | BetaTextBlockParam[] | null = null;
@@ -511,10 +512,42 @@ export class Anthropic extends ToolCallLLM<
),
};
let jsonTool: undefined | Tool;
if (responseFormat && this.metadata.structuredOutput) {
if (responseFormat instanceof z.ZodType) {
const schemaDefinition = z.toJSONSchema(responseFormat);
if (!schemaDefinition) {
console.error(responseFormat);
throw new Error(
"Failed to generate JSON schema for provided schema.",
);
}
jsonTool = {
name: "json",
description: "Respond with a JSON object.",
input_schema: schemaDefinition as Tool.InputSchema,
};
}
}
if (tools?.length) {
const apiTools = this.prepareToolsForAPI(tools);
if (jsonTool) {
apiTools.push(jsonTool);
}
Object.assign(apiParams, {
tools: this.prepareToolsForAPI(tools),
tools: apiTools,
});
} else {
const formatTools: Tool[] = [];
if (jsonTool) {
formatTools.push(jsonTool);
Object.assign(apiParams, {
tools: formatTools,
tool_choice: { name: "json", type: "tool" },
});
}
}
if (stream) {
@@ -531,6 +564,17 @@ export class Anthropic extends ToolCallLLM<
(content): content is ToolUseBlock => content.type === "tool_use",
);
let jsonResult: undefined | z.ZodSafeParseResult<unknown>;
if (toolUseBlock?.length) {
const jsonToolUse = toolUseBlock.filter(
(toolUse): toolUse is ToolUseBlock => toolUse.name === "json",
)[0];
if (jsonToolUse) {
jsonResult = (responseFormat as z.ZodType).safeParse(jsonToolUse.input);
}
}
const toolCall =
toolUseBlock.length > 0
? {
@@ -545,18 +589,25 @@ export class Anthropic extends ToolCallLLM<
}
: {};
let messageContent: { type: "text"; text: string }[] | object =
response.content
.filter(
(content): content is TextBlock =>
content.type === "text" && content.text?.trim().length > 0,
)
.map((content) => ({
type: "text" as const,
text: content.text,
}));
if (jsonResult) {
messageContent = jsonResult.data as object;
}
return {
raw: response,
message: {
content: response.content
.filter(
(content): content is TextBlock =>
content.type === "text" && content.text?.trim().length > 0,
)
.map((content) => ({
type: "text" as const,
text: content.text,
})),
content: messageContent as { type: "text"; text: string }[],
role: "assistant",
options: {
...toolCall,
+30
View File
@@ -0,0 +1,30 @@
import * as z from "zod/v4";
import { Anthropic } from "./src";
const responseSchema = z.object({
title: z.string(),
author: z.string(),
year: z.number(),
});
const llm = new Anthropic({ model: "claude-4-0-sonnet" });
async function main() {
const response = await llm.chat({
messages: [
{
role: "system",
content: `You are a book expert. Your task is, given a user message, extract the title, author and publication year of the book and output them in JSON format.`,
},
{
role: "user",
content: `I have been reading La Divina Commedia by Dante Alighieri, published in 1321, which tells the story of a guy who goes through Hell, Purgatory and Heaven just to meet his beloved ex-girlfriend.`,
},
],
responseFormat: responseSchema,
});
return response.message.content;
}
const antResponse = await main();
console.log(antResponse);
+57 -64
View File
@@ -1144,6 +1144,9 @@ importers:
remeda:
specifier: ^2.17.3
version: 2.21.3
zod:
specifier: ^3.25.67
version: 3.25.74
devDependencies:
'@llamaindex/core':
specifier: workspace:*
@@ -13912,11 +13915,6 @@ packages:
zimmerframe@1.1.2:
resolution: {integrity: sha512-rAbqEGa8ovJy4pyBxZM70hg4pE6gDgaQ0Sl9M3enG3I0d6H4XSAM3GeNGLKnsBpuijUow064sf7ww1nutC5/3w==}
zod-to-json-schema@3.24.5:
resolution: {integrity: sha512-/AuWwMP+YqiPbsJx5D6TfgRTc4kTLjsh5SOcd4bLsfUg2RcEXrFMJl1DGgdHy2aCfsIA/cr/1JM0xcB2GZji8g==}
peerDependencies:
zod: ^3.24.1
zod-to-json-schema@3.24.6:
resolution: {integrity: sha512-h/z3PKvcTcTetyjl1fkj79MHNEjm+HpD6NXheWjzOekY7kV+lwDYnHw+ivHkijnCSMz1yJaWBD9vu/Fcmk+vEg==}
peerDependencies:
@@ -15832,7 +15830,7 @@ snapshots:
semver: 7.7.1
vitest: 2.1.5(@edge-runtime/vm@5.0.0)(@types/node@22.14.1)(happy-dom@17.4.4)(lightningcss@1.29.3)(msw@2.7.4(@types/node@22.14.1)(typescript@5.7.3))(terser@5.39.0)
wrangler: 3.100.0(@cloudflare/workers-types@4.20250416.0)(bufferutil@4.0.9)
zod: 3.25.67
zod: 3.25.74
transitivePeerDependencies:
- '@cloudflare/workers-types'
- bufferutil
@@ -16528,8 +16526,8 @@ snapshots:
dependencies:
google-auth-library: 9.15.1
ws: 8.18.1(bufferutil@4.0.9)
zod: 3.25.67
zod-to-json-schema: 3.24.6(zod@3.25.67)
zod: 3.25.74
zod-to-json-schema: 3.24.6(zod@3.25.74)
optionalDependencies:
'@modelcontextprotocol/sdk': 1.13.0
transitivePeerDependencies:
@@ -16941,7 +16939,7 @@ snapshots:
'@jridgewell/gen-mapping@0.3.8':
dependencies:
'@jridgewell/set-array': 1.2.1
'@jridgewell/sourcemap-codec': 1.5.0
'@jridgewell/sourcemap-codec': 1.5.4
'@jridgewell/trace-mapping': 0.3.25
'@jridgewell/resolve-uri@3.1.2': {}
@@ -16960,7 +16958,7 @@ snapshots:
'@jridgewell/trace-mapping@0.3.25':
dependencies:
'@jridgewell/resolve-uri': 3.1.2
'@jridgewell/sourcemap-codec': 1.5.0
'@jridgewell/sourcemap-codec': 1.5.4
'@jridgewell/trace-mapping@0.3.9':
dependencies:
@@ -17128,8 +17126,8 @@ snapshots:
express-rate-limit: 7.5.0(express@5.1.0)
pkce-challenge: 5.0.0
raw-body: 3.0.0
zod: 3.25.67
zod-to-json-schema: 3.24.6(zod@3.25.67)
zod: 3.25.74
zod-to-json-schema: 3.24.6(zod@3.25.74)
transitivePeerDependencies:
- supports-color
@@ -18350,7 +18348,7 @@ snapshots:
vue-router: 4.5.0(vue@3.5.13(typescript@5.7.3))
whatwg-mimetype: 4.0.0
yaml: 2.7.1
zod: 3.25.67
zod: 3.25.74
transitivePeerDependencies:
- '@hyperjump/browser'
- '@vue/composition-api'
@@ -18443,7 +18441,7 @@ snapshots:
nanoid: 5.1.5
type-fest: 4.40.0
yaml: 2.7.1
zod: 3.25.67
zod: 3.25.74
transitivePeerDependencies:
- '@hyperjump/browser'
@@ -18473,7 +18471,7 @@ snapshots:
'@scalar/openapi-types@0.2.0':
dependencies:
zod: 3.25.67
zod: 3.25.74
'@scalar/postman-to-openapi@0.2.3(@hyperjump/browser@1.3.0)':
dependencies:
@@ -18496,7 +18494,7 @@ snapshots:
'@unhead/schema': 1.11.20
nanoid: 5.1.5
type-fest: 4.40.0
zod: 3.25.67
zod: 3.25.74
'@scalar/use-codemirror@0.11.92(typescript@5.7.3)':
dependencies:
@@ -18530,7 +18528,7 @@ snapshots:
'@scalar/use-toasts': 0.7.9(typescript@5.7.3)
'@vueuse/core': 10.11.1(vue@3.5.13(typescript@5.7.3))
vue: 3.5.13(typescript@5.7.3)
zod: 3.25.67
zod: 3.25.74
transitivePeerDependencies:
- '@vue/composition-api'
- typescript
@@ -19877,10 +19875,10 @@ snapshots:
transitivePeerDependencies:
- supports-color
'@typescript-eslint/eslint-plugin@8.30.1(@typescript-eslint/parser@8.30.1(eslint@9.29.0(jiti@2.4.2))(typescript@5.8.3))(eslint@9.29.0(jiti@2.4.2))(typescript@5.8.3)':
'@typescript-eslint/eslint-plugin@8.30.1(@typescript-eslint/parser@8.30.1(eslint@9.22.0(jiti@2.4.2))(typescript@5.7.3))(eslint@9.29.0(jiti@2.4.2))(typescript@5.8.3)':
dependencies:
'@eslint-community/regexpp': 4.12.1
'@typescript-eslint/parser': 8.30.1(eslint@9.29.0(jiti@2.4.2))(typescript@5.8.3)
'@typescript-eslint/parser': 8.30.1(eslint@9.22.0(jiti@2.4.2))(typescript@5.7.3)
'@typescript-eslint/scope-manager': 8.30.1
'@typescript-eslint/type-utils': 8.30.1(eslint@9.29.0(jiti@2.4.2))(typescript@5.8.3)
'@typescript-eslint/utils': 8.30.1(eslint@9.29.0(jiti@2.4.2))(typescript@5.8.3)
@@ -20734,7 +20732,7 @@ snapshots:
json-schema: 0.4.0
jsondiffpatch: 0.6.0
secure-json-parse: 2.7.0
zod-to-json-schema: 3.24.5(zod@3.25.67)
zod-to-json-schema: 3.24.6(zod@3.25.67)
optionalDependencies:
openai: 4.102.0(ws@8.18.1(bufferutil@4.0.9))(zod@3.25.67)
react: 19.1.0
@@ -22239,7 +22237,7 @@ snapshots:
eslint: 9.22.0(jiti@2.4.2)
eslint-import-resolver-node: 0.3.9
eslint-import-resolver-typescript: 3.7.0(eslint-plugin-import@2.31.0)(eslint@9.22.0(jiti@2.4.2))
eslint-plugin-import: 2.31.0(@typescript-eslint/parser@6.21.0(eslint@9.22.0(jiti@2.4.2))(typescript@5.7.3))(eslint-import-resolver-typescript@3.7.0)(eslint@9.22.0(jiti@2.4.2))
eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.30.1(eslint@9.22.0(jiti@2.4.2))(typescript@5.7.3))(eslint@9.22.0(jiti@2.4.2))
eslint-plugin-jsx-a11y: 6.10.2(eslint@9.22.0(jiti@2.4.2))
eslint-plugin-react: 7.37.2(eslint@9.22.0(jiti@2.4.2))
eslint-plugin-react-hooks: 5.2.0(eslint@9.22.0(jiti@2.4.2))
@@ -22296,18 +22294,7 @@ snapshots:
is-glob: 4.0.3
stable-hash: 0.0.4
optionalDependencies:
eslint-plugin-import: 2.31.0(@typescript-eslint/parser@6.21.0(eslint@9.22.0(jiti@2.4.2))(typescript@5.7.3))(eslint-import-resolver-typescript@3.7.0)(eslint@9.22.0(jiti@2.4.2))
transitivePeerDependencies:
- supports-color
eslint-module-utils@2.12.0(@typescript-eslint/parser@6.21.0(eslint@9.22.0(jiti@2.4.2))(typescript@5.7.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.7.0)(eslint@9.22.0(jiti@2.4.2)):
dependencies:
debug: 3.2.7
optionalDependencies:
'@typescript-eslint/parser': 6.21.0(eslint@9.22.0(jiti@2.4.2))(typescript@5.7.3)
eslint: 9.22.0(jiti@2.4.2)
eslint-import-resolver-node: 0.3.9
eslint-import-resolver-typescript: 3.7.0(eslint-plugin-import@2.31.0)(eslint@9.22.0(jiti@2.4.2))
eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.30.1(eslint@9.22.0(jiti@2.4.2))(typescript@5.7.3))(eslint@9.22.0(jiti@2.4.2))
transitivePeerDependencies:
- supports-color
@@ -22322,33 +22309,14 @@ snapshots:
transitivePeerDependencies:
- supports-color
eslint-plugin-import@2.31.0(@typescript-eslint/parser@6.21.0(eslint@9.22.0(jiti@2.4.2))(typescript@5.7.3))(eslint-import-resolver-typescript@3.7.0)(eslint@9.22.0(jiti@2.4.2)):
eslint-module-utils@2.12.0(@typescript-eslint/parser@8.30.1(eslint@9.22.0(jiti@2.4.2))(typescript@5.7.3))(eslint-import-resolver-node@0.3.9)(eslint@9.22.0(jiti@2.4.2)):
dependencies:
'@rtsao/scc': 1.1.0
array-includes: 3.1.8
array.prototype.findlastindex: 1.2.5
array.prototype.flat: 1.3.3
array.prototype.flatmap: 1.3.3
debug: 3.2.7
doctrine: 2.1.0
optionalDependencies:
'@typescript-eslint/parser': 8.30.1(eslint@9.22.0(jiti@2.4.2))(typescript@5.7.3)
eslint: 9.22.0(jiti@2.4.2)
eslint-import-resolver-node: 0.3.9
eslint-module-utils: 2.12.0(@typescript-eslint/parser@6.21.0(eslint@9.22.0(jiti@2.4.2))(typescript@5.7.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.7.0)(eslint@9.22.0(jiti@2.4.2))
hasown: 2.0.2
is-core-module: 2.16.1
is-glob: 4.0.3
minimatch: 3.1.2
object.fromentries: 2.0.8
object.groupby: 1.0.3
object.values: 1.2.1
semver: 6.3.1
string.prototype.trimend: 1.0.9
tsconfig-paths: 3.15.0
optionalDependencies:
'@typescript-eslint/parser': 6.21.0(eslint@9.22.0(jiti@2.4.2))(typescript@5.7.3)
transitivePeerDependencies:
- eslint-import-resolver-typescript
- eslint-import-resolver-webpack
- supports-color
eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.30.1(eslint@9.16.0(jiti@2.4.2))(typescript@5.7.3))(eslint-import-resolver-typescript@3.7.0)(eslint@9.16.0(jiti@2.4.2)):
@@ -22380,6 +22348,35 @@ snapshots:
- eslint-import-resolver-webpack
- supports-color
eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.30.1(eslint@9.22.0(jiti@2.4.2))(typescript@5.7.3))(eslint@9.22.0(jiti@2.4.2)):
dependencies:
'@rtsao/scc': 1.1.0
array-includes: 3.1.8
array.prototype.findlastindex: 1.2.5
array.prototype.flat: 1.3.3
array.prototype.flatmap: 1.3.3
debug: 3.2.7
doctrine: 2.1.0
eslint: 9.22.0(jiti@2.4.2)
eslint-import-resolver-node: 0.3.9
eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.30.1(eslint@9.22.0(jiti@2.4.2))(typescript@5.7.3))(eslint-import-resolver-node@0.3.9)(eslint@9.22.0(jiti@2.4.2))
hasown: 2.0.2
is-core-module: 2.16.1
is-glob: 4.0.3
minimatch: 3.1.2
object.fromentries: 2.0.8
object.groupby: 1.0.3
object.values: 1.2.1
semver: 6.3.1
string.prototype.trimend: 1.0.9
tsconfig-paths: 3.15.0
optionalDependencies:
'@typescript-eslint/parser': 8.30.1(eslint@9.22.0(jiti@2.4.2))(typescript@5.7.3)
transitivePeerDependencies:
- eslint-import-resolver-typescript
- eslint-import-resolver-webpack
- supports-color
eslint-plugin-jsx-a11y@6.10.2(eslint@9.16.0(jiti@2.4.2)):
dependencies:
aria-query: 5.3.2
@@ -23144,7 +23141,7 @@ snapshots:
npm-to-yarn: 3.0.1
oxc-transform: 0.53.0
unist-util-visit: 5.0.0
zod: 3.25.67
zod: 3.25.74
fumadocs-mdx@11.6.6(@fumadocs/mdx-remote@1.3.0(acorn@8.14.1)(fumadocs-core@15.5.0(@types/react@19.0.10)(next@15.3.3(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react@19.1.0))(acorn@8.14.1)(fumadocs-core@15.5.0(@types/react@19.0.10)(next@15.3.3(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(next@15.3.3(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)):
dependencies:
@@ -23162,7 +23159,7 @@ snapshots:
tinyexec: 1.0.1
tinyglobby: 0.2.13
unist-util-visit: 5.0.0
zod: 3.25.67
zod: 3.25.74
optionalDependencies:
'@fumadocs/mdx-remote': 1.3.0(acorn@8.14.1)(fumadocs-core@15.5.0(@types/react@19.0.10)(next@15.3.3(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react@19.1.0)
transitivePeerDependencies:
@@ -25247,7 +25244,7 @@ snapshots:
workerd: 1.20241230.0
ws: 8.18.1(bufferutil@4.0.9)
youch: 3.3.4
zod: 3.25.67
zod: 3.25.74
transitivePeerDependencies:
- bufferutil
- supports-color
@@ -27953,7 +27950,7 @@ snapshots:
terser@5.39.0:
dependencies:
'@jridgewell/source-map': 0.3.6
acorn: 8.14.1
acorn: 8.15.0
commander: 2.20.3
source-map-support: 0.5.21
@@ -28263,7 +28260,7 @@ snapshots:
typescript-eslint@8.30.1(eslint@9.29.0(jiti@2.4.2))(typescript@5.8.3):
dependencies:
'@typescript-eslint/eslint-plugin': 8.30.1(@typescript-eslint/parser@8.30.1(eslint@9.29.0(jiti@2.4.2))(typescript@5.8.3))(eslint@9.29.0(jiti@2.4.2))(typescript@5.8.3)
'@typescript-eslint/eslint-plugin': 8.30.1(@typescript-eslint/parser@8.30.1(eslint@9.22.0(jiti@2.4.2))(typescript@5.7.3))(eslint@9.29.0(jiti@2.4.2))(typescript@5.8.3)
'@typescript-eslint/parser': 8.30.1(eslint@9.29.0(jiti@2.4.2))(typescript@5.8.3)
'@typescript-eslint/utils': 8.30.1(eslint@9.29.0(jiti@2.4.2))(typescript@5.8.3)
eslint: 9.29.0(jiti@2.4.2)
@@ -29427,10 +29424,6 @@ snapshots:
zimmerframe@1.1.2: {}
zod-to-json-schema@3.24.5(zod@3.25.67):
dependencies:
zod: 3.25.67
zod-to-json-schema@3.24.6(zod@3.25.67):
dependencies:
zod: 3.25.67