UNPKG

@samchon/openapi

Version:

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

394 lines (393 loc) 19.9 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.ChatGptSchemaComposer = void 0; const ChatGptTypeChecker_1 = require("../../utils/ChatGptTypeChecker"); const LlmTypeCheckerV3_1_1 = require("../../utils/LlmTypeCheckerV3_1"); const NamingConvention_1 = require("../../utils/NamingConvention"); 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 LlmSchemaV3_1Composer_1 = require("./LlmSchemaV3_1Composer"); var ChatGptSchemaComposer; (function (ChatGptSchemaComposer) { /** * @internal */ ChatGptSchemaComposer.IS_DEFS = true; /* ----------------------------------------------------------- CONVERTERS ----------------------------------------------------------- */ ChatGptSchemaComposer.parameters = (props) => { var _a; var _b; // polyfill (_a = (_b = props.config).strict) !== null && _a !== void 0 ? _a : (_b.strict = false); // validate const result = LlmSchemaV3_1Composer_1.LlmSchemaV3_1Composer.parameters(Object.assign(Object.assign({}, props), { config: { reference: props.config.reference, constraint: false, }, validate: props.config.strict === true ? validateStrict : undefined })); if (result.success === false) return result; // returns with transformation for (const key of Object.keys(result.value.$defs)) result.value.$defs[key] = transform({ config: props.config, schema: result.value.$defs[key], }); return { success: true, value: transform({ config: props.config, schema: result.value, }), }; }; ChatGptSchemaComposer.schema = (props) => { var _a; var _b; // polyfill (_a = (_b = props.config).strict) !== null && _a !== void 0 ? _a : (_b.strict = false); // validate const oldbie = new Set(Object.keys(props.$defs)); const result = LlmSchemaV3_1Composer_1.LlmSchemaV3_1Composer.schema(Object.assign(Object.assign({}, props), { config: { reference: props.config.reference, constraint: false, }, validate: props.config.strict === true ? validateStrict : undefined })); if (result.success === false) return result; // returns with transformation for (const key of Object.keys(props.$defs)) if (oldbie.has(key) === false) props.$defs[key] = transform({ config: props.config, schema: props.$defs[key], }); return { success: true, value: transform({ config: props.config, schema: result.value, }), }; }; const validateStrict = (schema, accessor) => { var _a, _b; const reasons = []; if (OpenApiTypeChecker_1.OpenApiTypeChecker.isObject(schema)) { if (!!schema.additionalProperties) reasons.push({ schema: schema, accessor: `${accessor}.additionalProperties`, message: "ChatGPT does not allow additionalProperties in strict mode, the dynamic key typed object.", }); for (const key of Object.keys((_a = schema.properties) !== null && _a !== void 0 ? _a : {})) if (((_b = schema.required) === null || _b === void 0 ? void 0 : _b.includes(key)) === false) reasons.push({ schema: schema, accessor: `${accessor}.properties.${key}`, message: "ChatGPT does not allow optional properties in strict mode.", }); } return reasons; }; const transform = (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(ChatGptSchemaComposer.schema).filter(([key, value]) => key.startsWith("x-") && value !== undefined))); const visit = (input) => { if (LlmTypeCheckerV3_1_1.LlmTypeCheckerV3_1.isOneOf(input)) input.oneOf.forEach(visit); else if (LlmTypeCheckerV3_1_1.LlmTypeCheckerV3_1.isArray(input)) union.push(Object.assign(Object.assign({}, input), { items: transform({ config: props.config, schema: input.items, }) })); else if (LlmTypeCheckerV3_1_1.LlmTypeCheckerV3_1.isObject(input)) union.push(Object.assign(Object.assign({}, input), { properties: Object.fromEntries(Object.entries(input.properties).map(([key, value]) => [ key, transform({ config: props.config, schema: value, }), ])), additionalProperties: props.config.strict === true ? false : typeof input.additionalProperties === "object" && input.additionalProperties !== null ? transform({ config: props.config, schema: input.additionalProperties, }) : input.additionalProperties, description: JsonDescriptionUtil_1.JsonDescriptionUtil.take(input) })); else if (LlmTypeCheckerV3_1_1.LlmTypeCheckerV3_1.isConstant(input) === false) union.push(input); }; const visitConstant = (input) => { const insert = (value) => { var _a; const matched = union.find((u) => (u === null || u === void 0 ? void 0 : u.type) === typeof value); if (matched !== undefined) { (_a = matched.enum) !== null && _a !== void 0 ? _a : (matched.enum = []); matched.enum.push(value); } else union.push({ type: typeof value, enum: [value], }); }; if (OpenApiTypeChecker_1.OpenApiTypeChecker.isConstant(input)) insert(input.const); else if (OpenApiTypeChecker_1.OpenApiTypeChecker.isOneOf(input)) input.oneOf.forEach((s) => visitConstant(s)); }; visit(props.schema); visitConstant(props.schema); if (union.length === 0) return Object.assign(Object.assign({}, attribute), { type: undefined }); else if (union.length === 1) return Object.assign(Object.assign(Object.assign({}, attribute), union[0]), { description: ChatGptTypeChecker_1.ChatGptTypeChecker.isReference(union[0]) ? undefined : ((_a = union[0].description) !== null && _a !== void 0 ? _a : attribute.description) }); return Object.assign(Object.assign({}, attribute), { anyOf: union.map((u) => (Object.assign(Object.assign({}, u), { description: ChatGptTypeChecker_1.ChatGptTypeChecker.isReference(u) ? undefined : u.description }))) }); }; /* ----------------------------------------------------------- SEPARATORS ----------------------------------------------------------- */ ChatGptSchemaComposer.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({ predicate: props.predicate, convention, $defs: props.parameters.$defs, schema: props.parameters, }); if (llm === null || human === null) return { llm: (_b = llm) !== null && _b !== void 0 ? _b : { type: "object", properties: {}, required: [], additionalProperties: false, $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: ChatGptSchemaComposer.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 (ChatGptTypeChecker_1.ChatGptTypeChecker.isUnknown(props.schema) || ChatGptTypeChecker_1.ChatGptTypeChecker.isAnyOf(props.schema)) return [props.schema, null]; else if (ChatGptTypeChecker_1.ChatGptTypeChecker.isObject(props.schema)) return separateObject({ predicate: props.predicate, convention: props.convention, $defs: props.$defs, schema: props.schema, }); else if (ChatGptTypeChecker_1.ChatGptTypeChecker.isArray(props.schema)) return separateArray({ predicate: props.predicate, convention: props.convention, $defs: props.$defs, schema: props.schema, }); else if (ChatGptTypeChecker_1.ChatGptTypeChecker.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) => { s.required = s.required.filter((key) => { var _a; return ((_a = s.properties) === null || _a === void 0 ? void 0 : _a[key]) !== undefined; }); return s; }; /* ----------------------------------------------------------- INVERTERS ----------------------------------------------------------- */ ChatGptSchemaComposer.invert = (props) => { const union = []; const attribute = Object.assign(Object.assign({ title: props.schema.title, description: props.schema.description }, Object.fromEntries(Object.entries(props.schema).filter(([key, value]) => key.startsWith("x-") && value !== undefined))), { example: props.schema.example, examples: props.schema.examples }); const next = (schema) => ChatGptSchemaComposer.invert({ components: props.components, $defs: props.$defs, schema, }); const visit = (schema) => { var _a, _b, _c, _d, _e, _f; var _g; if (ChatGptTypeChecker_1.ChatGptTypeChecker.isArray(schema)) union.push(Object.assign(Object.assign(Object.assign({}, schema), LlmDescriptionInverter_1.LlmDescriptionInverter.array(schema.description)), { items: next(schema.items) })); else if (ChatGptTypeChecker_1.ChatGptTypeChecker.isObject(schema)) union.push(Object.assign(Object.assign({}, schema), { properties: Object.fromEntries(Object.entries(schema.properties).map(([key, value]) => [ key, next(value), ])), additionalProperties: typeof schema.additionalProperties === "object" && schema.additionalProperties !== null ? next(schema.additionalProperties) : schema.additionalProperties })); else if (ChatGptTypeChecker_1.ChatGptTypeChecker.isAnyOf(schema)) schema.anyOf.forEach(visit); else if (ChatGptTypeChecker_1.ChatGptTypeChecker.isReference(schema)) { const key = schema.$ref.split("#/$defs/")[1]; if (((_a = props.components.schemas) === null || _a === void 0 ? void 0 : _a[key]) === undefined) { (_b = (_g = props.components).schemas) !== null && _b !== void 0 ? _b : (_g.schemas = {}); props.components.schemas[key] = {}; props.components.schemas[key] = next((_c = props.$defs[key]) !== null && _c !== void 0 ? _c : {}); } union.push(Object.assign(Object.assign({}, schema), { $ref: `#/components/schemas/${key}` })); } else if (ChatGptTypeChecker_1.ChatGptTypeChecker.isBoolean(schema)) if (!!((_d = schema.enum) === null || _d === void 0 ? void 0 : _d.length)) schema.enum.forEach((v) => union.push({ const: v, })); else union.push(schema); else if (ChatGptTypeChecker_1.ChatGptTypeChecker.isInteger(schema) || ChatGptTypeChecker_1.ChatGptTypeChecker.isNumber(schema)) if (!!((_e = schema.enum) === null || _e === void 0 ? void 0 : _e.length)) schema.enum.forEach((v) => union.push({ const: v, })); else union.push(Object.assign(Object.assign(Object.assign({}, schema), LlmDescriptionInverter_1.LlmDescriptionInverter.numeric(schema.description)), { enum: undefined })); else if (ChatGptTypeChecker_1.ChatGptTypeChecker.isString(schema)) if (!!((_f = schema.enum) === null || _f === void 0 ? void 0 : _f.length)) schema.enum.forEach((v) => union.push({ const: v, })); else union.push(Object.assign(Object.assign(Object.assign({}, schema), LlmDescriptionInverter_1.LlmDescriptionInverter.string(schema.description)), { enum: undefined })); else union.push(Object.assign({}, schema)); }; visit(props.schema); return Object.assign(Object.assign({}, attribute), (union.length === 0 ? { type: undefined } : union.length === 1 ? Object.assign({}, union[0]) : { oneOf: union.map((u) => (Object.assign(Object.assign({}, u), { nullable: undefined }))) })); }; })(ChatGptSchemaComposer || (exports.ChatGptSchemaComposer = ChatGptSchemaComposer = {}));