@anthropic-ai/sdk
Version:
The official TypeScript library for the Anthropic API
125 lines (106 loc) • 3.67 kB
text/typescript
import { pop } from '../internal/utils';
// Supported string formats
const SUPPORTED_STRING_FORMATS = new Set([
'date-time',
'time',
'date',
'duration',
'email',
'hostname',
'uri',
'ipv4',
'ipv6',
'uuid',
]);
export type JSONSchema = Record<string, any>;
function deepClone<T>(obj: T): T {
return JSON.parse(JSON.stringify(obj));
}
export function transformJSONSchema(jsonSchema: JSONSchema): JSONSchema {
const workingCopy = deepClone(jsonSchema);
return _transformJSONSchema(workingCopy);
}
function _transformJSONSchema(jsonSchema: JSONSchema): JSONSchema {
const strictSchema: JSONSchema = {};
const ref = pop(jsonSchema, '$ref');
if (ref !== undefined) {
strictSchema['$ref'] = ref;
return strictSchema;
}
const defs = pop(jsonSchema, '$defs');
if (defs !== undefined) {
const strictDefs: Record<string, any> = {};
strictSchema['$defs'] = strictDefs;
for (const [name, defSchema] of Object.entries(defs)) {
strictDefs[name] = _transformJSONSchema(defSchema as JSONSchema);
}
}
const type = pop(jsonSchema, 'type');
const anyOf = pop(jsonSchema, 'anyOf');
const oneOf = pop(jsonSchema, 'oneOf');
const allOf = pop(jsonSchema, 'allOf');
if (Array.isArray(anyOf)) {
strictSchema['anyOf'] = anyOf.map((variant) => _transformJSONSchema(variant as JSONSchema));
} else if (Array.isArray(oneOf)) {
strictSchema['anyOf'] = oneOf.map((variant) => _transformJSONSchema(variant as JSONSchema));
} else if (Array.isArray(allOf)) {
strictSchema['allOf'] = allOf.map((entry) => _transformJSONSchema(entry as JSONSchema));
} else {
if (type === undefined) {
throw new Error('JSON schema must have a type defined if anyOf/oneOf/allOf are not used');
}
strictSchema['type'] = type;
}
const description = pop(jsonSchema, 'description');
if (description !== undefined) {
strictSchema['description'] = description;
}
const title = pop(jsonSchema, 'title');
if (title !== undefined) {
strictSchema['title'] = title;
}
if (type === 'object') {
const properties = pop(jsonSchema, 'properties') || {};
strictSchema['properties'] = Object.fromEntries(
Object.entries(properties).map(([key, propSchema]) => [
key,
_transformJSONSchema(propSchema as JSONSchema),
]),
);
pop(jsonSchema, 'additionalProperties');
strictSchema['additionalProperties'] = false;
const required = pop(jsonSchema, 'required');
if (required !== undefined) {
strictSchema['required'] = required;
}
} else if (type === 'string') {
const format = pop(jsonSchema, 'format');
if (format !== undefined && SUPPORTED_STRING_FORMATS.has(format)) {
strictSchema['format'] = format;
} else if (format !== undefined) {
jsonSchema['format'] = format;
}
} else if (type === 'array') {
const items = pop(jsonSchema, 'items');
if (items !== undefined) {
strictSchema['items'] = _transformJSONSchema(items as JSONSchema);
}
const minItems = pop(jsonSchema, 'minItems');
if (minItems !== undefined && (minItems === 0 || minItems === 1)) {
strictSchema['minItems'] = minItems;
} else if (minItems !== undefined) {
jsonSchema['minItems'] = minItems;
}
}
if (Object.keys(jsonSchema).length > 0) {
const existingDescription = strictSchema['description'];
strictSchema['description'] =
(existingDescription ? existingDescription + '\n\n' : '') +
'{' +
Object.entries(jsonSchema)
.map(([key, value]) => `${key}: ${JSON.stringify(value)}`)
.join(', ') +
'}';
}
return strictSchema;
}