From b71d16e96e60f36befeb3a8c350de26b58df00ca Mon Sep 17 00:00:00 2001 From: Jeongho Nam Date: Thu, 9 Jan 2025 15:10:43 +0900 Subject: [PATCH] `IHttpLlmFunction.name`'s maximum length limit. --- package.json | 2 +- src/HttpLlm.ts | 3 +- src/composers/HttpLlmApplicationComposer.ts | 52 ++++++++++++++++++- src/structures/IHttpLlmApplication.ts | 18 ++++++- src/structures/IHttpLlmFunction.ts | 2 + src/structures/ILlmFunction.ts | 2 + ...ttp_llm_application_funtion_name_length.ts | 25 +++++++++ 7 files changed, 100 insertions(+), 4 deletions(-) create mode 100644 test/features/llm/test_http_llm_application_funtion_name_length.ts diff --git a/package.json b/package.json index a34e769..04f6f02 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@samchon/openapi", - "version": "2.3.2", + "version": "2.3.3", "description": "OpenAPI definitions and converters for 'typia' and 'nestia'.", "main": "./lib/index.js", "module": "./lib/index.mjs", diff --git a/src/HttpLlm.ts b/src/HttpLlm.ts index caa25b3..5ceefcb 100644 --- a/src/HttpLlm.ts +++ b/src/HttpLlm.ts @@ -98,7 +98,8 @@ export namespace HttpLlm { options: { ...LlmSchemaComposer.defaultConfig(props.model), separate: props.options?.separate ?? null, - }, + maxLength: props.options?.maxLength ?? null, + } as any as IHttpLlmApplication.IOptions, }); }; diff --git a/src/composers/HttpLlmApplicationComposer.ts b/src/composers/HttpLlmApplicationComposer.ts index 7de045d..2d7a852 100644 --- a/src/composers/HttpLlmApplicationComposer.ts +++ b/src/composers/HttpLlmApplicationComposer.ts @@ -71,12 +71,15 @@ export namespace HttpLlmComposer { return func; }) .filter((v): v is IHttpLlmFunction => v !== null); - return { + + const app: IHttpLlmApplication = { model: props.model, options: props.options, functions, errors, }; + shorten(app, props.options?.maxLength ?? 64); + return app; }; const composeFunction = (props: { @@ -230,10 +233,57 @@ export namespace HttpLlmComposer { operation: () => props.route.operation(), }; }; + + export const shorten = ( + app: IHttpLlmApplication, + limit: number = 64, + ): void => { + const dictionary: Set = new Set(); + const longFunctions: IHttpLlmFunction[] = []; + for (const func of app.functions) { + dictionary.add(func.name); + if (func.name.length > limit) { + longFunctions.push(func); + } + } + if (longFunctions.length === 0) return; + + let index: number = 0; + for (const func of longFunctions) { + let success: boolean = false; + let rename = (str: string) => { + dictionary.delete(func.name); + dictionary.add(str); + func.name = str; + success = true; + }; + for (let i: number = 1; i < func.route().accessor.length; ++i) { + const shortName: string = func.route().accessor.slice(i).join("_"); + if (shortName.length > limit - 8) continue; + else if (dictionary.has(shortName) === false) rename(shortName); + else { + const newName: string = `_${index}_${shortName}`; + if (dictionary.has(newName) === true) continue; + rename(newName); + ++index; + } + break; + } + if (success === false) rename(randomFormatUuid()); + } + }; } +const randomFormatUuid = (): string => + "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => { + const r = (Math.random() * 16) | 0; + const v = c === "x" ? r : (r & 0x3) | 0x8; + return v.toString(16); + }); + const emend = (str: string): string => { for (const ch of FORBIDDEN) str = str.split(ch).join("_"); return str; }; + const FORBIDDEN = ["$", "%", "."]; diff --git a/src/structures/IHttpLlmApplication.ts b/src/structures/IHttpLlmApplication.ts index 7216bb4..c79225e 100644 --- a/src/structures/IHttpLlmApplication.ts +++ b/src/structures/IHttpLlmApplication.ts @@ -95,7 +95,23 @@ export interface IHttpLlmApplication { options: IHttpLlmApplication.IOptions; } export namespace IHttpLlmApplication { - export import IOptions = ILlmApplication.IOptions; + /** + * Options for the HTTP LLM application schema composition. + */ + export type IOptions = + ILlmApplication.IOptions & { + /** + * Maximum length of function name. + * + * When a function name is longer than this value, it will be truncated. + * + * If not possible to truncate due to the duplication, the function name + * would be modified to randomly generated (UUID v4). + * + * @default 64 + */ + maxLength?: number; + }; /** * Error occurred in the composition. diff --git a/src/structures/IHttpLlmFunction.ts b/src/structures/IHttpLlmFunction.ts index cf64f95..452b255 100644 --- a/src/structures/IHttpLlmFunction.ts +++ b/src/structures/IHttpLlmFunction.ts @@ -96,6 +96,8 @@ export interface IHttpLlmFunction { * > - Example 2 * > - endpoint: `GET /shoppings/sellers/sales/:saleId/reviews/:reviewId/comments/:id * > - accessor: `shoppings.sellers.sales.reviews.getBySaleIdAndReviewIdAndCommentId` + * + * @maxLength 64 */ name: string; diff --git a/src/structures/ILlmFunction.ts b/src/structures/ILlmFunction.ts index 2a27734..1d60918 100644 --- a/src/structures/ILlmFunction.ts +++ b/src/structures/ILlmFunction.ts @@ -25,6 +25,8 @@ import { ILlmSchema } from "./ILlmSchema"; export interface ILlmFunction { /** * Representative name of the function. + * + * @maxLength 64 */ name: string; diff --git a/test/features/llm/test_http_llm_application_funtion_name_length.ts b/test/features/llm/test_http_llm_application_funtion_name_length.ts new file mode 100644 index 0000000..37f820f --- /dev/null +++ b/test/features/llm/test_http_llm_application_funtion_name_length.ts @@ -0,0 +1,25 @@ +import { TestValidator } from "@nestia/e2e"; +import { HttpLlm, IHttpLlmApplication, OpenApi } from "@samchon/openapi"; + +export const test_http_llm_application_funtion_name_length = + async (): Promise => { + const document: OpenApi.IDocument = OpenApi.convert( + await fetch( + "https://wrtnio.github.io/connectors/swagger/swagger.json", + ).then((res) => res.json()), + ); + const application: IHttpLlmApplication<"chatgpt"> = HttpLlm.application({ + model: "chatgpt", + document, + }); + + TestValidator.predicate("overflow")(() => + application.functions.some( + (f) => f.route().accessor.join("_").length > 64, + ), + ); + + const names: string[] = application.functions.map((f) => f.name); + TestValidator.predicate("length")(() => names.every((s) => s.length <= 64)); + TestValidator.equals("unique")(true)(new Set(names).size === names.length); + };