From 13e5f04abf8552b384fa889f4a6dc7c2624f0558 Mon Sep 17 00:00:00 2001 From: Jeongho Nam Date: Fri, 19 Dec 2025 13:56:59 +0900 Subject: [PATCH 1/2] Remove individual LLM schemas --- src/HttpMigration.ts | 2 +- .../HttpMigrateApplicationComposer.ts | 8 +- .../{migrate => }/HttpMigrateRouteAccessor.ts | 8 +- .../{migrate => }/HttpMigrateRouteComposer.ts | 10 +- .../{llm => }/LlmDescriptionInverter.ts | 4 +- .../{llm => }/LlmParametersComposer.ts | 8 +- src/composers/LlmSchemaComposer.ts | 4 +- src/composers/llm/ChatGptSchemaComposer.ts | 178 ----- src/composers/llm/ClaudeSchemaComposer.ts | 63 -- src/composers/llm/GeminiSchemaComposer.ts | 594 --------------- src/composers/llm/LlmSchemaV3Composer.ts | 340 --------- src/composers/llm/LlmSchemaV3_1Composer.ts | 716 ------------------ src/index.ts | 46 +- src/structures/IChatGptSchema.ts | 342 --------- src/structures/IClaudeSchema.ts | 138 ---- src/structures/IGeminiSchema.ts | 411 ---------- src/structures/ILlmSchemaV3.ts | 410 ---------- src/structures/ILlmSchemaV3_1.ts | 450 ----------- src/utils/ChatGptTypeChecker.ts | 391 ---------- src/utils/ClaudeTypeChecker.ts | 11 - src/utils/DeepSeekTypeChecker.ts | 11 - src/utils/GeminiTypeChecker.ts | 407 ---------- src/utils/LlamaTypeChecker.ts | 11 - src/utils/LlmTypeCheckerV3.ts | 307 -------- src/utils/LlmTypeCheckerV3_1.ts | 206 ----- 25 files changed, 34 insertions(+), 5042 deletions(-) rename src/composers/{migrate => }/HttpMigrateApplicationComposer.ts (87%) rename src/composers/{migrate => }/HttpMigrateRouteAccessor.ts (95%) rename src/composers/{migrate => }/HttpMigrateRouteComposer.ts (98%) rename src/composers/{llm => }/LlmDescriptionInverter.ts (97%) rename src/composers/{llm => }/LlmParametersComposer.ts (85%) delete mode 100644 src/composers/llm/ChatGptSchemaComposer.ts delete mode 100644 src/composers/llm/ClaudeSchemaComposer.ts delete mode 100644 src/composers/llm/GeminiSchemaComposer.ts delete mode 100644 src/composers/llm/LlmSchemaV3Composer.ts delete mode 100644 src/composers/llm/LlmSchemaV3_1Composer.ts delete mode 100644 src/structures/IChatGptSchema.ts delete mode 100644 src/structures/IClaudeSchema.ts delete mode 100644 src/structures/IGeminiSchema.ts delete mode 100644 src/structures/ILlmSchemaV3.ts delete mode 100644 src/structures/ILlmSchemaV3_1.ts delete mode 100644 src/utils/ChatGptTypeChecker.ts delete mode 100644 src/utils/ClaudeTypeChecker.ts delete mode 100644 src/utils/DeepSeekTypeChecker.ts delete mode 100644 src/utils/GeminiTypeChecker.ts delete mode 100644 src/utils/LlamaTypeChecker.ts delete mode 100644 src/utils/LlmTypeCheckerV3.ts delete mode 100644 src/utils/LlmTypeCheckerV3_1.ts diff --git a/src/HttpMigration.ts b/src/HttpMigration.ts index c0a7d8ff..c9733f23 100644 --- a/src/HttpMigration.ts +++ b/src/HttpMigration.ts @@ -2,7 +2,7 @@ import { OpenApi } from "./OpenApi"; import { OpenApiV3 } from "./OpenApiV3"; import { OpenApiV3_1 } from "./OpenApiV3_1"; import { SwaggerV2 } from "./SwaggerV2"; -import { HttpMigrateApplicationComposer } from "./composers/migrate/HttpMigrateApplicationComposer"; +import { HttpMigrateApplicationComposer } from "./composers/HttpMigrateApplicationComposer"; import { HttpMigrateRouteFetcher } from "./http/HttpMigrateRouteFetcher"; import { IHttpConnection } from "./structures/IHttpConnection"; import { IHttpMigrateApplication } from "./structures/IHttpMigrateApplication"; diff --git a/src/composers/migrate/HttpMigrateApplicationComposer.ts b/src/composers/HttpMigrateApplicationComposer.ts similarity index 87% rename from src/composers/migrate/HttpMigrateApplicationComposer.ts rename to src/composers/HttpMigrateApplicationComposer.ts index 7ef93774..29f32335 100644 --- a/src/composers/migrate/HttpMigrateApplicationComposer.ts +++ b/src/composers/HttpMigrateApplicationComposer.ts @@ -1,7 +1,7 @@ -import { OpenApi } from "../../OpenApi"; -import { IHttpMigrateApplication } from "../../structures/IHttpMigrateApplication"; -import { IHttpMigrateRoute } from "../../structures/IHttpMigrateRoute"; -import { EndpointUtil } from "../../utils/EndpointUtil"; +import { OpenApi } from "../OpenApi"; +import { IHttpMigrateApplication } from "../structures/IHttpMigrateApplication"; +import { IHttpMigrateRoute } from "../structures/IHttpMigrateRoute"; +import { EndpointUtil } from "../utils/EndpointUtil"; import { HttpMigrateRouteAccessor } from "./HttpMigrateRouteAccessor"; import { HttpMigrateRouteComposer } from "./HttpMigrateRouteComposer"; diff --git a/src/composers/migrate/HttpMigrateRouteAccessor.ts b/src/composers/HttpMigrateRouteAccessor.ts similarity index 95% rename from src/composers/migrate/HttpMigrateRouteAccessor.ts rename to src/composers/HttpMigrateRouteAccessor.ts index 403d9be2..8918fccd 100644 --- a/src/composers/migrate/HttpMigrateRouteAccessor.ts +++ b/src/composers/HttpMigrateRouteAccessor.ts @@ -1,7 +1,7 @@ -import { IHttpMigrateRoute } from "../../structures/IHttpMigrateRoute"; -import { EndpointUtil } from "../../utils/EndpointUtil"; -import { Escaper } from "../../utils/Escaper"; -import { MapUtil } from "../../utils/MapUtil"; +import { IHttpMigrateRoute } from "../structures/IHttpMigrateRoute"; +import { EndpointUtil } from "../utils/EndpointUtil"; +import { Escaper } from "../utils/Escaper"; +import { MapUtil } from "../utils/MapUtil"; export namespace HttpMigrateRouteAccessor { export const overwrite = (routes: IHttpMigrateRoute[]): void => { diff --git a/src/composers/migrate/HttpMigrateRouteComposer.ts b/src/composers/HttpMigrateRouteComposer.ts similarity index 98% rename from src/composers/migrate/HttpMigrateRouteComposer.ts rename to src/composers/HttpMigrateRouteComposer.ts index cd9ee85b..0c9f3592 100644 --- a/src/composers/migrate/HttpMigrateRouteComposer.ts +++ b/src/composers/HttpMigrateRouteComposer.ts @@ -1,8 +1,8 @@ -import { OpenApi } from "../../OpenApi"; -import { IHttpMigrateRoute } from "../../structures/IHttpMigrateRoute"; -import { EndpointUtil } from "../../utils/EndpointUtil"; -import { Escaper } from "../../utils/Escaper"; -import { OpenApiTypeChecker } from "../../utils/OpenApiTypeChecker"; +import { OpenApi } from "../OpenApi"; +import { IHttpMigrateRoute } from "../structures/IHttpMigrateRoute"; +import { EndpointUtil } from "../utils/EndpointUtil"; +import { Escaper } from "../utils/Escaper"; +import { OpenApiTypeChecker } from "../utils/OpenApiTypeChecker"; export namespace HttpMigrateRouteComposer { export interface IProps { diff --git a/src/composers/llm/LlmDescriptionInverter.ts b/src/composers/LlmDescriptionInverter.ts similarity index 97% rename from src/composers/llm/LlmDescriptionInverter.ts rename to src/composers/LlmDescriptionInverter.ts index ad3cc926..5cbe9762 100644 --- a/src/composers/llm/LlmDescriptionInverter.ts +++ b/src/composers/LlmDescriptionInverter.ts @@ -1,5 +1,5 @@ -import { OpenApi } from "../../OpenApi"; -import { OpenApiExclusiveEmender } from "../../utils/OpenApiExclusiveEmender"; +import { OpenApi } from "../OpenApi"; +import { OpenApiExclusiveEmender } from "../utils/OpenApiExclusiveEmender"; export namespace LlmDescriptionInverter { export const numeric = ( diff --git a/src/composers/llm/LlmParametersComposer.ts b/src/composers/LlmParametersComposer.ts similarity index 85% rename from src/composers/llm/LlmParametersComposer.ts rename to src/composers/LlmParametersComposer.ts index 4671d8ea..2ec38440 100644 --- a/src/composers/llm/LlmParametersComposer.ts +++ b/src/composers/LlmParametersComposer.ts @@ -1,7 +1,7 @@ -import { OpenApi } from "../../OpenApi"; -import { IOpenApiSchemaError } from "../../structures/IOpenApiSchemaError"; -import { IResult } from "../../structures/IResult"; -import { OpenApiTypeChecker } from "../../utils/OpenApiTypeChecker"; +import { OpenApi } from "../OpenApi"; +import { IOpenApiSchemaError } from "../structures/IOpenApiSchemaError"; +import { IResult } from "../structures/IResult"; +import { OpenApiTypeChecker } from "../utils/OpenApiTypeChecker"; /** @internal */ export namespace LlmParametersFinder { diff --git a/src/composers/LlmSchemaComposer.ts b/src/composers/LlmSchemaComposer.ts index e3c1e50e..b65cebe3 100644 --- a/src/composers/LlmSchemaComposer.ts +++ b/src/composers/LlmSchemaComposer.ts @@ -10,8 +10,8 @@ import { OpenApiConstraintShifter } from "../utils/OpenApiConstraintShifter"; import { OpenApiTypeChecker } from "../utils/OpenApiTypeChecker"; import { OpenApiValidator } from "../utils/OpenApiValidator"; import { JsonDescriptionUtil } from "../utils/internal/JsonDescriptionUtil"; -import { LlmDescriptionInverter } from "./llm/LlmDescriptionInverter"; -import { LlmParametersFinder } from "./llm/LlmParametersComposer"; +import { LlmDescriptionInverter } from "./LlmDescriptionInverter"; +import { LlmParametersFinder } from "./LlmParametersComposer"; export namespace LlmSchemaComposer { /* ----------------------------------------------------------- diff --git a/src/composers/llm/ChatGptSchemaComposer.ts b/src/composers/llm/ChatGptSchemaComposer.ts deleted file mode 100644 index 58deb775..00000000 --- a/src/composers/llm/ChatGptSchemaComposer.ts +++ /dev/null @@ -1,178 +0,0 @@ -import { OpenApi } from "../../OpenApi"; -import { IChatGptSchema } from "../../structures/IChatGptSchema"; -import { IGeminiSchema } from "../../structures/IGeminiSchema"; -import { ILlmFunction } from "../../structures/ILlmFunction"; -import { ILlmSchemaV3_1 } from "../../structures/ILlmSchemaV3_1"; -import { IOpenApiSchemaError } from "../../structures/IOpenApiSchemaError"; -import { IResult } from "../../structures/IResult"; -import { GeminiTypeChecker } from "../../utils/GeminiTypeChecker"; -import { OpenApiConstraintShifter } from "../../utils/OpenApiConstraintShifter"; -import { OpenApiTypeChecker } from "../../utils/OpenApiTypeChecker"; -import { JsonDescriptionUtil } from "../../utils/internal/JsonDescriptionUtil"; -import { GeminiSchemaComposer } from "./GeminiSchemaComposer"; -import { LlmSchemaV3_1Composer } from "./LlmSchemaV3_1Composer"; - -export namespace ChatGptSchemaComposer { - /** @internal */ - export const IS_DEFS = true; - - export const DEFAULT_CONFIG: IChatGptSchema.IConfig = { - reference: true, - strict: false, - }; - - export const parameters = (props: { - config: IChatGptSchema.IConfig; - components: OpenApi.IComponents; - schema: OpenApi.IJsonSchema.IObject | OpenApi.IJsonSchema.IReference; - accessor?: string; - refAccessor?: string; - }): IResult => { - // polyfill - props.config.strict ??= false; - - // validate - const result: IResult = - LlmSchemaV3_1Composer.parameters({ - ...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, - }) as IChatGptSchema.IParameters, - }; - }; - - export const schema = (props: { - config: IChatGptSchema.IConfig; - components: OpenApi.IComponents; - $defs: Record; - schema: OpenApi.IJsonSchema; - accessor?: string; - refAccessor?: string; - }): IResult => { - // polyfill - props.config.strict ??= false; - - // validate - const oldbie: Set = new Set(Object.keys(props.$defs)); - const result: IResult = - LlmSchemaV3_1Composer.schema({ - ...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: OpenApi.IJsonSchema, - accessor: string, - ): IOpenApiSchemaError.IReason[] => { - const reasons: IOpenApiSchemaError.IReason[] = []; - if (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(schema.properties ?? {})) - if (schema.required?.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: { - config: IChatGptSchema.IConfig; - schema: ILlmSchemaV3_1; - }): IChatGptSchema => { - const schema: IGeminiSchema = GeminiSchemaComposer.transform(props); - GeminiTypeChecker.visit({ - closure: (next) => { - if (GeminiTypeChecker.isString(next)) - OpenApiConstraintShifter.shiftString(next); - else if ( - GeminiTypeChecker.isInteger(next) || - GeminiTypeChecker.isNumber(next) - ) - OpenApiConstraintShifter.shiftNumeric(next); - else if (GeminiTypeChecker.isArray(next)) - OpenApiConstraintShifter.shiftArray(next); - else if ( - GeminiTypeChecker.isObject(next) && - props.config.strict === true - ) { - next.additionalProperties = false; - next.description = JsonDescriptionUtil.take(next); - } - }, - schema, - }); - if (props.config.strict === true) - GeminiTypeChecker.visit({ - closure: (next) => { - if (GeminiTypeChecker.isReference(next)) { - next.title = undefined; - next.description = undefined; - } - }, - schema, - }); - return schema satisfies IChatGptSchema; - }; - - export const separateParameters = (props: { - parameters: IChatGptSchema.IParameters; - predicate: (schema: IChatGptSchema) => boolean; - convention?: (key: string, type: "llm" | "human") => string; - equals?: boolean; - }): ILlmFunction.ISeparated<"chatgpt"> => - GeminiSchemaComposer.separateParameters(props); - - export const invert = (props: { - components: OpenApi.IComponents; - schema: IChatGptSchema; - $defs: Record; - }): OpenApi.IJsonSchema => GeminiSchemaComposer.invert(props); -} diff --git a/src/composers/llm/ClaudeSchemaComposer.ts b/src/composers/llm/ClaudeSchemaComposer.ts deleted file mode 100644 index bf70b619..00000000 --- a/src/composers/llm/ClaudeSchemaComposer.ts +++ /dev/null @@ -1,63 +0,0 @@ -import { OpenApi } from "../../OpenApi"; -import { IClaudeSchema } from "../../structures/IClaudeSchema"; -import { ILlmFunction } from "../../structures/ILlmFunction"; -import { IOpenApiSchemaError } from "../../structures/IOpenApiSchemaError"; -import { IResult } from "../../structures/IResult"; -import { LlmSchemaV3_1Composer } from "./LlmSchemaV3_1Composer"; - -export namespace ClaudeSchemaComposer { - /** @internal */ - export const IS_DEFS = true; - - export const DEFAULT_CONFIG: IClaudeSchema.IConfig = { - reference: true, - }; - - export const parameters = (props: { - config: IClaudeSchema.IConfig; - components: OpenApi.IComponents; - schema: OpenApi.IJsonSchema.IObject | OpenApi.IJsonSchema.IReference; - accessor?: string; - refAccessor?: string; - }): IResult => - LlmSchemaV3_1Composer.parameters({ - ...props, - config: { - reference: props.config.reference, - constraint: true, - }, - }); - - export const schema = (props: { - config: IClaudeSchema.IConfig; - components: OpenApi.IComponents; - $defs: Record; - schema: OpenApi.IJsonSchema; - accessor?: string; - refAccessor?: string; - }): IResult => - LlmSchemaV3_1Composer.schema({ - ...props, - config: { - reference: props.config.reference, - constraint: true, - }, - }); - - export const separateParameters = (props: { - parameters: IClaudeSchema.IParameters; - predicate: (schema: IClaudeSchema) => boolean; - convention?: (key: string, type: "llm" | "human") => string; - equals?: boolean; - }): ILlmFunction.ISeparated<"claude"> => { - const separated: ILlmFunction.ISeparated<"3.1"> = - LlmSchemaV3_1Composer.separateParameters(props); - return separated as any as ILlmFunction.ISeparated<"claude">; - }; - - export const invert = (props: { - components: OpenApi.IComponents; - schema: IClaudeSchema; - $defs: Record; - }): OpenApi.IJsonSchema => LlmSchemaV3_1Composer.invert(props); -} diff --git a/src/composers/llm/GeminiSchemaComposer.ts b/src/composers/llm/GeminiSchemaComposer.ts deleted file mode 100644 index 7f4ac861..00000000 --- a/src/composers/llm/GeminiSchemaComposer.ts +++ /dev/null @@ -1,594 +0,0 @@ -import { OpenApi } from "../../OpenApi"; -import { IGeminiSchema } from "../../structures/IGeminiSchema"; -import { IJsonSchemaAttribute } from "../../structures/IJsonSchemaAttribute"; -import { ILlmFunction } from "../../structures/ILlmFunction"; -import { ILlmSchemaV3_1 } from "../../structures/ILlmSchemaV3_1"; -import { IOpenApiSchemaError } from "../../structures/IOpenApiSchemaError"; -import { IResult } from "../../structures/IResult"; -import { GeminiTypeChecker } from "../../utils/GeminiTypeChecker"; -import { LlmTypeCheckerV3_1 } from "../../utils/LlmTypeCheckerV3_1"; -import { NamingConvention } from "../../utils/NamingConvention"; -import { OpenApiTypeChecker } from "../../utils/OpenApiTypeChecker"; -import { OpenApiValidator } from "../../utils/OpenApiValidator"; -import { LlmDescriptionInverter } from "./LlmDescriptionInverter"; -import { LlmSchemaV3_1Composer } from "./LlmSchemaV3_1Composer"; - -export namespace GeminiSchemaComposer { - /** @internal */ - export const IS_DEFS = true; - export const DEFAULT_CONFIG: IGeminiSchema.IConfig = { - reference: true, - }; - - /* ----------------------------------------------------------- - CONVERTERS - ----------------------------------------------------------- */ - export const parameters = (props: { - config: IGeminiSchema.IConfig; - components: OpenApi.IComponents; - schema: OpenApi.IJsonSchema.IObject | OpenApi.IJsonSchema.IReference; - accessor?: string; - refAccessor?: string; - }): IResult => { - // validate - const result: IResult = - LlmSchemaV3_1Composer.parameters({ - ...props, - config: { - reference: props.config.reference, - constraint: true, - }, - }); - 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, - }) as IGeminiSchema.IParameters, - }; - }; - - export const schema = (props: { - config: IGeminiSchema.IConfig; - components: OpenApi.IComponents; - $defs: Record; - schema: OpenApi.IJsonSchema; - accessor?: string; - refAccessor?: string; - }): IResult => { - // validate - const oldbie: Set = new Set(Object.keys(props.$defs)); - const result: IResult = - LlmSchemaV3_1Composer.schema({ - ...props, - config: { - reference: props.config.reference, - constraint: true, - }, - }); - 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, - }), - }; - }; - - /** @internal */ - export const transform = (props: { - config: IGeminiSchema.IConfig; - schema: ILlmSchemaV3_1; - }): IGeminiSchema => { - const union: Array = []; - const attribute: IJsonSchemaAttribute = { - title: props.schema.title, - description: props.schema.description, - deprecated: props.schema.deprecated, - readOnly: props.schema.readOnly, - writeOnly: props.schema.writeOnly, - example: props.schema.example, - examples: props.schema.examples, - ...Object.fromEntries( - Object.entries(schema).filter( - ([key, value]) => key.startsWith("x-") && value !== undefined, - ), - ), - }; - const visit = (input: ILlmSchemaV3_1): void => { - if (LlmTypeCheckerV3_1.isOneOf(input)) input.oneOf.forEach(visit); - else if (LlmTypeCheckerV3_1.isArray(input)) - union.push({ - ...input, - items: transform({ - config: props.config, - schema: input.items, - }), - }); - else if (LlmTypeCheckerV3_1.isObject(input)) - union.push({ - ...input, - properties: Object.fromEntries( - Object.entries(input.properties).map(([key, value]) => [ - key, - transform({ - config: props.config, - schema: value, - }), - ]), - ), - additionalProperties: - typeof input.additionalProperties === "object" && - input.additionalProperties !== null - ? transform({ - config: props.config, - schema: input.additionalProperties, - }) - : input.additionalProperties, - }); - else if (LlmTypeCheckerV3_1.isConstant(input) === false) - union.push(input); - }; - const visitConstant = (input: ILlmSchemaV3_1): void => { - const insert = (value: any): void => { - const matched: IGeminiSchema.IString | undefined = union.find( - (u) => - (u as (IJsonSchemaAttribute & { type: string }) | undefined) - ?.type === typeof value, - ) as IGeminiSchema.IString | undefined; - if (matched !== undefined) { - matched.enum ??= []; - matched.enum.push(value); - } else - union.push({ - type: typeof value as "number", - enum: [value], - }); - }; - if (OpenApiTypeChecker.isConstant(input)) insert(input.const); - else if (OpenApiTypeChecker.isOneOf(input)) - input.oneOf.forEach((s) => visitConstant(s as ILlmSchemaV3_1)); - }; - visit(props.schema); - visitConstant(props.schema); - if (union.length === 0) - return { - ...attribute, - type: undefined, - }; - else if (union.length === 1) - return { - ...attribute, - ...union[0], - description: union[0].description ?? attribute.description, - }; - return { - ...attribute, - anyOf: union, - "x-discriminator": - LlmTypeCheckerV3_1.isOneOf(props.schema) && - props.schema.discriminator !== undefined && - props.schema.oneOf.length === union.length && - union.every( - (e) => - GeminiTypeChecker.isReference(e) || GeminiTypeChecker.isNull(e), - ) - ? props.schema.discriminator - : undefined, - }; - }; - - /* ----------------------------------------------------------- - SEPARATORS - ----------------------------------------------------------- */ - export const separateParameters = (props: { - parameters: IGeminiSchema.IParameters; - predicate: (schema: IGeminiSchema) => boolean; - convention?: (key: string, type: "llm" | "human") => string; - equals?: boolean; - }): ILlmFunction.ISeparated<"chatgpt"> => { - const convention = - props.convention ?? - ((key, type) => `${key}.${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: (llm as IGeminiSchema.IParameters | null) ?? { - type: "object", - properties: {} as Record, - required: [], - additionalProperties: false, - $defs: {}, - }, - human: human as IGeminiSchema.IParameters | null, - }; - const output: ILlmFunction.ISeparated<"chatgpt"> = { - llm: { - ...llm, - $defs: Object.fromEntries( - Object.entries(props.parameters.$defs).filter(([key]) => - key.endsWith(".Llm"), - ), - ), - additionalProperties: false, - }, - human: { - ...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: OpenApi.IComponents = {}; - output.validate = OpenApiValidator.create({ - components, - schema: invert({ - components, - schema: output.llm, - $defs: output.llm.$defs, - }), - required: true, - equals: props.equals, - }); - } - return output; - }; - - const separateStation = (props: { - predicate: (schema: IGeminiSchema) => boolean; - convention: (key: string, type: "llm" | "human") => string; - $defs: Record; - schema: IGeminiSchema; - }): [IGeminiSchema | null, IGeminiSchema | null] => { - if (props.predicate(props.schema) === true) return [null, props.schema]; - else if ( - GeminiTypeChecker.isUnknown(props.schema) || - GeminiTypeChecker.isAnyOf(props.schema) - ) - return [props.schema, null]; - else if (GeminiTypeChecker.isObject(props.schema)) - return separateObject({ - predicate: props.predicate, - convention: props.convention, - $defs: props.$defs, - schema: props.schema, - }); - else if (GeminiTypeChecker.isArray(props.schema)) - return separateArray({ - predicate: props.predicate, - convention: props.convention, - $defs: props.$defs, - schema: props.schema, - }); - else if (GeminiTypeChecker.isReference(props.schema)) - return separateReference({ - predicate: props.predicate, - convention: props.convention, - $defs: props.$defs, - schema: props.schema, - }); - return [props.schema, null]; - }; - - const separateArray = (props: { - predicate: (schema: IGeminiSchema) => boolean; - convention: (key: string, type: "llm" | "human") => string; - $defs: Record; - schema: IGeminiSchema.IArray; - }): [IGeminiSchema.IArray | null, IGeminiSchema.IArray | null] => { - const [x, y] = separateStation({ - predicate: props.predicate, - convention: props.convention, - $defs: props.$defs, - schema: props.schema.items, - }); - return [ - x !== null - ? { - ...props.schema, - items: x, - } - : null, - y !== null - ? { - ...props.schema, - items: y, - } - : null, - ]; - }; - - const separateObject = (props: { - $defs: Record; - predicate: (schema: IGeminiSchema) => boolean; - convention: (key: string, type: "llm" | "human") => string; - schema: IGeminiSchema.IObject; - }): [IGeminiSchema.IObject | null, IGeminiSchema.IObject | null] => { - // EMPTY OBJECT - if ( - Object.keys(props.schema.properties ?? {}).length === 0 && - !!props.schema.additionalProperties === false - ) - return [props.schema, null]; - - const llm = { - ...props.schema, - properties: {} as Record, - additionalProperties: props.schema.additionalProperties, - } satisfies IGeminiSchema.IObject; - const human = { - ...props.schema, - properties: {} as Record, - } satisfies IGeminiSchema.IObject; - - for (const [key, value] of Object.entries(props.schema.properties ?? {})) { - 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 ?? false; - human.additionalProperties = 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: { - predicate: (schema: IGeminiSchema) => boolean; - convention: (key: string, type: "llm" | "human") => string; - $defs: Record; - schema: IGeminiSchema.IReference; - }): [IGeminiSchema.IReference | null, IGeminiSchema.IReference | null] => { - const key: string = props.schema.$ref.split("#/$defs/")[1]; - const humanKey: string = props.convention(key, "human"); - const llmKey: string = props.convention(key, "llm"); - - // FIND EXISTING - if (props.$defs?.[humanKey] || props.$defs?.[llmKey]) - return [ - props.$defs?.[llmKey] - ? { - ...props.schema, - $ref: `#/$defs/${llmKey}`, - } - : null, - props.$defs?.[humanKey] - ? { - ...props.schema, - $ref: `#/$defs/${humanKey}`, - } - : null, - ]; - - // PRE-ASSIGNMENT - props.$defs![llmKey] = {}; - props.$defs![humanKey] = {}; - - // DO COMPOSE - const schema: IGeminiSchema = props.$defs?.[key]!; - const [llm, human] = separateStation({ - predicate: props.predicate, - convention: props.convention, - $defs: props.$defs, - schema, - }); - if (llm !== null) Object.assign(props.$defs[llmKey], llm); - if (human !== null) Object.assign(props.$defs[humanKey], human); - - // 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 - ? { - ...props.schema, - $ref: `#/$defs/${llmKey}`, - } - : null, - human !== null - ? { - ...props.schema, - $ref: `#/$defs/${humanKey}`, - } - : null, - ]; - }; - - const shrinkRequired = (s: IGeminiSchema.IObject): IGeminiSchema.IObject => { - s.required = s.required.filter((key) => s.properties?.[key] !== undefined); - return s; - }; - - /* ----------------------------------------------------------- - INVERTERS - ----------------------------------------------------------- */ - export const invert = (props: { - components: OpenApi.IComponents; - schema: IGeminiSchema; - $defs: Record; - }): OpenApi.IJsonSchema => { - const union: OpenApi.IJsonSchema[] = []; - const attribute: IJsonSchemaAttribute = { - title: props.schema.title, - description: props.schema.description, - deprecated: props.schema.deprecated, - readOnly: props.schema.readOnly, - writeOnly: props.schema.writeOnly, - example: props.schema.example, - examples: props.schema.examples, - ...Object.fromEntries( - Object.entries(props.schema).filter( - ([key, value]) => key.startsWith("x-") && value !== undefined, - ), - ), - }; - - const next = (schema: IGeminiSchema): OpenApi.IJsonSchema => - invert({ - components: props.components, - $defs: props.$defs, - schema, - }); - const visit = (schema: IGeminiSchema): void => { - if (GeminiTypeChecker.isArray(schema)) - union.push({ - ...schema, - ...LlmDescriptionInverter.array(schema.description), - items: next(schema.items), - }); - else if (GeminiTypeChecker.isObject(schema)) - union.push({ - ...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 (GeminiTypeChecker.isAnyOf(schema)) schema.anyOf.forEach(visit); - else if (GeminiTypeChecker.isReference(schema)) { - const key: string = schema.$ref.split("#/$defs/")[1]; - if (props.components.schemas?.[key] === undefined) { - props.components.schemas ??= {}; - props.components.schemas[key] = {}; - props.components.schemas[key] = next(props.$defs[key] ?? {}); - } - union.push({ - ...schema, - $ref: `#/components/schemas/${key}`, - }); - } else if (GeminiTypeChecker.isBoolean(schema)) - if (!!schema.enum?.length) - schema.enum.forEach((v) => - union.push({ - const: v, - }), - ); - else union.push(schema); - else if ( - GeminiTypeChecker.isInteger(schema) || - GeminiTypeChecker.isNumber(schema) - ) - if (!!schema.enum?.length) - schema.enum.forEach((v) => - union.push({ - const: v, - }), - ); - else - union.push({ - ...schema, - ...LlmDescriptionInverter.numeric(schema.description), - ...{ enum: undefined }, - }); - else if (GeminiTypeChecker.isString(schema)) - if (!!schema.enum?.length) - schema.enum.forEach((v) => - union.push({ - const: v, - }), - ); - else - union.push({ - ...schema, - ...LlmDescriptionInverter.string(schema.description), - ...{ enum: undefined }, - }); - else - union.push({ - ...schema, - }); - }; - visit(props.schema); - - return { - ...attribute, - ...(union.length === 0 - ? { type: undefined } - : union.length === 1 - ? { ...union[0] } - : { - oneOf: union.map((u) => ({ ...u, nullable: undefined })), - discriminator: - GeminiTypeChecker.isAnyOf(props.schema) && - props.schema["x-discriminator"] !== undefined - ? { - property: props.schema["x-discriminator"], - mapping: - props.schema["x-discriminator"].mapping !== undefined - ? Object.fromEntries( - Object.entries( - props.schema["x-discriminator"].mapping, - ).map(([key, value]) => [ - key, - `#/components/schemas/${value.split("/").at(-1)}`, - ]), - ) - : undefined, - } - : undefined, - }), - } satisfies OpenApi.IJsonSchema; - }; -} diff --git a/src/composers/llm/LlmSchemaV3Composer.ts b/src/composers/llm/LlmSchemaV3Composer.ts deleted file mode 100644 index bf395bbe..00000000 --- a/src/composers/llm/LlmSchemaV3Composer.ts +++ /dev/null @@ -1,340 +0,0 @@ -import { OpenApi } from "../../OpenApi"; -import { OpenApiV3Downgrader } from "../../converters/OpenApiV3Downgrader"; -import { OpenApiV3Upgrader } from "../../converters/OpenApiV3Upgrader"; -import { ILlmFunction } from "../../structures/ILlmFunction"; -import { ILlmSchemaV3 } from "../../structures/ILlmSchemaV3"; -import { IOpenApiSchemaError } from "../../structures/IOpenApiSchemaError"; -import { IResult } from "../../structures/IResult"; -import { LlmTypeCheckerV3 } from "../../utils/LlmTypeCheckerV3"; -import { OpenApiConstraintShifter } from "../../utils/OpenApiConstraintShifter"; -import { OpenApiTypeChecker } from "../../utils/OpenApiTypeChecker"; -import { OpenApiValidator } from "../../utils/OpenApiValidator"; -import { LlmDescriptionInverter } from "./LlmDescriptionInverter"; -import { LlmParametersFinder } from "./LlmParametersComposer"; - -export namespace LlmSchemaV3Composer { - /** @internal */ - export const IS_DEFS = false; - - export const DEFAULT_CONFIG: ILlmSchemaV3.IConfig = { - recursive: 3, - constraint: true, - }; - - /* ----------------------------------------------------------- - CONVERTERS - ----------------------------------------------------------- */ - export const parameters = (props: { - config: ILlmSchemaV3.IConfig; - components: OpenApi.IComponents; - schema: OpenApi.IJsonSchema.IObject | OpenApi.IJsonSchema.IReference; - /** @internal */ - validate?: ( - schema: OpenApi.IJsonSchema, - accessor: string, - ) => IOpenApiSchemaError.IReason[]; - accessor?: string; - refAccessor?: string; - }): IResult => { - const entity: IResult = - LlmParametersFinder.parameters({ - ...props, - method: "LlmSchemaV3Composer.parameters", - }); - if (entity.success === false) return entity; - - const result: IResult = schema(props); - if (result.success === false) return result; - return { - success: true, - value: { - ...(result.value as ILlmSchemaV3.IObject), - additionalProperties: false, - } satisfies ILlmSchemaV3.IParameters, - }; - }; - - export const schema = (props: { - config: ILlmSchemaV3.IConfig; - components: OpenApi.IComponents; - schema: OpenApi.IJsonSchema; - /** @internal */ - validate?: ( - schema: OpenApi.IJsonSchema, - accessor: string, - ) => IOpenApiSchemaError.IReason[]; - accessor?: string; - refAccessor?: string; - }): IResult => { - // CHECK TUPLE TYPE - const reasons: IOpenApiSchemaError.IReason[] = []; - OpenApiTypeChecker.visit({ - closure: (next, accessor) => { - if (props.validate) reasons.push(...props.validate(next, accessor)); - if (OpenApiTypeChecker.isTuple(next)) - reasons.push({ - accessor: accessor, - schema: next, - message: "LLM does not allow tuple type.", - }); - else if (OpenApiTypeChecker.isReference(next)) { - // UNABLE TO FIND MATCHED REFERENCE - const key = next.$ref.split("#/components/schemas/")[1]; - if (props.components.schemas?.[key] === undefined) { - reasons.push({ - schema: next, - message: `${accessor}: unable to find reference type ${JSON.stringify(key)}.`, - accessor: accessor, - }); - } - } - }, - components: props.components, - schema: props.schema, - accessor: props.accessor, - refAccessor: props.refAccessor, - }); - // if ((valid as boolean) === false) return null; - if (reasons.length > 0) - return { - success: false, - error: { - method: "LlmSchemaV3Composer.schema", - message: "Failed to compose LLM schema of v3", - reasons, - }, - }; - - // CHECK MISMATCHES - const escaped: IResult = - OpenApiTypeChecker.escape({ - ...props, - recursive: props.config.recursive, - }); - if (escaped.success === false) - // UNREACHABLE - return { - success: false, - error: { - method: "LlmSchemaV3Composer.schema", - message: "Failed to compose LLM schema of v3", - reasons: escaped.error.reasons, - }, - }; - - // SPECIALIZATIONS - const downgraded: ILlmSchemaV3 = OpenApiV3Downgrader.downgradeSchema({ - original: { - schemas: {}, - }, - downgraded: {}, - })(escaped.value) as ILlmSchemaV3; - LlmTypeCheckerV3.visit({ - closure: (next) => { - if ( - LlmTypeCheckerV3.isOneOf(next) && - (next as any).discriminator !== undefined - ) - delete (next as any).discriminator; - else if (LlmTypeCheckerV3.isObject(next)) { - next.properties ??= {}; - next.required ??= []; - } - if (props.config.constraint === false) { - if ( - LlmTypeCheckerV3.isInteger(next) || - LlmTypeCheckerV3.isNumber(next) - ) - OpenApiConstraintShifter.shiftNumeric( - next as - | OpenApi.IJsonSchema.IInteger - | OpenApi.IJsonSchema.INumber, - ); - else if (LlmTypeCheckerV3.isString(next)) - OpenApiConstraintShifter.shiftString( - next as OpenApi.IJsonSchema.IString, - ); - else if (LlmTypeCheckerV3.isArray(next)) - OpenApiConstraintShifter.shiftArray( - next as OpenApi.IJsonSchema.IArray, - ); - } - }, - schema: downgraded, - }); - return { - success: true, - value: downgraded, - }; - }; - - /* ----------------------------------------------------------- - SEPARATORS - ----------------------------------------------------------- */ - export const separateParameters = (props: { - predicate: (schema: ILlmSchemaV3) => boolean; - parameters: ILlmSchemaV3.IParameters; - equals?: boolean; - }): ILlmFunction.ISeparated<"3.0"> => { - const [llm, human] = separateObject({ - predicate: props.predicate, - schema: props.parameters, - }); - return { - llm: (llm as ILlmSchemaV3.IParameters | null) ?? { - type: "object", - properties: {}, - additionalProperties: false, - required: [], - }, - human: human as ILlmSchemaV3.IParameters | null, - validate: llm - ? OpenApiValidator.create({ - components: {}, - schema: invert({ schema: llm }), - required: true, - equals: props.equals, - }) - : undefined, - }; - }; - - const separateStation = (props: { - predicate: (schema: ILlmSchemaV3) => boolean; - schema: ILlmSchemaV3; - }): [ILlmSchemaV3 | null, ILlmSchemaV3 | null] => { - if (props.predicate(props.schema) === true) return [null, props.schema]; - else if ( - LlmTypeCheckerV3.isUnknown(props.schema) || - LlmTypeCheckerV3.isOneOf(props.schema) - ) - return [props.schema, null]; - else if (LlmTypeCheckerV3.isObject(props.schema)) - return separateObject({ - predicate: props.predicate, - schema: props.schema, - }); - else if (LlmTypeCheckerV3.isArray(props.schema)) - return separateArray({ - predicate: props.predicate, - schema: props.schema, - }); - return [props.schema, null]; - }; - - const separateArray = (props: { - predicate: (schema: ILlmSchemaV3) => boolean; - schema: ILlmSchemaV3.IArray; - }): [ILlmSchemaV3.IArray | null, ILlmSchemaV3.IArray | null] => { - const [x, y] = separateStation({ - predicate: props.predicate, - schema: props.schema.items, - }); - return [ - x !== null - ? { - ...props.schema, - items: x, - } - : null, - y !== null - ? { - ...props.schema, - items: y, - } - : null, - ]; - }; - - const separateObject = (props: { - predicate: (schema: ILlmSchemaV3) => boolean; - schema: ILlmSchemaV3.IObject; - }): [ILlmSchemaV3.IObject | null, ILlmSchemaV3.IObject | null] => { - // EMPTY OBJECT - if ( - Object.keys(props.schema.properties ?? {}).length === 0 && - !!props.schema.additionalProperties === false - ) - return [props.schema, null]; - - const llm = { - ...props.schema, - properties: {} as Record, - additionalProperties: props.schema.additionalProperties, - } satisfies ILlmSchemaV3.IObject; - const human = { - ...props.schema, - properties: {} as Record, - additionalProperties: props.schema.additionalProperties, - } satisfies ILlmSchemaV3.IObject; - - for (const [key, value] of Object.entries(props.schema.properties ?? {})) { - const [x, y] = separateStation({ - predicate: props.predicate, - 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, - schema: props.schema.additionalProperties, - }); - llm.additionalProperties = dx ?? false; - human.additionalProperties = dy ?? false; - } - return [ - !!Object.keys(llm.properties).length || !!llm.additionalProperties - ? shrinkRequired(llm) - : null, - !!Object.keys(human.properties).length || !!human.additionalProperties - ? shrinkRequired(human) - : null, - ]; - }; - - const shrinkRequired = (s: ILlmSchemaV3.IObject): ILlmSchemaV3.IObject => { - s.required = s.required.filter((key) => s.properties[key] !== undefined); - return s; - }; - - /* ----------------------------------------------------------- - INVERTERS - ----------------------------------------------------------- */ - export const invert = (props: { - schema: ILlmSchemaV3; - }): OpenApi.IJsonSchema => { - const upgraded: OpenApi.IJsonSchema = OpenApiV3Upgrader.convertSchema({})( - props.schema, - ); - OpenApiTypeChecker.visit({ - closure: (schema) => { - if (OpenApiTypeChecker.isArray(schema)) - Object.assign(schema, { - ...schema, - ...LlmDescriptionInverter.array(schema.description), - }); - else if ( - OpenApiTypeChecker.isInteger(schema) || - OpenApiTypeChecker.isNumber(schema) - ) - Object.assign(schema, { - ...schema, - ...LlmDescriptionInverter.numeric(schema.description), - }); - else if (OpenApiTypeChecker.isString(schema)) - Object.assign(schema, { - ...schema, - ...LlmDescriptionInverter.string(schema.description), - }); - }, - components: {}, - schema: upgraded, - }); - return upgraded; - }; -} diff --git a/src/composers/llm/LlmSchemaV3_1Composer.ts b/src/composers/llm/LlmSchemaV3_1Composer.ts deleted file mode 100644 index cda921ee..00000000 --- a/src/composers/llm/LlmSchemaV3_1Composer.ts +++ /dev/null @@ -1,716 +0,0 @@ -import { OpenApi } from "../../OpenApi"; -import { IJsonSchemaAttribute } from "../../structures/IJsonSchemaAttribute"; -import { ILlmFunction } from "../../structures/ILlmFunction"; -import { ILlmSchemaV3_1 } from "../../structures/ILlmSchemaV3_1"; -import { IOpenApiSchemaError } from "../../structures/IOpenApiSchemaError"; -import { IResult } from "../../structures/IResult"; -import { LlmTypeCheckerV3_1 } from "../../utils/LlmTypeCheckerV3_1"; -import { NamingConvention } from "../../utils/NamingConvention"; -import { OpenApiConstraintShifter } from "../../utils/OpenApiConstraintShifter"; -import { OpenApiTypeChecker } from "../../utils/OpenApiTypeChecker"; -import { OpenApiValidator } from "../../utils/OpenApiValidator"; -import { JsonDescriptionUtil } from "../../utils/internal/JsonDescriptionUtil"; -import { LlmDescriptionInverter } from "./LlmDescriptionInverter"; -import { LlmParametersFinder } from "./LlmParametersComposer"; - -export namespace LlmSchemaV3_1Composer { - /** @internal */ - export const IS_DEFS = true; - - export const DEFAULT_CONFIG: ILlmSchemaV3_1.IConfig = { - reference: true, - constraint: true, - }; - - /* ----------------------------------------------------------- - CONVERTERS - ----------------------------------------------------------- */ - export const parameters = (props: { - config: ILlmSchemaV3_1.IConfig; - components: OpenApi.IComponents; - schema: OpenApi.IJsonSchema.IObject | OpenApi.IJsonSchema.IReference; - errors?: string[]; - /** @internal */ - validate?: ( - input: OpenApi.IJsonSchema, - accessor: string, - ) => IOpenApiSchemaError.IReason[]; - accessor?: string; - refAccessor?: string; - }): IResult => { - const entity: IResult = - LlmParametersFinder.parameters({ - ...props, - method: "LlmSchemaV3_1Composer.parameters", - }); - if (entity.success === false) return entity; - - const $defs: Record = {}; - const result: IResult = schema({ - ...props, - $defs, - schema: entity.value, - }); - if (result.success === false) return result; - return { - success: true, - value: { - ...(result.value as ILlmSchemaV3_1.IObject), - additionalProperties: false, - $defs, - description: OpenApiTypeChecker.isReference(props.schema) - ? JsonDescriptionUtil.cascade({ - prefix: "#/components/schemas/", - components: props.components, - schema: props.schema, - escape: true, - }) - : result.value.description, - } satisfies ILlmSchemaV3_1.IParameters, - }; - }; - - export const schema = (props: { - config: ILlmSchemaV3_1.IConfig; - components: OpenApi.IComponents; - $defs: Record; - schema: OpenApi.IJsonSchema; - /** @internal */ - validate?: ( - input: OpenApi.IJsonSchema, - accessor: string, - ) => IOpenApiSchemaError.IReason[]; - accessor?: string; - refAccessor?: string; - }): IResult => { - const union: Array = []; - const attribute: IJsonSchemaAttribute = { - title: props.schema.title, - description: props.schema.description, - deprecated: props.schema.deprecated, - readOnly: props.schema.readOnly, - writeOnly: props.schema.writeOnly, - example: props.schema.example, - examples: props.schema.examples, - ...Object.fromEntries( - Object.entries(props.schema).filter( - ([key, value]) => key.startsWith("x-") && value !== undefined, - ), - ), - }; - - const reasons: IOpenApiSchemaError.IReason[] = []; - OpenApiTypeChecker.visit({ - closure: (next, accessor) => { - if (props.validate) { - // CUSTOM VALIDATION - reasons.push(...props.validate(next, accessor)); - } - if (OpenApiTypeChecker.isTuple(next)) - reasons.push({ - schema: next, - accessor: accessor, - message: `LLM does not allow tuple type.`, - }); - else if (OpenApiTypeChecker.isReference(next)) { - // UNABLE TO FIND MATCHED REFERENCE - const key = next.$ref.split("#/components/schemas/")[1]; - if (props.components.schemas?.[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: OpenApi.IJsonSchema, accessor: string): number => { - if (OpenApiTypeChecker.isOneOf(input)) { - // UNION TYPE - input.oneOf.forEach((s, i) => visit(s, `${accessor}.oneOf[${i}]`)); - return 0; - } else if (OpenApiTypeChecker.isReference(input)) { - // REFERENCE TYPE - const key: string = input.$ref.split("#/components/schemas/")[1]; - const target: OpenApi.IJsonSchema | undefined = - props.components.schemas?.[key]; - if (target === undefined) - return union.push(null); // UNREACHABLEE - else if ( - // KEEP THE REFERENCE TYPE - props.config.reference === true || - OpenApiTypeChecker.isRecursiveReference({ - components: props.components, - schema: input, - }) - ) { - const out = () => - union.push({ - ...input, - $ref: `#/$defs/${key}`, - }); - if (props.$defs[key] !== undefined) return out(); - props.$defs[key] = {}; - const converted: IResult = - schema({ - config: props.config, - components: props.components, - $defs: props.$defs, - schema: target, - refAccessor: props.refAccessor, - accessor: `${props.refAccessor ?? "$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: number = union.length; - visit(target, accessor); - if (length === union.length - 1 && union[union.length - 1] !== null) - union[union.length - 1] = { - ...union[union.length - 1]!, - description: JsonDescriptionUtil.cascade({ - prefix: "#/components/schemas/", - components: props.components, - schema: input, - escape: true, - }), - }; - else - attribute.description = JsonDescriptionUtil.cascade({ - prefix: "#/components/schemas/", - components: props.components, - schema: input, - escape: true, - }); - return union.length; - } - } else if (OpenApiTypeChecker.isObject(input)) { - // OBJECT TYPE - const properties: Record = - Object.entries(input.properties ?? {}).reduce( - (acc, [key, value]) => { - const converted: IResult = - 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; - }, - {} as Record, - ); - if (Object.values(properties).some((v) => v === null)) - return union.push(null); - const additionalProperties: - | ILlmSchemaV3_1 - | boolean - | null - | undefined = (() => { - if ( - typeof input.additionalProperties === "object" && - input.additionalProperties !== null - ) { - const converted: IResult = - 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({ - ...input, - properties: properties as Record, - additionalProperties, - required: input.required ?? [], - }); - } else if (OpenApiTypeChecker.isArray(input)) { - const items: IResult = 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: ILlmSchemaV3_1.IArray) => x - : (x: ILlmSchemaV3_1.IArray) => - OpenApiConstraintShifter.shiftArray(x))({ - ...input, - items: items.value, - }), - ); - } else if (OpenApiTypeChecker.isString(input)) - return union.push( - (props.config.constraint - ? (x: ILlmSchemaV3_1.IString) => x - : (x: ILlmSchemaV3_1.IString) => - OpenApiConstraintShifter.shiftString(x))({ - ...input, - }), - ); - else if ( - OpenApiTypeChecker.isNumber(input) || - OpenApiTypeChecker.isInteger(input) - ) - return union.push( - (props.config.constraint - ? (x: ILlmSchemaV3_1.INumber | ILlmSchemaV3_1.IInteger) => x - : (x: ILlmSchemaV3_1.INumber | ILlmSchemaV3_1.IInteger) => - OpenApiConstraintShifter.shiftNumeric(x))({ - ...input, - }), - ); - else if (OpenApiTypeChecker.isTuple(input)) - return union.push(null); // UNREACHABLE - else return union.push({ ...input }); - }; - visit(props.schema, props.accessor ?? "$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: { - ...attribute, - type: undefined, - }, - }; - else if (union.length === 1) - return { - success: true, - value: { - ...attribute, - ...union[0]!, - }, - }; - return { - success: true, - value: { - ...attribute, - oneOf: union.filter((u) => u !== null), - discriminator: - OpenApiTypeChecker.isOneOf(props.schema) && - props.schema.discriminator !== undefined && - union - .filter((u) => u !== null) - .every( - (e) => - LlmTypeCheckerV3_1.isReference(e) || - LlmTypeCheckerV3_1.isNull(e), - ) - ? { - propertyName: props.schema.discriminator.propertyName, - mapping: - props.schema.discriminator.mapping !== undefined - ? Object.fromEntries( - Object.entries(props.schema.discriminator.mapping).map( - ([key, value]) => [ - key, - `#/$defs/${value.split("/").at(-1)}`, - ], - ), - ) - : undefined, - } - : undefined, - }, - }; - }; - - /* ----------------------------------------------------------- - SEPARATORS - ----------------------------------------------------------- */ - export const separateParameters = (props: { - parameters: ILlmSchemaV3_1.IParameters; - predicate: (schema: ILlmSchemaV3_1) => boolean; - convention?: (key: string, type: "llm" | "human") => string; - equals?: boolean; - }): ILlmFunction.ISeparated<"3.1"> => { - const convention = - props.convention ?? - ((key, type) => `${key}.${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: (llm as ILlmSchemaV3_1.IParameters | null) ?? { - type: "object", - properties: {}, - additionalProperties: false, - required: [], - $defs: {}, - }, - human: human as ILlmSchemaV3_1.IParameters | null, - }; - const output: ILlmFunction.ISeparated<"3.1"> = { - llm: { - ...llm, - $defs: Object.fromEntries( - Object.entries(props.parameters.$defs).filter(([key]) => - key.endsWith(".Llm"), - ), - ), - additionalProperties: false, - }, - human: { - ...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: OpenApi.IComponents = {}; - output.validate = OpenApiValidator.create({ - components, - schema: invert({ - components, - schema: output.llm, - $defs: output.llm.$defs, - }), - required: true, - equals: props.equals, - }); - } - return output; - }; - - const separateStation = (props: { - predicate: (schema: ILlmSchemaV3_1) => boolean; - convention: (key: string, type: "llm" | "human") => string; - $defs: Record; - schema: ILlmSchemaV3_1; - }): [ILlmSchemaV3_1 | null, ILlmSchemaV3_1 | null] => { - if (props.predicate(props.schema) === true) return [null, props.schema]; - else if ( - LlmTypeCheckerV3_1.isUnknown(props.schema) || - LlmTypeCheckerV3_1.isOneOf(props.schema) - ) - return [props.schema, null]; - else if (LlmTypeCheckerV3_1.isObject(props.schema)) - return separateObject({ - predicate: props.predicate, - convention: props.convention, - $defs: props.$defs, - schema: props.schema, - }); - else if (LlmTypeCheckerV3_1.isArray(props.schema)) - return separateArray({ - predicate: props.predicate, - convention: props.convention, - $defs: props.$defs, - schema: props.schema, - }); - else if (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: { - predicate: (schema: ILlmSchemaV3_1) => boolean; - convention: (key: string, type: "llm" | "human") => string; - $defs: Record; - schema: ILlmSchemaV3_1.IArray; - }): [ILlmSchemaV3_1.IArray | null, ILlmSchemaV3_1.IArray | null] => { - const [x, y] = separateStation({ - predicate: props.predicate, - convention: props.convention, - $defs: props.$defs, - schema: props.schema.items, - }); - return [ - x !== null - ? { - ...props.schema, - items: x, - } - : null, - y !== null - ? { - ...props.schema, - items: y, - } - : null, - ]; - }; - - const separateObject = (props: { - predicate: (schema: ILlmSchemaV3_1) => boolean; - convention: (key: string, type: "llm" | "human") => string; - $defs: Record; - schema: ILlmSchemaV3_1.IObject; - }): [ILlmSchemaV3_1.IObject | null, ILlmSchemaV3_1.IObject | null] => { - // EMPTY OBJECT - if ( - Object.keys(props.schema.properties ?? {}).length === 0 && - !!props.schema.additionalProperties === false - ) - return [props.schema, null]; - - const llm = { - ...props.schema, - properties: {} as Record, - additionalProperties: props.schema.additionalProperties, - } satisfies ILlmSchemaV3_1.IObject; - const human = { - ...props.schema, - properties: {} as Record, - } satisfies ILlmSchemaV3_1.IObject; - - for (const [key, value] of Object.entries(props.schema.properties ?? {})) { - 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 ?? false; - human.additionalProperties = 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: { - predicate: (schema: ILlmSchemaV3_1) => boolean; - convention: (key: string, type: "llm" | "human") => string; - $defs: Record; - schema: ILlmSchemaV3_1.IReference; - }): [ILlmSchemaV3_1.IReference | null, ILlmSchemaV3_1.IReference | null] => { - const key: string = props.schema.$ref.split("#/$defs/")[1]; - const humanKey: string = props.convention(key, "human"); - const llmKey: string = props.convention(key, "llm"); - - // FIND EXISTING - if (props.$defs?.[humanKey] || props.$defs?.[llmKey]) - return [ - props.$defs?.[llmKey] - ? { - ...props.schema, - $ref: `#/$defs/${llmKey}`, - } - : null, - props.$defs?.[humanKey] - ? { - ...props.schema, - $ref: `#/$defs/${humanKey}`, - } - : null, - ]; - - // PRE-ASSIGNMENT - props.$defs![llmKey] = {}; - props.$defs![humanKey] = {}; - - // DO COMPOSE - const schema: ILlmSchemaV3_1 = props.$defs?.[key]!; - const [llm, human] = separateStation({ - predicate: props.predicate, - convention: props.convention, - $defs: props.$defs, - schema, - }); - if (llm !== null) Object.assign(props.$defs[llmKey], llm); - if (human !== null) Object.assign(props.$defs[humanKey], human); - - // 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 - ? { - ...props.schema, - $ref: `#/$defs/${llmKey}`, - } - : null, - human !== null - ? { - ...props.schema, - $ref: `#/$defs/${humanKey}`, - } - : null, - ]; - }; - - const shrinkRequired = ( - s: ILlmSchemaV3_1.IObject, - ): ILlmSchemaV3_1.IObject => { - if (s.required !== undefined) - s.required = s.required.filter( - (key) => s.properties?.[key] !== undefined, - ); - return s; - }; - - /* ----------------------------------------------------------- - INVERTERS - ----------------------------------------------------------- */ - export const invert = (props: { - components: OpenApi.IComponents; - schema: ILlmSchemaV3_1; - $defs: Record; - }): OpenApi.IJsonSchema => { - const next = (schema: ILlmSchemaV3_1): OpenApi.IJsonSchema => - invert({ - components: props.components, - $defs: props.$defs, - schema, - }); - if (LlmTypeCheckerV3_1.isArray(props.schema)) - return { - ...props.schema, - ...LlmDescriptionInverter.array(props.schema.description), - items: next(props.schema.items), - }; - else if (LlmTypeCheckerV3_1.isObject(props.schema)) - return { - ...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.isReference(props.schema)) { - const key: string = props.schema.$ref.split("#/$defs/").at(-1) ?? ""; - if (props.components.schemas?.[key] === undefined) { - props.components.schemas ??= {}; - props.components.schemas[key] = {}; - props.components.schemas[key] = next(props.$defs[key] ?? {}); - } - return { - ...props.schema, - $ref: `#/components/schemas/${key}`, - }; - } else if (LlmTypeCheckerV3_1.isOneOf(props.schema)) - return { - ...props.schema, - oneOf: props.schema.oneOf.map(next), - discriminator: - props.schema.discriminator !== undefined - ? { - propertyName: props.schema.discriminator.propertyName, - mapping: - props.schema.discriminator.mapping !== undefined - ? Object.fromEntries( - Object.entries(props.schema.discriminator.mapping).map( - ([key, value]) => [ - key, - `#/components/schemas/${value.split("/").at(-1)}`, - ], - ), - ) - : undefined, - } - : undefined, - }; - else if ( - LlmTypeCheckerV3_1.isInteger(props.schema) || - LlmTypeCheckerV3_1.isNumber(props.schema) - ) - return { - ...props.schema, - ...LlmDescriptionInverter.numeric(props.schema.description), - }; - else if (LlmTypeCheckerV3_1.isString(props.schema)) - return { - ...props.schema, - ...LlmDescriptionInverter.string(props.schema.description), - }; - return props.schema; - }; -} diff --git a/src/index.ts b/src/index.ts index 30063e7d..8f009b98 100644 --- a/src/index.ts +++ b/src/index.ts @@ -29,49 +29,27 @@ export * from "./HttpMigration"; //---- // LLM //---- -// VALIDATIONS -export * from "./structures/IOpenApiSchemaError"; -export * from "./structures/IResult"; -export * from "./structures/IValidation"; +// STRUCTURES // CONTROLLERS export * from "./structures/IHttpLlmController"; -export * from "./structures/ILlmController"; -export * from "./structures/IMcpLlmController"; - -// APPLICATIONS export * from "./structures/IHttpLlmApplication"; export * from "./structures/IHttpLlmFunction"; -export * from "./structures/ILlmApplication"; +export * from "./structures/ILlmController"; export * from "./structures/ILlmFunction"; +export * from "./structures/ILlmApplication"; +export * from "./structures/IMcpLlmApplication"; +export * from "./structures/IMcpLlmController"; +export * from "./structures/IMcpLlmFunction"; +export * from "./structures/IMcpTool"; -// SCHEMAS -export * from "./structures/IChatGptSchema"; -export * from "./structures/IClaudeSchema"; -export * from "./structures/IGeminiSchema"; +// SCHEMA export * from "./structures/ILlmSchema"; -export * from "./structures/ILlmSchemaV3"; -export * from "./structures/ILlmSchemaV3_1"; - -// TYPE CHECKERS -export * from "./utils/ChatGptTypeChecker"; -export * from "./utils/ClaudeTypeChecker"; -export * from "./utils/DeepSeekTypeChecker"; -export * from "./utils/GeminiTypeChecker"; -export * from "./utils/LlamaTypeChecker"; -export * from "./utils/LlmTypeCheckerV3"; -export * from "./utils/LlmTypeCheckerV3_1"; -export * from "./utils/LlmTypeChecker"; +export * from "./structures/IOpenApiSchemaError"; +export * from "./structures/IResult"; +export * from "./structures/IValidation"; // FACADE export * from "./HttpLlm"; - -//---- -// MCP -//---- export * from "./McpLlm"; - -export * from "./structures/IMcpLlmApplication"; -export * from "./structures/IMcpLlmFunction"; - -export * from "./structures/IMcpTool"; +export * from "./utils/LlmTypeChecker"; diff --git a/src/structures/IChatGptSchema.ts b/src/structures/IChatGptSchema.ts deleted file mode 100644 index 9f3f027d..00000000 --- a/src/structures/IChatGptSchema.ts +++ /dev/null @@ -1,342 +0,0 @@ -import { IJsonSchemaAttribute } from "./IJsonSchemaAttribute"; - -/** - * Type schema info for OpenAI function calling. - * - * `IChatGptSchema` is a type schema info for OpenAI function calling. The type - * name "ChatGpt" is intentionally used to avoid confusion with "OpenAPI" - * specification, even though this is designed for OpenAI models. - * - * `IChatGptSchema` basically follows the JSON schema definition of the OpenAPI - * v3.1 specification; {@link OpenApiV3_1.IJsonSchema}. However, it deviates from - * the standard JSON schema specification and omits many features when used in - * {@link IChatGptSchema.IConfig.strict} mode for OpenAI function calling. - * - * `IChatGptSchema` supports all JSON schema features through workaround - * expressions using JSDoc tags in the `description` property, so using - * `IChatGptSchema` does not degrade function calling performance even in strict - * mode. - * - * Here is the list of how `IChatGptSchema` is different with the OpenAPI v3.1 - * JSON schema: - * - * - Decompose mixed type: {@link OpenApiV3_1.IJsonSchema.IMixed} - * - Resolve nullable property: - * {@link OpenApiV3_1.IJsonSchema.__ISignificant.nullable} - * - Tuple type is banned: {@link OpenApiV3_1.IJsonSchema.ITuple.prefixItems} - * - Constant type is banned: {@link OpenApiV3_1.IJsonSchema.IConstant} - * - Merge {@link OpenApiV3_1.IJsonSchema.IOneOf} to {@link IChatGptSchema.IAnyOf} - * - Merge {@link OpenApiV3_1.IJsonSchema.IAllOf} to {@link IChatGptSchema.IObject} - * - Merge {@link OpenApiV3_1.IJsonSchema.IRecursiveReference} to - * {@link IChatGptSchema.IReference} - * - When {@link IChatGptSchema.IConfig.strict} mode: - * - * - Every object properties must be required - * - Do not allow {@link IChatGptSchema.IObject.additionalProperties} - * - * Compared to {@link OpenApi.IJsonSchema}, the emended JSON schema - * specification: - * - * - {@link IChatGptSchema.IAnyOf} instead of {@link OpenApi.IJsonSchema.IOneOf} - * - {@link IChatGptSchema.IParameters.$defs} instead of - * {@link OpenApi.IComponents.schemas} - * - {@link IChatGptSchema.IString.enum} instead of - * {@link OpenApi.IJsonSchema.IConstant} - * - {@link IChatGptSchema.additionalProperties} is fixed to `false` in strict mode - * - {@link IChatGptSchema.properties} and {@link IChatGptSchema.required} are - * always defined - * - No tuple type {@link OpenApi.IJsonSchema.ITuple} support - * - When {@link IChatGptSchema.IConfig.strict} mode: - * - * - Every object properties must be required - * - Do not allow {@link IChatGptSchema.IObject.additionalProperties} - * - * For reference, if you compose the `IChatGptSchema` type with the - * {@link IChatGptSchema.IConfig.reference} `false` option (default is `false`), - * only recursively named types are archived into the - * {@link IChatGptSchema.IParameters.$defs}, and others are escaped from the - * {@link IChatGptSchema.IReference} type. - * - * Also, OpenAI has banned the following constraint properties. Instead, - * `IChatGptSchema` fills the {@link IChatGptSchema.description} property with - * workaround expressions using JSDoc tags like `"@format uuid"` to convey these - * constraints: - * - * - {@link OpenApi.IJsonSchema.INumber.minimum} - * - {@link OpenApi.IJsonSchema.INumber.maximum} - * - {@link OpenApi.IJsonSchema.INumber.multipleOf} - * - {@link OpenApi.IJsonSchema.IString.minLength} - * - {@link OpenApi.IJsonSchema.IString.maxLength} - * - {@link OpenApi.IJsonSchema.IString.format} - * - {@link OpenApi.IJsonSchema.IString.pattern} - * - {@link OpenApi.IJsonSchema.IString.contentMediaType} - * - {@link OpenApi.IJsonSchema.IString.default} - * - {@link OpenApi.IJsonSchema.IArray.minItems} - * - {@link OpenApi.IJsonSchema.IArray.maxItems} - * - {@link OpenApi.IJsonSchema.IArray.unique} - * - * Additionally, OpenAI cannot define the {@link IChatGptSchema.description} - * property for the {@link IChatGptSchema.IReference} type, and does not - * understand encapsulation of the {@link IChatGptSchema.IAnyOf} type. Therefore, - * the {@link IChatGptSchema.description} is written to the parent object type, - * not the reference type. - * - * ```json - * { - * "type": "object", - * "description": "### Description of {@link something} property.\n\n> Hello?", - * "properties": { - * "something": { - * "$ref": "#/$defs/SomeObject" - * } - * } - * } - * ``` - * - * @author Jeongho Nam - https://github.com/samchon - * @reference https://platform.openai.com/docs/guides/function-calling - * @reference https://platform.openai.com/docs/guides/structured-outputs - * @warning Specified not only by official documentation, but also by - * experimental validation. Therefore, definitions may be inaccurate or - * change in the future. If you find wrong or outdated definitions, - * please report via issue. - * @issue https://github.com/samchon/openapi/issues - */ -export type IChatGptSchema = - | IChatGptSchema.IBoolean - | IChatGptSchema.IInteger - | IChatGptSchema.INumber - | IChatGptSchema.IString - | IChatGptSchema.IArray - | IChatGptSchema.IObject - | IChatGptSchema.IReference - | IChatGptSchema.IAnyOf - | IChatGptSchema.INull - | IChatGptSchema.IUnknown; -export namespace IChatGptSchema { - /** Configuration for ChatGPT schema composition. */ - export interface IConfig { - /** - * Whether to allow reference type in everywhere. - * - * If you configure this property to `false`, most of reference types - * represented by {@link IChatGptSchema.IReference} would be escaped to a - * plain type unless recursive type case. - * - * This is because the lower version of ChatGPT does not understand the - * reference type well, and even the modern version of ChatGPT sometimes - * occur the hallucination. - * - * However, the reference type makes the schema size smaller, so that - * reduces the LLM token cost. Therefore, if you're using the modern version - * of ChatGPT, and want to reduce the LLM token cost, you can configure this - * property to `true`. - * - * @default true - */ - reference: boolean; - - /** - * Whether to apply the strict mode. - * - * If you configure this property to `true`, the ChatGPT function calling - * does not allow optional properties and dynamic key typed properties in - * the {@link IChatGptSchema.IObject} type. Instead, it increases the success - * rate of the function calling. - * - * By the way, if you utilize the {@link typia.validate} function and give - * its validation feedback to the ChatGPT, its performance is much better - * than the strict mode. Therefore, I recommend you to just turn off the - * strict mode and utilize the {@link typia.validate} function instead. - * - * @default false - */ - strict?: boolean; - } - - /** - * Type for function parameters. - * - * `IChatGptSchema.IParameters` defines a function's parameters as a keyword - * object type, where each property represents a named parameter. - * - * It can also be used for structured output metadata to define the expected - * format of ChatGPT responses. - * - * @reference https://platform.openai.com/docs/guides/structured-outputs - */ - export interface IParameters extends Omit { - /** Collection of the named types. */ - $defs: Record; - - /** - * Additional properties information. - * - * The `additionalProperties` defines the type schema for additional - * properties that are not listed in the {@link properties}. - * - * By the way, it is not allowed at the parameters level. - */ - additionalProperties: false; - } - - /** Boolean type info. */ - export interface IBoolean extends IJsonSchemaAttribute.IBoolean { - /** Enumeration values. */ - enum?: Array; - } - - /** Integer type info. */ - export interface IInteger extends IJsonSchemaAttribute.IInteger { - /** Enumeration values. */ - enum?: Array; - } - - /** Number (double) type info. */ - export interface INumber extends IJsonSchemaAttribute.INumber { - /** Enumeration values. */ - enum?: Array; - } - - /** String type info. */ - export interface IString extends IJsonSchemaAttribute.IString { - /** Enumeration values. */ - enum?: Array; - - /** Default value. */ - default?: string; - } - - /** Array type info. */ - export interface IArray extends IJsonSchemaAttribute.IArray { - /** - * Items type info. - * - * The `items` means the type of the array elements. In other words, it is - * the type schema info of the `T` in the TypeScript array type `Array`. - */ - items: IChatGptSchema; - } - - /** Object type info. */ - export interface IObject extends IJsonSchemaAttribute.IObject { - /** - * Properties of the object. - * - * The `properties` means a list of key-value pairs of the object's regular - * properties. The key is the name of the regular property, and the value is - * the type schema info. - */ - properties: Record; - - /** - * Additional properties information. - * - * The `additionalProperties` defines the type schema for additional - * properties that are not listed in the {@link properties}. - * - * If the value is `true`, it means that the additional properties are not - * restricted. They can be any type. Otherwise, if the value is - * {@link IChatGptSchema} type, it means that the additional properties must - * follow the type schema info. - * - * - `true`: `Record` - * - `IChatGptSchema`: `Record` - * - * Note: If you've configured {@link IChatGptSchema.IConfig.strict} as - * `true`, ChatGPT function calling does not support dynamic key typed - * properties, so `additionalProperties` is always `false`. - */ - additionalProperties?: boolean | IChatGptSchema; - - /** - * List of required property keys. - * - * The `required` contains a list of property keys from {@link properties} - * that must be provided. Properties not listed in `required` are optional, - * while those listed must be filled. - * - * Below is an example of {@link properties} and `required`: - * - * ```typescript - * interface SomeObject { - * id: string; - * email: string; - * name?: string; - * } - * ``` - * - * As you can see, `id` and `email` {@link properties} are {@link required}, - * so they are listed in the `required` array. - * - * ```json - * { - * "type": "object", - * "properties": { - * "id": { "type": "string" }, - * "email": { "type": "string" }, - * "name": { "type": "string" } - * }, - * "required": ["id", "email"] - * } - * ``` - */ - required: string[]; - } - - /** Reference type directing to named schema. */ - export interface IReference extends IJsonSchemaAttribute { - /** - * Reference to the named schema. - * - * The `$ref` is a reference to a named schema. The format follows the JSON - * Pointer specification. In OpenAPI, the `$ref` starts with `#/$defs/` - * which indicates the type is stored in the - * {@link IChatGptSchema.IParameters.$defs} object. - * - * - `#/$defs/SomeObject` - * - `#/$defs/AnotherObject` - */ - $ref: string; - } - - /** - * Union type. - * - * `IAnyOf` represents a union type in TypeScript (`A | B | C`). - * - * For reference, even if your Swagger (or OpenAPI) document defines `anyOf` - * instead of `oneOf`, {@link IChatGptSchema} forcibly converts it to `anyOf` - * type. - */ - export interface IAnyOf extends IJsonSchemaAttribute { - /** List of the union types. */ - anyOf: Exclude[]; - - /** Discriminator info of the union type. */ - "x-discriminator"?: IAnyOf.IDiscriminator; - } - export namespace IAnyOf { - /** Discriminator info of the union type. */ - export interface IDiscriminator { - /** Property name for the discriminator. */ - propertyName: string; - - /** - * Mapping of discriminator values to schema names. - * - * This property is valid only for {@link IReference} typed - * {@link IAnyOf.anyOf} elements. Therefore, the `key` of `mapping` is the - * discriminator value, and the `value` of `mapping` is the schema name - * like `#/components/schemas/SomeObject`. - */ - mapping?: Record; - } - } - - /** Null type. */ - export interface INull extends IJsonSchemaAttribute.INull {} - - /** Unknown, the `any` type. */ - export interface IUnknown extends IJsonSchemaAttribute.IUnknown {} -} diff --git a/src/structures/IClaudeSchema.ts b/src/structures/IClaudeSchema.ts deleted file mode 100644 index 9294e22b..00000000 --- a/src/structures/IClaudeSchema.ts +++ /dev/null @@ -1,138 +0,0 @@ -import { ILlmSchemaV3_1 } from "./ILlmSchemaV3_1"; - -/** - * Type schema for Claude function calling. - * - * `IClaudeSchema` defines the type schema format for Claude function calling. - * - * `IClaudeSchema` appears to fully support the JSON schema definition of the - * OpenAPI v3.1 specification; {@link OpenApiV3_1.IJsonSchema}. However, since - * {@link OpenApiV3_1.IJsonSchema} has many ambiguous and duplicated expressions, - * `IClaudeSchema` is designed to be clear and simple for Claude function - * calling by utilizing {@link ILlmSchemaV3_1}, which has been transformed from - * {@link OpenApi.IJsonSchema} for convenience and clarity. - * - * Therefore, `IClaudeSchema` does not follow the entire OpenAPI v3.1 - * specification. It has specific restrictions and definitions. Here are the - * differences between `ILlmSchemaV3_1` and the OpenAPI v3.1 JSON schema: - * - * - Decompose mixed type: {@link OpenApiV3_1.IJsonSchema.IMixed} - * - Resolve nullable property: - * {@link OpenApiV3_1.IJsonSchema.__ISignificant.nullable} - * - Tuple type is banned: {@link OpenApiV3_1.IJsonSchema.ITuple.prefixItems} - * - Constant type is banned: {@link OpenApiV3_1.IJsonSchema.IConstant} - * - Merge {@link OpenApiV3_1.IJsonSchema.IAnyOf} to {@link IClaudeSchema.IOneOf} - * - Merge {@link OpenApiV3_1.IJsonSchema.IAllOf} to {@link IClaudeSchema.IObject} - * - Merge {@link OpenApiV3_1.IJsonSchema.IRecursiveReference} to - * {@link IClaudeSchema.IReference} - * - Do not support {@link OpenApiV3_1.IJsonSchema.ITuple} type - * - * Compared to {@link OpenApi.IJsonSchema}, the emended JSON schema - * specification: - * - * - {@link IClaudeSchema.IParameters.$defs} instead of - * {@link OpenApi.IComponents.schemas} - * - Do not support {@link OpenApi.IJsonSchema.ITuple} type - * - {@link IClaudeSchema.properties} and {@link IClaudeSchema.required} are always - * defined - * - * For reference, if you compose the `IClaudeSchema` type with the - * {@link IClaudeSchema.IConfig.reference} `false` option (default is `false`), - * only recursively named types will be archived into the - * {@link IClaudeSchema.IParameters.$defs}, and others will be escaped from the - * {@link IClaudeSchema.IReference} type. - * - * @author Jeongho Nam - https://github.com/samchon - * @reference https://docs.anthropic.com/en/docs/build-with-claude/tool-use - * @reference https://docs.anthropic.com/en/docs/test-and-evaluate/strengthen-guardrails/increase-consistency - */ -export type IClaudeSchema = - | IClaudeSchema.IConstant - | IClaudeSchema.IBoolean - | IClaudeSchema.IInteger - | IClaudeSchema.INumber - | IClaudeSchema.IString - | IClaudeSchema.IArray - | IClaudeSchema.IObject - | IClaudeSchema.IReference - | IClaudeSchema.IOneOf - | IClaudeSchema.INull - | IClaudeSchema.IUnknown; -export namespace IClaudeSchema { - /** Configuration for Claude schema composition. */ - export interface IConfig { - /** - * Whether to allow reference types everywhere. - * - * If you configure this property to `false`, most reference types - * represented by {@link IClaudeSchema.IReference} will be escaped to plain - * types unless in recursive type cases. - * - * This is because some smaller LLM models do not understand reference types - * well, and even large LLM models sometimes experience hallucinations. - * - * However, reference types make the schema size smaller, reducing LLM token - * costs. Therefore, if you're using a large LLM model and want to reduce - * token costs, you can configure this property to `true`. - * - * @default true - */ - reference: boolean; - } - - /** - * Type for function parameters. - * - * `IClaudeSchema.IParameters` defines a function's parameters as a keyword - * object type. - * - * It can also be used for structured output metadata. - * - * @reference https://platform.openai.com/docs/guides/structured-outputs - */ - export type IParameters = ILlmSchemaV3_1.IParameters; - - /** Constant value type. */ - export type IConstant = ILlmSchemaV3_1.IConstant; - - /** Boolean type info. */ - export type IBoolean = ILlmSchemaV3_1.IBoolean; - - /** Integer type info. */ - export type IInteger = ILlmSchemaV3_1.IInteger; - - /** Number (double) type info. */ - export type INumber = ILlmSchemaV3_1.INumber; - - /** String type info. */ - export type IString = ILlmSchemaV3_1.IString; - - /** Array type info. */ - export type IArray = ILlmSchemaV3_1.IArray; - - /** Object type info. */ - export type IObject = ILlmSchemaV3_1.IObject; - - /** Reference type directing to named schema. */ - export type IReference = ILlmSchemaV3_1.IReference; - - /** - * Union type. - * - * `IOneOf` represents a union type in TypeScript (`A | B | C`). - * - * For reference, even if your Swagger (or OpenAPI) document defines `anyOf` - * instead of `oneOf`, {@link OpenApi} forcibly converts it to `oneOf` type. - */ - export type IOneOf = ILlmSchemaV3_1.IOneOf; - export namespace IOneOf { - /** Discriminator information of the union type. */ - export type IDiscriminator = ILlmSchemaV3_1.IOneOf.IDiscriminator; - } - - /** Null type. */ - export type INull = ILlmSchemaV3_1.INull; - - /** Unknown, the `any` type. */ - export type IUnknown = ILlmSchemaV3_1.IUnknown; -} diff --git a/src/structures/IGeminiSchema.ts b/src/structures/IGeminiSchema.ts deleted file mode 100644 index cdc7d122..00000000 --- a/src/structures/IGeminiSchema.ts +++ /dev/null @@ -1,411 +0,0 @@ -import { IJsonSchemaAttribute } from "./IJsonSchemaAttribute"; - -/** - * Type schema info for Gemini function calling. - * - * `IGeminiSchema` is a type schema info for Gemini function calling, - * implemented according to the official Gemini guide documentation - * specification. - * - * `IGeminiSchema` basically follows the JSON schema definition of the OpenAPI - * v3.1 specification; {@link OpenApiV3_1.IJsonSchema}. Although Gemini had - * significant limitations in earlier versions (prior to 2025-11-05), it now - * supports nearly all JSON schema features including union types, reference - * types, and various constraint properties. - * - * In earlier versions, Gemini blocked virtually all JSON schema specifications - * such as `anyOf`, `$ref`, `format`, `maxItems`, making function calling - * practically impossible. However, these limitations have been removed in recent - * updates. - * - * `IGeminiSchema` provides a type definition that strictly follows the Gemini - * official specification. - * - * Here is the list of how `IGeminiSchema` is different with the OpenAPI v3.1 - * JSON schema: - * - * - Decompose mixed type: {@link OpenApiV3_1.IJsonSchema.IMixed} - * - Resolve nullable property: - * {@link OpenApiV3_1.IJsonSchema.__ISignificant.nullable} - * - Tuple type is banned: {@link OpenApiV3_1.IJsonSchema.ITuple.prefixItems} - * - Constant type is banned: {@link OpenApiV3_1.IJsonSchema.IConstant} - * - Merge {@link OpenApiV3_1.IJsonSchema.IOneOf} to {@link IGeminiSchema.IAnyOf} - * - Merge {@link OpenApiV3_1.IJsonSchema.IAllOf} to {@link IGeminiSchema.IObject} - * - Merge {@link OpenApiV3_1.IJsonSchema.IRecursiveReference} to - * {@link IGeminiSchema.IReference} - * - * Compared to {@link OpenApi.IJsonSchema}, the emended JSON schema - * specification: - * - * - {@link IGeminiSchema.IAnyOf} instead of {@link OpenApi.IJsonSchema.IOneOf} - * - {@link IGeminiSchema.IParameters.$defs} instead of - * {@link OpenApi.IJsonSchema.IComponents.schemas} - * - Do not support {@link OpenApi.IJsonSchema.ITuple} type - * - {@link IGeminiSchema.properties} and {@link IGeminiSchema.required} are always - * defined - * - * For reference, if you compose the `IGeminiSchema` type with the - * {@link IGeminiSchema.IConfig.reference} `false` option (default is `false`), - * only recursively named types are archived into the - * {@link IGeminiSchema.IParameters.$defs}, and others are escaped from the - * {@link IGeminiSchema.IReference} type. - * - * @author Jeongho Nam - https://github.com/samchon - * @reference https://cloud.google.com/vertex-ai/generative-ai/docs/model-reference/function-calling - * @reference https://cloud.google.com/vertex-ai/generative-ai/docs/multimodal/function-calling - * @reference https://ai.google.dev/gemini-api/docs/structured-output - * @warning Specified not only by the official documentation, but also by - * experimental validation. Therefore, definitions may be inaccurate or - * change in the future. If you find wrong or outdated definitions, - * please report via issue. - * @issue https://github.com/samchon/openapi/issues - */ -export type IGeminiSchema = - | IGeminiSchema.IBoolean - | IGeminiSchema.IInteger - | IGeminiSchema.INumber - | IGeminiSchema.IString - | IGeminiSchema.IArray - | IGeminiSchema.IObject - | IGeminiSchema.IReference - | IGeminiSchema.IAnyOf - | IGeminiSchema.INull - | IGeminiSchema.IUnknown; -export namespace IGeminiSchema { - /** Configuration for the Gemini schema composition. */ - export interface IConfig { - /** - * Whether to allow reference type in everywhere. - * - * If you configure this property to `false`, most of reference types - * represented by {@link IGeminiSchema.IReference} would be escaped to a - * plain type unless recursive type case. - * - * This is because the lower version of ChatGPT does not understand the - * reference type well, and even the modern version of ChatGPT sometimes - * occur the hallucination. - * - * However, the reference type makes the schema size smaller, so that - * reduces the LLM token cost. Therefore, if you're using the modern version - * of ChatGPT, and want to reduce the LLM token cost, you can configure this - * property to `true`. - * - * @default true - */ - reference: boolean; - } - - /** - * Type for function parameters. - * - * `IGeminiSchema.IParameters` defines a function's parameters as a keyword - * object type, where each property represents a named parameter. - * - * It can also be used for structured output metadata to define the expected - * format of ChatGPT responses. - * - * @reference https://platform.openai.com/docs/guides/structured-outputs - */ - export interface IParameters extends Omit { - /** Collection of the named types. */ - $defs: Record; - - /** - * Additional properties information. - * - * The `additionalProperties` defines the type schema for additional - * properties that are not listed in the {@link properties}. - * - * By the way, it is not allowed at the parameters level. - */ - additionalProperties: false; - } - - /** Boolean type info. */ - export interface IBoolean extends IJsonSchemaAttribute.IBoolean { - /** Enumeration values. */ - enum?: Array; - - /** Default value. */ - default?: boolean; - } - - /** Integer type info. */ - export interface IInteger extends IJsonSchemaAttribute.IInteger { - /** Enumeration values. */ - enum?: Array; - - /** - * Default value. - * - * @type int64 - */ - default?: number; - - /** - * Minimum value restriction. - * - * @type int64 - */ - minimum?: number; - - /** - * Maximum value restriction. - * - * @type int64 - */ - maximum?: number; - - /** Exclusive minimum value restriction. */ - exclusiveMinimum?: number; - - /** Exclusive maximum value restriction. */ - exclusiveMaximum?: number; - - /** - * Multiple of value restriction. - * - * @type uint64 - * @exclusiveMinimum 0 - */ - multipleOf?: number; - } - - /** Number (double) type info. */ - export interface INumber extends IJsonSchemaAttribute.INumber { - /** Enumeration values. */ - enum?: Array; - - /** Default value. */ - default?: number; - - /** Minimum value restriction. */ - minimum?: number; - - /** Maximum value restriction. */ - maximum?: number; - - /** Exclusive minimum value restriction. */ - exclusiveMinimum?: number; - - /** Exclusive maximum value restriction. */ - exclusiveMaximum?: number; - - /** - * Multiple of value restriction. - * - * @exclusiveMinimum 0 - */ - multipleOf?: number; - } - - /** String type info. */ - export interface IString extends IJsonSchemaAttribute.IString { - /** Enumeration values. */ - enum?: Array; - - /** Default value. */ - default?: string; - - /** Format restriction. */ - format?: - | "binary" - | "byte" - | "password" - | "regex" - | "uuid" - | "email" - | "hostname" - | "idn-email" - | "idn-hostname" - | "iri" - | "iri-reference" - | "ipv4" - | "ipv6" - | "uri" - | "uri-reference" - | "uri-template" - | "url" - | "date-time" - | "date" - | "time" - | "duration" - | "json-pointer" - | "relative-json-pointer" - | (string & {}); - - /** Pattern restriction. */ - pattern?: string; - - /** Content media type restriction. */ - contentMediaType?: string; - - /** - * Minimum length restriction. - * - * @type uint64 - */ - minLength?: number; - - /** - * Maximum length restriction. - * - * @type uint64 - */ - maxLength?: number; - } - - /** Array type info. */ - export interface IArray extends IJsonSchemaAttribute.IArray { - /** - * Items type info. - * - * The `items` means the type of the array elements. In other words, it is - * the type schema info of the `T` in the TypeScript array type `Array`. - */ - items: IGeminiSchema; - - /** - * Unique items restriction. - * - * If this property value is `true`, target array must have unique items. - */ - uniqueItems?: boolean; - - /** - * Minimum items restriction. - * - * Restriction of minimum number of items in the array. - * - * @type uint64 - */ - minItems?: number; - - /** - * Maximum items restriction. - * - * Restriction of maximum number of items in the array. - * - * @type uint64 - */ - maxItems?: number; - } - - /** Object type info. */ - export interface IObject extends IJsonSchemaAttribute.IObject { - /** - * Properties of the object. - * - * The `properties` means a list of key-value pairs of the object's regular - * properties. The key is the name of the regular property, and the value is - * the type schema info. - */ - properties: Record; - - /** - * Additional properties' info. - * - * The `additionalProperties` means the type schema info of the additional - * properties that are not listed in the {@link properties}. - * - * If the value is `true`, it means that the additional properties are not - * restricted. They can be any type. Otherwise, if the value is - * {@link IGeminiSchema} type, it means that the additional properties must - * follow the type schema info. - * - * - `true`: `Record` - * - `IGeminiSchema`: `Record` - */ - additionalProperties?: boolean | IGeminiSchema; - - /** - * List of required property keys. - * - * The `required` contains a list of property keys from {@link properties} - * that must be provided. Properties not listed in `required` are optional, - * while those listed must be filled. - * - * Below is an example of {@link properties} and `required`: - * - * ```typescript - * interface SomeObject { - * id: string; - * email: string; - * name?: string; - * } - * ``` - * - * As you can see, `id` and `email` {@link properties} are {@link required}, - * so they are listed in the `required` array. - * - * ```json - * { - * "type": "object", - * "properties": { - * "id": { "type": "string" }, - * "email": { "type": "string" }, - * "name": { "type": "string" } - * }, - * "required": ["id", "email"] - * } - * ``` - */ - required: string[]; - } - - /** Reference type directing to named schema. */ - export interface IReference extends IJsonSchemaAttribute { - /** - * Reference to the named schema. - * - * The `$ref` is a reference to a named schema. The format follows the JSON - * Pointer specification. In OpenAPI, the `$ref` starts with `#/$defs/` - * which indicates the type is stored in the - * {@link IGeminiSchema.IParameters.$defs} object. - * - * - `#/$defs/SomeObject` - * - `#/$defs/AnotherObject` - */ - $ref: string; - } - - /** - * Union type. - * - * `IAnyOf` represents a union type in TypeScript (`A | B | C`). - * - * For reference, even if your Swagger (or OpenAPI) document defines `anyOf` - * instead of `oneOf`, {@link IGeminiSchema} forcibly converts it to `anyOf` - * type. - */ - export interface IAnyOf extends IJsonSchemaAttribute { - /** List of the union types. */ - anyOf: Exclude[]; - - /** Discriminator info of the union type. */ - "x-discriminator"?: IAnyOf.IDiscriminator; - } - export namespace IAnyOf { - /** Discriminator info of the union type. */ - export interface IDiscriminator { - /** Property name for the discriminator. */ - propertyName: string; - - /** - * Mapping of discriminator values to schema names. - * - * This property is valid only for {@link IReference} typed - * {@link IAnyOf.anyOf} elements. Therefore, the `key` of `mapping` is the - * discriminator value, and the `value` of `mapping` is the schema name - * like `#/components/schemas/SomeObject`. - */ - mapping?: Record; - } - } - - /** Null type. */ - export interface INull extends IJsonSchemaAttribute.INull {} - - /** Unknown, the `any` type. */ - export interface IUnknown extends IJsonSchemaAttribute.IUnknown {} -} diff --git a/src/structures/ILlmSchemaV3.ts b/src/structures/ILlmSchemaV3.ts deleted file mode 100644 index 8a41a3cf..00000000 --- a/src/structures/ILlmSchemaV3.ts +++ /dev/null @@ -1,410 +0,0 @@ -import { IJsonSchemaAttribute } from "./IJsonSchemaAttribute"; - -/** - * Type schema based on OpenAPI v3.0 for LLM function calling. - * - * `ILlmSchemaV3` is a type metadata for LLM (Large Language Model) function - * calling, based on the OpenAPI v3.0 specification. This type is not the final - * type for the LLM function calling, but the intermediate structure for the - * conversion to the final type like {@link IGeminiSchema}. - * - * `ILlmSchemaV3` basically follows the JSON schema definition of OpenAPI v3.0 - * specification; {@link OpenApiV3.IJsonSchema}. However, `ILlmSchemaV3` does not - * have the reference type; {@link OpenApiV3.IJsonSchema.IReference}. It's - * because the LLM cannot compose the reference typed arguments. If recursive - * type comes, its type would be repeated in - * {@link ILlmSchemaV3.IConfig.recursive} times. Otherwise you've configured it - * to `false`, the recursive types are not allowed. - * - * For reference, the OpenAPI v3.0 based JSON schema definition can't express - * the tuple array type. It has been supported since OpenAPI v3.1; - * {@link OpenApi.IJsonSchema.ITuple}. Therefore, it would better to avoid using - * the tuple array type in the LLM function calling. - * - * Also, if you configure {@link ILlmSchemaV3.IConfig.constraint} to `false`, - * these properties would be banned and written to the - * {@link ILlmSchemaV3.__IAttribute.description} property instead. It's because - * there are some LLM models which does not support the constraint properties. - * - * - {@link ILlmSchemaV3.INumber.minimum} - * - {@link ILlmSchemaV3.INumber.maximum} - * - {@link ILlmSchemaV3.INumber.multipleOf} - * - {@link ILlmSchemaV3.IString.minLength} - * - {@link ILlmSchemaV3.IString.maxLength} - * - {@link ILlmSchemaV3.IString.format} - * - {@link ILlmSchemaV3.IString.pattern} - * - {@link ILlmSchemaV3.IString.contentMediaType} - * - {@link ILlmSchemaV3.IArray.minItems} - * - {@link ILlmSchemaV3.IArray.maxItems} - * - {@link ILlmSchemaV3.IArray.unique} - * - * @author Jeongho Nam - https://github.com/samchon - * @reference https://platform.openai.com/docs/guides/function-calling - */ -export type ILlmSchemaV3 = - | ILlmSchemaV3.IBoolean - | ILlmSchemaV3.IInteger - | ILlmSchemaV3.INumber - | ILlmSchemaV3.IString - | ILlmSchemaV3.IArray - | ILlmSchemaV3.IObject - | ILlmSchemaV3.IUnknown - | ILlmSchemaV3.INullOnly - | ILlmSchemaV3.IOneOf; -export namespace ILlmSchemaV3 { - /** Configuration for OpenAPI v3.0 based LLM schema composition. */ - export interface IConfig { - /** - * Whether to allow constraint properties or not. - * - * If you configure this property to `false`, the schemas do not contain the - * constraint properties of below. Instead, below properties would be - * written to the {@link ILlmSchemaV3.__IAttribute.description} property as a - * comment string like `"@format uuid"`. - * - * This is because some LLM schema model like {@link IGeminiSchema} has - * banned such constraint, because their LLM cannot understand the - * constraint properties and occur the hallucination. - * - * Therefore, considering your LLM model's performance, capability, and the - * complexity of your parameter types, determine which is better, to allow - * the constraint properties or not. - * - * - {@link ILlmSchemaV3.INumber.minimum} - * - {@link ILlmSchemaV3.INumber.maximum} - * - {@link ILlmSchemaV3.INumber.multipleOf} - * - {@link ILlmSchemaV3.IString.minLength} - * - {@link ILlmSchemaV3.IString.maxLength} - * - {@link ILlmSchemaV3.IString.format} - * - {@link ILlmSchemaV3.IString.pattern} - * - {@link ILlmSchemaV3.IString.contentMediaType} - * - {@link ILlmSchemaV3.IString.default} - * - {@link ILlmSchemaV3.IArray.minItems} - * - {@link ILlmSchemaV3.IArray.maxItems} - * - {@link ILlmSchemaV3.IArray.unique} - * - * @default true - */ - constraint: boolean; - - /** - * Whether to allow recursive types or not. - * - * If allow, then how many times to repeat the recursive types. - * - * By the way, if the model is "chatgpt", the recursive types are always - * allowed without any limitation, due to it supports the reference type. - * - * @default 3 - */ - recursive: false | number; - } - - /** - * Type of the function parameters. - * - * `ILlmSchemaV3.IParameters` is a type defining a function's parameters as a - * keyworded object type. - * - * It also can be utilized for the structured output metadata. - * - * @reference https://platform.openai.com/docs/guides/structured-outputs - */ - export interface IParameters extends Omit { - /** - * Additional properties' info. - * - * The `additionalProperties` means the type schema info of the additional - * properties that are not listed in the {@link properties}. - * - * By the way, it is not allowed in the parameters level. - */ - additionalProperties: false; - } - - /** Boolean type schema info. */ - export interface IBoolean extends IJsonSchemaAttribute.IBoolean { - /** Whether to allow `null` value or not. */ - nullable?: boolean; - - /** Default value. */ - default?: boolean | null; - - /** Enumeration values. */ - enum?: Array; - } - - /** Integer type schema info. */ - export interface IInteger extends IJsonSchemaAttribute.IInteger { - /** Whether to allow `null` value or not. */ - nullable?: boolean; - - /** - * Default value. - * - * @type int64 - */ - default?: number | null; - - /** - * Enumeration values. - * - * @type int64 - */ - enum?: Array; - - /** - * Minimum value restriction. - * - * @type int64 - */ - minimum?: number; - - /** - * Maximum value restriction. - * - * @type int64 - */ - maximum?: number; - - /** Exclusive minimum value restriction. */ - exclusiveMinimum?: number; - - /** Exclusive maximum value restriction. */ - exclusiveMaximum?: number; - - /** - * Multiple of value restriction. - * - * @type uint64 - * @exclusiveMinimum 0 - */ - multipleOf?: number; - } - - /** Number type schema info. */ - export interface INumber extends IJsonSchemaAttribute.INumber { - /** Whether to allow `null` value or not. */ - nullable?: boolean; - - /** Default value. */ - default?: number | null; - - /** Enumeration values. */ - enum?: Array; - - /** Minimum value restriction. */ - minimum?: number; - - /** Maximum value restriction. */ - maximum?: number; - - /** Exclusive minimum value restriction. */ - exclusiveMinimum?: number; - - /** Exclusive maximum value restriction. */ - exclusiveMaximum?: number; - - /** - * Multiple of value restriction. - * - * @exclusiveMinimum 0 - */ - multipleOf?: number; - } - - /** String type schema info. */ - export interface IString extends IJsonSchemaAttribute.IString { - /** Whether to allow `null` value or not. */ - nullable?: boolean; - - /** Default value. */ - default?: string | null; - - /** Enumeration values. */ - enum?: Array; - - /** Format restriction. */ - format?: - | "binary" - | "byte" - | "password" - | "regex" - | "uuid" - | "email" - | "hostname" - | "idn-email" - | "idn-hostname" - | "iri" - | "iri-reference" - | "ipv4" - | "ipv6" - | "uri" - | "uri-reference" - | "uri-template" - | "url" - | "date-time" - | "date" - | "time" - | "duration" - | "json-pointer" - | "relative-json-pointer" - | (string & {}); - - /** Pattern restriction. */ - pattern?: string; - - /** - * Minimum length restriction. - * - * @type uint64 - */ - minLength?: number; - - /** - * Maximum length restriction. - * - * @type uint64 - */ - maxLength?: number; - - /** Content media type restriction. */ - contentMediaType?: string; - } - - /** Array type schema info. */ - export interface IArray extends IJsonSchemaAttribute.IArray { - /** Whether to allow `null` value or not. */ - nullable?: boolean; - - /** - * Items type schema info. - * - * The `items` means the type of the array elements. In other words, it is - * the type schema info of the `T` in the TypeScript array type `Array`. - */ - items: ILlmSchemaV3; - - /** - * Unique items restriction. - * - * If this property value is `true`, target array must have unique items. - */ - uniqueItems?: boolean; - - /** - * Minimum items restriction. - * - * Restriction of minimum number of items in the array. - * - * @type uint64 - */ - minItems?: number; - - /** - * Maximum items restriction. - * - * Restriction of maximum number of items in the array. - * - * @type uint64 - */ - maxItems?: number; - } - - /** Object type schema info. */ - export interface IObject extends IJsonSchemaAttribute.IObject { - /** Whether to allow `null` value or not. */ - nullable?: boolean; - - /** - * Properties of the object. - * - * The `properties` means a list of key-value pairs of the object's regular - * properties. The key is the name of the regular property, and the value is - * the type schema info. - * - * If you need additional properties that is represented by dynamic key, you - * can use the {@link additionalProperties} instead. - */ - properties: Record; - - /** - * List of key values of the required properties. - * - * The `required` means a list of the key values of the required - * {@link properties}. If some property key is not listed in the `required` - * list, it means that property is optional. Otherwise some property key - * exists in the `required` list, it means that the property must be - * filled. - * - * Below is an example of the {@link properties} and `required`. - * - * ```typescript - * interface SomeObject { - * id: string; - * email: string; - * name?: string; - * } - * ``` - * - * As you can see, `id` and `email` {@link properties} are {@link required}, - * so that they are listed in the `required` list. - * - * ```json - * { - * "type": "object", - * "properties": { - * "id": { "type": "string" }, - * "email": { "type": "string" }, - * "name": { "type": "string" } - * }, - * "required": ["id", "email"] - * } - * ``` - */ - required: string[]; - - /** - * Additional properties' info. - * - * The `additionalProperties` means the type schema info of the additional - * properties that are not listed in the {@link properties}. - * - * If the value is `true`, it means that the additional properties are not - * restricted. They can be any type. Otherwise, if the value is - * {@link ILlmSchemaV3} type, it means that the additional properties must - * follow the type schema info. - * - * - `true`: `Record` - * - `IOpenAiSchema`: `Record` - */ - additionalProperties?: boolean | ILlmSchemaV3; - } - - /** - * One of type schema info. - * - * `IOneOf` represents an union type of the TypeScript (`A | B | C`). - * - * For reference, even though your Swagger (or OpenAPI) document has defined - * `anyOf` instead of the `oneOf`, it has been forcibly converted to `oneOf` - * type by {@link OpenApi.convert OpenAPI conversion}. - */ - export interface IOneOf extends IJsonSchemaAttribute { - /** List of the union types. */ - oneOf: Exclude[]; - } - - /** Null only type schema info. */ - export interface INullOnly extends IJsonSchemaAttribute.INull { - /** Default value. */ - default?: null; - } - - /** - * Unknown type schema info. - * - * It means the type of the value is `any`. - */ - export interface IUnknown extends IJsonSchemaAttribute.IUnknown {} -} diff --git a/src/structures/ILlmSchemaV3_1.ts b/src/structures/ILlmSchemaV3_1.ts deleted file mode 100644 index 355c04e1..00000000 --- a/src/structures/ILlmSchemaV3_1.ts +++ /dev/null @@ -1,450 +0,0 @@ -import { IJsonSchemaAttribute } from "./IJsonSchemaAttribute"; - -/** - * Type schema based on OpenAPI v3.1 for LLM function calling. - * - * `ILlmSchemaV3_1` is type metadata for LLM (Large Language Model) function - * calling, based on the OpenAPI v3.1 specification. This type is not the final - * type for LLM function calling, but an intermediate structure for conversion - * to the final types: - * - * - {@link IChatGptSchema} - * - {@link IClaudeSchema} - * - {@link ILlamaSchema} - * - * However, `ILlmSchemaV3_1` does not follow the entire OpenAPI v3.1 - * specification. It has specific restrictions and definitions. Here are the - * differences between `ILlmSchemaV3_1` and the OpenAPI v3.1 JSON schema: - * - * - Decompose mixed type: {@link OpenApiV3_1.IJsonSchema.IMixed} - * - Resolve nullable property: - * {@link OpenApiV3_1.IJsonSchema.__ISignificant.nullable} - * - Constant type is banned: {@link OpenApiV3_1.IJsonSchema.IConstant} - * - Merge {@link OpenApiV3_1.IJsonSchema.IAnyOf} to {@link ILlmSchemaV3_1.IOneOf} - * - Merge {@link OpenApiV3_1.IJsonSchema.IAllOf} to {@link ILlmSchemaV3_1.IObject} - * - Merge {@link OpenApiV3_1.IJsonSchema.IRecursiveReference} to - * {@link ILlmSchemaV3_1.IReference} - * - Do not support {@link OpenApiV3_1.IJsonSchema.ITuple} type - * - * Compared to {@link OpenApi.IJsonSchema}, the emended JSON schema - * specification: - * - * - {@link ILlmSchemaV3_1.IParameters.$defs} instead of the - * {@link OpenApi.IJsonSchema.schemas} - * - Do not support {@link OpenApi.IJsonSchema.ITuple} type - * - {@link ILlmSchemaV3_1.properties} and {@link ILlmSchemaV3_1.required} are - * always defined - * - * For reference, if you've composed the `ILlmSchemaV3_1` type with the - * {@link ILlmSchemaV3_1.IConfig.reference} `false` option (default is `false`), - * only the recursived named types would be archived into the - * {@link ILlmSchemaV3_1.IParameters.$defs}, and the others would be ecaped from - * the {@link ILlmSchemaV3_1.IReference} type. - * - * Also, if you've composed the `ILlmSchemaV3_1` type with the - * {@link ILlmSchemaV3_1.IConfig.constraint} `false` option (default `false`), - * the `ILlmSchemaV3_1` would not compose these properties. Instead, these - * properties would be written on - * {@link ILlmSchemaV3_1.__IAttribute.descripotion} field like `@format uuid` - * case. - * - * - {@link ILlmSchemaV3_1.INumber.minimum} - * - {@link ILlmSchemaV3_1.INumber.maximum} - * - {@link ILlmSchemaV3_1.INumber.multipleOf} - * - {@link ILlmSchemaV3_1.IString.minLength} - * - {@link ILlmSchemaV3_1.IString.maxLength} - * - {@link ILlmSchemaV3_1.IString.format} - * - {@link ILlmSchemaV3_1.IString.pattern} - * - {@link ILlmSchemaV3_1.IString.contentMediaType} - * - {@link ILlmSchemaV3_1.IArray.minItems} - * - {@link ILlmSchemaV3_1.IArray.maxItems} - * - {@link ILlmSchemaV3_1.IArray.unique} - * - * @author Jeongho Nam - https://github.com/samchon - * @reference https://platform.openai.com/docs/guides/function-calling - * @reference https://platform.openai.com/docs/guides/structured-outputs - */ -export type ILlmSchemaV3_1 = - | ILlmSchemaV3_1.IConstant - | ILlmSchemaV3_1.IBoolean - | ILlmSchemaV3_1.IInteger - | ILlmSchemaV3_1.INumber - | ILlmSchemaV3_1.IString - | ILlmSchemaV3_1.IArray - | ILlmSchemaV3_1.IObject - | ILlmSchemaV3_1.IReference - | ILlmSchemaV3_1.IOneOf - | ILlmSchemaV3_1.INull - | ILlmSchemaV3_1.IUnknown; -export namespace ILlmSchemaV3_1 { - /** Configuration for OpenAPI v3.1 based LLM schema composition. */ - export interface IConfig { - /** - * Whether to allow constraint properties or not. - * - * If you configure this property to `false`, the schemas do not contain the - * constraint properties of below. Instead, below properties would be - * written to the {@link ILlmSchemaV3_1.__IAttribute.description} property as - * a comment string like `"@format uuid"`. - * - * This is because some LLM schema model like {@link IChatGptSchema} has - * banned such constraint, because their LLM cannot understand the - * constraint properties and occur the hallucination. - * - * Therefore, considering your LLM model's performance, capability, and the - * complexity of your parameter types, determine which is better, to allow - * the constraint properties or not. - * - * - {@link ILlmSchemaV3_1.INumber.minimum} - * - {@link ILlmSchemaV3_1.INumber.maximum} - * - {@link ILlmSchemaV3_1.INumber.multipleOf} - * - {@link ILlmSchemaV3_1.IString.minLength} - * - {@link ILlmSchemaV3_1.IString.maxLength} - * - {@link ILlmSchemaV3_1.IString.format} - * - {@link ILlmSchemaV3_1.IString.pattern} - * - {@link ILlmSchemaV3_1.IString.contentMediaType} - * - {@link ILlmSchemaV3_1.IString.default} - * - {@link ILlmSchemaV3_1.IArray.minItems} - * - {@link ILlmSchemaV3_1.IArray.maxItems} - * - {@link ILlmSchemaV3_1.IArray.unique} - * - * @default true - */ - constraint: boolean; - - /** - * Whether to allow reference type in everywhere. - * - * If you configure this property to `false`, most of reference types - * represented by {@link ILlmSchemaV3_1.IReference} would be escaped to a - * plain type unless recursive type case. - * - * This is because some low sized LLM models does not understand the - * reference type well, and even the large size LLM models sometimes occur - * the hallucination. - * - * However, the reference type makes the schema size smaller, so that - * reduces the LLM token cost. Therefore, if you're using the large size of - * LLM model, and want to reduce the LLM token cost, you can configure this - * property to `true`. - * - * @default true - */ - reference: boolean; - } - - /** - * Type of the function parameters. - * - * `ILlmSchemaV3_1.IParameters` is a type defining a function's parameters as - * a keyworded object type. - * - * It also can be utilized for the structured output metadata. - * - * @reference https://platform.openai.com/docs/guides/structured-outputs - */ - export interface IParameters extends Omit { - /** Collection of the named types. */ - $defs: Record; - - /** - * Additional properties' info. - * - * The `additionalProperties` means the type schema info of the additional - * properties that are not listed in the {@link properties}. - * - * By the way, it is not allowed in the parameters level. - */ - additionalProperties: false; - } - - /** Constant value type. */ - export interface IConstant extends IJsonSchemaAttribute { - /** The constant value. */ - const: boolean | number | string; - } - - /** Boolean type info. */ - export interface IBoolean extends IJsonSchemaAttribute.IBoolean { - /** The default value. */ - default?: boolean; - } - - /** Integer type info. */ - export interface IInteger extends IJsonSchemaAttribute.IInteger { - /** - * Default value. - * - * @type int64 - */ - default?: number; - - /** - * Minimum value restriction. - * - * @type int64 - */ - minimum?: number; - - /** - * Maximum value restriction. - * - * @type int64 - */ - maximum?: number; - - /** Exclusive minimum value restriction. */ - exclusiveMinimum?: number; - - /** Exclusive maximum value restriction. */ - exclusiveMaximum?: number; - - /** - * Multiple of value restriction. - * - * @type uint64 - * @exclusiveMinimum 0 - */ - multipleOf?: number; - } - - /** Number (double) type info. */ - export interface INumber extends IJsonSchemaAttribute.INumber { - /** Default value. */ - default?: number; - - /** Minimum value restriction. */ - minimum?: number; - - /** Maximum value restriction. */ - maximum?: number; - - /** Exclusive minimum value restriction. */ - exclusiveMinimum?: number; - - /** Exclusive maximum value restriction. */ - exclusiveMaximum?: number; - - /** - * Multiple of value restriction. - * - * @exclusiveMinimum 0 - */ - multipleOf?: number; - } - - /** String type info. */ - export interface IString extends IJsonSchemaAttribute.IString { - /** Default value. */ - default?: string; - - /** Format restriction. */ - format?: - | "binary" - | "byte" - | "password" - | "regex" - | "uuid" - | "email" - | "hostname" - | "idn-email" - | "idn-hostname" - | "iri" - | "iri-reference" - | "ipv4" - | "ipv6" - | "uri" - | "uri-reference" - | "uri-template" - | "url" - | "date-time" - | "date" - | "time" - | "duration" - | "json-pointer" - | "relative-json-pointer" - | (string & {}); - - /** Pattern restriction. */ - pattern?: string; - - /** Content media type restriction. */ - contentMediaType?: string; - - /** - * Minimum length restriction. - * - * @type uint64 - */ - minLength?: number; - - /** - * Maximum length restriction. - * - * @type uint64 - */ - maxLength?: number; - } - - /** Array type info. */ - export interface IArray extends IJsonSchemaAttribute.IArray { - /** - * Items type info. - * - * The `items` means the type of the array elements. In other words, it is - * the type schema info of the `T` in the TypeScript array type `Array`. - */ - items: ILlmSchemaV3_1; - - /** - * Unique items restriction. - * - * If this property value is `true`, target array must have unique items. - */ - uniqueItems?: boolean; - - /** - * Minimum items restriction. - * - * Restriction of minimum number of items in the array. - * - * @type uint64 - */ - minItems?: number; - - /** - * Maximum items restriction. - * - * Restriction of maximum number of items in the array. - * - * @type uint64 - */ - maxItems?: number; - } - - /** Object type info. */ - export interface IObject extends IJsonSchemaAttribute.IObject { - /** - * Properties of the object. - * - * The `properties` means a list of key-value pairs of the object's regular - * properties. The key is the name of the regular property, and the value is - * the type schema info. - * - * If you need additional properties that is represented by dynamic key, you - * can use the {@link additionalProperties} instead. - */ - properties: Record; - - /** - * Additional properties' info. - * - * The `additionalProperties` means the type schema info of the additional - * properties that are not listed in the {@link properties}. - * - * If the value is `true`, it means that the additional properties are not - * restricted. They can be any type. Otherwise, if the value is - * {@link ILlmSchemaV3_1} type, it means that the additional properties must - * follow the type schema info. - * - * - `true`: `Record` - * - `ILlmSchemaV3_1`: `Record` - */ - additionalProperties?: boolean | ILlmSchemaV3_1; - - /** - * List of key values of the required properties. - * - * The `required` means a list of the key values of the required - * {@link properties}. If some property key is not listed in the `required` - * list, it means that property is optional. Otherwise some property key - * exists in the `required` list, it means that the property must be - * filled. - * - * Below is an example of the {@link properties} and `required`. - * - * ```typescript - * interface SomeObject { - * id: string; - * email: string; - * name?: string; - * } - * ``` - * - * As you can see, `id` and `email` {@link properties} are {@link required}, - * so that they are listed in the `required` list. - * - * ```json - * { - * "type": "object", - * "properties": { - * "id": { "type": "string" }, - * "email": { "type": "string" }, - * "name": { "type": "string" } - * }, - * "required": ["id", "email"] - * } - * ``` - */ - required: string[]; - } - - /** Reference type directing named schema. */ - export interface IReference extends IJsonSchemaAttribute { - /** - * Reference to the named schema. - * - * The `ref` is a reference to the named schema. Format of the `$ref` is - * following the JSON Pointer specification. In the OpenAPI, the `$ref` - * starts with `#/$defs/` which means the type is stored in the - * {@link ILlmSchemaV3_1.IParameters.$defs} object. - * - * - `#/$defs/SomeObject` - * - `#/$defs/AnotherObject` - */ - $ref: string; - } - - /** - * Union type. - * - * `IOneOf` represents an union type of the TypeScript (`A | B | C`). - * - * For reference, even though your Swagger (or OpenAPI) document has defined - * `anyOf` instead of the `oneOf`, {@link OpenApi} forcibly converts it to - * `oneOf` type. - */ - export interface IOneOf extends IJsonSchemaAttribute { - /** List of the union types. */ - oneOf: Exclude[]; - - /** Discriminator info of the union type. */ - discriminator?: IOneOf.IDiscriminator; - } - export namespace IOneOf { - /** Discriminator info of the union type. */ - export interface IDiscriminator { - /** Property name for the discriminator. */ - propertyName: string; - - /** - * Mapping of the discriminator value to the schema name. - * - * This property is valid only for {@link IReference} typed - * {@link IOneOf.oneof} elements. Therefore, `key` of `mapping` is the - * discriminator value, and `value` of `mapping` is the schema name like - * `#/components/schemas/SomeObject`. - */ - mapping?: Record; - } - } - - /** Null type. */ - export interface INull extends IJsonSchemaAttribute.INull { - /** Default value. */ - default?: null; - } - - /** Unknown, the `any` type. */ - export interface IUnknown extends IJsonSchemaAttribute.IUnknown {} -} diff --git a/src/utils/ChatGptTypeChecker.ts b/src/utils/ChatGptTypeChecker.ts deleted file mode 100644 index 21d70db4..00000000 --- a/src/utils/ChatGptTypeChecker.ts +++ /dev/null @@ -1,391 +0,0 @@ -import { IChatGptSchema } from "../structures/IChatGptSchema"; -import { MapUtil } from "./MapUtil"; - -/** - * Type checker for ChatGPT type schema. - * - * `ChatGptTypeChecker` is a type checker of {@link IChatGptSchema}. - * - * @author Jeongho Nam - https://github.com/samchon - */ -export namespace ChatGptTypeChecker { - /* ----------------------------------------------------------- - TYPE CHECKERS - ----------------------------------------------------------- */ - /** - * Test whether the schema is a null type. - * - * @param schema Target schema - * @returns Whether null type or not - */ - export const isNull = ( - schema: IChatGptSchema, - ): schema is IChatGptSchema.INull => - (schema as IChatGptSchema.INull).type === "null"; - - /** - * Test whether the schema is an unknown type. - * - * @param schema Target schema - * @returns Whether unknown type or not - */ - export const isUnknown = ( - schema: IChatGptSchema, - ): schema is IChatGptSchema.IUnknown => - (schema as IChatGptSchema.IUnknown).type === undefined && - !isAnyOf(schema) && - !isReference(schema); - - /** - * Test whether the schema is a boolean type. - * - * @param schema Target schema - * @returns Whether boolean type or not - */ - export const isBoolean = ( - schema: IChatGptSchema, - ): schema is IChatGptSchema.IBoolean => - (schema as IChatGptSchema.IBoolean).type === "boolean"; - - /** - * Test whether the schema is an integer type. - * - * @param schema Target schema - * @returns Whether integer type or not - */ - export const isInteger = ( - schema: IChatGptSchema, - ): schema is IChatGptSchema.IInteger => - (schema as IChatGptSchema.IInteger).type === "integer"; - - /** - * Test whether the schema is a number type. - * - * @param schema Target schema - * @returns Whether number type or not - */ - export const isNumber = ( - schema: IChatGptSchema, - ): schema is IChatGptSchema.INumber => - (schema as IChatGptSchema.INumber).type === "number"; - - /** - * Test whether the schema is a string type. - * - * @param schema Target schema - * @returns Whether string type or not - */ - export const isString = ( - schema: IChatGptSchema, - ): schema is IChatGptSchema.IString => - (schema as IChatGptSchema.IString).type === "string"; - - /** - * Test whether the schema is an array type. - * - * @param schema Target schema - * @returns Whether array type or not - */ - export const isArray = ( - schema: IChatGptSchema, - ): schema is IChatGptSchema.IArray => - (schema as IChatGptSchema.IArray).type === "array" && - (schema as IChatGptSchema.IArray).items !== undefined; - - /** - * Test whether the schema is an object type. - * - * @param schema Target schema - * @returns Whether object type or not - */ - export const isObject = ( - schema: IChatGptSchema, - ): schema is IChatGptSchema.IObject => - (schema as IChatGptSchema.IObject).type === "object"; - - /** - * Test whether the schema is a reference type. - * - * @param schema Target schema - * @returns Whether reference type or not - */ - export const isReference = ( - schema: IChatGptSchema, - ): schema is IChatGptSchema.IReference => (schema as any).$ref !== undefined; - - /** - * Test whether the schema is an union type. - * - * @param schema Target schema - * @returns Whether union type or not - */ - export const isAnyOf = ( - schema: IChatGptSchema, - ): schema is IChatGptSchema.IAnyOf => - (schema as IChatGptSchema.IAnyOf).anyOf !== undefined; - - /* ----------------------------------------------------------- - OPERATORS - ----------------------------------------------------------- */ - /** - * Visit every nested schemas. - * - * Visit every nested schemas of the target, and apply the `props.closure` - * function. - * - * Here is the list of occurring nested visitings: - * - * - {@link IChatGptSchema.IAnyOf.anyOf} - * - {@link IChatGptSchema.IReference} - * - {@link IChatGptSchema.IObject.properties} - * - {@link IChatGptSchema.IArray.items} - * - * @param props Properties for visiting - */ - export const visit = (props: { - closure: (schema: IChatGptSchema, accessor: string) => void; - $defs?: Record | undefined; - schema: IChatGptSchema; - accessor?: string; - refAccessor?: string; - }): void => { - const already: Set = new Set(); - const refAccessor: string = props.refAccessor ?? "$input.$defs"; - const next = (schema: IChatGptSchema, accessor: string): void => { - props.closure(schema, accessor); - if (ChatGptTypeChecker.isReference(schema)) { - const key: string = schema.$ref.split("#/$defs/").pop()!; - if (already.has(key) === true) return; - already.add(key); - const found: IChatGptSchema | undefined = props.$defs?.[key]; - if (found !== undefined) next(found, `${refAccessor}[${key}]`); - } else if (ChatGptTypeChecker.isAnyOf(schema)) - schema.anyOf.forEach((s, i) => next(s, `${accessor}.anyOf[${i}]`)); - else if (ChatGptTypeChecker.isObject(schema)) { - for (const [key, value] of Object.entries(schema.properties)) - next(value, `${accessor}.properties[${JSON.stringify(key)}]`); - if ( - typeof schema.additionalProperties === "object" && - schema.additionalProperties !== null - ) - next(schema.additionalProperties, `${accessor}.additionalProperties`); - } else if (ChatGptTypeChecker.isArray(schema)) - next(schema.items, `${accessor}.items`); - }; - next(props.schema, props.accessor ?? "$input.schemas"); - }; - - /** - * Test whether the `x` schema covers the `y` schema. - * - * @param props Properties for testing - * @returns Whether the `x` schema covers the `y` schema - */ - export const covers = (props: { - $defs?: Record | undefined; - x: IChatGptSchema; - y: IChatGptSchema; - }): boolean => - coverStation({ - $defs: props.$defs, - x: props.x, - y: props.y, - visited: new Map(), - }); - - const coverStation = (p: { - $defs?: Record | undefined; - visited: Map>; - x: IChatGptSchema; - y: IChatGptSchema; - }): boolean => { - const cache: boolean | undefined = p.visited.get(p.x)?.get(p.y); - if (cache !== undefined) return cache; - - // FOR RECURSIVE CASE - const nested: Map = MapUtil.take(p.visited)(p.x)( - () => new Map(), - ); - nested.set(p.y, true); - - // COMPUTE IT - const result: boolean = coverSchema(p); - nested.set(p.y, result); - return result; - }; - - const coverSchema = (p: { - $defs?: Record | undefined; - visited: Map>; - x: IChatGptSchema; - y: IChatGptSchema; - }): boolean => { - // CHECK EQUALITY - if (p.x === p.y) return true; - else if (isReference(p.x) && isReference(p.y) && p.x.$ref === p.y.$ref) - return true; - - // COMPARE WITH FLATTENING - const alpha: IChatGptSchema[] = flatSchema(p.$defs, p.x); - const beta: IChatGptSchema[] = flatSchema(p.$defs, p.y); - if (alpha.some((x) => isUnknown(x))) return true; - else if (beta.some((x) => isUnknown(x))) return false; - return beta.every((b) => - alpha.some((a) => - coverEscapedSchema({ - $defs: p.$defs, - visited: p.visited, - x: a, - y: b, - }), - ), - ); - }; - - const coverEscapedSchema = (p: { - $defs?: Record | undefined; - visited: Map>; - x: IChatGptSchema; - y: IChatGptSchema; - }): boolean => { - // CHECK EQUALITY - if (p.x === p.y) return true; - else if (isUnknown(p.x)) return true; - else if (isUnknown(p.y)) return false; - else if (isNull(p.x)) return isNull(p.y); - // ATOMIC CASE - else if (isBoolean(p.x)) return isBoolean(p.y) && coverBoolean(p.x, p.y); - else if (isInteger(p.x)) return isInteger(p.y) && coverInteger(p.x, p.y); - else if (isNumber(p.x)) return isNumber(p.y) && coverNumber(p.x, p.y); - else if (isString(p.x)) return isString(p.y) && coverString(p.x, p.y); - // INSTANCE CASE - else if (isArray(p.x)) - return ( - isArray(p.y) && - coverArray({ - $defs: p.$defs, - visited: p.visited, - x: p.x, - y: p.y, - }) - ); - else if (isObject(p.x)) - return ( - isObject(p.y) && - coverObject({ - $defs: p.$defs, - visited: p.visited, - x: p.x, - y: p.y, - }) - ); - else if (isReference(p.x)) return isReference(p.y) && p.x.$ref === p.y.$ref; - return false; - }; - - const coverArray = (p: { - $defs?: Record | undefined; - visited: Map>; - x: IChatGptSchema.IArray; - y: IChatGptSchema.IArray; - }): boolean => - coverStation({ - $defs: p.$defs, - visited: p.visited, - x: p.x.items, - y: p.y.items, - }); - - const coverObject = (p: { - $defs?: Record | undefined; - visited: Map>; - x: IChatGptSchema.IObject; - y: IChatGptSchema.IObject; - }): boolean => { - if (!p.x.additionalProperties && !!p.y.additionalProperties) return false; - else if ( - !!p.x.additionalProperties && - !!p.y.additionalProperties && - ((typeof p.x.additionalProperties === "object" && - p.y.additionalProperties === true) || - (typeof p.x.additionalProperties === "object" && - typeof p.y.additionalProperties === "object" && - !coverStation({ - $defs: p.$defs, - visited: p.visited, - x: p.x.additionalProperties, - y: p.y.additionalProperties, - }))) - ) - return false; - return Object.entries(p.y.properties ?? {}).every(([key, b]) => { - const a: IChatGptSchema | undefined = p.x.properties?.[key]; - if (a === undefined) return false; - else if ( - (p.x.required?.includes(key) ?? false) === true && - (p.y.required?.includes(key) ?? false) === false - ) - return false; - return coverStation({ - $defs: p.$defs, - visited: p.visited, - x: a, - y: b, - }); - }); - }; - - const coverBoolean = ( - x: IChatGptSchema.IBoolean, - y: IChatGptSchema.IBoolean, - ): boolean => { - if (!!x.enum?.length) - return !!y.enum?.length && y.enum.every((v) => x.enum!.includes(v)); - return true; - }; - - const coverInteger = ( - x: IChatGptSchema.IInteger, - y: IChatGptSchema.IInteger, - ): boolean => { - if (!!x.enum?.length) - return !!y.enum?.length && y.enum.every((v) => x.enum!.includes(v)); - return x.type === y.type; - }; - - const coverNumber = ( - x: IChatGptSchema.INumber, - y: IChatGptSchema.IInteger | IChatGptSchema.INumber, - ): boolean => { - if (!!x.enum?.length) - return !!y.enum?.length && y.enum.every((v) => x.enum!.includes(v)); - return x.type === y.type || (x.type === "number" && y.type === "integer"); - }; - - const coverString = ( - x: IChatGptSchema.IString, - y: IChatGptSchema.IString, - ): boolean => { - if (!!x.enum?.length) - return !!y.enum?.length && y.enum.every((v) => x.enum!.includes(v)); - return x.type === y.type; - }; - - const flatSchema = ( - $defs: Record | undefined, - schema: IChatGptSchema, - ): IChatGptSchema[] => { - schema = escapeReference($defs, schema); - if (isAnyOf(schema)) - return schema.anyOf.map((v) => flatSchema($defs, v)).flat(); - return [schema]; - }; - - const escapeReference = ( - $defs: Record | undefined, - schema: IChatGptSchema, - ): Exclude => - isReference(schema) - ? escapeReference($defs, $defs![schema.$ref.replace("#/$defs/", "")]!) - : schema; -} diff --git a/src/utils/ClaudeTypeChecker.ts b/src/utils/ClaudeTypeChecker.ts deleted file mode 100644 index 718e05f9..00000000 --- a/src/utils/ClaudeTypeChecker.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { LlmTypeCheckerV3_1 } from "./LlmTypeCheckerV3_1"; - -/** - * Type checker for Claude type schema. - * - * `ClaudeTypeChecker` is an alias for {@link LlmTypeCheckerV3_1} since Claude - * supports the same schema structure as LLM Schema v3.1. - * - * @author Jeongho Nam - https://github.com/samchon - */ -export const ClaudeTypeChecker = LlmTypeCheckerV3_1; diff --git a/src/utils/DeepSeekTypeChecker.ts b/src/utils/DeepSeekTypeChecker.ts deleted file mode 100644 index b223dcf4..00000000 --- a/src/utils/DeepSeekTypeChecker.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { LlmTypeCheckerV3_1 } from "./LlmTypeCheckerV3_1"; - -/** - * Type checker for DeepSeek type schema. - * - * `DeepSeekTypeChecker` is an alias for {@link LlmTypeCheckerV3_1} since DeepSeek - * supports the same schema structure as LLM Schema v3.1. - * - * @author Jeongho Nam - https://github.com/samchon - */ -export const DeepSeekTypeChecker = LlmTypeCheckerV3_1; diff --git a/src/utils/GeminiTypeChecker.ts b/src/utils/GeminiTypeChecker.ts deleted file mode 100644 index b1d1fe8d..00000000 --- a/src/utils/GeminiTypeChecker.ts +++ /dev/null @@ -1,407 +0,0 @@ -import { IGeminiSchema } from "../structures/IGeminiSchema"; -import { MapUtil } from "./MapUtil"; -import { OpenApiTypeCheckerBase } from "./internal/OpenApiTypeCheckerBase"; - -/** - * Type checker for Gemini type schema. - * - * `GeminiTypeChecker` is a type checker of {@link IGeminiSchema}. - * - * @author Jeongho Nam - https://github.com/samchon - */ -export namespace GeminiTypeChecker { - /* ----------------------------------------------------------- - TYPE CHECKERS - ----------------------------------------------------------- */ - /** - * Test whether the schema is a null type. - * - * @param schema Target schema - * @returns Whether null type or not - */ - export const isNull = ( - schema: IGeminiSchema, - ): schema is IGeminiSchema.INull => - (schema as IGeminiSchema.INull).type === "null"; - - /** - * Test whether the schema is an unknown type. - * - * @param schema Target schema - * @returns Whether unknown type or not - */ - export const isUnknown = ( - schema: IGeminiSchema, - ): schema is IGeminiSchema.IUnknown => - (schema as IGeminiSchema.IUnknown).type === undefined && - !isAnyOf(schema) && - !isReference(schema); - - /** - * Test whether the schema is a boolean type. - * - * @param schema Target schema - * @returns Whether boolean type or not - */ - export const isBoolean = ( - schema: IGeminiSchema, - ): schema is IGeminiSchema.IBoolean => - (schema as IGeminiSchema.IBoolean).type === "boolean"; - - /** - * Test whether the schema is an integer type. - * - * @param schema Target schema - * @returns Whether integer type or not - */ - export const isInteger = ( - schema: IGeminiSchema, - ): schema is IGeminiSchema.IInteger => - (schema as IGeminiSchema.IInteger).type === "integer"; - - /** - * Test whether the schema is a number type. - * - * @param schema Target schema - * @returns Whether number type or not - */ - export const isNumber = ( - schema: IGeminiSchema, - ): schema is IGeminiSchema.INumber => - (schema as IGeminiSchema.INumber).type === "number"; - - /** - * Test whether the schema is a string type. - * - * @param schema Target schema - * @returns Whether string type or not - */ - export const isString = ( - schema: IGeminiSchema, - ): schema is IGeminiSchema.IString => - (schema as IGeminiSchema.IString).type === "string"; - - /** - * Test whether the schema is an array type. - * - * @param schema Target schema - * @returns Whether array type or not - */ - export const isArray = ( - schema: IGeminiSchema, - ): schema is IGeminiSchema.IArray => - (schema as IGeminiSchema.IArray).type === "array" && - (schema as IGeminiSchema.IArray).items !== undefined; - - /** - * Test whether the schema is an object type. - * - * @param schema Target schema - * @returns Whether object type or not - */ - export const isObject = ( - schema: IGeminiSchema, - ): schema is IGeminiSchema.IObject => - (schema as IGeminiSchema.IObject).type === "object"; - - /** - * Test whether the schema is a reference type. - * - * @param schema Target schema - * @returns Whether reference type or not - */ - export const isReference = ( - schema: IGeminiSchema, - ): schema is IGeminiSchema.IReference => (schema as any).$ref !== undefined; - - /** - * Test whether the schema is an union type. - * - * @param schema Target schema - * @returns Whether union type or not - */ - export const isAnyOf = ( - schema: IGeminiSchema, - ): schema is IGeminiSchema.IAnyOf => - (schema as IGeminiSchema.IAnyOf).anyOf !== undefined; - - /* ----------------------------------------------------------- - OPERATORS - ----------------------------------------------------------- */ - /** - * Visit every nested schemas. - * - * Visit every nested schemas of the target, and apply the `props.closure` - * function. - * - * Here is the list of occurring nested visitings: - * - * - {@link IGeminiSchema.IAnyOf.anyOf} - * - {@link IGeminiSchema.IReference} - * - {@link IGeminiSchema.IObject.properties} - * - {@link IGeminiSchema.IArray.items} - * - * @param props Properties for visiting - */ - export const visit = (props: { - closure: (schema: IGeminiSchema, accessor: string) => void; - $defs?: Record | undefined; - schema: IGeminiSchema; - accessor?: string; - refAccessor?: string; - }): void => { - const already: Set = new Set(); - const refAccessor: string = props.refAccessor ?? "$input.$defs"; - const next = (schema: IGeminiSchema, accessor: string): void => { - props.closure(schema, accessor); - if (GeminiTypeChecker.isReference(schema)) { - const key: string = schema.$ref.split("#/$defs/").pop()!; - if (already.has(key) === true) return; - already.add(key); - const found: IGeminiSchema | undefined = props.$defs?.[key]; - if (found !== undefined) next(found, `${refAccessor}[${key}]`); - } else if (GeminiTypeChecker.isAnyOf(schema)) - schema.anyOf.forEach((s, i) => next(s, `${accessor}.anyOf[${i}]`)); - else if (GeminiTypeChecker.isObject(schema)) { - for (const [key, value] of Object.entries(schema.properties)) - next(value, `${accessor}.properties[${JSON.stringify(key)}]`); - if ( - typeof schema.additionalProperties === "object" && - schema.additionalProperties !== null - ) - next(schema.additionalProperties, `${accessor}.additionalProperties`); - } else if (GeminiTypeChecker.isArray(schema)) - next(schema.items, `${accessor}.items`); - }; - next(props.schema, props.accessor ?? "$input.schemas"); - }; - - /** - * Test whether the `x` schema covers the `y` schema. - * - * @param props Properties for testing - * @returns Whether the `x` schema covers the `y` schema - */ - export const covers = (props: { - $defs?: Record | undefined; - x: IGeminiSchema; - y: IGeminiSchema; - }): boolean => - coverStation({ - $defs: props.$defs, - x: props.x, - y: props.y, - visited: new Map(), - }); - - const coverStation = (p: { - $defs?: Record | undefined; - visited: Map>; - x: IGeminiSchema; - y: IGeminiSchema; - }): boolean => { - const cache: boolean | undefined = p.visited.get(p.x)?.get(p.y); - if (cache !== undefined) return cache; - - // FOR RECURSIVE CASE - const nested: Map = MapUtil.take(p.visited)(p.x)( - () => new Map(), - ); - nested.set(p.y, true); - - // COMPUTE IT - const result: boolean = coverSchema(p); - nested.set(p.y, result); - return result; - }; - - const coverSchema = (p: { - $defs?: Record | undefined; - visited: Map>; - x: IGeminiSchema; - y: IGeminiSchema; - }): boolean => { - // CHECK EQUALITY - if (p.x === p.y) return true; - else if (isReference(p.x) && isReference(p.y) && p.x.$ref === p.y.$ref) - return true; - - // COMPARE WITH FLATTENING - const alpha: IGeminiSchema[] = flatSchema(p.$defs, p.x); - const beta: IGeminiSchema[] = flatSchema(p.$defs, p.y); - if (alpha.some((x) => isUnknown(x))) return true; - else if (beta.some((x) => isUnknown(x))) return false; - return beta.every((b) => - alpha.some((a) => - coverEscapedSchema({ - $defs: p.$defs, - visited: p.visited, - x: a, - y: b, - }), - ), - ); - }; - - const coverEscapedSchema = (p: { - $defs?: Record | undefined; - visited: Map>; - x: IGeminiSchema; - y: IGeminiSchema; - }): boolean => { - // CHECK EQUALITY - if (p.x === p.y) return true; - else if (isUnknown(p.x)) return true; - else if (isUnknown(p.y)) return false; - else if (isNull(p.x)) return isNull(p.y); - // ATOMIC CASE - else if (isBoolean(p.x)) return isBoolean(p.y) && coverBoolean(p.x, p.y); - else if (isInteger(p.x)) return isInteger(p.y) && coverInteger(p.x, p.y); - else if (isNumber(p.x)) return isNumber(p.y) && coverNumber(p.x, p.y); - else if (isString(p.x)) return isString(p.y) && coverString(p.x, p.y); - // INSTANCE CASE - else if (isArray(p.x)) - return ( - isArray(p.y) && - coverArray({ - $defs: p.$defs, - visited: p.visited, - x: p.x, - y: p.y, - }) - ); - else if (isObject(p.x)) - return ( - isObject(p.y) && - coverObject({ - $defs: p.$defs, - visited: p.visited, - x: p.x, - y: p.y, - }) - ); - else if (isReference(p.x)) return isReference(p.y) && p.x.$ref === p.y.$ref; - return false; - }; - - const coverArray = (p: { - $defs?: Record | undefined; - visited: Map>; - x: IGeminiSchema.IArray; - y: IGeminiSchema.IArray; - }): boolean => { - if ( - !( - p.x.minItems === undefined || - (p.y.minItems !== undefined && p.x.minItems <= p.y.minItems) - ) - ) - return false; - else if ( - !( - p.x.maxItems === undefined || - (p.y.maxItems !== undefined && p.x.maxItems >= p.y.maxItems) - ) - ) - return false; - return coverStation({ - $defs: p.$defs, - visited: p.visited, - x: p.x.items, - y: p.y.items, - }); - } - - const coverObject = (p: { - $defs?: Record | undefined; - visited: Map>; - x: IGeminiSchema.IObject; - y: IGeminiSchema.IObject; - }): boolean => { - if (!p.x.additionalProperties && !!p.y.additionalProperties) return false; - else if ( - !!p.x.additionalProperties && - !!p.y.additionalProperties && - ((typeof p.x.additionalProperties === "object" && - p.y.additionalProperties === true) || - (typeof p.x.additionalProperties === "object" && - typeof p.y.additionalProperties === "object" && - !coverStation({ - $defs: p.$defs, - visited: p.visited, - x: p.x.additionalProperties, - y: p.y.additionalProperties, - }))) - ) - return false; - return Object.entries(p.y.properties ?? {}).every(([key, b]) => { - const a: IGeminiSchema | undefined = p.x.properties?.[key]; - if (a === undefined) return false; - else if ( - (p.x.required?.includes(key) ?? false) === true && - (p.y.required?.includes(key) ?? false) === false - ) - return false; - return coverStation({ - $defs: p.$defs, - visited: p.visited, - x: a, - y: b, - }); - }); - }; - - const coverBoolean = ( - x: IGeminiSchema.IBoolean, - y: IGeminiSchema.IBoolean, - ): boolean => { - if (!!x.enum?.length) - return !!y.enum?.length && y.enum.every((v) => x.enum!.includes(v)); - return true; - }; - - const coverInteger = ( - x: IGeminiSchema.IInteger, - y: IGeminiSchema.IInteger, - ): boolean => { - if (!!x.enum?.length) - return !!y.enum?.length && y.enum.every((v) => x.enum!.includes(v)); - return OpenApiTypeCheckerBase.coverInteger(x, y); - }; - - const coverNumber = ( - x: IGeminiSchema.INumber, - y: IGeminiSchema.IInteger | IGeminiSchema.INumber, - ): boolean => { - if (!!x.enum?.length) - return !!y.enum?.length && y.enum.every((v) => x.enum!.includes(v)); - return OpenApiTypeCheckerBase.coverNumber(x, y); - }; - - const coverString = ( - x: IGeminiSchema.IString, - y: IGeminiSchema.IString, - ): boolean => { - if (!!x.enum?.length) - return !!y.enum?.length && y.enum.every((v) => x.enum!.includes(v)); - return OpenApiTypeCheckerBase.coverString(x, y); - }; - - const flatSchema = ( - $defs: Record | undefined, - schema: IGeminiSchema, - ): IGeminiSchema[] => { - schema = escapeReference($defs, schema); - if (isAnyOf(schema)) - return schema.anyOf.map((v) => flatSchema($defs, v)).flat(); - return [schema]; - }; - - const escapeReference = ( - $defs: Record | undefined, - schema: IGeminiSchema, - ): Exclude => - isReference(schema) - ? escapeReference($defs, $defs![schema.$ref.replace("#/$defs/", "")]!) - : schema; -} diff --git a/src/utils/LlamaTypeChecker.ts b/src/utils/LlamaTypeChecker.ts deleted file mode 100644 index de791101..00000000 --- a/src/utils/LlamaTypeChecker.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { LlmTypeCheckerV3_1 } from "./LlmTypeCheckerV3_1"; - -/** - * Type checker for Llama type schema. - * - * `LlamaTypeChecker` is an alias for {@link LlmTypeCheckerV3_1} since Llama - * supports the same schema structure as LLM Schema v3.1. - * - * @author Jeongho Nam - https://github.com/samchon - */ -export const LlamaTypeChecker = LlmTypeCheckerV3_1; diff --git a/src/utils/LlmTypeCheckerV3.ts b/src/utils/LlmTypeCheckerV3.ts deleted file mode 100644 index dd05a7e3..00000000 --- a/src/utils/LlmTypeCheckerV3.ts +++ /dev/null @@ -1,307 +0,0 @@ -import { ILlmSchemaV3 } from "../structures/ILlmSchemaV3"; -import { OpenApiTypeCheckerBase } from "./internal/OpenApiTypeCheckerBase"; - -/** - * Type checker for LLM type schema v3. - * - * `LlmTypeCheckerV3` is a type checker of {@link ILlmSchemaV3}. - * - * @author Jeongho Nam - https://github.com/samchon - */ -export namespace LlmTypeCheckerV3 { - /* ----------------------------------------------------------- - OPERATORS - ----------------------------------------------------------- */ - /** - * Visit every nested schemas. - * - * Visit every nested schemas of the target, and apply the `props.closure` - * function. - * - * Here is the list of occurring nested visitings: - * - * - {@link ILlmSchemaV3.IOneOf.oneOf} - * - {@link ILlmSchemaV3.IObject.additionalProperties} - * - {@link ILlmSchemaV3.IArray.items} - * - * @param props Properties for visiting - */ - export const visit = (props: { - closure: (schema: ILlmSchemaV3, accessor: string) => void; - schema: ILlmSchemaV3; - accessor?: string; - }): void => { - const accessor: string = props.accessor ?? "$input.schema"; - props.closure(props.schema, accessor); - if (isOneOf(props.schema)) - props.schema.oneOf.forEach((s, i) => - visit({ - closure: props.closure, - schema: s, - accessor: `${accessor}.oneOf[${i}]`, - }), - ); - else if (isObject(props.schema)) { - for (const [k, s] of Object.entries(props.schema.properties)) - visit({ - closure: props.closure, - schema: s, - accessor: `${accessor}.properties[${JSON.stringify(k)}]`, - }); - if ( - typeof props.schema.additionalProperties === "object" && - props.schema.additionalProperties !== null - ) - visit({ - closure: props.closure, - schema: props.schema.additionalProperties, - accessor: `${accessor}.additionalProperties`, - }); - } else if (isArray(props.schema)) - visit({ - closure: props.closure, - schema: props.schema.items, - accessor: `${accessor}.items`, - }); - }; - - export const covers = (x: ILlmSchemaV3, y: ILlmSchemaV3): boolean => { - const alpha: ILlmSchemaV3[] = flatSchema(x); - const beta: ILlmSchemaV3[] = flatSchema(y); - if (alpha.some((x) => isUnknown(x))) return true; - else if (beta.some((x) => isUnknown(x))) return false; - return beta.every((b) => - alpha.some((a) => { - // CHECK EQUALITY - if (a === b) return true; - else if (isUnknown(a)) return true; - else if (isUnknown(b)) return false; - else if (isNullOnly(a)) return isNullOnly(b); - else if (isNullOnly(b)) return isNullable(a); - else if (isNullable(a) && !isNullable(b)) return false; - // ATOMIC CASE - else if (isBoolean(a)) return isBoolean(b) && coverBoolean(a, b); - else if (isInteger(a)) return isInteger(b) && coverInteger(a, b); - else if (isNumber(a)) - return (isNumber(b) || isInteger(b)) && coverNumber(a, b); - else if (isString(a)) return isString(b) && covertString(a, b); - // INSTANCE CASE - else if (isArray(a)) return isArray(b) && coverArray(a, b); - else if (isObject(a)) return isObject(b) && coverObject(a, b); - else if (isOneOf(a)) return false; - }), - ); - }; - - /** @internal */ - const coverBoolean = ( - x: ILlmSchemaV3.IBoolean, - y: ILlmSchemaV3.IBoolean, - ): boolean => - x.enum === undefined || - (y.enum !== undefined && x.enum.every((v) => y.enum!.includes(v))); - - /** @internal */ - const coverInteger = ( - x: ILlmSchemaV3.IInteger, - y: ILlmSchemaV3.IInteger, - ): boolean => { - if (x.enum !== undefined) - return y.enum !== undefined && x.enum.every((v) => y.enum!.includes(v)); - return x.type === y.type && OpenApiTypeCheckerBase.coverNumericRange(x, y); - }; - - /** @internal */ - const coverNumber = ( - x: ILlmSchemaV3.INumber, - y: ILlmSchemaV3.INumber | ILlmSchemaV3.IInteger, - ): boolean => { - if (x.enum !== undefined) - return y.enum !== undefined && x.enum.every((v) => y.enum!.includes(v)); - return ( - (x.type === y.type || (x.type === "number" && y.type === "integer")) && - OpenApiTypeCheckerBase.coverNumericRange(x, y) - ); - }; - - /** @internal */ - const covertString = ( - x: ILlmSchemaV3.IString, - y: ILlmSchemaV3.IString, - ): boolean => { - if (x.enum !== undefined) - return y.enum !== undefined && x.enum.every((v) => y.enum!.includes(v)); - return [ - x.type === y.type, - x.format === undefined || - (y.format !== undefined && coverFormat(x.format, y.format)), - x.pattern === undefined || x.pattern === y.pattern, - x.minLength === undefined || - (y.minLength !== undefined && x.minLength <= y.minLength), - x.maxLength === undefined || - (y.maxLength !== undefined && x.maxLength >= y.maxLength), - ].every((v) => v); - }; - - const coverFormat = ( - x: Required["format"], - y: Required["format"], - ): boolean => - x === y || - (x === "idn-email" && y === "email") || - (x === "idn-hostname" && y === "hostname") || - (["uri", "iri"].includes(x) && y === "url") || - (x === "iri" && y === "uri") || - (x === "iri-reference" && y === "uri-reference"); - - /** @internal */ - const coverArray = ( - x: ILlmSchemaV3.IArray, - y: ILlmSchemaV3.IArray, - ): boolean => covers(x.items, y.items); - - const coverObject = ( - x: ILlmSchemaV3.IObject, - y: ILlmSchemaV3.IObject, - ): boolean => { - if (!x.additionalProperties && !!y.additionalProperties) return false; - else if ( - (!!x.additionalProperties && - !!y.additionalProperties && - typeof x.additionalProperties === "object" && - y.additionalProperties === true) || - (typeof x.additionalProperties === "object" && - typeof y.additionalProperties === "object" && - !covers(x.additionalProperties, y.additionalProperties)) - ) - return false; - return Object.entries(y.properties ?? {}).every(([key, b]) => { - const a: ILlmSchemaV3 | undefined = x.properties?.[key]; - if (a === undefined) return false; - else if ( - (x.required?.includes(key) ?? false) === true && - (y.required?.includes(key) ?? false) === false - ) - return false; - return covers(a, b); - }); - }; - - const flatSchema = (schema: ILlmSchemaV3): ILlmSchemaV3[] => - isOneOf(schema) ? schema.oneOf.flatMap(flatSchema) : [schema]; - - /* ----------------------------------------------------------- - TYPE CHECKERS - ----------------------------------------------------------- */ - /** - * Test whether the schema is an union type. - * - * @param schema Target schema - * @returns Whether union type or not - */ - export const isOneOf = ( - schema: ILlmSchemaV3, - ): schema is ILlmSchemaV3.IOneOf => - (schema as ILlmSchemaV3.IOneOf).oneOf !== undefined; - - /** - * Test whether the schema is an object type. - * - * @param schema Target schema - * @returns Whether object type or not - */ - export const isObject = ( - schema: ILlmSchemaV3, - ): schema is ILlmSchemaV3.IObject => - (schema as ILlmSchemaV3.IObject).type === "object"; - - /** - * Test whether the schema is an array type. - * - * @param schema Target schema - * @returns Whether array type or not - */ - export const isArray = ( - schema: ILlmSchemaV3, - ): schema is ILlmSchemaV3.IArray => - (schema as ILlmSchemaV3.IArray).type === "array"; - - /** - * Test whether the schema is a boolean type. - * - * @param schema Target schema - * @returns Whether boolean type or not - */ - export const isBoolean = ( - schema: ILlmSchemaV3, - ): schema is ILlmSchemaV3.IBoolean => - (schema as ILlmSchemaV3.IBoolean).type === "boolean"; - - /** - * Test whether the schema is an integer type. - * - * @param schema Target schema - * @returns Whether integer type or not - */ - export const isInteger = ( - schema: ILlmSchemaV3, - ): schema is ILlmSchemaV3.IInteger => - (schema as ILlmSchemaV3.IInteger).type === "integer"; - - /** - * Test whether the schema is a number type. - * - * @param schema Target schema - * @returns Whether number type or not - */ - export const isNumber = ( - schema: ILlmSchemaV3, - ): schema is ILlmSchemaV3.INumber => - (schema as ILlmSchemaV3.INumber).type === "number"; - - /** - * Test whether the schema is a string type. - * - * @param schema Target schema - * @returns Whether string type or not - */ - export const isString = ( - schema: ILlmSchemaV3, - ): schema is ILlmSchemaV3.IString => - (schema as ILlmSchemaV3.IString).type === "string"; - - /** - * Test whether the schema is a null type. - * - * @param schema Target schema - * @returns Whether null type or not - */ - export const isNullOnly = ( - schema: ILlmSchemaV3, - ): schema is ILlmSchemaV3.INullOnly => - (schema as ILlmSchemaV3.INullOnly).type === "null"; - - /** - * Test whether the schema is a nullable type. - * - * @param schema Target schema - * @returns Whether nullable type or not - */ - export const isNullable = (schema: ILlmSchemaV3): boolean => - !isUnknown(schema) && - (isNullOnly(schema) || - (isOneOf(schema) - ? schema.oneOf.some(isNullable) - : schema.nullable === true)); - - /** - * Test whether the schema is an unknown type. - * - * @param schema Target schema - * @returns Whether unknown type or not - */ - export const isUnknown = ( - schema: ILlmSchemaV3, - ): schema is ILlmSchemaV3.IUnknown => - !isOneOf(schema) && (schema as ILlmSchemaV3.IUnknown).type === undefined; -} diff --git a/src/utils/LlmTypeCheckerV3_1.ts b/src/utils/LlmTypeCheckerV3_1.ts deleted file mode 100644 index 701fc3aa..00000000 --- a/src/utils/LlmTypeCheckerV3_1.ts +++ /dev/null @@ -1,206 +0,0 @@ -import { ILlmSchemaV3_1 } from "../structures/ILlmSchemaV3_1"; -import { OpenApiTypeCheckerBase } from "./internal/OpenApiTypeCheckerBase"; - -/** - * Type checker for LLM type schema v3.1. - * - * `LlmTypeCheckerV3_1` is a type checker of {@link ILlmSchemaV3_1}. - * - * @author Jeongho Nam - https://github.com/samchon - */ -export namespace LlmTypeCheckerV3_1 { - /* ----------------------------------------------------------- - TYPE CHECKERS - ----------------------------------------------------------- */ - /** - * Test whether the schema is a nul type. - * - * @param schema Target schema - * @returns Whether null type or not - */ - export const isNull = ( - schema: ILlmSchemaV3_1, - ): schema is ILlmSchemaV3_1.INull => OpenApiTypeCheckerBase.isNull(schema); - - /** - * Test whether the schema is an unknown type. - * - * @param schema Target schema - * @returns Whether unknown type or not - */ - export const isUnknown = ( - schema: ILlmSchemaV3_1, - ): schema is ILlmSchemaV3_1.IUnknown => - OpenApiTypeCheckerBase.isUnknown(schema); - - /** - * Test whether the schema is a constant type. - * - * @param schema Target schema - * @returns Whether constant type or not - */ - export const isConstant = ( - schema: ILlmSchemaV3_1, - ): schema is ILlmSchemaV3_1.IConstant => - OpenApiTypeCheckerBase.isConstant(schema); - - /** - * Test whether the schema is a boolean type. - * - * @param schema Target schema - * @returns Whether boolean type or not - */ - export const isBoolean = ( - schema: ILlmSchemaV3_1, - ): schema is ILlmSchemaV3_1.IBoolean => - OpenApiTypeCheckerBase.isBoolean(schema); - - /** - * Test whether the schema is an integer type. - * - * @param schema Target schema - * @returns Whether integer type or not - */ - export const isInteger = ( - schema: ILlmSchemaV3_1, - ): schema is ILlmSchemaV3_1.IInteger => - OpenApiTypeCheckerBase.isInteger(schema); - - /** - * Test whether the schema is a number type. - * - * @param schema Target schema - * @returns Whether number type or not - */ - export const isNumber = ( - schema: ILlmSchemaV3_1, - ): schema is ILlmSchemaV3_1.INumber => - OpenApiTypeCheckerBase.isNumber(schema); - - /** - * Test whether the schema is a string type. - * - * @param schema Target schema - * @returns Whether string type or not - */ - export const isString = ( - schema: ILlmSchemaV3_1, - ): schema is ILlmSchemaV3_1.IString => - OpenApiTypeCheckerBase.isString(schema); - - /** - * Test whether the schema is an array type. - * - * @param schema Target schema - * @returns Whether array type or not - */ - export const isArray = ( - schema: ILlmSchemaV3_1, - ): schema is ILlmSchemaV3_1.IArray => OpenApiTypeCheckerBase.isArray(schema); - - /** - * Test whether the schema is an object type. - * - * @param schema Target schema - * @returns Whether object type or not - */ - export const isObject = ( - schema: ILlmSchemaV3_1, - ): schema is ILlmSchemaV3_1.IObject => - OpenApiTypeCheckerBase.isObject(schema); - - /** - * Test whether the schema is a reference type. - * - * @param schema Target schema - * @returns Whether reference type or not - */ - export const isReference = ( - schema: ILlmSchemaV3_1, - ): schema is ILlmSchemaV3_1.IReference => - OpenApiTypeCheckerBase.isReference(schema); - - /** - * Test whether the schema is an union type. - * - * @param schema Target schema - * @returns Whether union type or not - */ - export const isOneOf = ( - schema: ILlmSchemaV3_1, - ): schema is ILlmSchemaV3_1.IOneOf => OpenApiTypeCheckerBase.isOneOf(schema); - - /** - * Test whether the schema is recursive reference type. - * - * Test whether the target schema is a reference type, and test one thing more - * that the reference is self-recursive or not. - * - * @param props Properties for recursive reference test - * @returns Whether the schema is recursive reference type or not - */ - export const isRecursiveReference = (props: { - $defs?: Record; - schema: ILlmSchemaV3_1; - }): boolean => - OpenApiTypeCheckerBase.isRecursiveReference({ - prefix: "#/$defs/", - components: { - schemas: props.$defs, - }, - schema: props.schema, - }); - - /* ----------------------------------------------------------- - OPERATORS - ----------------------------------------------------------- */ - /** - * Test whether the `x` schema covers the `y` schema. - * - * @param props Properties for testing - * @returns Whether the `x` schema covers the `y` schema - */ - export const covers = (props: { - $defs?: Record; - x: ILlmSchemaV3_1; - y: ILlmSchemaV3_1; - }): boolean => - OpenApiTypeCheckerBase.covers({ - prefix: "#/$defs/", - components: { - schemas: props.$defs, - }, - x: props.x, - y: props.y, - }); - - /** - * Visit every nested schemas. - * - * Visit every nested schemas of the target, and apply the `props.closure` - * function. - * - * Here is the list of occurring nested visitings: - * - * - {@link ILlmSchemaV3_1.IOneOf.oneOf} - * - {@link ILlmSchemaV3_1.IReference} - * - {@link ILlmSchemaV3_1.IObject.properties} - * - {@link ILlmSchemaV3_1.IObject.additionalProperties} - * - {@link ILlmSchemaV3_1.IArray.items} - * - * @param props Properties for visiting - */ - export const visit = (props: { - closure: (schema: ILlmSchemaV3_1, accessor: string) => void; - $defs?: Record; - schema: ILlmSchemaV3_1; - }): void => - OpenApiTypeCheckerBase.visit({ - prefix: "#/$defs/", - components: { - schemas: props.$defs, - }, - closure: props.closure as any, - schema: props.schema, - }); -} From 7d02f22b3f10c321118fc7730c0a804a16ea64f4 Mon Sep 17 00:00:00 2001 From: Jeongho Nam Date: Fri, 19 Dec 2025 14:02:46 +0900 Subject: [PATCH 2/2] fix --- src/index.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/index.ts b/src/index.ts index 8f009b98..367cce13 100644 --- a/src/index.ts +++ b/src/index.ts @@ -29,8 +29,6 @@ export * from "./HttpMigration"; //---- // LLM //---- -// STRUCTURES - // CONTROLLERS export * from "./structures/IHttpLlmController"; export * from "./structures/IHttpLlmApplication";