@typia/utils 12.0.0-dev.20260303-2 → 12.0.0-dev.20260305
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/http/internal/HttpLlmApplicationComposer.d.ts +20 -0
- package/lib/http/internal/HttpLlmApplicationComposer.js +86 -36
- package/lib/http/internal/HttpLlmApplicationComposer.js.map +1 -1
- package/lib/http/internal/HttpLlmApplicationComposer.mjs +79 -31
- package/lib/http/internal/HttpLlmApplicationComposer.mjs.map +1 -1
- package/lib/http/internal/HttpMigrateRouteComposer.js +4 -12
- package/lib/http/internal/HttpMigrateRouteComposer.js.map +1 -1
- package/lib/http/internal/HttpMigrateRouteComposer.mjs +4 -12
- package/lib/http/internal/HttpMigrateRouteComposer.mjs.map +1 -1
- package/lib/index.mjs +10 -10
- package/lib/validators/internal/OpenApiOneOfValidator.mjs +1 -4
- package/lib/validators/internal/OpenApiOneOfValidator.mjs.map +1 -1
- package/package.json +2 -2
- package/src/http/internal/HttpLlmApplicationComposer.ts +93 -35
- package/src/http/internal/HttpMigrateRouteComposer.ts +4 -12
|
@@ -1,8 +1,28 @@
|
|
|
1
1
|
import { IHttpLlmApplication, IHttpMigrateApplication } from "@typia/interface";
|
|
2
|
+
/**
|
|
3
|
+
* Composes {@link IHttpLlmApplication} from an {@link IHttpMigrateApplication}.
|
|
4
|
+
*
|
|
5
|
+
* Converts OpenAPI-migrated HTTP routes into LLM function calling schemas,
|
|
6
|
+
* filtering out unsupported methods (HEAD) and content types
|
|
7
|
+
* (multipart/form-data), and shortening function names to fit the configured
|
|
8
|
+
* maximum length.
|
|
9
|
+
*/
|
|
2
10
|
export declare namespace HttpLlmApplicationComposer {
|
|
11
|
+
/**
|
|
12
|
+
* Builds an {@link IHttpLlmApplication} from migrated HTTP routes.
|
|
13
|
+
*
|
|
14
|
+
* Iterates all routes, converts each to an {@link IHttpLlmFunction}, and
|
|
15
|
+
* collects conversion errors. Applies function name shortening at the end.
|
|
16
|
+
*/
|
|
3
17
|
const application: (props: {
|
|
4
18
|
migrate: IHttpMigrateApplication;
|
|
5
19
|
config?: Partial<IHttpLlmApplication.IConfig>;
|
|
6
20
|
}) => IHttpLlmApplication;
|
|
21
|
+
/**
|
|
22
|
+
* Shortens function names exceeding the character limit.
|
|
23
|
+
*
|
|
24
|
+
* Tries progressively shorter accessor suffixes first, then falls back to
|
|
25
|
+
* index-prefixed names, and finally UUID as a last resort.
|
|
26
|
+
*/
|
|
7
27
|
const shorten: (app: IHttpLlmApplication, limit?: number) => void;
|
|
8
28
|
}
|
|
@@ -3,11 +3,25 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.HttpLlmApplicationComposer = void 0;
|
|
4
4
|
const converters_1 = require("../../converters");
|
|
5
5
|
const OpenApiValidator_1 = require("../../validators/OpenApiValidator");
|
|
6
|
+
/**
|
|
7
|
+
* Composes {@link IHttpLlmApplication} from an {@link IHttpMigrateApplication}.
|
|
8
|
+
*
|
|
9
|
+
* Converts OpenAPI-migrated HTTP routes into LLM function calling schemas,
|
|
10
|
+
* filtering out unsupported methods (HEAD) and content types
|
|
11
|
+
* (multipart/form-data), and shortening function names to fit the configured
|
|
12
|
+
* maximum length.
|
|
13
|
+
*/
|
|
6
14
|
var HttpLlmApplicationComposer;
|
|
7
15
|
(function (HttpLlmApplicationComposer) {
|
|
16
|
+
/**
|
|
17
|
+
* Builds an {@link IHttpLlmApplication} from migrated HTTP routes.
|
|
18
|
+
*
|
|
19
|
+
* Iterates all routes, converts each to an {@link IHttpLlmFunction}, and
|
|
20
|
+
* collects conversion errors. Applies function name shortening at the end.
|
|
21
|
+
*/
|
|
8
22
|
HttpLlmApplicationComposer.application = (props) => {
|
|
9
23
|
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
|
|
10
|
-
//
|
|
24
|
+
// fill in config defaults
|
|
11
25
|
const config = {
|
|
12
26
|
separate: (_b = (_a = props.config) === null || _a === void 0 ? void 0 : _a.separate) !== null && _b !== void 0 ? _b : null,
|
|
13
27
|
maxLength: (_d = (_c = props.config) === null || _c === void 0 ? void 0 : _c.maxLength) !== null && _d !== void 0 ? _d : 64,
|
|
@@ -15,6 +29,7 @@ var HttpLlmApplicationComposer;
|
|
|
15
29
|
reference: (_h = (_g = props.config) === null || _g === void 0 ? void 0 : _g.reference) !== null && _h !== void 0 ? _h : true,
|
|
16
30
|
strict: (_k = (_j = props.config) === null || _j === void 0 ? void 0 : _j.strict) !== null && _k !== void 0 ? _k : false,
|
|
17
31
|
};
|
|
32
|
+
// seed with pre-existing migration errors, excluding human-only endpoints
|
|
18
33
|
const errors = props.migrate.errors
|
|
19
34
|
.filter((e) => e.operation()["x-samchon-human"] !== true)
|
|
20
35
|
.map((e) => ({
|
|
@@ -24,10 +39,12 @@ var HttpLlmApplicationComposer;
|
|
|
24
39
|
operation: () => e.operation(),
|
|
25
40
|
route: () => undefined,
|
|
26
41
|
}));
|
|
42
|
+
// convert each route to an LLM function, rejecting unsupported ones
|
|
27
43
|
const functions = props.migrate.routes
|
|
28
44
|
.filter((e) => e.operation()["x-samchon-human"] !== true)
|
|
29
45
|
.map((route, i) => {
|
|
30
46
|
var _a, _b;
|
|
47
|
+
// reject HEAD — LLMs cannot interpret header-only responses
|
|
31
48
|
if (route.method === "head") {
|
|
32
49
|
errors.push({
|
|
33
50
|
method: route.method,
|
|
@@ -37,6 +54,7 @@ var HttpLlmApplicationComposer;
|
|
|
37
54
|
route: () => route,
|
|
38
55
|
});
|
|
39
56
|
return null;
|
|
57
|
+
// reject multipart/form-data — binary uploads not expressible in JSON Schema
|
|
40
58
|
}
|
|
41
59
|
else if (((_a = route.body) === null || _a === void 0 ? void 0 : _a.type) === "multipart/form-data" ||
|
|
42
60
|
((_b = route.success) === null || _b === void 0 ? void 0 : _b.type) === "multipart/form-data") {
|
|
@@ -78,42 +96,39 @@ var HttpLlmApplicationComposer;
|
|
|
78
96
|
HttpLlmApplicationComposer.shorten(app, (_m = (_l = props.config) === null || _l === void 0 ? void 0 : _l.maxLength) !== null && _m !== void 0 ? _m : 64);
|
|
79
97
|
return app;
|
|
80
98
|
};
|
|
99
|
+
/**
|
|
100
|
+
* Converts a single {@link IHttpMigrateRoute} into an {@link IHttpLlmFunction}
|
|
101
|
+
* by composing parameter/output schemas and validating function name
|
|
102
|
+
* constraints.
|
|
103
|
+
*/
|
|
81
104
|
const composeFunction = (props) => {
|
|
82
|
-
var _a, _b, _c, _d, _e, _f, _g;
|
|
83
|
-
//
|
|
105
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j;
|
|
106
|
+
// accessor prefix for error messages (mirrors OpenAPI document structure)
|
|
84
107
|
const endpoint = `$input.paths[${JSON.stringify(props.route.path)}][${JSON.stringify(props.route.method)}]`;
|
|
85
108
|
const operation = props.route.operation();
|
|
86
|
-
const description = (
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
];
|
|
93
|
-
const summary = operation.summary.endsWith(".")
|
|
94
|
-
? operation.summary.slice(0, -1)
|
|
95
|
-
: operation.summary;
|
|
96
|
-
const final = operation.description.startsWith(summary)
|
|
97
|
-
? operation.description
|
|
98
|
-
: summary + ".\n\n" + operation.description;
|
|
99
|
-
return [final, final.length];
|
|
100
|
-
})();
|
|
101
|
-
if (description[1] > 1024) {
|
|
102
|
-
props.errors.push(`The description of the function is too long (must be equal or less than 1,024 characters, but ${description[1].toLocaleString()} length).`);
|
|
109
|
+
const description = concatDescription({
|
|
110
|
+
summary: operation.summary,
|
|
111
|
+
description: operation.description,
|
|
112
|
+
});
|
|
113
|
+
if (((_a = description === null || description === void 0 ? void 0 : description.length) !== null && _a !== void 0 ? _a : 0) > 1024) {
|
|
114
|
+
props.errors.push(`The description of the function is too long (must be equal or less than 1,024 characters, but ${description.length.toLocaleString()} length).`);
|
|
103
115
|
}
|
|
104
|
-
//
|
|
116
|
+
// build function name from route accessor, replacing forbidden chars
|
|
105
117
|
const name = emend(props.route.accessor.join("_"));
|
|
106
118
|
const isNameVariable = /^[a-zA-Z0-9_-]+$/.test(name);
|
|
107
|
-
const isNameStartsWithNumber = /^[0-9]/.test((
|
|
119
|
+
const isNameStartsWithNumber = /^[0-9]/.test((_b = name[0]) !== null && _b !== void 0 ? _b : "");
|
|
108
120
|
if (isNameVariable === false)
|
|
109
121
|
props.errors.push(`Elements of path (separated by '/') must be composed with alphabets, numbers, underscores, and hyphens`);
|
|
122
|
+
if (isNameStartsWithNumber === true)
|
|
123
|
+
props.errors.push(`Function name cannot start with a number.`);
|
|
110
124
|
//----
|
|
111
125
|
// CONSTRUCT SCHEMAS
|
|
112
126
|
//----
|
|
113
|
-
//
|
|
127
|
+
// merge path parameters, query, and body into a single object schema
|
|
114
128
|
const parameters = {
|
|
115
129
|
type: "object",
|
|
116
130
|
properties: Object.fromEntries([
|
|
131
|
+
// path parameters (e.g., /users/:id)
|
|
117
132
|
...props.route.parameters.map((s) => {
|
|
118
133
|
var _a;
|
|
119
134
|
return [
|
|
@@ -121,59 +136,65 @@ var HttpLlmApplicationComposer;
|
|
|
121
136
|
Object.assign(Object.assign({}, s.schema), { description: (_a = s.parameter().description) !== null && _a !== void 0 ? _a : s.schema.description }),
|
|
122
137
|
];
|
|
123
138
|
}),
|
|
139
|
+
// query parameters
|
|
124
140
|
...(props.route.query
|
|
125
141
|
? [
|
|
126
142
|
[
|
|
127
143
|
props.route.query.key,
|
|
128
|
-
Object.assign(Object.assign({}, props.route.query.schema), { title: (
|
|
144
|
+
Object.assign(Object.assign({}, props.route.query.schema), { title: (_c = props.route.query.title()) !== null && _c !== void 0 ? _c : props.route.query.schema.title, description: (_d = props.route.query.description()) !== null && _d !== void 0 ? _d : props.route.query.schema.description }),
|
|
129
145
|
],
|
|
130
146
|
]
|
|
131
147
|
: []),
|
|
148
|
+
// request body
|
|
132
149
|
...(props.route.body
|
|
133
150
|
? [
|
|
134
151
|
[
|
|
135
152
|
props.route.body.key,
|
|
136
|
-
Object.assign(Object.assign({}, props.route.body.schema), { description: (
|
|
153
|
+
Object.assign(Object.assign({}, props.route.body.schema), { description: (_e = props.route.body.description()) !== null && _e !== void 0 ? _e : props.route.body.schema.description }),
|
|
137
154
|
],
|
|
138
155
|
]
|
|
139
156
|
: []),
|
|
140
157
|
]),
|
|
141
158
|
};
|
|
142
|
-
parameters.required = Object.keys((
|
|
159
|
+
parameters.required = Object.keys((_f = parameters.properties) !== null && _f !== void 0 ? _f : {});
|
|
160
|
+
// convert merged object schema to LLM parameters
|
|
143
161
|
const llmParameters = converters_1.LlmSchemaConverter.parameters({
|
|
144
162
|
config: props.config,
|
|
145
163
|
components: props.components,
|
|
146
164
|
schema: parameters,
|
|
147
165
|
accessor: `${endpoint}.parameters`,
|
|
148
166
|
});
|
|
149
|
-
//
|
|
167
|
+
// convert response schema to LLM output parameters
|
|
150
168
|
const output = props.route.success
|
|
151
|
-
? converters_1.LlmSchemaConverter.
|
|
169
|
+
? converters_1.LlmSchemaConverter.parameters({
|
|
152
170
|
config: props.config,
|
|
153
171
|
components: props.components,
|
|
154
172
|
schema: props.route.success.schema,
|
|
155
173
|
accessor: `${endpoint}.responses[${JSON.stringify(props.route.success.status)}][${JSON.stringify(props.route.success.type)}].schema`,
|
|
156
|
-
$defs: llmParameters.success ? llmParameters.value.$defs : {},
|
|
157
174
|
})
|
|
158
175
|
: undefined;
|
|
159
176
|
//----
|
|
160
177
|
// CONVERSION
|
|
161
178
|
//----
|
|
179
|
+
// bail out if any validation or conversion failed
|
|
162
180
|
if ((output === null || output === void 0 ? void 0 : output.success) === false ||
|
|
163
181
|
llmParameters.success === false ||
|
|
164
182
|
isNameVariable === false ||
|
|
165
183
|
isNameStartsWithNumber === true ||
|
|
166
|
-
description
|
|
184
|
+
((_g = description === null || description === void 0 ? void 0 : description.length) !== null && _g !== void 0 ? _g : 0) > 1024) {
|
|
167
185
|
if ((output === null || output === void 0 ? void 0 : output.success) === false)
|
|
168
186
|
props.errors.push(...output.error.reasons.map((r) => `${r.accessor}: ${r.message}`));
|
|
169
187
|
if (llmParameters.success === false)
|
|
170
|
-
props.errors.push(
|
|
188
|
+
props.errors.push(
|
|
189
|
+
// rewrite internal accessor to match OpenAPI requestBody path
|
|
190
|
+
...llmParameters.error.reasons.map((r) => {
|
|
171
191
|
var _a, _b;
|
|
172
192
|
const accessor = r.accessor.replace(`parameters.properties["body"]`, `requestBody.content[${JSON.stringify((_b = (_a = props.route.body) === null || _a === void 0 ? void 0 : _a.type) !== null && _b !== void 0 ? _b : "application/json")}].schema`);
|
|
173
193
|
return `${accessor}: ${r.message}`;
|
|
174
194
|
}));
|
|
175
195
|
return null;
|
|
176
196
|
}
|
|
197
|
+
// assemble the LLM function
|
|
177
198
|
return {
|
|
178
199
|
method: props.route.method,
|
|
179
200
|
path: props.route.path,
|
|
@@ -183,24 +204,31 @@ var HttpLlmApplicationComposer;
|
|
|
183
204
|
? converters_1.LlmSchemaConverter.separate({
|
|
184
205
|
predicate: props.config.separate,
|
|
185
206
|
parameters: llmParameters.value,
|
|
186
|
-
equals: (
|
|
207
|
+
equals: (_h = props.config.equals) !== null && _h !== void 0 ? _h : false,
|
|
187
208
|
})
|
|
188
209
|
: undefined,
|
|
189
210
|
output: output === null || output === void 0 ? void 0 : output.value,
|
|
190
|
-
description
|
|
211
|
+
description,
|
|
191
212
|
deprecated: operation.deprecated,
|
|
192
213
|
tags: operation.tags,
|
|
193
214
|
validate: OpenApiValidator_1.OpenApiValidator.create({
|
|
194
215
|
components: props.components,
|
|
195
216
|
schema: parameters,
|
|
196
217
|
required: true,
|
|
197
|
-
equals: (
|
|
218
|
+
equals: (_j = props.config.equals) !== null && _j !== void 0 ? _j : false,
|
|
198
219
|
}),
|
|
199
220
|
route: () => props.route,
|
|
200
221
|
operation: () => props.route.operation(),
|
|
201
222
|
};
|
|
202
223
|
};
|
|
224
|
+
/**
|
|
225
|
+
* Shortens function names exceeding the character limit.
|
|
226
|
+
*
|
|
227
|
+
* Tries progressively shorter accessor suffixes first, then falls back to
|
|
228
|
+
* index-prefixed names, and finally UUID as a last resort.
|
|
229
|
+
*/
|
|
203
230
|
HttpLlmApplicationComposer.shorten = (app, limit = 64) => {
|
|
231
|
+
// collect all names for uniqueness checks
|
|
204
232
|
const dictionary = new Set();
|
|
205
233
|
const longFunctions = [];
|
|
206
234
|
for (const func of app.functions) {
|
|
@@ -214,19 +242,22 @@ var HttpLlmApplicationComposer;
|
|
|
214
242
|
let index = 0;
|
|
215
243
|
for (const func of longFunctions) {
|
|
216
244
|
let success = false;
|
|
217
|
-
|
|
245
|
+
const rename = (str) => {
|
|
218
246
|
dictionary.delete(func.name);
|
|
219
247
|
dictionary.add(str);
|
|
220
248
|
func.name = str;
|
|
221
249
|
success = true;
|
|
222
250
|
};
|
|
251
|
+
// try dropping leading accessor segments to shorten the name
|
|
252
|
+
// (e.g., "api_users_getById" → "users_getById" → "getById")
|
|
223
253
|
for (let i = 1; i < func.route().accessor.length; ++i) {
|
|
224
254
|
const shortName = func.route().accessor.slice(i).join("_");
|
|
225
255
|
if (shortName.length > limit - 8)
|
|
226
|
-
continue;
|
|
256
|
+
continue; // reserve room for "_N_" prefix
|
|
227
257
|
else if (dictionary.has(shortName) === false)
|
|
228
258
|
rename(shortName);
|
|
229
259
|
else {
|
|
260
|
+
// name collision — prefix with a counter to disambiguate
|
|
230
261
|
const newName = `_${index}_${shortName}`;
|
|
231
262
|
if (dictionary.has(newName) === true)
|
|
232
263
|
continue;
|
|
@@ -235,6 +266,7 @@ var HttpLlmApplicationComposer;
|
|
|
235
266
|
}
|
|
236
267
|
break;
|
|
237
268
|
}
|
|
269
|
+
// last resort — all suffix attempts failed or collided
|
|
238
270
|
if (success === false)
|
|
239
271
|
rename(randomFormatUuid());
|
|
240
272
|
}
|
|
@@ -245,10 +277,28 @@ const randomFormatUuid = () => "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[
|
|
|
245
277
|
const v = c === "x" ? r : (r & 0x3) | 0x8;
|
|
246
278
|
return v.toString(16);
|
|
247
279
|
});
|
|
280
|
+
/** Replaces forbidden characters (`$`, `%`, `.`) with underscores. */
|
|
248
281
|
const emend = (str) => {
|
|
249
282
|
for (const ch of FORBIDDEN)
|
|
250
283
|
str = str.split(ch).join("_");
|
|
251
284
|
return str;
|
|
252
285
|
};
|
|
253
286
|
const FORBIDDEN = ["$", "%", "."];
|
|
287
|
+
/**
|
|
288
|
+
* Concatenates summary and description into a single string.
|
|
289
|
+
*
|
|
290
|
+
* If both are present, joins them with a period and double newline, avoiding
|
|
291
|
+
* duplication when the description already starts with the summary.
|
|
292
|
+
*/
|
|
293
|
+
const concatDescription = (p) => {
|
|
294
|
+
var _a, _b;
|
|
295
|
+
if (!((_a = p.summary) === null || _a === void 0 ? void 0 : _a.length) || !((_b = p.description) === null || _b === void 0 ? void 0 : _b.length))
|
|
296
|
+
return p.summary || p.description;
|
|
297
|
+
const summary = p.summary.endsWith(".")
|
|
298
|
+
? p.summary.slice(0, -1)
|
|
299
|
+
: p.summary;
|
|
300
|
+
return p.description.startsWith(summary)
|
|
301
|
+
? p.description
|
|
302
|
+
: summary + ".\n\n" + p.description;
|
|
303
|
+
};
|
|
254
304
|
//# sourceMappingURL=HttpLlmApplicationComposer.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"HttpLlmApplicationComposer.js","sourceRoot":"","sources":["../../../src/http/internal/HttpLlmApplicationComposer.ts"],"names":[],"mappings":";;;AAWA,iDAAsD;AACtD,wEAAqE;AAErE,IAAiB,0BAA0B,
|
|
1
|
+
{"version":3,"file":"HttpLlmApplicationComposer.js","sourceRoot":"","sources":["../../../src/http/internal/HttpLlmApplicationComposer.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,12 +1,24 @@
|
|
|
1
|
-
import { LlmSchemaConverter } from '../../converters/LlmSchemaConverter.mjs';
|
|
2
|
-
import '../../converters/OpenApiConverter.mjs';
|
|
3
|
-
import '../../converters/internal/OpenApiExclusiveEmender.mjs';
|
|
4
1
|
import { OpenApiValidator } from '../../validators/OpenApiValidator.mjs';
|
|
2
|
+
import { LlmSchemaConverter } from '../../converters/LlmSchemaConverter.mjs';
|
|
5
3
|
|
|
4
|
+
/**
|
|
5
|
+
* Composes {@link IHttpLlmApplication} from an {@link IHttpMigrateApplication}.
|
|
6
|
+
*
|
|
7
|
+
* Converts OpenAPI-migrated HTTP routes into LLM function calling schemas,
|
|
8
|
+
* filtering out unsupported methods (HEAD) and content types
|
|
9
|
+
* (multipart/form-data), and shortening function names to fit the configured
|
|
10
|
+
* maximum length.
|
|
11
|
+
*/
|
|
6
12
|
var HttpLlmApplicationComposer;
|
|
7
13
|
(function (HttpLlmApplicationComposer) {
|
|
14
|
+
/**
|
|
15
|
+
* Builds an {@link IHttpLlmApplication} from migrated HTTP routes.
|
|
16
|
+
*
|
|
17
|
+
* Iterates all routes, converts each to an {@link IHttpLlmFunction}, and
|
|
18
|
+
* collects conversion errors. Applies function name shortening at the end.
|
|
19
|
+
*/
|
|
8
20
|
HttpLlmApplicationComposer.application = (props) => {
|
|
9
|
-
//
|
|
21
|
+
// fill in config defaults
|
|
10
22
|
const config = {
|
|
11
23
|
separate: props.config?.separate ?? null,
|
|
12
24
|
maxLength: props.config?.maxLength ?? 64,
|
|
@@ -14,6 +26,7 @@ var HttpLlmApplicationComposer;
|
|
|
14
26
|
reference: props.config?.reference ?? true,
|
|
15
27
|
strict: props.config?.strict ?? false,
|
|
16
28
|
};
|
|
29
|
+
// seed with pre-existing migration errors, excluding human-only endpoints
|
|
17
30
|
const errors = props.migrate.errors
|
|
18
31
|
.filter((e) => e.operation()["x-samchon-human"] !== true)
|
|
19
32
|
.map((e) => ({
|
|
@@ -23,9 +36,11 @@ var HttpLlmApplicationComposer;
|
|
|
23
36
|
operation: () => e.operation(),
|
|
24
37
|
route: () => undefined,
|
|
25
38
|
}));
|
|
39
|
+
// convert each route to an LLM function, rejecting unsupported ones
|
|
26
40
|
const functions = props.migrate.routes
|
|
27
41
|
.filter((e) => e.operation()["x-samchon-human"] !== true)
|
|
28
42
|
.map((route, i) => {
|
|
43
|
+
// reject HEAD — LLMs cannot interpret header-only responses
|
|
29
44
|
if (route.method === "head") {
|
|
30
45
|
errors.push({
|
|
31
46
|
method: route.method,
|
|
@@ -35,6 +50,7 @@ var HttpLlmApplicationComposer;
|
|
|
35
50
|
route: () => route,
|
|
36
51
|
});
|
|
37
52
|
return null;
|
|
53
|
+
// reject multipart/form-data — binary uploads not expressible in JSON Schema
|
|
38
54
|
}
|
|
39
55
|
else if (route.body?.type === "multipart/form-data" ||
|
|
40
56
|
route.success?.type === "multipart/form-data") {
|
|
@@ -74,40 +90,38 @@ var HttpLlmApplicationComposer;
|
|
|
74
90
|
HttpLlmApplicationComposer.shorten(app, props.config?.maxLength ?? 64);
|
|
75
91
|
return app;
|
|
76
92
|
};
|
|
93
|
+
/**
|
|
94
|
+
* Converts a single {@link IHttpMigrateRoute} into an {@link IHttpLlmFunction}
|
|
95
|
+
* by composing parameter/output schemas and validating function name
|
|
96
|
+
* constraints.
|
|
97
|
+
*/
|
|
77
98
|
const composeFunction = (props) => {
|
|
78
|
-
//
|
|
99
|
+
// accessor prefix for error messages (mirrors OpenAPI document structure)
|
|
79
100
|
const endpoint = `$input.paths[${JSON.stringify(props.route.path)}][${JSON.stringify(props.route.method)}]`;
|
|
80
101
|
const operation = props.route.operation();
|
|
81
|
-
const description = (
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
const summary = operation.summary.endsWith(".")
|
|
88
|
-
? operation.summary.slice(0, -1)
|
|
89
|
-
: operation.summary;
|
|
90
|
-
const final = operation.description.startsWith(summary)
|
|
91
|
-
? operation.description
|
|
92
|
-
: summary + ".\n\n" + operation.description;
|
|
93
|
-
return [final, final.length];
|
|
94
|
-
})();
|
|
95
|
-
if (description[1] > 1_024) {
|
|
96
|
-
props.errors.push(`The description of the function is too long (must be equal or less than 1,024 characters, but ${description[1].toLocaleString()} length).`);
|
|
102
|
+
const description = concatDescription({
|
|
103
|
+
summary: operation.summary,
|
|
104
|
+
description: operation.description,
|
|
105
|
+
});
|
|
106
|
+
if ((description?.length ?? 0) > 1_024) {
|
|
107
|
+
props.errors.push(`The description of the function is too long (must be equal or less than 1,024 characters, but ${description.length.toLocaleString()} length).`);
|
|
97
108
|
}
|
|
98
|
-
//
|
|
109
|
+
// build function name from route accessor, replacing forbidden chars
|
|
99
110
|
const name = emend(props.route.accessor.join("_"));
|
|
100
111
|
const isNameVariable = /^[a-zA-Z0-9_-]+$/.test(name);
|
|
101
112
|
const isNameStartsWithNumber = /^[0-9]/.test(name[0] ?? "");
|
|
102
113
|
if (isNameVariable === false)
|
|
103
114
|
props.errors.push(`Elements of path (separated by '/') must be composed with alphabets, numbers, underscores, and hyphens`);
|
|
115
|
+
if (isNameStartsWithNumber === true)
|
|
116
|
+
props.errors.push(`Function name cannot start with a number.`);
|
|
104
117
|
//----
|
|
105
118
|
// CONSTRUCT SCHEMAS
|
|
106
119
|
//----
|
|
107
|
-
//
|
|
120
|
+
// merge path parameters, query, and body into a single object schema
|
|
108
121
|
const parameters = {
|
|
109
122
|
type: "object",
|
|
110
123
|
properties: Object.fromEntries([
|
|
124
|
+
// path parameters (e.g., /users/:id)
|
|
111
125
|
...props.route.parameters.map((s) => [
|
|
112
126
|
s.key,
|
|
113
127
|
{
|
|
@@ -115,6 +129,7 @@ var HttpLlmApplicationComposer;
|
|
|
115
129
|
description: s.parameter().description ?? s.schema.description,
|
|
116
130
|
},
|
|
117
131
|
]),
|
|
132
|
+
// query parameters
|
|
118
133
|
...(props.route.query
|
|
119
134
|
? [
|
|
120
135
|
[
|
|
@@ -128,6 +143,7 @@ var HttpLlmApplicationComposer;
|
|
|
128
143
|
],
|
|
129
144
|
]
|
|
130
145
|
: []),
|
|
146
|
+
// request body
|
|
131
147
|
...(props.route.body
|
|
132
148
|
? [
|
|
133
149
|
[
|
|
@@ -143,39 +159,43 @@ var HttpLlmApplicationComposer;
|
|
|
143
159
|
]),
|
|
144
160
|
};
|
|
145
161
|
parameters.required = Object.keys(parameters.properties ?? {});
|
|
162
|
+
// convert merged object schema to LLM parameters
|
|
146
163
|
const llmParameters = LlmSchemaConverter.parameters({
|
|
147
164
|
config: props.config,
|
|
148
165
|
components: props.components,
|
|
149
166
|
schema: parameters,
|
|
150
167
|
accessor: `${endpoint}.parameters`,
|
|
151
168
|
});
|
|
152
|
-
//
|
|
169
|
+
// convert response schema to LLM output parameters
|
|
153
170
|
const output = props.route.success
|
|
154
|
-
? LlmSchemaConverter.
|
|
171
|
+
? LlmSchemaConverter.parameters({
|
|
155
172
|
config: props.config,
|
|
156
173
|
components: props.components,
|
|
157
174
|
schema: props.route.success.schema,
|
|
158
175
|
accessor: `${endpoint}.responses[${JSON.stringify(props.route.success.status)}][${JSON.stringify(props.route.success.type)}].schema`,
|
|
159
|
-
$defs: llmParameters.success ? llmParameters.value.$defs : {},
|
|
160
176
|
})
|
|
161
177
|
: undefined;
|
|
162
178
|
//----
|
|
163
179
|
// CONVERSION
|
|
164
180
|
//----
|
|
181
|
+
// bail out if any validation or conversion failed
|
|
165
182
|
if (output?.success === false ||
|
|
166
183
|
llmParameters.success === false ||
|
|
167
184
|
isNameVariable === false ||
|
|
168
185
|
isNameStartsWithNumber === true ||
|
|
169
|
-
description
|
|
186
|
+
(description?.length ?? 0) > 1_024) {
|
|
170
187
|
if (output?.success === false)
|
|
171
188
|
props.errors.push(...output.error.reasons.map((r) => `${r.accessor}: ${r.message}`));
|
|
172
189
|
if (llmParameters.success === false)
|
|
173
|
-
props.errors.push(
|
|
190
|
+
props.errors.push(
|
|
191
|
+
// rewrite internal accessor to match OpenAPI requestBody path
|
|
192
|
+
...llmParameters.error.reasons.map((r) => {
|
|
174
193
|
const accessor = r.accessor.replace(`parameters.properties["body"]`, `requestBody.content[${JSON.stringify(props.route.body?.type ?? "application/json")}].schema`);
|
|
175
194
|
return `${accessor}: ${r.message}`;
|
|
176
195
|
}));
|
|
177
196
|
return null;
|
|
178
197
|
}
|
|
198
|
+
// assemble the LLM function
|
|
179
199
|
return {
|
|
180
200
|
method: props.route.method,
|
|
181
201
|
path: props.route.path,
|
|
@@ -189,7 +209,7 @@ var HttpLlmApplicationComposer;
|
|
|
189
209
|
})
|
|
190
210
|
: undefined,
|
|
191
211
|
output: output?.value,
|
|
192
|
-
description
|
|
212
|
+
description,
|
|
193
213
|
deprecated: operation.deprecated,
|
|
194
214
|
tags: operation.tags,
|
|
195
215
|
validate: OpenApiValidator.create({
|
|
@@ -202,7 +222,14 @@ var HttpLlmApplicationComposer;
|
|
|
202
222
|
operation: () => props.route.operation(),
|
|
203
223
|
};
|
|
204
224
|
};
|
|
225
|
+
/**
|
|
226
|
+
* Shortens function names exceeding the character limit.
|
|
227
|
+
*
|
|
228
|
+
* Tries progressively shorter accessor suffixes first, then falls back to
|
|
229
|
+
* index-prefixed names, and finally UUID as a last resort.
|
|
230
|
+
*/
|
|
205
231
|
HttpLlmApplicationComposer.shorten = (app, limit = 64) => {
|
|
232
|
+
// collect all names for uniqueness checks
|
|
206
233
|
const dictionary = new Set();
|
|
207
234
|
const longFunctions = [];
|
|
208
235
|
for (const func of app.functions) {
|
|
@@ -216,19 +243,22 @@ var HttpLlmApplicationComposer;
|
|
|
216
243
|
let index = 0;
|
|
217
244
|
for (const func of longFunctions) {
|
|
218
245
|
let success = false;
|
|
219
|
-
|
|
246
|
+
const rename = (str) => {
|
|
220
247
|
dictionary.delete(func.name);
|
|
221
248
|
dictionary.add(str);
|
|
222
249
|
func.name = str;
|
|
223
250
|
success = true;
|
|
224
251
|
};
|
|
252
|
+
// try dropping leading accessor segments to shorten the name
|
|
253
|
+
// (e.g., "api_users_getById" → "users_getById" → "getById")
|
|
225
254
|
for (let i = 1; i < func.route().accessor.length; ++i) {
|
|
226
255
|
const shortName = func.route().accessor.slice(i).join("_");
|
|
227
256
|
if (shortName.length > limit - 8)
|
|
228
|
-
continue;
|
|
257
|
+
continue; // reserve room for "_N_" prefix
|
|
229
258
|
else if (dictionary.has(shortName) === false)
|
|
230
259
|
rename(shortName);
|
|
231
260
|
else {
|
|
261
|
+
// name collision — prefix with a counter to disambiguate
|
|
232
262
|
const newName = `_${index}_${shortName}`;
|
|
233
263
|
if (dictionary.has(newName) === true)
|
|
234
264
|
continue;
|
|
@@ -237,6 +267,7 @@ var HttpLlmApplicationComposer;
|
|
|
237
267
|
}
|
|
238
268
|
break;
|
|
239
269
|
}
|
|
270
|
+
// last resort — all suffix attempts failed or collided
|
|
240
271
|
if (success === false)
|
|
241
272
|
rename(randomFormatUuid());
|
|
242
273
|
}
|
|
@@ -247,12 +278,29 @@ const randomFormatUuid = () => "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[
|
|
|
247
278
|
const v = c === "x" ? r : (r & 0x3) | 0x8;
|
|
248
279
|
return v.toString(16);
|
|
249
280
|
});
|
|
281
|
+
/** Replaces forbidden characters (`$`, `%`, `.`) with underscores. */
|
|
250
282
|
const emend = (str) => {
|
|
251
283
|
for (const ch of FORBIDDEN)
|
|
252
284
|
str = str.split(ch).join("_");
|
|
253
285
|
return str;
|
|
254
286
|
};
|
|
255
287
|
const FORBIDDEN = ["$", "%", "."];
|
|
288
|
+
/**
|
|
289
|
+
* Concatenates summary and description into a single string.
|
|
290
|
+
*
|
|
291
|
+
* If both are present, joins them with a period and double newline, avoiding
|
|
292
|
+
* duplication when the description already starts with the summary.
|
|
293
|
+
*/
|
|
294
|
+
const concatDescription = (p) => {
|
|
295
|
+
if (!p.summary?.length || !p.description?.length)
|
|
296
|
+
return p.summary || p.description;
|
|
297
|
+
const summary = p.summary.endsWith(".")
|
|
298
|
+
? p.summary.slice(0, -1)
|
|
299
|
+
: p.summary;
|
|
300
|
+
return p.description.startsWith(summary)
|
|
301
|
+
? p.description
|
|
302
|
+
: summary + ".\n\n" + p.description;
|
|
303
|
+
};
|
|
256
304
|
|
|
257
305
|
export { HttpLlmApplicationComposer };
|
|
258
306
|
//# sourceMappingURL=HttpLlmApplicationComposer.mjs.map
|