@ai-sdk/anthropic 4.0.0-beta.39 → 4.0.0-beta.40
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.
- package/CHANGELOG.md +6 -0
- package/dist/index.js +148 -2
- package/dist/index.js.map +1 -1
- package/dist/internal/index.js +147 -1
- package/dist/internal/index.js.map +1 -1
- package/package.json +1 -1
- package/src/anthropic-language-model.ts +2 -1
- package/src/sanitize-json-schema.ts +203 -0
package/package.json
CHANGED
|
@@ -60,6 +60,7 @@ import {
|
|
|
60
60
|
import { convertToAnthropicPrompt } from './convert-to-anthropic-prompt';
|
|
61
61
|
import { CacheControlValidator } from './get-cache-control';
|
|
62
62
|
import { mapAnthropicStopReason } from './map-anthropic-stop-reason';
|
|
63
|
+
import { sanitizeJsonSchema } from './sanitize-json-schema';
|
|
63
64
|
|
|
64
65
|
function createCitationSource(
|
|
65
66
|
citation: Citation,
|
|
@@ -468,7 +469,7 @@ export class AnthropicLanguageModel implements LanguageModelV4 {
|
|
|
468
469
|
responseFormat.schema != null && {
|
|
469
470
|
format: {
|
|
470
471
|
type: 'json_schema',
|
|
471
|
-
schema: responseFormat.schema,
|
|
472
|
+
schema: sanitizeJsonSchema(responseFormat.schema),
|
|
472
473
|
},
|
|
473
474
|
}),
|
|
474
475
|
},
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
import type { JSONSchema7, JSONSchema7Definition } from '@ai-sdk/provider';
|
|
2
|
+
|
|
3
|
+
const SUPPORTED_STRING_FORMATS = new Set([
|
|
4
|
+
'date-time',
|
|
5
|
+
'time',
|
|
6
|
+
'date',
|
|
7
|
+
'duration',
|
|
8
|
+
'email',
|
|
9
|
+
'hostname',
|
|
10
|
+
'uri',
|
|
11
|
+
'ipv4',
|
|
12
|
+
'ipv6',
|
|
13
|
+
'uuid',
|
|
14
|
+
]);
|
|
15
|
+
|
|
16
|
+
const DESCRIPTION_CONSTRAINT_KEYS = [
|
|
17
|
+
'minimum',
|
|
18
|
+
'maximum',
|
|
19
|
+
'exclusiveMinimum',
|
|
20
|
+
'exclusiveMaximum',
|
|
21
|
+
'multipleOf',
|
|
22
|
+
'minLength',
|
|
23
|
+
'maxLength',
|
|
24
|
+
'pattern',
|
|
25
|
+
'minItems',
|
|
26
|
+
'maxItems',
|
|
27
|
+
'uniqueItems',
|
|
28
|
+
'minProperties',
|
|
29
|
+
'maxProperties',
|
|
30
|
+
'not',
|
|
31
|
+
] satisfies Array<keyof JSONSchema7>;
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Removes JSON Schema keywords that Anthropic rejects in
|
|
35
|
+
* `output_config.format.schema`.
|
|
36
|
+
*
|
|
37
|
+
* The full original schema is still used by AI SDK result validation. This
|
|
38
|
+
* only relaxes the schema sent to Anthropic's constrained decoder.
|
|
39
|
+
*/
|
|
40
|
+
export function sanitizeJsonSchema(schema: JSONSchema7): JSONSchema7 {
|
|
41
|
+
return sanitizeSchema(schema) as JSONSchema7;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function sanitizeDefinition(
|
|
45
|
+
definition: JSONSchema7Definition,
|
|
46
|
+
): JSONSchema7Definition {
|
|
47
|
+
if (typeof definition === 'boolean' || !isPlainObject(definition)) {
|
|
48
|
+
return definition;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return sanitizeSchema(definition as JSONSchema7);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function sanitizeSchema(schema: JSONSchema7): JSONSchema7 {
|
|
55
|
+
const result: JSONSchema7 = {};
|
|
56
|
+
const schemaWithDefs = schema as JSONSchema7 & {
|
|
57
|
+
$defs?: Record<string, JSONSchema7Definition>;
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
if (schema.$ref != null) {
|
|
61
|
+
return { $ref: schema.$ref };
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (schema.$schema != null) {
|
|
65
|
+
result.$schema = schema.$schema;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (schema.$id != null) {
|
|
69
|
+
result.$id = schema.$id;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (schema.title != null) {
|
|
73
|
+
result.title = schema.title;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (schema.description != null) {
|
|
77
|
+
result.description = schema.description;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (schema.default !== undefined) {
|
|
81
|
+
result.default = schema.default;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (schema.const !== undefined) {
|
|
85
|
+
result.const = schema.const;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (schema.enum != null) {
|
|
89
|
+
result.enum = schema.enum;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (schema.type != null) {
|
|
93
|
+
result.type = schema.type;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (schema.anyOf != null) {
|
|
97
|
+
result.anyOf = schema.anyOf.map(sanitizeDefinition);
|
|
98
|
+
} else if (schema.oneOf != null) {
|
|
99
|
+
result.anyOf = schema.oneOf.map(sanitizeDefinition);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (schema.allOf != null) {
|
|
103
|
+
result.allOf = schema.allOf.map(sanitizeDefinition);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (schema.definitions != null) {
|
|
107
|
+
result.definitions = Object.fromEntries(
|
|
108
|
+
Object.entries(schema.definitions).map(([name, definition]) => [
|
|
109
|
+
name,
|
|
110
|
+
sanitizeDefinition(definition),
|
|
111
|
+
]),
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
if (schemaWithDefs.$defs != null) {
|
|
116
|
+
const resultWithDefs = result as JSONSchema7 & {
|
|
117
|
+
$defs?: Record<string, JSONSchema7Definition>;
|
|
118
|
+
};
|
|
119
|
+
resultWithDefs.$defs = Object.fromEntries(
|
|
120
|
+
Object.entries(schemaWithDefs.$defs).map(([name, definition]) => [
|
|
121
|
+
name,
|
|
122
|
+
sanitizeDefinition(definition),
|
|
123
|
+
]),
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
if (schema.type === 'object' || schema.properties != null) {
|
|
128
|
+
if (schema.properties != null) {
|
|
129
|
+
result.properties = Object.fromEntries(
|
|
130
|
+
Object.entries(schema.properties).map(([name, definition]) => [
|
|
131
|
+
name,
|
|
132
|
+
sanitizeDefinition(definition),
|
|
133
|
+
]),
|
|
134
|
+
);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
result.additionalProperties = false;
|
|
138
|
+
|
|
139
|
+
if (schema.required != null) {
|
|
140
|
+
result.required = schema.required;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
if (schema.items != null) {
|
|
145
|
+
result.items = Array.isArray(schema.items)
|
|
146
|
+
? schema.items.map(sanitizeDefinition)
|
|
147
|
+
: sanitizeDefinition(schema.items);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
if (
|
|
151
|
+
typeof schema.format === 'string' &&
|
|
152
|
+
SUPPORTED_STRING_FORMATS.has(schema.format)
|
|
153
|
+
) {
|
|
154
|
+
result.format = schema.format;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const constraintDescription = getConstraintDescription(schema);
|
|
158
|
+
if (constraintDescription != null) {
|
|
159
|
+
result.description =
|
|
160
|
+
result.description == null
|
|
161
|
+
? constraintDescription
|
|
162
|
+
: `${result.description}\n${constraintDescription}`;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
return result;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function getConstraintDescription(schema: JSONSchema7): string | undefined {
|
|
169
|
+
const descriptions = DESCRIPTION_CONSTRAINT_KEYS.flatMap(key => {
|
|
170
|
+
const value = schema[key];
|
|
171
|
+
|
|
172
|
+
if (value == null || value === false) {
|
|
173
|
+
return [];
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
return `${formatConstraintName(key)}: ${formatConstraintValue(value)}`;
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
if (
|
|
180
|
+
typeof schema.format === 'string' &&
|
|
181
|
+
!SUPPORTED_STRING_FORMATS.has(schema.format)
|
|
182
|
+
) {
|
|
183
|
+
descriptions.push(`format: ${schema.format}`);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
return descriptions.length === 0 ? undefined : `${descriptions.join('; ')}.`;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
function formatConstraintName(key: string): string {
|
|
190
|
+
return key.replace(/[A-Z]/g, match => ` ${match.toLowerCase()}`);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
function formatConstraintValue(value: unknown): string {
|
|
194
|
+
if (typeof value === 'string') {
|
|
195
|
+
return value;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
return JSON.stringify(value);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function isPlainObject(value: unknown): value is Record<string, unknown> {
|
|
202
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
203
|
+
}
|