From 11b508e7945493245eb0d3150b4390b1d8b0a180 Mon Sep 17 00:00:00 2001 From: Jeongho Nam Date: Wed, 11 Dec 2024 18:02:50 +0900 Subject: [PATCH] Fix `additionalProperties`' separation problem --- package.json | 2 +- src/composers/llm/LlmSchemaV3Composer.ts | 15 ++-- src/composers/llm/LlmSchemaV3_1Composer.ts | 9 +- ...rs_separate_object_additionalProperties.ts | 82 ++++++++++++------- 4 files changed, 69 insertions(+), 39 deletions(-) diff --git a/package.json b/package.json index fc8f8d2..145bf15 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@samchon/openapi", - "version": "2.0.2", + "version": "2.0.3", "description": "OpenAPI definitions and converters for 'typia' and 'nestia'.", "main": "./lib/index.js", "module": "./lib/index.mjs", diff --git a/src/composers/llm/LlmSchemaV3Composer.ts b/src/composers/llm/LlmSchemaV3Composer.ts index a107c3e..abdba3a 100644 --- a/src/composers/llm/LlmSchemaV3Composer.ts +++ b/src/composers/llm/LlmSchemaV3Composer.ts @@ -219,18 +219,15 @@ export namespace LlmSchemaV3Composer { predicate: (schema: ILlmSchemaV3) => boolean; schema: ILlmSchemaV3.IObject; }): [ILlmSchemaV3.IObject | null, ILlmSchemaV3.IObject | null] => { - if ( - !!props.schema.additionalProperties || - Object.keys(props.schema.properties ?? {}).length === 0 - ) - 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({ @@ -252,8 +249,12 @@ export namespace LlmSchemaV3Composer { human.additionalProperties = dy ?? false; } return [ - Object.keys(llm.properties).length === 0 ? null : shrinkRequired(llm), - Object.keys(human.properties).length === 0 ? null : shrinkRequired(human), + !!Object.keys(llm.properties).length || !!llm.additionalProperties + ? shrinkRequired(llm) + : null, + !!Object.keys(human.properties).length || !!human.additionalProperties + ? shrinkRequired(human) + : null, ]; }; diff --git a/src/composers/llm/LlmSchemaV3_1Composer.ts b/src/composers/llm/LlmSchemaV3_1Composer.ts index d3d7fe3..9f62105 100644 --- a/src/composers/llm/LlmSchemaV3_1Composer.ts +++ b/src/composers/llm/LlmSchemaV3_1Composer.ts @@ -427,6 +427,7 @@ export namespace LlmSchemaV3_1Composer { const llm = { ...props.schema, properties: {} as Record, + additionalProperties: props.schema.additionalProperties, } satisfies ILlmSchemaV3_1.IObject; const human = { ...props.schema, @@ -454,8 +455,12 @@ export namespace LlmSchemaV3_1Composer { human.additionalProperties = dy ?? false; } return [ - Object.keys(llm.properties).length === 0 ? null : shrinkRequired(llm), - Object.keys(human.properties).length === 0 ? null : shrinkRequired(human), + !!Object.keys(llm.properties).length || !!llm.additionalProperties + ? shrinkRequired(llm) + : null, + !!Object.keys(human.properties).length || human.additionalProperties + ? shrinkRequired(human) + : null, ]; }; diff --git a/test/features/llm/validate_llm_parameters_separate_object_additionalProperties.ts b/test/features/llm/validate_llm_parameters_separate_object_additionalProperties.ts index 4f0136b..481e2a1 100644 --- a/test/features/llm/validate_llm_parameters_separate_object_additionalProperties.ts +++ b/test/features/llm/validate_llm_parameters_separate_object_additionalProperties.ts @@ -59,44 +59,68 @@ const validate_llm_parameters_separate_object_additionalProperties = < : s.description?.includes("@contentMediaType") === true), parameters: schema as any, }); - const member: ILlmSchema.IParameters = schema( + const params: ILlmSchema.IParameters = schema( model, constraint, - )(typia.json.schemas<[IWrapper]>()); - const upload: ILlmSchema.IParameters = schema( - model, - constraint, - )(typia.json.schemas<[IWrapper]>()); - const combined: ILlmSchema.IParameters = schema( - model, - constraint, - )(typia.json.schemas<[IWrapper]>()); - - TestValidator.equals("member")(separator(member))({ - llm: member, - human: null, - }); - TestValidator.equals("upload")(separator(upload))({ - llm: null, - human: upload, - }); - TestValidator.equals("combined")(separator(combined))({ - llm: member, - human: upload, + )(typia.json.schemas<[IParameters]>()); + TestValidator.equals(model)(separator(params))({ + llm: schema( + model, + constraint, + )( + typia.json.schemas< + [ + { + input: { + email: string; + hobbies: Record< + string, + { + id: string; + name: string; + } + >; + }; + }, + ] + >(), + ), + human: schema( + model, + constraint, + )( + typia.json.schemas< + [ + { + input: { + hobbies: Record< + string, + { + thumbnail: string & + tags.Format<"uri"> & + tags.ContentMediaType<"image/*">; + } + >; + }; + }, + ] + >(), + ), }); }; -interface IWrapper { - data: T; +interface IParameters { + input: IMember; } interface IMember { - id: number; - name: string; + email: string; + hobbies: Record; } -interface IFileUpload { - file: string & tags.Format<"uri"> & tags.ContentMediaType<"image/png">; +interface IHobby { + id: string; + name: string; + thumbnail: string & tags.Format<"uri"> & tags.ContentMediaType<"image/*">; } -interface ICombined extends IMember, IFileUpload {} const schema = (model: Model, constraint: boolean) =>