|
1 | 1 | import { OpenApi } from "../OpenApi";
|
2 |
| -import { IChatGptSchema } from "../structures/IChatGptSchema"; |
3 | 2 | import { IHttpLlmApplication } from "../structures/IHttpLlmApplication";
|
4 | 3 | import { IHttpLlmFunction } from "../structures/IHttpLlmFunction";
|
5 | 4 | import { IHttpMigrateApplication } from "../structures/IHttpMigrateApplication";
|
6 | 5 | import { IHttpMigrateRoute } from "../structures/IHttpMigrateRoute";
|
7 | 6 | import { ILlmFunction } from "../structures/ILlmFunction";
|
8 | 7 | import { ILlmSchema } from "../structures/ILlmSchema";
|
| 8 | +import { IOpenApiSchemaError } from "../structures/IOpenApiSchemaError"; |
| 9 | +import { IResult } from "../structures/IResult"; |
| 10 | +import { OpenApiValidator } from "../utils/OpenApiValidator"; |
9 | 11 | import { LlmSchemaComposer } from "./LlmSchemaComposer";
|
10 | 12 |
|
11 | 13 | export namespace HttpLlmComposer {
|
@@ -54,7 +56,7 @@ export namespace HttpLlmComposer {
|
54 | 56 | const localErrors: string[] = [];
|
55 | 57 | const func: IHttpLlmFunction<Model> | null = composeFunction<Model>({
|
56 | 58 | model: props.model,
|
57 |
| - options: props.options, |
| 59 | + config: props.options, |
58 | 60 | components: props.migrate.document().components,
|
59 | 61 | route: route,
|
60 | 62 | errors: localErrors,
|
@@ -86,95 +88,12 @@ export namespace HttpLlmComposer {
|
86 | 88 | model: Model;
|
87 | 89 | components: OpenApi.IComponents;
|
88 | 90 | route: IHttpMigrateRoute;
|
89 |
| - options: IHttpLlmApplication.IOptions<Model>; |
| 91 | + config: IHttpLlmApplication.IOptions<Model>; |
90 | 92 | errors: string[];
|
91 | 93 | index: number;
|
92 | 94 | }): IHttpLlmFunction<Model> | null => {
|
93 |
| - const $defs: Record<string, IChatGptSchema> = {}; |
94 |
| - const cast = ( |
95 |
| - s: OpenApi.IJsonSchema, |
96 |
| - accessor: string, |
97 |
| - ): ILlmSchema.ModelSchema[Model] | null => { |
98 |
| - const result = LlmSchemaComposer.schema(props.model)({ |
99 |
| - config: props.options as any, |
100 |
| - schema: s, |
101 |
| - components: props.components, |
102 |
| - $defs, |
103 |
| - accessor, |
104 |
| - refAccessor: `$input.components.schemas`, |
105 |
| - }); |
106 |
| - if (result.success === false) { |
107 |
| - props.errors.push( |
108 |
| - ...result.error.reasons.map((r) => `${r.accessor}: ${r.message}`), |
109 |
| - ); |
110 |
| - return null; |
111 |
| - } |
112 |
| - return result.value as ILlmSchema.ModelSchema[Model]; |
113 |
| - }; |
114 |
| - |
115 | 95 | // METADATA
|
116 | 96 | const endpoint: string = `$input.paths[${JSON.stringify(props.route.path)}][${JSON.stringify(props.route.method)}]`;
|
117 |
| - const output: ILlmSchema.ModelSchema[Model] | null | undefined = props.route |
118 |
| - .success |
119 |
| - ? cast( |
120 |
| - props.route.success.schema, |
121 |
| - `${endpoint}.responses[${JSON.stringify(props.route.success.status)}][${JSON.stringify(props.route.success.type)}].schema`, |
122 |
| - ) |
123 |
| - : undefined; |
124 |
| - const properties: Array< |
125 |
| - readonly [string, ILlmSchema.ModelSchema[Model] | null] |
126 |
| - > = [ |
127 |
| - ...props.route.parameters.map( |
128 |
| - (s) => |
129 |
| - [ |
130 |
| - s.key, |
131 |
| - cast( |
132 |
| - { |
133 |
| - ...s.schema, |
134 |
| - title: s.parameter().title ?? s.schema.title, |
135 |
| - description: s.parameter().description ?? s.schema.description, |
136 |
| - }, |
137 |
| - `${endpoint}.parameters[${JSON.stringify(s.key)}].schema`, |
138 |
| - ), |
139 |
| - ] as const, |
140 |
| - ), |
141 |
| - ...(props.route.query |
142 |
| - ? [ |
143 |
| - [ |
144 |
| - props.route.query.key, |
145 |
| - cast( |
146 |
| - { |
147 |
| - ...props.route.query.schema, |
148 |
| - title: |
149 |
| - props.route.query.title() ?? props.route.query.schema.title, |
150 |
| - description: |
151 |
| - props.route.query.description() ?? |
152 |
| - props.route.query.schema.description, |
153 |
| - }, |
154 |
| - `${endpoint}.parameters[${JSON.stringify(props.route.query.key)}].schema`, |
155 |
| - ), |
156 |
| - ] as const, |
157 |
| - ] |
158 |
| - : []), |
159 |
| - ...(props.route.body |
160 |
| - ? [ |
161 |
| - [ |
162 |
| - props.route.body.key, |
163 |
| - cast( |
164 |
| - { |
165 |
| - ...props.route.body.schema, |
166 |
| - description: |
167 |
| - props.route.body.description() ?? |
168 |
| - props.route.body.schema.description, |
169 |
| - }, |
170 |
| - `${endpoint}.requestBody.content[${JSON.stringify(props.route.body.type)}].schema`, |
171 |
| - ), |
172 |
| - ] as const, |
173 |
| - ] |
174 |
| - : []), |
175 |
| - ]; |
176 |
| - |
177 |
| - // DESCRIPTION |
178 | 97 | const operation: OpenApi.IOperation = props.route.operation();
|
179 | 98 | const description: [string | undefined, number] = (() => {
|
180 | 99 | if (!operation.summary?.length || !operation.description?.length)
|
@@ -204,44 +123,128 @@ export namespace HttpLlmComposer {
|
204 | 123 | props.errors.push(
|
205 | 124 | `Elements of path (separated by '/') must be composed with alphabets, numbers, underscores, and hyphens`,
|
206 | 125 | );
|
| 126 | + |
| 127 | + //---- |
| 128 | + // CONSTRUCT SCHEMAS |
| 129 | + //---- |
| 130 | + // PARAMETERS |
| 131 | + const parameters: OpenApi.IJsonSchema.IObject = { |
| 132 | + type: "object", |
| 133 | + properties: Object.fromEntries([ |
| 134 | + ...props.route.parameters.map( |
| 135 | + (s) => |
| 136 | + [ |
| 137 | + s.key, |
| 138 | + { |
| 139 | + ...s.schema, |
| 140 | + title: s.parameter().title ?? s.schema.title, |
| 141 | + description: s.parameter().description ?? s.schema.description, |
| 142 | + }, |
| 143 | + ] as const, |
| 144 | + ), |
| 145 | + ...(props.route.query |
| 146 | + ? [ |
| 147 | + [ |
| 148 | + props.route.query.key, |
| 149 | + { |
| 150 | + ...props.route.query.schema, |
| 151 | + title: |
| 152 | + props.route.query.title() ?? props.route.query.schema.title, |
| 153 | + description: |
| 154 | + props.route.query.description() ?? |
| 155 | + props.route.query.schema.description, |
| 156 | + }, |
| 157 | + ] as const, |
| 158 | + ] |
| 159 | + : []), |
| 160 | + ...(props.route.body |
| 161 | + ? [ |
| 162 | + [ |
| 163 | + props.route.body.key, |
| 164 | + { |
| 165 | + ...props.route.body.schema, |
| 166 | + description: |
| 167 | + props.route.body.description() ?? |
| 168 | + props.route.body.schema.description, |
| 169 | + }, |
| 170 | + ] as const, |
| 171 | + ] |
| 172 | + : []), |
| 173 | + ]), |
| 174 | + }; |
| 175 | + parameters.required = Object.keys(parameters.properties ?? {}); |
| 176 | + |
| 177 | + const llmParameters: IResult< |
| 178 | + ILlmSchema.IParameters<Model>, |
| 179 | + IOpenApiSchemaError |
| 180 | + > = LlmSchemaComposer.parameters(props.model)({ |
| 181 | + config: props.config as any, |
| 182 | + components: props.components, |
| 183 | + schema: parameters, |
| 184 | + accessor: `${endpoint}.parameters`, |
| 185 | + }) as IResult<ILlmSchema.IParameters<Model>, IOpenApiSchemaError>; |
| 186 | + |
| 187 | + // RETURN VALUE |
| 188 | + const output: IResult<ILlmSchema<Model>, IOpenApiSchemaError> | undefined = |
| 189 | + props.route.success |
| 190 | + ? (LlmSchemaComposer.schema(props.model)({ |
| 191 | + config: props.config as any, |
| 192 | + components: props.components, |
| 193 | + schema: props.route.success.schema, |
| 194 | + accessor: `${endpoint}.responses[${JSON.stringify(props.route.success.status)}][${JSON.stringify(props.route.success.type)}].schema`, |
| 195 | + $defs: llmParameters.success |
| 196 | + ? (llmParameters.value as any).$defs! |
| 197 | + : {}, |
| 198 | + }) as IResult<ILlmSchema<Model>, IOpenApiSchemaError>) |
| 199 | + : undefined; |
| 200 | + |
| 201 | + //---- |
| 202 | + // CONVERSION |
| 203 | + //---- |
207 | 204 | if (
|
208 |
| - output === null || |
209 |
| - properties.some(([_k, v]) => v === null) || |
| 205 | + output?.success === false || |
| 206 | + llmParameters.success === false || |
210 | 207 | isNameVariable === false ||
|
211 | 208 | isNameStartsWithNumber === true ||
|
212 | 209 | description[1] > 1_024
|
213 |
| - ) |
| 210 | + ) { |
| 211 | + if (output?.success === false) |
| 212 | + props.errors.push( |
| 213 | + ...output.error.reasons.map((r) => `${r.accessor}: ${r.message}`), |
| 214 | + ); |
| 215 | + if (llmParameters.success === false) |
| 216 | + props.errors.push( |
| 217 | + ...llmParameters.error.reasons.map((r) => { |
| 218 | + const accessor: string = r.accessor.replace( |
| 219 | + `parameters.properties["body"]`, |
| 220 | + `requestBody.content[${JSON.stringify(props.route.body?.type ?? "application/json")}].schema`, |
| 221 | + ); |
| 222 | + return `${accessor}: ${r.message}`; |
| 223 | + }), |
| 224 | + ); |
214 | 225 | return null;
|
215 |
| - |
216 |
| - // COMPOSE PARAMETERS |
217 |
| - const parameters: ILlmSchema.ModelParameters[Model] = { |
218 |
| - type: "object", |
219 |
| - properties: Object.fromEntries( |
220 |
| - properties as [string, ILlmSchema.ModelSchema[Model]][], |
221 |
| - ), |
222 |
| - additionalProperties: false, |
223 |
| - required: properties.map(([k]) => k), |
224 |
| - } as any as ILlmSchema.ModelParameters[Model]; |
225 |
| - if (LlmSchemaComposer.isDefs(props.model)) |
226 |
| - (parameters as any as IChatGptSchema.IParameters).$defs = $defs; |
227 |
| - |
228 |
| - // FINALIZATION |
| 226 | + } |
229 | 227 | return {
|
230 | 228 | method: props.route.method as "get",
|
231 | 229 | path: props.route.path,
|
232 | 230 | name,
|
233 |
| - parameters, |
234 |
| - separated: props.options.separate |
| 231 | + parameters: llmParameters.value, |
| 232 | + separated: props.config.separate |
235 | 233 | ? (LlmSchemaComposer.separateParameters(props.model)({
|
236 |
| - predicate: props.options.separate as any, |
| 234 | + predicate: props.config.separate as any, |
237 | 235 | parameters:
|
238 |
| - parameters satisfies ILlmSchema.ModelParameters[Model] as any, |
| 236 | + llmParameters.value satisfies ILlmSchema.ModelParameters[Model] as any, |
239 | 237 | }) as ILlmFunction.ISeparated<Model>)
|
240 | 238 | : undefined,
|
241 |
| - output: output as any, |
| 239 | + output: output?.value, |
242 | 240 | description: description[0],
|
243 | 241 | deprecated: operation.deprecated,
|
244 | 242 | tags: operation.tags,
|
| 243 | + validate: OpenApiValidator.create({ |
| 244 | + components: props.components, |
| 245 | + schema: parameters, |
| 246 | + required: true, |
| 247 | + }), |
245 | 248 | route: () => props.route as any,
|
246 | 249 | operation: () => props.route.operation(),
|
247 | 250 | };
|
|
0 commit comments