librechat-data-provider 0.7.4 → 0.7.7
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/check_updates.sh +1 -0
- package/dist/index.es.js +1 -1
- package/dist/index.es.js.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/react-query/index.es.js +1 -1
- package/dist/react-query/index.es.js.map +1 -1
- package/dist/react-query/package.json +1 -1
- package/package.json +6 -6
- package/react-query/package.json +1 -1
- package/server-rollup.config.js +3 -3
- package/specs/actions.spec.ts +700 -36
- package/specs/azure.spec.ts +8 -5
- package/specs/filetypes.spec.ts +1 -7
- package/specs/mcp.spec.ts +52 -0
- package/specs/openapiSpecs.ts +127 -0
- package/specs/utils.spec.ts +129 -0
- package/src/actions.ts +311 -101
- package/src/api-endpoints.ts +70 -13
- package/src/artifacts.ts +3104 -0
- package/src/azure.ts +40 -33
- package/src/bedrock.ts +227 -0
- package/src/config.ts +344 -78
- package/src/createPayload.ts +3 -1
- package/src/data-service.ts +353 -90
- package/src/file-config.ts +13 -2
- package/src/generate.ts +31 -2
- package/src/index.ts +12 -4
- package/src/keys.ts +17 -0
- package/src/mcp.ts +87 -0
- package/src/models.ts +1 -1
- package/src/parsers.ts +118 -60
- package/src/react-query/react-query-service.ts +54 -115
- package/src/request.ts +31 -7
- package/src/roles.ts +91 -2
- package/src/schemas.ts +513 -340
- package/src/types/agents.ts +276 -0
- package/src/types/assistants.ts +181 -27
- package/src/types/files.ts +6 -0
- package/src/types/mutations.ts +170 -7
- package/src/types/queries.ts +43 -21
- package/src/types/runs.ts +23 -0
- package/src/types.ts +132 -67
- package/src/utils.ts +44 -0
- package/src/zod.spec.ts +526 -0
- package/src/zod.ts +86 -0
- package/tsconfig.json +1 -2
- package/specs/parsers.spec.ts +0 -48
- package/src/sse.js +0 -242
package/src/actions.ts
CHANGED
|
@@ -1,8 +1,15 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import _axios from 'axios';
|
|
2
3
|
import { URL } from 'url';
|
|
3
4
|
import crypto from 'crypto';
|
|
4
5
|
import { load } from 'js-yaml';
|
|
5
|
-
import type {
|
|
6
|
+
import type {
|
|
7
|
+
FunctionTool,
|
|
8
|
+
Schema,
|
|
9
|
+
Reference,
|
|
10
|
+
ActionMetadata,
|
|
11
|
+
ActionMetadataRuntime,
|
|
12
|
+
} from './types/assistants';
|
|
6
13
|
import type { OpenAPIV3 } from 'openapi-types';
|
|
7
14
|
import { Tools, AuthTypeEnum, AuthorizationTypeEnum } from './types/assistants';
|
|
8
15
|
|
|
@@ -10,8 +17,14 @@ export type ParametersSchema = {
|
|
|
10
17
|
type: string;
|
|
11
18
|
properties: Record<string, Reference | Schema>;
|
|
12
19
|
required: string[];
|
|
20
|
+
additionalProperties?: boolean;
|
|
13
21
|
};
|
|
14
22
|
|
|
23
|
+
export type OpenAPISchema = OpenAPIV3.SchemaObject &
|
|
24
|
+
ParametersSchema & {
|
|
25
|
+
items?: OpenAPIV3.ReferenceObject | OpenAPIV3.SchemaObject;
|
|
26
|
+
};
|
|
27
|
+
|
|
15
28
|
export type ApiKeyCredentials = {
|
|
16
29
|
api_key: string;
|
|
17
30
|
custom_auth_header?: string;
|
|
@@ -27,6 +40,16 @@ export type OAuthCredentials = {
|
|
|
27
40
|
|
|
28
41
|
export type Credentials = ApiKeyCredentials | OAuthCredentials;
|
|
29
42
|
|
|
43
|
+
type MediaTypeObject =
|
|
44
|
+
| undefined
|
|
45
|
+
| {
|
|
46
|
+
[media: string]: OpenAPIV3.MediaTypeObject | undefined;
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
type RequestBodyObject = Omit<OpenAPIV3.RequestBodyObject, 'content'> & {
|
|
50
|
+
content: MediaTypeObject;
|
|
51
|
+
};
|
|
52
|
+
|
|
30
53
|
export function sha1(input: string) {
|
|
31
54
|
return crypto.createHash('sha1').update(input).digest('hex');
|
|
32
55
|
}
|
|
@@ -38,65 +61,129 @@ export function createURL(domain: string, path: string) {
|
|
|
38
61
|
return new URL(fullURL).toString();
|
|
39
62
|
}
|
|
40
63
|
|
|
64
|
+
const schemaTypeHandlers: Record<string, (schema: OpenAPISchema) => z.ZodTypeAny> = {
|
|
65
|
+
string: (schema) => {
|
|
66
|
+
if (schema.enum) {
|
|
67
|
+
return z.enum(schema.enum as [string, ...string[]]);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
let stringSchema = z.string();
|
|
71
|
+
if (schema.minLength !== undefined) {
|
|
72
|
+
stringSchema = stringSchema.min(schema.minLength);
|
|
73
|
+
}
|
|
74
|
+
if (schema.maxLength !== undefined) {
|
|
75
|
+
stringSchema = stringSchema.max(schema.maxLength);
|
|
76
|
+
}
|
|
77
|
+
return stringSchema;
|
|
78
|
+
},
|
|
79
|
+
number: (schema) => {
|
|
80
|
+
let numberSchema = z.number();
|
|
81
|
+
if (schema.minimum !== undefined) {
|
|
82
|
+
numberSchema = numberSchema.min(schema.minimum);
|
|
83
|
+
}
|
|
84
|
+
if (schema.maximum !== undefined) {
|
|
85
|
+
numberSchema = numberSchema.max(schema.maximum);
|
|
86
|
+
}
|
|
87
|
+
return numberSchema;
|
|
88
|
+
},
|
|
89
|
+
integer: (schema) => (schemaTypeHandlers.number(schema) as z.ZodNumber).int(),
|
|
90
|
+
boolean: () => z.boolean(),
|
|
91
|
+
array: (schema) => {
|
|
92
|
+
if (schema.items) {
|
|
93
|
+
const zodSchema = openAPISchemaToZod(schema.items as OpenAPISchema);
|
|
94
|
+
if (zodSchema) {
|
|
95
|
+
return z.array(zodSchema);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return z.array(z.unknown());
|
|
99
|
+
}
|
|
100
|
+
return z.array(z.unknown());
|
|
101
|
+
},
|
|
102
|
+
object: (schema) => {
|
|
103
|
+
const shape: { [key: string]: z.ZodTypeAny } = {};
|
|
104
|
+
if (schema.properties) {
|
|
105
|
+
Object.entries(schema.properties).forEach(([key, value]) => {
|
|
106
|
+
const zodSchema = openAPISchemaToZod(value as OpenAPISchema);
|
|
107
|
+
shape[key] = zodSchema || z.unknown();
|
|
108
|
+
if (schema.required && schema.required.includes(key)) {
|
|
109
|
+
shape[key] = shape[key].describe(value.description || '');
|
|
110
|
+
} else {
|
|
111
|
+
shape[key] = shape[key].optional().describe(value.description || '');
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
return z.object(shape);
|
|
116
|
+
},
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
function openAPISchemaToZod(schema: OpenAPISchema): z.ZodTypeAny | undefined {
|
|
120
|
+
if (schema.type === 'object' && Object.keys(schema.properties || {}).length === 0) {
|
|
121
|
+
return undefined;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const handler = schemaTypeHandlers[schema.type as string] || (() => z.unknown());
|
|
125
|
+
return handler(schema);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Class representing a function signature.
|
|
130
|
+
*/
|
|
41
131
|
export class FunctionSignature {
|
|
42
132
|
name: string;
|
|
43
133
|
description: string;
|
|
44
134
|
parameters: ParametersSchema;
|
|
135
|
+
strict: boolean;
|
|
45
136
|
|
|
46
|
-
constructor(name: string, description: string, parameters: ParametersSchema) {
|
|
137
|
+
constructor(name: string, description: string, parameters: ParametersSchema, strict?: boolean) {
|
|
47
138
|
this.name = name;
|
|
48
139
|
this.description = description;
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
} else {
|
|
52
|
-
this.parameters = parameters;
|
|
53
|
-
}
|
|
140
|
+
this.parameters = parameters;
|
|
141
|
+
this.strict = strict ?? false;
|
|
54
142
|
}
|
|
55
143
|
|
|
56
144
|
toObjectTool(): FunctionTool {
|
|
145
|
+
const parameters = {
|
|
146
|
+
...this.parameters,
|
|
147
|
+
additionalProperties: this.strict ? false : undefined,
|
|
148
|
+
};
|
|
149
|
+
|
|
57
150
|
return {
|
|
58
151
|
type: Tools.function,
|
|
59
152
|
function: {
|
|
60
153
|
name: this.name,
|
|
61
154
|
description: this.description,
|
|
62
|
-
parameters
|
|
155
|
+
parameters,
|
|
156
|
+
...(this.strict ? { strict: this.strict } : {}),
|
|
63
157
|
},
|
|
64
158
|
};
|
|
65
159
|
}
|
|
66
160
|
}
|
|
67
161
|
|
|
68
|
-
|
|
69
|
-
domain: string;
|
|
70
|
-
path: string;
|
|
71
|
-
method: string;
|
|
72
|
-
operation: string;
|
|
73
|
-
operationHash?: string;
|
|
74
|
-
isConsequential: boolean;
|
|
75
|
-
contentType: string;
|
|
76
|
-
params?: object;
|
|
77
|
-
|
|
162
|
+
class RequestConfig {
|
|
78
163
|
constructor(
|
|
79
|
-
domain: string,
|
|
80
|
-
|
|
81
|
-
method: string,
|
|
82
|
-
operation: string,
|
|
83
|
-
isConsequential: boolean,
|
|
84
|
-
contentType: string,
|
|
85
|
-
) {
|
|
86
|
-
|
|
87
|
-
this.path = path;
|
|
88
|
-
this.method = method;
|
|
89
|
-
this.operation = operation;
|
|
90
|
-
this.isConsequential = isConsequential;
|
|
91
|
-
this.contentType = contentType;
|
|
92
|
-
}
|
|
164
|
+
readonly domain: string,
|
|
165
|
+
readonly basePath: string,
|
|
166
|
+
readonly method: string,
|
|
167
|
+
readonly operation: string,
|
|
168
|
+
readonly isConsequential: boolean,
|
|
169
|
+
readonly contentType: string,
|
|
170
|
+
) {}
|
|
171
|
+
}
|
|
93
172
|
|
|
173
|
+
class RequestExecutor {
|
|
174
|
+
path: string;
|
|
175
|
+
params?: object;
|
|
176
|
+
private operationHash?: string;
|
|
94
177
|
private authHeaders: Record<string, string> = {};
|
|
95
178
|
private authToken?: string;
|
|
96
179
|
|
|
180
|
+
constructor(private config: RequestConfig) {
|
|
181
|
+
this.path = config.basePath;
|
|
182
|
+
}
|
|
183
|
+
|
|
97
184
|
setParams(params: object) {
|
|
98
185
|
this.operationHash = sha1(JSON.stringify(params));
|
|
99
|
-
this.params = params;
|
|
186
|
+
this.params = Object.assign({}, params);
|
|
100
187
|
|
|
101
188
|
for (const [key, value] of Object.entries(params)) {
|
|
102
189
|
const paramPattern = `{${key}}`;
|
|
@@ -105,11 +192,12 @@ export class ActionRequest {
|
|
|
105
192
|
delete (this.params as Record<string, unknown>)[key];
|
|
106
193
|
}
|
|
107
194
|
}
|
|
195
|
+
return this;
|
|
108
196
|
}
|
|
109
197
|
|
|
110
|
-
async setAuth(metadata:
|
|
198
|
+
async setAuth(metadata: ActionMetadataRuntime) {
|
|
111
199
|
if (!metadata.auth) {
|
|
112
|
-
return;
|
|
200
|
+
return this;
|
|
113
201
|
}
|
|
114
202
|
|
|
115
203
|
const {
|
|
@@ -130,17 +218,25 @@ export class ActionRequest {
|
|
|
130
218
|
/* OAuth */
|
|
131
219
|
oauth_client_id,
|
|
132
220
|
oauth_client_secret,
|
|
221
|
+
oauth_token_expires_at,
|
|
222
|
+
oauth_access_token = '',
|
|
133
223
|
} = metadata;
|
|
134
224
|
|
|
135
|
-
const isApiKey = api_key && type === AuthTypeEnum.ServiceHttp;
|
|
136
|
-
const isOAuth =
|
|
225
|
+
const isApiKey = api_key != null && api_key.length > 0 && type === AuthTypeEnum.ServiceHttp;
|
|
226
|
+
const isOAuth = !!(
|
|
227
|
+
oauth_client_id != null &&
|
|
137
228
|
oauth_client_id &&
|
|
229
|
+
oauth_client_secret != null &&
|
|
138
230
|
oauth_client_secret &&
|
|
139
231
|
type === AuthTypeEnum.OAuth &&
|
|
232
|
+
authorization_url != null &&
|
|
140
233
|
authorization_url &&
|
|
234
|
+
client_url != null &&
|
|
141
235
|
client_url &&
|
|
236
|
+
scope != null &&
|
|
142
237
|
scope &&
|
|
143
|
-
token_exchange_method
|
|
238
|
+
token_exchange_method
|
|
239
|
+
);
|
|
144
240
|
|
|
145
241
|
if (isApiKey && authorization_type === AuthorizationTypeEnum.Basic) {
|
|
146
242
|
const basicToken = Buffer.from(api_key).toString('base64');
|
|
@@ -150,39 +246,42 @@ export class ActionRequest {
|
|
|
150
246
|
} else if (
|
|
151
247
|
isApiKey &&
|
|
152
248
|
authorization_type === AuthorizationTypeEnum.Custom &&
|
|
249
|
+
custom_auth_header != null &&
|
|
153
250
|
custom_auth_header
|
|
154
251
|
) {
|
|
155
252
|
this.authHeaders[custom_auth_header] = api_key;
|
|
156
253
|
} else if (isOAuth) {
|
|
157
|
-
// TODO:
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
scope: scope,
|
|
165
|
-
grant_type: 'client_credentials',
|
|
166
|
-
},
|
|
167
|
-
{
|
|
168
|
-
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
|
169
|
-
},
|
|
170
|
-
);
|
|
171
|
-
this.authToken = tokenResponse.data.access_token;
|
|
254
|
+
// TODO: maybe doing it in a different way later on. but we want that the user needs to folllow the oauth flow.
|
|
255
|
+
// If we do not have a valid token, bail or ask user to sign in
|
|
256
|
+
const now = new Date();
|
|
257
|
+
|
|
258
|
+
// 1. Check if token is set
|
|
259
|
+
if (!oauth_access_token) {
|
|
260
|
+
throw new Error('No access token found. Please log in first.');
|
|
172
261
|
}
|
|
262
|
+
|
|
263
|
+
// 2. Check if token is expired
|
|
264
|
+
if (oauth_token_expires_at && now >= new Date(oauth_token_expires_at)) {
|
|
265
|
+
// Optionally check refresh_token logic, or just prompt user to re-login
|
|
266
|
+
throw new Error('Access token is expired. Please re-login.');
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// If valid, use it
|
|
270
|
+
this.authToken = oauth_access_token;
|
|
173
271
|
this.authHeaders['Authorization'] = `Bearer ${this.authToken}`;
|
|
174
272
|
}
|
|
273
|
+
return this;
|
|
175
274
|
}
|
|
176
275
|
|
|
177
276
|
async execute() {
|
|
178
|
-
const url = createURL(this.domain, this.path);
|
|
277
|
+
const url = createURL(this.config.domain, this.path);
|
|
179
278
|
const headers = {
|
|
180
279
|
...this.authHeaders,
|
|
181
|
-
'Content-Type': this.contentType,
|
|
280
|
+
'Content-Type': this.config.contentType,
|
|
182
281
|
};
|
|
183
282
|
|
|
184
|
-
const method = this.method.toLowerCase();
|
|
185
|
-
|
|
283
|
+
const method = this.config.method.toLowerCase();
|
|
284
|
+
const axios = _axios.create();
|
|
186
285
|
if (method === 'get') {
|
|
187
286
|
return axios.get(url, { headers, params: this.params });
|
|
188
287
|
} else if (method === 'post') {
|
|
@@ -194,39 +293,114 @@ export class ActionRequest {
|
|
|
194
293
|
} else if (method === 'patch') {
|
|
195
294
|
return axios.patch(url, this.params, { headers });
|
|
196
295
|
} else {
|
|
197
|
-
throw new Error(`Unsupported HTTP method: ${
|
|
296
|
+
throw new Error(`Unsupported HTTP method: ${method}`);
|
|
198
297
|
}
|
|
199
298
|
}
|
|
299
|
+
|
|
300
|
+
getConfig() {
|
|
301
|
+
return this.config;
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
export class ActionRequest {
|
|
306
|
+
private config: RequestConfig;
|
|
307
|
+
|
|
308
|
+
constructor(
|
|
309
|
+
domain: string,
|
|
310
|
+
path: string,
|
|
311
|
+
method: string,
|
|
312
|
+
operation: string,
|
|
313
|
+
isConsequential: boolean,
|
|
314
|
+
contentType: string,
|
|
315
|
+
) {
|
|
316
|
+
this.config = new RequestConfig(domain, path, method, operation, isConsequential, contentType);
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
// Add getters to maintain backward compatibility
|
|
320
|
+
get domain() {
|
|
321
|
+
return this.config.domain;
|
|
322
|
+
}
|
|
323
|
+
get path() {
|
|
324
|
+
return this.config.basePath;
|
|
325
|
+
}
|
|
326
|
+
get method() {
|
|
327
|
+
return this.config.method;
|
|
328
|
+
}
|
|
329
|
+
get operation() {
|
|
330
|
+
return this.config.operation;
|
|
331
|
+
}
|
|
332
|
+
get isConsequential() {
|
|
333
|
+
return this.config.isConsequential;
|
|
334
|
+
}
|
|
335
|
+
get contentType() {
|
|
336
|
+
return this.config.contentType;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
createExecutor() {
|
|
340
|
+
return new RequestExecutor(this.config);
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
// Maintain backward compatibility by delegating to a new executor
|
|
344
|
+
setParams(params: object) {
|
|
345
|
+
const executor = this.createExecutor();
|
|
346
|
+
executor.setParams(params);
|
|
347
|
+
return executor;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
async setAuth(metadata: ActionMetadata) {
|
|
351
|
+
const executor = this.createExecutor();
|
|
352
|
+
return executor.setAuth(metadata);
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
async execute() {
|
|
356
|
+
const executor = this.createExecutor();
|
|
357
|
+
return executor.execute();
|
|
358
|
+
}
|
|
200
359
|
}
|
|
201
360
|
|
|
202
|
-
export function resolveRef
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
361
|
+
export function resolveRef<
|
|
362
|
+
T extends
|
|
363
|
+
| OpenAPIV3.ReferenceObject
|
|
364
|
+
| OpenAPIV3.SchemaObject
|
|
365
|
+
| OpenAPIV3.ParameterObject
|
|
366
|
+
| OpenAPIV3.RequestBodyObject,
|
|
367
|
+
>(obj: T, components?: OpenAPIV3.ComponentsObject): Exclude<T, OpenAPIV3.ReferenceObject> {
|
|
368
|
+
if ('$ref' in obj && components) {
|
|
369
|
+
const refPath = obj.$ref.replace(/^#\/components\//, '').split('/');
|
|
370
|
+
|
|
371
|
+
let resolved: unknown = components as Record<string, unknown>;
|
|
372
|
+
for (const segment of refPath) {
|
|
373
|
+
if (typeof resolved === 'object' && resolved !== null && segment in resolved) {
|
|
374
|
+
resolved = (resolved as Record<string, unknown>)[segment];
|
|
375
|
+
} else {
|
|
376
|
+
throw new Error(`Could not resolve reference: ${obj.$ref}`);
|
|
377
|
+
}
|
|
211
378
|
}
|
|
212
|
-
|
|
379
|
+
|
|
380
|
+
return resolveRef(resolved as typeof obj, components) as Exclude<T, OpenAPIV3.ReferenceObject>;
|
|
213
381
|
}
|
|
214
|
-
|
|
382
|
+
|
|
383
|
+
return obj as Exclude<T, OpenAPIV3.ReferenceObject>;
|
|
215
384
|
}
|
|
216
385
|
|
|
217
386
|
function sanitizeOperationId(input: string) {
|
|
218
387
|
return input.replace(/[^a-zA-Z0-9_-]/g, '');
|
|
219
388
|
}
|
|
220
389
|
|
|
221
|
-
/**
|
|
222
|
-
|
|
390
|
+
/**
|
|
391
|
+
* Converts an OpenAPI spec to function signatures and request builders.
|
|
392
|
+
*/
|
|
393
|
+
export function openapiToFunction(
|
|
394
|
+
openapiSpec: OpenAPIV3.Document,
|
|
395
|
+
generateZodSchemas = false,
|
|
396
|
+
): {
|
|
223
397
|
functionSignatures: FunctionSignature[];
|
|
224
398
|
requestBuilders: Record<string, ActionRequest>;
|
|
399
|
+
zodSchemas?: Record<string, z.ZodTypeAny>;
|
|
225
400
|
} {
|
|
226
401
|
const functionSignatures: FunctionSignature[] = [];
|
|
227
402
|
const requestBuilders: Record<string, ActionRequest> = {};
|
|
228
|
-
|
|
229
|
-
// Base URL from OpenAPI spec servers
|
|
403
|
+
const zodSchemas: Record<string, z.ZodTypeAny> = {};
|
|
230
404
|
const baseUrl = openapiSpec.servers?.[0]?.url ?? '';
|
|
231
405
|
|
|
232
406
|
// Iterate over each path and method in the OpenAPI spec
|
|
@@ -234,45 +408,70 @@ export function openapiToFunction(openapiSpec: OpenAPIV3.Document): {
|
|
|
234
408
|
for (const [method, operation] of Object.entries(methods as OpenAPIV3.PathsObject)) {
|
|
235
409
|
const operationObj = operation as OpenAPIV3.OperationObject & {
|
|
236
410
|
'x-openai-isConsequential'?: boolean;
|
|
411
|
+
} & {
|
|
412
|
+
'x-strict'?: boolean;
|
|
237
413
|
};
|
|
238
414
|
|
|
239
415
|
// Operation ID is used as the function name
|
|
240
416
|
const defaultOperationId = `${method}_${path}`;
|
|
241
417
|
const operationId = operationObj.operationId || sanitizeOperationId(defaultOperationId);
|
|
242
418
|
const description = operationObj.summary || operationObj.description || '';
|
|
419
|
+
const isStrict = operationObj['x-strict'] ?? false;
|
|
420
|
+
|
|
421
|
+
const parametersSchema: OpenAPISchema = {
|
|
422
|
+
type: 'object',
|
|
423
|
+
properties: {},
|
|
424
|
+
required: [],
|
|
425
|
+
};
|
|
426
|
+
|
|
427
|
+
if (operationObj.parameters) {
|
|
428
|
+
for (const param of operationObj.parameters ?? []) {
|
|
429
|
+
const resolvedParam = resolveRef(
|
|
430
|
+
param,
|
|
431
|
+
openapiSpec.components,
|
|
432
|
+
) as OpenAPIV3.ParameterObject;
|
|
243
433
|
|
|
244
|
-
|
|
434
|
+
const paramName = resolvedParam.name;
|
|
435
|
+
if (!paramName || !resolvedParam.schema) {
|
|
436
|
+
continue;
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
const paramSchema = resolveRef(
|
|
440
|
+
resolvedParam.schema,
|
|
441
|
+
openapiSpec.components,
|
|
442
|
+
) as OpenAPIV3.SchemaObject;
|
|
443
|
+
|
|
444
|
+
parametersSchema.properties[paramName] = paramSchema;
|
|
445
|
+
if (resolvedParam.required) {
|
|
446
|
+
parametersSchema.required.push(paramName);
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
}
|
|
245
450
|
|
|
246
451
|
if (operationObj.requestBody) {
|
|
247
|
-
const requestBody = operationObj.requestBody as
|
|
452
|
+
const requestBody = operationObj.requestBody as RequestBodyObject;
|
|
248
453
|
const content = requestBody.content;
|
|
249
|
-
const contentType = Object.keys(content)[0];
|
|
250
|
-
const schema = content[contentType]?.schema;
|
|
454
|
+
const contentType = Object.keys(content ?? {})[0];
|
|
455
|
+
const schema = content?.[contentType]?.schema;
|
|
251
456
|
const resolvedSchema = resolveRef(
|
|
252
457
|
schema as OpenAPIV3.ReferenceObject | OpenAPIV3.SchemaObject,
|
|
253
458
|
openapiSpec.components,
|
|
254
459
|
);
|
|
255
|
-
parametersSchema.properties
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
const resolvedSchema = resolveRef(
|
|
262
|
-
{ ...paramObj.schema } as OpenAPIV3.ReferenceObject | OpenAPIV3.SchemaObject,
|
|
263
|
-
openapiSpec.components,
|
|
264
|
-
);
|
|
265
|
-
parametersSchema.properties[paramObj.name] = resolvedSchema;
|
|
266
|
-
if (paramObj.required) {
|
|
267
|
-
parametersSchema.required.push(paramObj.name);
|
|
268
|
-
}
|
|
269
|
-
if (paramObj.description && !('$$ref' in parametersSchema.properties[paramObj.name])) {
|
|
270
|
-
parametersSchema.properties[paramObj.name].description = paramObj.description;
|
|
271
|
-
}
|
|
460
|
+
parametersSchema.properties = {
|
|
461
|
+
...parametersSchema.properties,
|
|
462
|
+
...resolvedSchema.properties,
|
|
463
|
+
};
|
|
464
|
+
if (resolvedSchema.required) {
|
|
465
|
+
parametersSchema.required.push(...resolvedSchema.required);
|
|
272
466
|
}
|
|
273
467
|
}
|
|
274
468
|
|
|
275
|
-
const functionSignature = new FunctionSignature(
|
|
469
|
+
const functionSignature = new FunctionSignature(
|
|
470
|
+
operationId,
|
|
471
|
+
description,
|
|
472
|
+
parametersSchema,
|
|
473
|
+
isStrict,
|
|
474
|
+
);
|
|
276
475
|
functionSignatures.push(functionSignature);
|
|
277
476
|
|
|
278
477
|
const actionRequest = new ActionRequest(
|
|
@@ -280,15 +479,22 @@ export function openapiToFunction(openapiSpec: OpenAPIV3.Document): {
|
|
|
280
479
|
path,
|
|
281
480
|
method,
|
|
282
481
|
operationId,
|
|
283
|
-
!!operationObj['x-openai-isConsequential']
|
|
284
|
-
operationObj.requestBody ? 'application/json' : '
|
|
482
|
+
!!(operationObj['x-openai-isConsequential'] ?? false),
|
|
483
|
+
operationObj.requestBody ? 'application/json' : '',
|
|
285
484
|
);
|
|
286
485
|
|
|
287
486
|
requestBuilders[operationId] = actionRequest;
|
|
487
|
+
|
|
488
|
+
if (generateZodSchemas && Object.keys(parametersSchema.properties).length > 0) {
|
|
489
|
+
const schema = openAPISchemaToZod(parametersSchema);
|
|
490
|
+
if (schema) {
|
|
491
|
+
zodSchemas[operationId] = schema;
|
|
492
|
+
}
|
|
493
|
+
}
|
|
288
494
|
}
|
|
289
495
|
}
|
|
290
496
|
|
|
291
|
-
return { functionSignatures, requestBuilders };
|
|
497
|
+
return { functionSignatures, requestBuilders, zodSchemas };
|
|
292
498
|
}
|
|
293
499
|
|
|
294
500
|
export type ValidationResult = {
|
|
@@ -297,6 +503,9 @@ export type ValidationResult = {
|
|
|
297
503
|
spec?: OpenAPIV3.Document;
|
|
298
504
|
};
|
|
299
505
|
|
|
506
|
+
/**
|
|
507
|
+
* Validates and parses an OpenAPI spec.
|
|
508
|
+
*/
|
|
300
509
|
export function validateAndParseOpenAPISpec(specString: string): ValidationResult {
|
|
301
510
|
try {
|
|
302
511
|
let parsedSpec;
|
|
@@ -331,10 +540,10 @@ export function validateAndParseOpenAPISpec(specString: string): ValidationResul
|
|
|
331
540
|
for (const [path, methods] of Object.entries(paths)) {
|
|
332
541
|
for (const [httpMethod, operation] of Object.entries(methods as OpenAPIV3.PathItemObject)) {
|
|
333
542
|
// Ensure operation is a valid operation object
|
|
334
|
-
const { responses } = operation as OpenAPIV3.OperationObject;
|
|
543
|
+
const { responses } = operation as OpenAPIV3.OperationObject | { responses: undefined };
|
|
335
544
|
if (typeof operation === 'object' && responses) {
|
|
336
545
|
for (const [statusCode, response] of Object.entries(responses)) {
|
|
337
|
-
const content = (response as OpenAPIV3.ResponseObject).content;
|
|
546
|
+
const content = (response as OpenAPIV3.ResponseObject).content as MediaTypeObject;
|
|
338
547
|
if (content && content['application/json'] && content['application/json'].schema) {
|
|
339
548
|
const schema = content['application/json'].schema;
|
|
340
549
|
if ('$ref' in schema && typeof schema.$ref === 'string') {
|
|
@@ -357,6 +566,7 @@ export function validateAndParseOpenAPISpec(specString: string): ValidationResul
|
|
|
357
566
|
spec: parsedSpec,
|
|
358
567
|
};
|
|
359
568
|
} catch (error) {
|
|
569
|
+
console.error(error);
|
|
360
570
|
return { status: false, message: 'Error parsing OpenAPI spec.' };
|
|
361
571
|
}
|
|
362
572
|
}
|