UNPKG

@tanstack/ai

Version:

Type-safe TypeScript AI SDK for streaming chat, tool calling, agents, structured outputs, and multimodal generation.

168 lines (167 loc) 6.63 kB
function toJsonSchema(obj) { const result = {}; for (const [key, value] of Object.entries(obj)) { if (key === "$schema") continue; result[key] = value; } return result; } function isPropertyCarrier(schema) { return (typeof schema === "object" || typeof schema === "function") && schema !== null; } function isStandardJSONSchema(schema) { if (!isPropertyCarrier(schema) || !("~standard" in schema)) return false; const standard = schema["~standard"]; if (typeof standard !== "object" || standard === null || !("version" in standard) || standard.version !== 1 || !("jsonSchema" in standard) || typeof standard.jsonSchema !== "object" || standard.jsonSchema === null || !("input" in standard.jsonSchema)) { return false; } return typeof standard.jsonSchema.input === "function"; } function isStandardSchema(schema) { return isPropertyCarrier(schema) && "~standard" in schema && typeof schema["~standard"] === "object" && schema["~standard"] !== null && "version" in schema["~standard"] && schema["~standard"].version === 1 && "validate" in schema["~standard"] && typeof schema["~standard"].validate === "function"; } function pruneMap(map) { return Object.keys(map).length > 0 ? map : void 0; } function makeStructuredOutputCompatible(schema, originalRequired = []) { const result = { ...schema }; const map = {}; if (result.type === "object" && result.properties) { const properties = { ...result.properties }; const allPropertyNames = Object.keys(properties); const propertyMaps = {}; for (const propName of allPropertyNames) { const prop = properties[propName]; if (!prop) continue; const wasOptional = !originalRequired.includes(propName); let widenedHere = false; let childMap; if (prop.type === "object" && prop.properties) { const nested = makeStructuredOutputCompatible(prop, prop.required || []); properties[propName] = wasOptional ? { ...nested.schema, type: ["object", "null"] } : nested.schema; widenedHere = wasOptional; childMap = nested.nullWidening; } else if (prop.type === "array" && prop.items) { const items = Array.isArray(prop.items) ? prop.items[0] : prop.items; const nestedItems = items ? makeStructuredOutputCompatible(items, items.required || []) : void 0; properties[propName] = { ...prop, items: nestedItems ? nestedItems.schema : prop.items, ...wasOptional ? { type: ["array", "null"] } : {} }; widenedHere = wasOptional; childMap = nestedItems?.nullWidening ? { items: nestedItems.nullWidening } : void 0; } else if (wasOptional) { if (prop.type && !Array.isArray(prop.type)) { properties[propName] = { ...prop, type: [prop.type, "null"] }; widenedHere = true; } else if (Array.isArray(prop.type) && !prop.type.includes("null")) { properties[propName] = { ...prop, type: [...prop.type, "null"] }; widenedHere = true; } } if (widenedHere || childMap) { propertyMaps[propName] = { ...childMap ?? {}, ...widenedHere ? { widened: true } : {} }; } } result.properties = properties; result.required = allPropertyNames; result.additionalProperties = false; if (Object.keys(propertyMaps).length > 0) map.properties = propertyMaps; } if (result.type === "array" && result.items) { const items = Array.isArray(result.items) ? result.items[0] : result.items; if (items) { const nestedItems = makeStructuredOutputCompatible( items, items.required || [] ); result.items = nestedItems.schema; if (nestedItems.nullWidening) map.items = nestedItems.nullWidening; } } return { schema: result, nullWidening: pruneMap(map) }; } function toTypedJsonSchema(schema) { if (isStandardJSONSchema(schema)) { const jsonSchema = schema["~standard"].jsonSchema.input({ target: "draft-07" }); const result = toJsonSchema(jsonSchema); if ("properties" in result && !result.type) result.type = "object"; if (result.type === "object" && !("properties" in result)) { result.properties = {}; } if (result.type === "object" && !("required" in result)) { result.required = []; } return result; } if (isStandardSchema(schema)) { throw new Error( "Schema is a Standard Schema validator but does not expose a JSON Schema converter on `~standard.jsonSchema`. Use Zod v4.2+, ArkType v2.1.28+, or wrap a Valibot schema with `toStandardJsonSchema()` from `@valibot/to-json-schema` before passing it as `outputSchema`." ); } if (typeof schema !== "object") return schema; return toJsonSchema(schema); } function convertSchemaToJsonSchema(schema, options = {}) { if (!schema) return void 0; const { forStructuredOutput = false } = options; if (!forStructuredOutput && !isStandardJSONSchema(schema) && !isStandardSchema(schema)) { return schema; } const base = toTypedJsonSchema(schema); if (!base || typeof base !== "object") return base; if (!forStructuredOutput) return base; return makeStructuredOutputCompatible(base, base.required || []).schema; } function convertSchemaForStructuredOutput(schema) { if (!schema) return { jsonSchema: void 0, nullWideningMap: void 0 }; const base = toTypedJsonSchema(schema); if (!base || typeof base !== "object") { return { jsonSchema: base, nullWideningMap: void 0 }; } const { schema: jsonSchema, nullWidening } = makeStructuredOutputCompatible( base, base.required || [] ); return { jsonSchema, nullWideningMap: nullWidening }; } class StandardSchemaValidationError extends Error { name = "StandardSchemaValidationError"; issues; constructor(issues) { super( `Validation failed: ${issues.map((i) => i.message || "Validation failed").join(", ")}` ); this.issues = issues; } } function parseWithStandardSchema(schema, data) { if (!isStandardSchema(schema)) { return data; } const result = schema["~standard"].validate(data); if (result instanceof Promise) { throw new Error( "Schema validation returned a Promise. Use validateWithStandardSchema for async validation." ); } if (!result.issues) { return result.value; } throw new StandardSchemaValidationError(result.issues); } export { StandardSchemaValidationError, convertSchemaForStructuredOutput, convertSchemaToJsonSchema, isStandardJSONSchema, isStandardSchema, parseWithStandardSchema }; //# sourceMappingURL=schema-converter.js.map