@samchon/openapi
Version:
Universal OpenAPI to LLM function calling schemas. Transform any Swagger/OpenAPI document into type-safe schemas for OpenAI, Claude, Qwen, and more.
1 lines • 16.9 kB
Source Map (JSON)
{"version":3,"file":"HttpLlmApplicationComposer.mjs","sources":["../../src/composers/HttpLlmApplicationComposer.ts"],"sourcesContent":["import { OpenApi } from \"../OpenApi\";\nimport { IHttpLlmApplication } from \"../structures/IHttpLlmApplication\";\nimport { IHttpLlmFunction } from \"../structures/IHttpLlmFunction\";\nimport { IHttpMigrateApplication } from \"../structures/IHttpMigrateApplication\";\nimport { IHttpMigrateRoute } from \"../structures/IHttpMigrateRoute\";\nimport { ILlmSchema } from \"../structures/ILlmSchema\";\nimport { IOpenApiSchemaError } from \"../structures/IOpenApiSchemaError\";\nimport { IResult } from \"../structures/IResult\";\nimport { OpenApiValidator } from \"../utils/OpenApiValidator\";\nimport { LlmSchemaComposer } from \"./LlmSchemaComposer\";\n\nexport namespace HttpLlmComposer {\n export const application = (props: {\n migrate: IHttpMigrateApplication;\n config?: Partial<IHttpLlmApplication.IConfig>;\n }): IHttpLlmApplication => {\n // COMPOSE FUNCTIONS\n const config: IHttpLlmApplication.IConfig = {\n separate: props.config?.separate ?? null,\n maxLength: props.config?.maxLength ?? 64,\n equals: props.config?.equals ?? false,\n reference: props.config?.reference ?? true,\n strict: props.config?.strict ?? false,\n };\n const errors: IHttpLlmApplication.IError[] = props.migrate.errors\n .filter((e) => e.operation()[\"x-samchon-human\"] !== true)\n .map((e) => ({\n method: e.method,\n path: e.path,\n messages: e.messages,\n operation: () => e.operation(),\n route: () => undefined,\n }));\n const functions: IHttpLlmFunction[] = props.migrate.routes\n .filter((e) => e.operation()[\"x-samchon-human\"] !== true)\n .map((route, i) => {\n if (route.method === \"head\") {\n errors.push({\n method: route.method,\n path: route.path,\n messages: [\"HEAD method is not supported in the LLM application.\"],\n operation: () => route.operation(),\n route: () => route as any as IHttpMigrateRoute,\n });\n return null;\n } else if (\n route.body?.type === \"multipart/form-data\" ||\n route.success?.type === \"multipart/form-data\"\n ) {\n errors.push({\n method: route.method,\n path: route.path,\n messages: [\n `The \"multipart/form-data\" content type is not supported in the LLM application.`,\n ],\n operation: () => route.operation(),\n route: () => route as any as IHttpMigrateRoute,\n });\n return null;\n }\n const localErrors: string[] = [];\n const func: IHttpLlmFunction | null = composeFunction({\n components: props.migrate.document().components,\n config,\n route,\n errors: localErrors,\n index: i,\n });\n if (func === null)\n errors.push({\n method: route.method,\n path: route.path,\n messages: localErrors,\n operation: () => route.operation(),\n route: () => route as any as IHttpMigrateRoute,\n });\n return func;\n })\n .filter((v): v is IHttpLlmFunction => v !== null);\n\n const app: IHttpLlmApplication = {\n config,\n functions,\n errors,\n };\n shorten(app, props.config?.maxLength ?? 64);\n return app;\n };\n\n const composeFunction = (props: {\n components: OpenApi.IComponents;\n route: IHttpMigrateRoute;\n config: IHttpLlmApplication.IConfig;\n errors: string[];\n index: number;\n }): IHttpLlmFunction | null => {\n // METADATA\n const endpoint: string = `$input.paths[${JSON.stringify(props.route.path)}][${JSON.stringify(props.route.method)}]`;\n const operation: OpenApi.IOperation = props.route.operation();\n const description: [string | undefined, number] = (() => {\n if (!operation.summary?.length || !operation.description?.length)\n return [\n operation.summary || operation.description,\n operation.summary?.length ?? operation.description?.length ?? 0,\n ];\n const summary: string = operation.summary.endsWith(\".\")\n ? operation.summary.slice(0, -1)\n : operation.summary;\n const final: string = operation.description.startsWith(summary)\n ? operation.description\n : summary + \".\\n\\n\" + operation.description;\n return [final, final.length];\n })();\n if (description[1] > 1_024) {\n props.errors.push(\n `The description of the function is too long (must be equal or less than 1,024 characters, but ${description[1].toLocaleString()} length).`,\n );\n }\n\n // FUNCTION NAME\n const name: string = emend(props.route.accessor.join(\"_\"));\n const isNameVariable: boolean = /^[a-zA-Z0-9_-]+$/.test(name);\n const isNameStartsWithNumber: boolean = /^[0-9]/.test(name[0] ?? \"\");\n if (isNameVariable === false)\n props.errors.push(\n `Elements of path (separated by '/') must be composed with alphabets, numbers, underscores, and hyphens`,\n );\n\n //----\n // CONSTRUCT SCHEMAS\n //----\n // PARAMETERS\n const parameters: OpenApi.IJsonSchema.IObject = {\n type: \"object\",\n properties: Object.fromEntries([\n ...props.route.parameters.map(\n (s) =>\n [\n s.key,\n {\n ...s.schema,\n description: s.parameter().description ?? s.schema.description,\n },\n ] as const,\n ),\n ...(props.route.query\n ? [\n [\n props.route.query.key,\n {\n ...props.route.query.schema,\n title:\n props.route.query.title() ?? props.route.query.schema.title,\n description:\n props.route.query.description() ??\n props.route.query.schema.description,\n },\n ] as const,\n ]\n : []),\n ...(props.route.body\n ? [\n [\n props.route.body.key,\n {\n ...props.route.body.schema,\n description:\n props.route.body.description() ??\n props.route.body.schema.description,\n },\n ] as const,\n ]\n : []),\n ]),\n };\n parameters.required = Object.keys(parameters.properties ?? {});\n\n const llmParameters: IResult<ILlmSchema.IParameters, IOpenApiSchemaError> =\n LlmSchemaComposer.parameters({\n config: props.config,\n components: props.components,\n schema: parameters,\n accessor: `${endpoint}.parameters`,\n });\n\n // RETURN VALUE\n const output: IResult<ILlmSchema, IOpenApiSchemaError> | undefined = props\n .route.success\n ? LlmSchemaComposer.schema({\n config: props.config,\n components: props.components,\n schema: props.route.success.schema,\n accessor: `${endpoint}.responses[${JSON.stringify(props.route.success.status)}][${JSON.stringify(props.route.success.type)}].schema`,\n $defs: llmParameters.success ? llmParameters.value.$defs : {},\n })\n : undefined;\n\n //----\n // CONVERSION\n //----\n if (\n output?.success === false ||\n llmParameters.success === false ||\n isNameVariable === false ||\n isNameStartsWithNumber === true ||\n description[1] > 1_024\n ) {\n if (output?.success === false)\n props.errors.push(\n ...output.error.reasons.map((r) => `${r.accessor}: ${r.message}`),\n );\n if (llmParameters.success === false)\n props.errors.push(\n ...llmParameters.error.reasons.map((r) => {\n const accessor: string = r.accessor.replace(\n `parameters.properties[\"body\"]`,\n `requestBody.content[${JSON.stringify(props.route.body?.type ?? \"application/json\")}].schema`,\n );\n return `${accessor}: ${r.message}`;\n }),\n );\n return null;\n }\n return {\n method: props.route.method as \"get\",\n path: props.route.path,\n name,\n parameters: llmParameters.value,\n separated: props.config.separate\n ? LlmSchemaComposer.separate({\n predicate: props.config.separate,\n parameters: llmParameters.value,\n equals: props.config.equals ?? false,\n })\n : undefined,\n output: output?.value,\n description: description[0],\n deprecated: operation.deprecated,\n tags: operation.tags,\n validate: OpenApiValidator.create({\n components: props.components,\n schema: parameters,\n required: true,\n equals: props.config.equals ?? false,\n }),\n route: () => props.route as any,\n operation: () => props.route.operation(),\n };\n };\n\n export const shorten = (\n app: IHttpLlmApplication,\n limit: number = 64,\n ): void => {\n const dictionary: Set<string> = new Set();\n const longFunctions: IHttpLlmFunction[] = [];\n for (const func of app.functions) {\n dictionary.add(func.name);\n if (func.name.length > limit) {\n longFunctions.push(func);\n }\n }\n if (longFunctions.length === 0) return;\n\n let index: number = 0;\n for (const func of longFunctions) {\n let success: boolean = false;\n let rename = (str: string) => {\n dictionary.delete(func.name);\n dictionary.add(str);\n func.name = str;\n success = true;\n };\n for (let i: number = 1; i < func.route().accessor.length; ++i) {\n const shortName: string = func.route().accessor.slice(i).join(\"_\");\n if (shortName.length > limit - 8) continue;\n else if (dictionary.has(shortName) === false) rename(shortName);\n else {\n const newName: string = `_${index}_${shortName}`;\n if (dictionary.has(newName) === true) continue;\n rename(newName);\n ++index;\n }\n break;\n }\n if (success === false) rename(randomFormatUuid());\n }\n };\n}\n\nconst randomFormatUuid = (): string =>\n \"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx\".replace(/[xy]/g, (c) => {\n const r = (Math.random() * 16) | 0;\n const v = c === \"x\" ? r : (r & 0x3) | 0x8;\n return v.toString(16);\n });\n\nconst emend = (str: string): string => {\n for (const ch of FORBIDDEN) str = str.split(ch).join(\"_\");\n return str;\n};\n\nconst FORBIDDEN = [\"$\", \"%\", \".\"];\n"],"names":["HttpLlmComposer","application","props","config","separate","maxLength","equals","reference","strict","errors","migrate","filter","e","operation","map","method","path","messages","route","undefined","functions","routes","i","push","body","type","success","localErrors","func","composeFunction","components","document","v","app","shorten","endpoint","JSON","stringify","description","summary","length","endsWith","slice","final","startsWith","toLocaleString","name","emend","accessor","join","isNameVariable","test","isNameStartsWithNumber","parameters","properties","Object","fromEntries","s","key","schema","parameter","query","title","required","keys","llmParameters","LlmSchemaComposer","output","status","$defs","value","error","reasons","r","message","replace","separated","predicate","deprecated","tags","validate","OpenApiValidator","create","limit","dictionary","Set","longFunctions","add","index","rename","str","delete","shortName","has","newName","randomFormatUuid","c","Math","random","toString","ch","FORBIDDEN","split"],"mappings":";;;;AAWM,IAAWA;;CAAjB,SAAiBA;IACFA,gBAAAC,cAAeC;QAK1B,MAAMC,SAAsC;YAC1CC,UAAUF,MAAMC,QAAQC,YAAY;YACpCC,WAAWH,MAAMC,QAAQE,aAAa;YACtCC,QAAQJ,MAAMC,QAAQG,UAAU;YAChCC,WAAWL,MAAMC,QAAQI,aAAa;YACtCC,QAAQN,MAAMC,QAAQK,UAAU;;QAElC,MAAMC,SAAuCP,MAAMQ,QAAQD,OACxDE,OAAQC,KAAMA,EAAEC,YAAY,uBAAuB,MACnDC,IAAKF,MAAC;YACLG,QAAQH,EAAEG;YACVC,MAAMJ,EAAEI;YACRC,UAAUL,EAAEK;YACZJ,WAAW,MAAMD,EAAEC;YACnBK,OAAO,MAAMC;;QAEjB,MAAMC,YAAgClB,MAAMQ,QAAQW,OACjDV,OAAQC,KAAMA,EAAEC,YAAY,uBAAuB,MACnDC,IAAI,CAACI,OAAOI;YACX,IAAIJ,MAAMH,WAAW,QAAQ;gBAC3BN,OAAOc,KAAK;oBACVR,QAAQG,MAAMH;oBACdC,MAAME,MAAMF;oBACZC,UAAU,EAAC;oBACXJ,WAAW,MAAMK,MAAML;oBACvBK,OAAO,MAAMA;;gBAEf,OAAO;AACT,mBAAO,IACLA,MAAMM,MAAMC,SAAS,yBACrBP,MAAMQ,SAASD,SAAS,uBACxB;gBACAhB,OAAOc,KAAK;oBACVR,QAAQG,MAAMH;oBACdC,MAAME,MAAMF;oBACZC,UAAU,EACR;oBAEFJ,WAAW,MAAMK,MAAML;oBACvBK,OAAO,MAAMA;;gBAEf,OAAO;AACT;YACA,MAAMS,cAAwB;YAC9B,MAAMC,OAAgCC,gBAAgB;gBACpDC,YAAY5B,MAAMQ,QAAQqB,WAAWD;gBACrC3B;gBACAe;gBACAT,QAAQkB;;YAGV,IAAIC,SAAS,MACXnB,OAAOc,KAAK;gBACVR,QAAQG,MAAMH;gBACdC,MAAME,MAAMF;gBACZC,UAAUU;gBACVd,WAAW,MAAMK,MAAML;gBACvBK,OAAO,MAAMA;;YAEjB,OAAOU;WAERjB,OAAQqB,KAA6BA,MAAM;QAE9C,MAAMC,MAA2B;YAC/B9B;YACAiB;YACAX;;QAEFT,gBAAAkC,QAAQD,KAAK/B,MAAMC,QAAQE,aAAa;QACxC,OAAO4B;;IAGT,MAAMJ,kBAAmB3B;QAQvB,MAAMiC,WAAmB,gBAAgBC,KAAKC,UAAUnC,MAAMgB,MAAMF,UAAUoB,KAAKC,UAAUnC,MAAMgB,MAAMH;QACzG,MAAMF,YAAgCX,MAAMgB,MAAML;QAClD,MAAMyB,cAA4C;YAChD,KAAKzB,UAAU0B,SAASC,WAAW3B,UAAUyB,aAAaE,QACxD,OAAO,EACL3B,UAAU0B,WAAW1B,UAAUyB,aAC/BzB,UAAU0B,SAASC,UAAU3B,UAAUyB,aAAaE,UAAU;YAElE,MAAMD,UAAkB1B,UAAU0B,QAAQE,SAAS,OAC/C5B,UAAU0B,QAAQG,MAAM,IAAG,KAC3B7B,UAAU0B;YACd,MAAMI,QAAgB9B,UAAUyB,YAAYM,WAAWL,WACnD1B,UAAUyB,cACVC,UAAU,UAAU1B,UAAUyB;YAClC,OAAO,EAACK,OAAOA,MAAMH;AACtB,UAbiD;QAclD,IAAIF,YAAY,KAAK,MAAO;YAC1BpC,MAAMO,OAAOc,KACX,iGAAiGe,YAAY,GAAGO;AAEpH;QAGA,MAAMC,OAAeC,MAAM7C,MAAMgB,MAAM8B,SAASC,KAAK;QACrD,MAAMC,iBAA0B,mBAAmBC,KAAKL;QACxD,MAAMM,yBAAkC,SAASD,KAAKL,KAAK,MAAM;QACjE,IAAII,mBAAmB,OACrBhD,MAAMO,OAAOc,KACX;QAOJ,MAAM8B,aAA0C;YAC9C5B,MAAM;YACN6B,YAAYC,OAAOC,YAAY,KAC1BtD,MAAMgB,MAAMmC,WAAWvC,IACvB2C,KACC,EACEA,EAAEC,KACF;mBACKD,EAAEE;gBACLrB,aAAamB,EAAEG,YAAYtB,eAAemB,EAAEE,OAAOrB;qBAIvDpC,MAAMgB,MAAM2C,QACZ,EACE,EACE3D,MAAMgB,MAAM2C,MAAMH,KAClB;mBACKxD,MAAMgB,MAAM2C,MAAMF;gBACrBG,OACE5D,MAAMgB,MAAM2C,MAAMC,WAAW5D,MAAMgB,MAAM2C,MAAMF,OAAOG;gBACxDxB,aACEpC,MAAMgB,MAAM2C,MAAMvB,iBAClBpC,MAAMgB,MAAM2C,MAAMF,OAAOrB;oBAIjC,OACApC,MAAMgB,MAAMM,OACZ,EACE,EACEtB,MAAMgB,MAAMM,KAAKkC,KACjB;mBACKxD,MAAMgB,MAAMM,KAAKmC;gBACpBrB,aACEpC,MAAMgB,MAAMM,KAAKc,iBACjBpC,MAAMgB,MAAMM,KAAKmC,OAAOrB;oBAIhC;;QAGRe,WAAWU,WAAWR,OAAOS,KAAKX,WAAWC,cAAc;QAE3D,MAAMW,gBACJC,kBAAkBb,WAAW;YAC3BlD,QAAQD,MAAMC;YACd2B,YAAY5B,MAAM4B;YAClB6B,QAAQN;YACRL,UAAU,GAAGb;;QAIjB,MAAMgC,SAA+DjE,MAClEgB,MAAMQ,UACLwC,kBAAkBP,OAAO;YACvBxD,QAAQD,MAAMC;YACd2B,YAAY5B,MAAM4B;YAClB6B,QAAQzD,MAAMgB,MAAMQ,QAAQiC;YAC5BX,UAAU,GAAGb,sBAAsBC,KAAKC,UAAUnC,MAAMgB,MAAMQ,QAAQ0C,YAAYhC,KAAKC,UAAUnC,MAAMgB,MAAMQ,QAAQD;YACrH4C,OAAOJ,cAAcvC,UAAUuC,cAAcK,MAAMD,QAAQ,CAAA;aAE7DlD;QAKJ,IACEgD,QAAQzC,YAAY,SACpBuC,cAAcvC,YAAY,SAC1BwB,mBAAmB,SACnBE,2BAA2B,QAC3Bd,YAAY,KAAK,MACjB;YACA,IAAI6B,QAAQzC,YAAY,OACtBxB,MAAMO,OAAOc,QACR4C,OAAOI,MAAMC,QAAQ1D,IAAK2D,KAAM,GAAGA,EAAEzB,aAAayB,EAAEC;YAE3D,IAAIT,cAAcvC,YAAY,OAC5BxB,MAAMO,OAAOc,QACR0C,cAAcM,MAAMC,QAAQ1D,IAAK2D;gBAClC,MAAMzB,WAAmByB,EAAEzB,SAAS2B,QAClC,iCACA,uBAAuBvC,KAAKC,UAAUnC,MAAMgB,MAAMM,MAAMC,QAAQ;gBAElE,OAAO,GAAGuB,aAAayB,EAAEC;;YAG/B,OAAO;AACT;QACA,OAAO;YACL3D,QAAQb,MAAMgB,MAAMH;YACpBC,MAAMd,MAAMgB,MAAMF;YAClB8B;YACAO,YAAYY,cAAcK;YAC1BM,WAAW1E,MAAMC,OAAOC,WACpB8D,kBAAkB9D,SAAS;gBACzByE,WAAW3E,MAAMC,OAAOC;gBACxBiD,YAAYY,cAAcK;gBAC1BhE,QAAQJ,MAAMC,OAAOG,UAAU;iBAEjCa;YACJgD,QAAQA,QAAQG;YAChBhC,aAAaA,YAAY;YACzBwC,YAAYjE,UAAUiE;YACtBC,MAAMlE,UAAUkE;YAChBC,UAAUC,iBAAiBC,OAAO;gBAChCpD,YAAY5B,MAAM4B;gBAClB6B,QAAQN;gBACRU,UAAU;gBACVzD,QAAQJ,MAAMC,OAAOG,UAAU;;YAEjCY,OAAO,MAAMhB,MAAMgB;YACnBL,WAAW,MAAMX,MAAMgB,MAAML;;;IAIpBb,gBAAAkC,UAAU,CACrBD,KACAkD,QAAgB;QAEhB,MAAMC,aAA0B,IAAIC;QACpC,MAAMC,gBAAoC;QAC1C,KAAK,MAAM1D,QAAQK,IAAIb,WAAW;YAChCgE,WAAWG,IAAI3D,KAAKkB;YACpB,IAAIlB,KAAKkB,KAAKN,SAAS2C,OAAO;gBAC5BG,cAAc/D,KAAKK;AACrB;AACF;QACA,IAAI0D,cAAc9C,WAAW,GAAG;QAEhC,IAAIgD,QAAgB;QACpB,KAAK,MAAM5D,QAAQ0D,eAAe;YAChC,IAAI5D,UAAmB;YACvB,IAAI+D,SAAUC;gBACZN,WAAWO,OAAO/D,KAAKkB;gBACvBsC,WAAWG,IAAIG;gBACf9D,KAAKkB,OAAO4C;gBACZhE,UAAU;;YAEZ,KAAK,IAAIJ,IAAY,GAAGA,IAAIM,KAAKV,QAAQ8B,SAASR,UAAUlB,GAAG;gBAC7D,MAAMsE,YAAoBhE,KAAKV,QAAQ8B,SAASN,MAAMpB,GAAG2B,KAAK;gBAC9D,IAAI2C,UAAUpD,SAAS2C,QAAQ,GAAG,eAC7B,IAAIC,WAAWS,IAAID,eAAe,OAAOH,OAAOG,iBAChD;oBACH,MAAME,UAAkB,IAAIN,SAASI;oBACrC,IAAIR,WAAWS,IAAIC,aAAa,MAAM;oBACtCL,OAAOK;sBACLN;AACJ;gBACA;AACF;YACA,IAAI9D,YAAY,OAAO+D,OAAOM;AAChC;;AAEH,EArRD,CAAiB/F,oBAAAA,kBAAe,CAAA;;AAuRhC,MAAM+F,mBAAmB,MACvB,uCAAuCpB,QAAQ,SAAUqB;IACvD,MAAMvB,IAAKwB,KAAKC,WAAW,KAAM;IACjC,MAAMlE,IAAIgE,MAAM,MAAMvB,IAAKA,IAAI,IAAO;IACtC,OAAOzC,EAAEmE,SAAS;;;AAGtB,MAAMpD,QAAS2C;IACb,KAAK,MAAMU,MAAMC,WAAWX,MAAMA,IAAIY,MAAMF,IAAInD,KAAK;IACrD,OAAOyC;;;AAGT,MAAMW,YAAY,EAAC,KAAK,KAAK;;"}