UNPKG

@samchon/openapi

Version:

OpenAPI definitions and converters for 'typia' and 'nestia'.

449 lines (448 loc) 22.9 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.LlmSchemaV3_1Composer = void 0; const LlmTypeCheckerV3_1_1 = require("../../utils/LlmTypeCheckerV3_1"); const NamingConvention_1 = require("../../utils/NamingConvention"); const OpenApiConstraintShifter_1 = require("../../utils/OpenApiConstraintShifter"); const OpenApiTypeChecker_1 = require("../../utils/OpenApiTypeChecker"); const OpenApiValidator_1 = require("../../utils/OpenApiValidator"); const JsonDescriptionUtil_1 = require("../../utils/internal/JsonDescriptionUtil"); const LlmDescriptionInverter_1 = require("./LlmDescriptionInverter"); const LlmParametersComposer_1 = require("./LlmParametersComposer"); var LlmSchemaV3_1Composer; (function (LlmSchemaV3_1Composer) { /** * @internal */ LlmSchemaV3_1Composer.IS_DEFS = true; /* ----------------------------------------------------------- CONVERTERS ----------------------------------------------------------- */ LlmSchemaV3_1Composer.parameters = (props) => { const entity = LlmParametersComposer_1.LlmParametersFinder.parameters(Object.assign(Object.assign({}, props), { method: "LlmSchemaV3_1Composer.parameters" })); if (entity.success === false) return entity; const $defs = {}; const result = LlmSchemaV3_1Composer.schema(Object.assign(Object.assign({}, props), { $defs, schema: entity.value })); if (result.success === false) return result; return { success: true, value: Object.assign(Object.assign({}, result.value), { additionalProperties: false, $defs, description: OpenApiTypeChecker_1.OpenApiTypeChecker.isReference(props.schema) ? JsonDescriptionUtil_1.JsonDescriptionUtil.cascade({ prefix: "#/components/schemas/", components: props.components, schema: props.schema, escape: true, }) : result.value.description }), }; }; LlmSchemaV3_1Composer.schema = (props) => { var _a; const union = []; const attribute = Object.assign({ title: props.schema.title, description: props.schema.description, example: props.schema.example, examples: props.schema.examples }, Object.fromEntries(Object.entries(props.schema).filter(([key, value]) => key.startsWith("x-") && value !== undefined))); const reasons = []; OpenApiTypeChecker_1.OpenApiTypeChecker.visit({ closure: (next, accessor) => { var _a; if (props.validate) { // CUSTOM VALIDATION reasons.push(...props.validate(next, accessor)); } if (OpenApiTypeChecker_1.OpenApiTypeChecker.isTuple(next)) reasons.push({ schema: next, accessor: accessor, message: `LLM does not allow tuple type.`, }); else if (OpenApiTypeChecker_1.OpenApiTypeChecker.isReference(next)) { // UNABLE TO FIND MATCHED REFERENCE const key = next.$ref.split("#/components/schemas/")[1]; if (((_a = props.components.schemas) === null || _a === void 0 ? void 0 : _a[key]) === undefined) reasons.push({ schema: next, accessor: accessor, message: `unable to find reference type ${JSON.stringify(key)}.`, }); } }, components: props.components, schema: props.schema, accessor: props.accessor, refAccessor: props.refAccessor, }); if (reasons.length > 0) return { success: false, error: { method: "LlmSchemaV3_1Composer.schema", message: "Failed to compose LLM schema of v3.1", reasons, }, }; const visit = (input, accessor) => { var _a, _b, _c, _d; if (OpenApiTypeChecker_1.OpenApiTypeChecker.isOneOf(input)) { // UNION TYPE input.oneOf.forEach((s, i) => visit(s, `${accessor}.oneOf[${i}]`)); return 0; } else if (OpenApiTypeChecker_1.OpenApiTypeChecker.isReference(input)) { // REFERENCE TYPE const key = input.$ref.split("#/components/schemas/")[1]; const target = (_a = props.components.schemas) === null || _a === void 0 ? void 0 : _a[key]; if (target === undefined) return union.push(null); // UNREACHABLEE else if ( // KEEP THE REFERENCE TYPE props.config.reference === true || OpenApiTypeChecker_1.OpenApiTypeChecker.isRecursiveReference({ components: props.components, schema: input, })) { const out = () => union.push(Object.assign(Object.assign({}, input), { $ref: `#/$defs/${key}` })); if (props.$defs[key] !== undefined) return out(); props.$defs[key] = {}; const converted = LlmSchemaV3_1Composer.schema({ config: props.config, components: props.components, $defs: props.$defs, schema: target, refAccessor: props.refAccessor, accessor: `${(_b = props.refAccessor) !== null && _b !== void 0 ? _b : "$def"}[${JSON.stringify(key)}]`, }); if (converted.success === false) return union.push(null); // UNREACHABLE props.$defs[key] = converted.value; return out(); } else { // DISCARD THE REFERENCE TYPE const length = union.length; visit(target, accessor); if (length === union.length - 1 && union[union.length - 1] !== null) union[union.length - 1] = Object.assign(Object.assign({}, union[union.length - 1]), { description: JsonDescriptionUtil_1.JsonDescriptionUtil.cascade({ prefix: "#/components/schemas/", components: props.components, schema: input, escape: true, }) }); else attribute.description = JsonDescriptionUtil_1.JsonDescriptionUtil.cascade({ prefix: "#/components/schemas/", components: props.components, schema: input, escape: true, }); return union.length; } } else if (OpenApiTypeChecker_1.OpenApiTypeChecker.isObject(input)) { // OBJECT TYPE const properties = Object.entries((_c = input.properties) !== null && _c !== void 0 ? _c : {}).reduce((acc, [key, value]) => { const converted = LlmSchemaV3_1Composer.schema({ config: props.config, components: props.components, $defs: props.$defs, schema: value, refAccessor: props.refAccessor, accessor: `${accessor}.properties[${JSON.stringify(key)}]`, }); acc[key] = converted.success ? converted.value : null; if (converted.success === false) reasons.push(...converted.error.reasons); return acc; }, {}); if (Object.values(properties).some((v) => v === null)) return union.push(null); const additionalProperties = (() => { if (typeof input.additionalProperties === "object" && input.additionalProperties !== null) { const converted = LlmSchemaV3_1Composer.schema({ config: props.config, components: props.components, $defs: props.$defs, schema: input.additionalProperties, refAccessor: props.refAccessor, accessor: `${accessor}.additionalProperties`, }); if (converted.success === false) { reasons.push(...converted.error.reasons); return null; } return converted.value; } return input.additionalProperties; })(); if (additionalProperties === null) return union.push(null); return union.push(Object.assign(Object.assign({}, input), { properties: properties, additionalProperties, required: (_d = input.required) !== null && _d !== void 0 ? _d : [] })); } else if (OpenApiTypeChecker_1.OpenApiTypeChecker.isArray(input)) { const items = LlmSchemaV3_1Composer.schema({ config: props.config, components: props.components, $defs: props.$defs, schema: input.items, refAccessor: props.refAccessor, accessor: `${accessor}.items`, }); if (items.success === false) { reasons.push(...items.error.reasons); return union.push(null); } return union.push((props.config.constraint ? (x) => x : (x) => OpenApiConstraintShifter_1.OpenApiConstraintShifter.shiftArray(x))(Object.assign(Object.assign({}, input), { items: items.value }))); } else if (OpenApiTypeChecker_1.OpenApiTypeChecker.isString(input)) return union.push((props.config.constraint ? (x) => x : (x) => OpenApiConstraintShifter_1.OpenApiConstraintShifter.shiftString(x))(Object.assign({}, input))); else if (OpenApiTypeChecker_1.OpenApiTypeChecker.isNumber(input) || OpenApiTypeChecker_1.OpenApiTypeChecker.isInteger(input)) return union.push((props.config.constraint ? (x) => x : (x) => OpenApiConstraintShifter_1.OpenApiConstraintShifter.shiftNumeric(x))(Object.assign({}, input))); else if (OpenApiTypeChecker_1.OpenApiTypeChecker.isTuple(input)) return union.push(null); // UNREACHABLE else return union.push(Object.assign({}, input)); }; visit(props.schema, (_a = props.accessor) !== null && _a !== void 0 ? _a : "$input.schema"); if (union.some((u) => u === null)) return { success: false, error: { method: "LlmSchemaV3_1Composer.schema", message: "Failed to compose LLM schema of v3.1", reasons, }, }; else if (union.length === 0) return { success: true, value: Object.assign(Object.assign({}, attribute), { type: undefined }), }; else if (union.length === 1) return { success: true, value: Object.assign(Object.assign({}, attribute), union[0]), }; return { success: true, value: Object.assign(Object.assign({}, attribute), { oneOf: union.filter((u) => u !== null) }), }; }; /* ----------------------------------------------------------- SEPARATORS ----------------------------------------------------------- */ LlmSchemaV3_1Composer.separateParameters = (props) => { var _a, _b; const convention = (_a = props.convention) !== null && _a !== void 0 ? _a : ((key, type) => `${key}.${NamingConvention_1.NamingConvention.capitalize(type)}`); const [llm, human] = separateObject({ $defs: props.parameters.$defs, schema: props.parameters, predicate: props.predicate, convention, }); if (llm === null || human === null) return { llm: (_b = llm) !== null && _b !== void 0 ? _b : { type: "object", properties: {}, additionalProperties: false, required: [], $defs: {}, }, human: human, }; const output = { llm: Object.assign(Object.assign({}, llm), { $defs: Object.fromEntries(Object.entries(props.parameters.$defs).filter(([key]) => key.endsWith(".Llm"))), additionalProperties: false }), human: Object.assign(Object.assign({}, human), { $defs: Object.fromEntries(Object.entries(props.parameters.$defs).filter(([key]) => key.endsWith(".Human"))), additionalProperties: false }), }; for (const key of Object.keys(props.parameters.$defs)) if (key.endsWith(".Llm") === false && key.endsWith(".Human") === false) delete props.parameters.$defs[key]; if (Object.keys(output.llm.properties).length !== 0) { const components = {}; output.validate = OpenApiValidator_1.OpenApiValidator.create({ components, schema: LlmSchemaV3_1Composer.invert({ components, schema: output.llm, $defs: output.llm.$defs, }), required: true, }); } return output; }; const separateStation = (props) => { if (props.predicate(props.schema) === true) return [null, props.schema]; else if (LlmTypeCheckerV3_1_1.LlmTypeCheckerV3_1.isUnknown(props.schema) || LlmTypeCheckerV3_1_1.LlmTypeCheckerV3_1.isOneOf(props.schema)) return [props.schema, null]; else if (LlmTypeCheckerV3_1_1.LlmTypeCheckerV3_1.isObject(props.schema)) return separateObject({ predicate: props.predicate, convention: props.convention, $defs: props.$defs, schema: props.schema, }); else if (LlmTypeCheckerV3_1_1.LlmTypeCheckerV3_1.isArray(props.schema)) return separateArray({ predicate: props.predicate, convention: props.convention, $defs: props.$defs, schema: props.schema, }); else if (LlmTypeCheckerV3_1_1.LlmTypeCheckerV3_1.isReference(props.schema)) return separateReference({ predicate: props.predicate, convention: props.convention, $defs: props.$defs, schema: props.schema, }); return [props.schema, null]; }; const separateArray = (props) => { const [x, y] = separateStation({ predicate: props.predicate, convention: props.convention, $defs: props.$defs, schema: props.schema.items, }); return [ x !== null ? Object.assign(Object.assign({}, props.schema), { items: x }) : null, y !== null ? Object.assign(Object.assign({}, props.schema), { items: y }) : null, ]; }; const separateObject = (props) => { var _a, _b; // EMPTY OBJECT if (Object.keys((_a = props.schema.properties) !== null && _a !== void 0 ? _a : {}).length === 0 && !!props.schema.additionalProperties === false) return [props.schema, null]; const llm = Object.assign(Object.assign({}, props.schema), { properties: {}, additionalProperties: props.schema.additionalProperties }); const human = Object.assign(Object.assign({}, props.schema), { properties: {} }); for (const [key, value] of Object.entries((_b = props.schema.properties) !== null && _b !== void 0 ? _b : {})) { const [x, y] = separateStation({ predicate: props.predicate, convention: props.convention, $defs: props.$defs, schema: value, }); if (x !== null) llm.properties[key] = x; if (y !== null) human.properties[key] = y; } if (typeof props.schema.additionalProperties === "object" && props.schema.additionalProperties !== null) { const [dx, dy] = separateStation({ predicate: props.predicate, convention: props.convention, $defs: props.$defs, schema: props.schema.additionalProperties, }); llm.additionalProperties = dx !== null && dx !== void 0 ? dx : false; human.additionalProperties = dy !== null && dy !== void 0 ? dy : false; } return [ !!Object.keys(llm.properties).length || !!llm.additionalProperties ? shrinkRequired(llm) : null, !!Object.keys(human.properties).length || human.additionalProperties ? shrinkRequired(human) : null, ]; }; const separateReference = (props) => { var _a, _b, _c, _d, _e; const key = props.schema.$ref.split("#/$defs/")[1]; const humanKey = props.convention(key, "human"); const llmKey = props.convention(key, "llm"); // FIND EXISTING if (((_a = props.$defs) === null || _a === void 0 ? void 0 : _a[humanKey]) || ((_b = props.$defs) === null || _b === void 0 ? void 0 : _b[llmKey])) return [ ((_c = props.$defs) === null || _c === void 0 ? void 0 : _c[llmKey]) ? Object.assign(Object.assign({}, props.schema), { $ref: `#/$defs/${llmKey}` }) : null, ((_d = props.$defs) === null || _d === void 0 ? void 0 : _d[humanKey]) ? Object.assign(Object.assign({}, props.schema), { $ref: `#/$defs/${humanKey}` }) : null, ]; // PRE-ASSIGNMENT props.$defs[llmKey] = {}; props.$defs[humanKey] = {}; // DO COMPOSE const schema = (_e = props.$defs) === null || _e === void 0 ? void 0 : _e[key]; const [llm, human] = separateStation({ predicate: props.predicate, convention: props.convention, $defs: props.$defs, schema, }); // ONLY ONE if (llm === null || human === null) { delete props.$defs[llmKey]; delete props.$defs[humanKey]; return llm === null ? [null, props.schema] : [props.schema, null]; } // BOTH OF THEM return [ llm !== null ? Object.assign(Object.assign({}, props.schema), { $ref: `#/$defs/${llmKey}` }) : null, human !== null ? Object.assign(Object.assign({}, props.schema), { $ref: `#/$defs/${humanKey}` }) : null, ]; }; const shrinkRequired = (s) => { if (s.required !== undefined) s.required = s.required.filter((key) => { var _a; return ((_a = s.properties) === null || _a === void 0 ? void 0 : _a[key]) !== undefined; }); return s; }; /* ----------------------------------------------------------- INVERTERS ----------------------------------------------------------- */ LlmSchemaV3_1Composer.invert = (props) => { var _a, _b, _c, _d; var _e; const next = (schema) => LlmSchemaV3_1Composer.invert({ components: props.components, $defs: props.$defs, schema, }); if (LlmTypeCheckerV3_1_1.LlmTypeCheckerV3_1.isArray(props.schema)) return Object.assign(Object.assign(Object.assign({}, props.schema), LlmDescriptionInverter_1.LlmDescriptionInverter.array(props.schema.description)), { items: next(props.schema.items) }); else if (LlmTypeCheckerV3_1_1.LlmTypeCheckerV3_1.isObject(props.schema)) return Object.assign(Object.assign({}, props.schema), { properties: props.schema.properties ? Object.fromEntries(Object.entries(props.schema.properties).map(([key, value]) => [ key, next(value), ])) : undefined, additionalProperties: typeof props.schema.additionalProperties === "object" && props.schema.additionalProperties !== null ? next(props.schema.additionalProperties) : props.schema.additionalProperties }); else if (LlmTypeCheckerV3_1_1.LlmTypeCheckerV3_1.isReference(props.schema)) { const key = (_a = props.schema.$ref.split("#/$defs/").at(-1)) !== null && _a !== void 0 ? _a : ""; if (((_b = props.components.schemas) === null || _b === void 0 ? void 0 : _b[key]) === undefined) { (_c = (_e = props.components).schemas) !== null && _c !== void 0 ? _c : (_e.schemas = {}); props.components.schemas[key] = {}; props.components.schemas[key] = next((_d = props.$defs[key]) !== null && _d !== void 0 ? _d : {}); } return Object.assign(Object.assign({}, props.schema), { $ref: `#/components/schemas/${key}` }); } else if (LlmTypeCheckerV3_1_1.LlmTypeCheckerV3_1.isInteger(props.schema) || LlmTypeCheckerV3_1_1.LlmTypeCheckerV3_1.isNumber(props.schema)) return Object.assign(Object.assign({}, props.schema), LlmDescriptionInverter_1.LlmDescriptionInverter.numeric(props.schema.description)); else if (LlmTypeCheckerV3_1_1.LlmTypeCheckerV3_1.isString(props.schema)) return Object.assign(Object.assign({}, props.schema), LlmDescriptionInverter_1.LlmDescriptionInverter.string(props.schema.description)); return props.schema; }; })(LlmSchemaV3_1Composer || (exports.LlmSchemaV3_1Composer = LlmSchemaV3_1Composer = {}));