librechat-data-provider 0.7.3 → 0.7.5
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/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 +3 -3
- package/react-query/package.json +1 -1
- package/specs/actions.spec.ts +276 -1
- package/specs/openapiSpecs.ts +127 -0
- package/src/actions.ts +108 -25
- package/src/api-endpoints.ts +36 -2
- package/src/artifacts.ts +3104 -0
- package/src/bedrock.ts +147 -0
- package/src/config.ts +185 -23
- package/src/data-service.ts +247 -78
- package/src/file-config.ts +4 -1
- package/src/generate.ts +30 -1
- package/src/index.ts +5 -0
- package/src/keys.ts +11 -0
- package/src/parsers.ts +85 -27
- package/src/react-query/react-query-service.ts +25 -0
- package/src/request.ts +3 -0
- package/src/roles.ts +59 -2
- package/src/schemas.ts +361 -173
- package/src/types/agents.ts +220 -0
- package/src/types/assistants.ts +150 -27
- package/src/types/files.ts +5 -0
- package/src/types/mutations.ts +75 -0
- package/src/types/queries.ts +6 -2
- package/src/types/runs.ts +22 -0
- package/src/types.ts +59 -3
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "librechat-data-provider",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.5",
|
|
4
4
|
"description": "data services for librechat apps",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.es.js",
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
"homepage": "https://librechat.ai",
|
|
41
41
|
"dependencies": {
|
|
42
42
|
"@types/js-yaml": "^4.0.9",
|
|
43
|
-
"axios": "^1.
|
|
43
|
+
"axios": "^1.7.7",
|
|
44
44
|
"js-yaml": "^4.1.0",
|
|
45
45
|
"openai": "4.11.1",
|
|
46
46
|
"openapi-types": "^12.1.3",
|
|
@@ -62,7 +62,7 @@
|
|
|
62
62
|
"jest": "^29.5.0",
|
|
63
63
|
"jest-junit": "^16.0.0",
|
|
64
64
|
"rimraf": "^5.0.1",
|
|
65
|
-
"rollup": "^
|
|
65
|
+
"rollup": "^4.22.4",
|
|
66
66
|
"rollup-plugin-generate-package-json": "^3.2.0",
|
|
67
67
|
"rollup-plugin-peer-deps-external": "^2.2.4",
|
|
68
68
|
"rollup-plugin-typescript2": "^0.35.0",
|
package/react-query/package.json
CHANGED
package/specs/actions.spec.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import axios from 'axios';
|
|
2
|
+
import { z } from 'zod';
|
|
2
3
|
import { OpenAPIV3 } from 'openapi-types';
|
|
3
4
|
import {
|
|
4
5
|
createURL,
|
|
@@ -8,7 +9,12 @@ import {
|
|
|
8
9
|
FunctionSignature,
|
|
9
10
|
validateAndParseOpenAPISpec,
|
|
10
11
|
} from '../src/actions';
|
|
11
|
-
import {
|
|
12
|
+
import {
|
|
13
|
+
getWeatherOpenapiSpec,
|
|
14
|
+
whimsicalOpenapiSpec,
|
|
15
|
+
scholarAIOpenapiSpec,
|
|
16
|
+
swapidev,
|
|
17
|
+
} from './openapiSpecs';
|
|
12
18
|
import { AuthorizationTypeEnum, AuthTypeEnum } from '../src/types/assistants';
|
|
13
19
|
import type { FlowchartSchema } from './openapiSpecs';
|
|
14
20
|
import type { ParametersSchema } from '../src/actions';
|
|
@@ -548,4 +554,273 @@ describe('createURL', () => {
|
|
|
548
554
|
'https://example.com/subdirectory/api/v1/users',
|
|
549
555
|
);
|
|
550
556
|
});
|
|
557
|
+
|
|
558
|
+
describe('openapiToFunction zodSchemas', () => {
|
|
559
|
+
describe('getWeatherOpenapiSpec', () => {
|
|
560
|
+
const { zodSchemas } = openapiToFunction(getWeatherOpenapiSpec, true);
|
|
561
|
+
|
|
562
|
+
it('generates correct Zod schema for GetCurrentWeather', () => {
|
|
563
|
+
expect(zodSchemas).toBeDefined();
|
|
564
|
+
expect(zodSchemas?.GetCurrentWeather).toBeDefined();
|
|
565
|
+
|
|
566
|
+
const GetCurrentWeatherSchema = zodSchemas?.GetCurrentWeather;
|
|
567
|
+
|
|
568
|
+
expect(GetCurrentWeatherSchema instanceof z.ZodObject).toBe(true);
|
|
569
|
+
|
|
570
|
+
if (!(GetCurrentWeatherSchema instanceof z.ZodObject)) {
|
|
571
|
+
throw new Error('GetCurrentWeatherSchema is not a ZodObject');
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
const shape = GetCurrentWeatherSchema.shape;
|
|
575
|
+
expect(shape.location instanceof z.ZodString).toBe(true);
|
|
576
|
+
|
|
577
|
+
// Check locations property
|
|
578
|
+
expect(shape.locations).toBeDefined();
|
|
579
|
+
expect(shape.locations instanceof z.ZodOptional).toBe(true);
|
|
580
|
+
|
|
581
|
+
if (!(shape.locations instanceof z.ZodOptional)) {
|
|
582
|
+
throw new Error('locations is not a ZodOptional');
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
const locationsInnerType = shape.locations._def.innerType;
|
|
586
|
+
expect(locationsInnerType instanceof z.ZodArray).toBe(true);
|
|
587
|
+
|
|
588
|
+
if (!(locationsInnerType instanceof z.ZodArray)) {
|
|
589
|
+
throw new Error('locationsInnerType is not a ZodArray');
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
const locationsItemSchema = locationsInnerType.element;
|
|
593
|
+
expect(locationsItemSchema instanceof z.ZodObject).toBe(true);
|
|
594
|
+
|
|
595
|
+
if (!(locationsItemSchema instanceof z.ZodObject)) {
|
|
596
|
+
throw new Error('locationsItemSchema is not a ZodObject');
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
// Validate the structure of locationsItemSchema
|
|
600
|
+
expect(locationsItemSchema.shape.city instanceof z.ZodString).toBe(true);
|
|
601
|
+
expect(locationsItemSchema.shape.state instanceof z.ZodString).toBe(true);
|
|
602
|
+
expect(locationsItemSchema.shape.countryCode instanceof z.ZodString).toBe(true);
|
|
603
|
+
|
|
604
|
+
// Check if time is optional
|
|
605
|
+
const timeSchema = locationsItemSchema.shape.time;
|
|
606
|
+
expect(timeSchema instanceof z.ZodOptional).toBe(true);
|
|
607
|
+
|
|
608
|
+
if (!(timeSchema instanceof z.ZodOptional)) {
|
|
609
|
+
throw new Error('timeSchema is not a ZodOptional');
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
expect(timeSchema._def.innerType instanceof z.ZodString).toBe(true);
|
|
613
|
+
|
|
614
|
+
// Check the description
|
|
615
|
+
expect(shape.locations._def.description).toBe(
|
|
616
|
+
'A list of locations to retrieve the weather for.',
|
|
617
|
+
);
|
|
618
|
+
});
|
|
619
|
+
|
|
620
|
+
it('validates correct data for GetCurrentWeather', () => {
|
|
621
|
+
const GetCurrentWeatherSchema = zodSchemas?.GetCurrentWeather as z.ZodTypeAny;
|
|
622
|
+
const validData = {
|
|
623
|
+
location: 'New York',
|
|
624
|
+
locations: [
|
|
625
|
+
{ city: 'New York', state: 'NY', countryCode: 'US', time: '2023-12-04T14:00:00Z' },
|
|
626
|
+
],
|
|
627
|
+
};
|
|
628
|
+
expect(() => GetCurrentWeatherSchema.parse(validData)).not.toThrow();
|
|
629
|
+
});
|
|
630
|
+
|
|
631
|
+
it('throws error for invalid data for GetCurrentWeather', () => {
|
|
632
|
+
const GetCurrentWeatherSchema = zodSchemas?.GetCurrentWeather as z.ZodTypeAny;
|
|
633
|
+
const invalidData = {
|
|
634
|
+
location: 123,
|
|
635
|
+
locations: [{ city: 'New York', state: 'NY', countryCode: 'US', time: 'invalid-time' }],
|
|
636
|
+
};
|
|
637
|
+
expect(() => GetCurrentWeatherSchema.parse(invalidData)).toThrow();
|
|
638
|
+
});
|
|
639
|
+
});
|
|
640
|
+
|
|
641
|
+
describe('whimsicalOpenapiSpec', () => {
|
|
642
|
+
const { zodSchemas } = openapiToFunction(whimsicalOpenapiSpec, true);
|
|
643
|
+
|
|
644
|
+
it('generates correct Zod schema for postRenderFlowchart', () => {
|
|
645
|
+
expect(zodSchemas).toBeDefined();
|
|
646
|
+
expect(zodSchemas?.postRenderFlowchart).toBeDefined();
|
|
647
|
+
|
|
648
|
+
const PostRenderFlowchartSchema = zodSchemas?.postRenderFlowchart;
|
|
649
|
+
expect(PostRenderFlowchartSchema).toBeInstanceOf(z.ZodObject);
|
|
650
|
+
|
|
651
|
+
if (!(PostRenderFlowchartSchema instanceof z.ZodObject)) {
|
|
652
|
+
return;
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
const shape = PostRenderFlowchartSchema.shape;
|
|
656
|
+
expect(shape.mermaid).toBeInstanceOf(z.ZodString);
|
|
657
|
+
expect(shape.title).toBeInstanceOf(z.ZodOptional);
|
|
658
|
+
expect((shape.title as z.ZodOptional<z.ZodString>)._def.innerType).toBeInstanceOf(
|
|
659
|
+
z.ZodString,
|
|
660
|
+
);
|
|
661
|
+
});
|
|
662
|
+
|
|
663
|
+
it('validates correct data for postRenderFlowchart', () => {
|
|
664
|
+
const PostRenderFlowchartSchema = zodSchemas?.postRenderFlowchart;
|
|
665
|
+
const validData = {
|
|
666
|
+
mermaid: 'graph TD; A-->B; B-->C; C-->D;',
|
|
667
|
+
title: 'Test Flowchart',
|
|
668
|
+
};
|
|
669
|
+
expect(() => PostRenderFlowchartSchema?.parse(validData)).not.toThrow();
|
|
670
|
+
});
|
|
671
|
+
|
|
672
|
+
it('throws error for invalid data for postRenderFlowchart', () => {
|
|
673
|
+
const PostRenderFlowchartSchema = zodSchemas?.postRenderFlowchart;
|
|
674
|
+
const invalidData = {
|
|
675
|
+
mermaid: 123,
|
|
676
|
+
title: 42,
|
|
677
|
+
};
|
|
678
|
+
expect(() => PostRenderFlowchartSchema?.parse(invalidData)).toThrow();
|
|
679
|
+
});
|
|
680
|
+
});
|
|
681
|
+
|
|
682
|
+
describe('scholarAIOpenapiSpec', () => {
|
|
683
|
+
const result = validateAndParseOpenAPISpec(scholarAIOpenapiSpec);
|
|
684
|
+
const spec = result.spec as OpenAPIV3.Document;
|
|
685
|
+
const { zodSchemas } = openapiToFunction(spec, true);
|
|
686
|
+
|
|
687
|
+
it('generates correct Zod schema for searchAbstracts', () => {
|
|
688
|
+
expect(zodSchemas).toBeDefined();
|
|
689
|
+
expect(zodSchemas?.searchAbstracts).toBeDefined();
|
|
690
|
+
|
|
691
|
+
const SearchAbstractsSchema = zodSchemas?.searchAbstracts;
|
|
692
|
+
expect(SearchAbstractsSchema).toBeInstanceOf(z.ZodObject);
|
|
693
|
+
|
|
694
|
+
if (!(SearchAbstractsSchema instanceof z.ZodObject)) {
|
|
695
|
+
return;
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
const shape = SearchAbstractsSchema.shape;
|
|
699
|
+
expect(shape.keywords).toBeInstanceOf(z.ZodString);
|
|
700
|
+
expect(shape.sort).toBeInstanceOf(z.ZodOptional);
|
|
701
|
+
expect(
|
|
702
|
+
(shape.sort as z.ZodOptional<z.ZodEnum<[string, ...string[]]>>)._def.innerType,
|
|
703
|
+
).toBeInstanceOf(z.ZodEnum);
|
|
704
|
+
expect(shape.query).toBeInstanceOf(z.ZodString);
|
|
705
|
+
expect(shape.peer_reviewed_only).toBeInstanceOf(z.ZodOptional);
|
|
706
|
+
expect(shape.start_year).toBeInstanceOf(z.ZodOptional);
|
|
707
|
+
expect(shape.end_year).toBeInstanceOf(z.ZodOptional);
|
|
708
|
+
expect(shape.offset).toBeInstanceOf(z.ZodOptional);
|
|
709
|
+
});
|
|
710
|
+
|
|
711
|
+
it('validates correct data for searchAbstracts', () => {
|
|
712
|
+
const SearchAbstractsSchema = zodSchemas?.searchAbstracts;
|
|
713
|
+
const validData = {
|
|
714
|
+
keywords: 'machine learning',
|
|
715
|
+
sort: 'cited_by_count',
|
|
716
|
+
query: 'AI applications',
|
|
717
|
+
peer_reviewed_only: 'true',
|
|
718
|
+
start_year: '2020',
|
|
719
|
+
end_year: '2023',
|
|
720
|
+
offset: '0',
|
|
721
|
+
};
|
|
722
|
+
expect(() => SearchAbstractsSchema?.parse(validData)).not.toThrow();
|
|
723
|
+
});
|
|
724
|
+
|
|
725
|
+
it('throws error for invalid data for searchAbstracts', () => {
|
|
726
|
+
const SearchAbstractsSchema = zodSchemas?.searchAbstracts;
|
|
727
|
+
const invalidData = {
|
|
728
|
+
keywords: 123,
|
|
729
|
+
sort: 'invalid_sort',
|
|
730
|
+
query: 42,
|
|
731
|
+
peer_reviewed_only: 'maybe',
|
|
732
|
+
start_year: 2020,
|
|
733
|
+
end_year: 2023,
|
|
734
|
+
offset: 0,
|
|
735
|
+
};
|
|
736
|
+
expect(() => SearchAbstractsSchema?.parse(invalidData)).toThrow();
|
|
737
|
+
});
|
|
738
|
+
|
|
739
|
+
it('generates correct Zod schema for getFullText', () => {
|
|
740
|
+
expect(zodSchemas?.getFullText).toBeDefined();
|
|
741
|
+
|
|
742
|
+
const GetFullTextSchema = zodSchemas?.getFullText;
|
|
743
|
+
expect(GetFullTextSchema).toBeInstanceOf(z.ZodObject);
|
|
744
|
+
|
|
745
|
+
if (!(GetFullTextSchema instanceof z.ZodObject)) {
|
|
746
|
+
return;
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
const shape = GetFullTextSchema.shape;
|
|
750
|
+
expect(shape.pdf_url).toBeInstanceOf(z.ZodString);
|
|
751
|
+
expect(shape.chunk).toBeInstanceOf(z.ZodOptional);
|
|
752
|
+
expect((shape.chunk as z.ZodOptional<z.ZodNumber>)._def.innerType).toBeInstanceOf(
|
|
753
|
+
z.ZodNumber,
|
|
754
|
+
);
|
|
755
|
+
});
|
|
756
|
+
|
|
757
|
+
it('generates correct Zod schema for saveCitation', () => {
|
|
758
|
+
expect(zodSchemas?.saveCitation).toBeDefined();
|
|
759
|
+
|
|
760
|
+
const SaveCitationSchema = zodSchemas?.saveCitation;
|
|
761
|
+
expect(SaveCitationSchema).toBeInstanceOf(z.ZodObject);
|
|
762
|
+
|
|
763
|
+
if (!(SaveCitationSchema instanceof z.ZodObject)) {
|
|
764
|
+
return;
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
const shape = SaveCitationSchema.shape;
|
|
768
|
+
expect(shape.doi).toBeInstanceOf(z.ZodString);
|
|
769
|
+
expect(shape.zotero_user_id).toBeInstanceOf(z.ZodString);
|
|
770
|
+
expect(shape.zotero_api_key).toBeInstanceOf(z.ZodString);
|
|
771
|
+
});
|
|
772
|
+
});
|
|
773
|
+
});
|
|
774
|
+
|
|
775
|
+
describe('openapiToFunction zodSchemas for SWAPI', () => {
|
|
776
|
+
const result = validateAndParseOpenAPISpec(swapidev);
|
|
777
|
+
const spec = result.spec as OpenAPIV3.Document;
|
|
778
|
+
const { zodSchemas } = openapiToFunction(spec, true);
|
|
779
|
+
|
|
780
|
+
describe('getPeople schema', () => {
|
|
781
|
+
it('does not generate Zod schema for getPeople (no parameters)', () => {
|
|
782
|
+
expect(zodSchemas).toBeDefined();
|
|
783
|
+
expect(zodSchemas?.getPeople).toBeUndefined();
|
|
784
|
+
});
|
|
785
|
+
|
|
786
|
+
it('validates correct data for getPeople', () => {
|
|
787
|
+
const GetPeopleSchema = zodSchemas?.getPeople;
|
|
788
|
+
expect(GetPeopleSchema).toBeUndefined();
|
|
789
|
+
});
|
|
790
|
+
|
|
791
|
+
it('does not throw for invalid data for getPeople', () => {
|
|
792
|
+
const GetPeopleSchema = zodSchemas?.getPeople;
|
|
793
|
+
expect(GetPeopleSchema).toBeUndefined();
|
|
794
|
+
});
|
|
795
|
+
});
|
|
796
|
+
|
|
797
|
+
describe('getPersonById schema', () => {
|
|
798
|
+
it('generates correct Zod schema for getPersonById', () => {
|
|
799
|
+
expect(zodSchemas).toBeDefined();
|
|
800
|
+
expect(zodSchemas?.getPersonById).toBeDefined();
|
|
801
|
+
|
|
802
|
+
const GetPersonByIdSchema = zodSchemas?.getPersonById;
|
|
803
|
+
expect(GetPersonByIdSchema).toBeInstanceOf(z.ZodObject);
|
|
804
|
+
|
|
805
|
+
if (!(GetPersonByIdSchema instanceof z.ZodObject)) {
|
|
806
|
+
return;
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
const shape = GetPersonByIdSchema.shape;
|
|
810
|
+
expect(shape.id).toBeInstanceOf(z.ZodString);
|
|
811
|
+
});
|
|
812
|
+
|
|
813
|
+
it('validates correct data for getPersonById', () => {
|
|
814
|
+
const GetPersonByIdSchema = zodSchemas?.getPersonById;
|
|
815
|
+
const validData = { id: '1' };
|
|
816
|
+
expect(() => GetPersonByIdSchema?.parse(validData)).not.toThrow();
|
|
817
|
+
});
|
|
818
|
+
|
|
819
|
+
it('throws error for invalid data for getPersonById', () => {
|
|
820
|
+
const GetPersonByIdSchema = zodSchemas?.getPersonById;
|
|
821
|
+
const invalidData = { id: 1 }; // should be string
|
|
822
|
+
expect(() => GetPersonByIdSchema?.parse(invalidData)).toThrow();
|
|
823
|
+
});
|
|
824
|
+
});
|
|
825
|
+
});
|
|
551
826
|
});
|
package/specs/openapiSpecs.ts
CHANGED
|
@@ -348,3 +348,130 @@ components:
|
|
|
348
348
|
message:
|
|
349
349
|
type: string
|
|
350
350
|
description: Confirmation of successful save or error message.`;
|
|
351
|
+
|
|
352
|
+
export const swapidev = `
|
|
353
|
+
openapi: 3.0.3
|
|
354
|
+
info:
|
|
355
|
+
title: Star Wars API
|
|
356
|
+
description: This is a simple API that provides information about the Star Wars universe.
|
|
357
|
+
version: 1.0.0
|
|
358
|
+
servers:
|
|
359
|
+
- url: https://swapi.dev
|
|
360
|
+
|
|
361
|
+
paths:
|
|
362
|
+
/api/people:
|
|
363
|
+
get:
|
|
364
|
+
summary: List all people
|
|
365
|
+
operationId: getPeople
|
|
366
|
+
tags:
|
|
367
|
+
- People
|
|
368
|
+
responses:
|
|
369
|
+
'200':
|
|
370
|
+
description: A list of people
|
|
371
|
+
content:
|
|
372
|
+
application/json:
|
|
373
|
+
schema:
|
|
374
|
+
type: object
|
|
375
|
+
properties:
|
|
376
|
+
count:
|
|
377
|
+
type: integer
|
|
378
|
+
example: 82
|
|
379
|
+
next:
|
|
380
|
+
type: string
|
|
381
|
+
nullable: true
|
|
382
|
+
example: https://swapi.dev/api/people/?page=2
|
|
383
|
+
previous:
|
|
384
|
+
type: string
|
|
385
|
+
nullable: true
|
|
386
|
+
example: null
|
|
387
|
+
results:
|
|
388
|
+
type: array
|
|
389
|
+
items:
|
|
390
|
+
$ref: '#/components/schemas/Person'
|
|
391
|
+
|
|
392
|
+
/api/people/{id}:
|
|
393
|
+
get:
|
|
394
|
+
summary: Get a person by ID
|
|
395
|
+
operationId: getPersonById
|
|
396
|
+
tags:
|
|
397
|
+
- People
|
|
398
|
+
parameters:
|
|
399
|
+
- name: id
|
|
400
|
+
in: path
|
|
401
|
+
required: true
|
|
402
|
+
description: The ID of the person to retrieve
|
|
403
|
+
schema:
|
|
404
|
+
type: string
|
|
405
|
+
responses:
|
|
406
|
+
'200':
|
|
407
|
+
description: A single person
|
|
408
|
+
content:
|
|
409
|
+
application/json:
|
|
410
|
+
schema:
|
|
411
|
+
$ref: '#/components/schemas/Person'
|
|
412
|
+
'404':
|
|
413
|
+
description: Person not found
|
|
414
|
+
|
|
415
|
+
components:
|
|
416
|
+
schemas:
|
|
417
|
+
Person:
|
|
418
|
+
type: object
|
|
419
|
+
properties:
|
|
420
|
+
name:
|
|
421
|
+
type: string
|
|
422
|
+
example: Luke Skywalker
|
|
423
|
+
height:
|
|
424
|
+
type: string
|
|
425
|
+
example: "172"
|
|
426
|
+
mass:
|
|
427
|
+
type: string
|
|
428
|
+
example: "77"
|
|
429
|
+
hair_color:
|
|
430
|
+
type: string
|
|
431
|
+
example: blond
|
|
432
|
+
skin_color:
|
|
433
|
+
type: string
|
|
434
|
+
example: fair
|
|
435
|
+
eye_color:
|
|
436
|
+
type: string
|
|
437
|
+
example: blue
|
|
438
|
+
birth_year:
|
|
439
|
+
type: string
|
|
440
|
+
example: "19BBY"
|
|
441
|
+
gender:
|
|
442
|
+
type: string
|
|
443
|
+
example: male
|
|
444
|
+
homeworld:
|
|
445
|
+
type: string
|
|
446
|
+
example: https://swapi.dev/api/planets/1/
|
|
447
|
+
films:
|
|
448
|
+
type: array
|
|
449
|
+
items:
|
|
450
|
+
type: string
|
|
451
|
+
example: https://swapi.dev/api/films/1/
|
|
452
|
+
species:
|
|
453
|
+
type: array
|
|
454
|
+
items:
|
|
455
|
+
type: string
|
|
456
|
+
example: https://swapi.dev/api/species/1/
|
|
457
|
+
vehicles:
|
|
458
|
+
type: array
|
|
459
|
+
items:
|
|
460
|
+
type: string
|
|
461
|
+
example: https://swapi.dev/api/vehicles/14/
|
|
462
|
+
starships:
|
|
463
|
+
type: array
|
|
464
|
+
items:
|
|
465
|
+
type: string
|
|
466
|
+
example: https://swapi.dev/api/starships/12/
|
|
467
|
+
created:
|
|
468
|
+
type: string
|
|
469
|
+
format: date-time
|
|
470
|
+
example: 2014-12-09T13:50:51.644000Z
|
|
471
|
+
edited:
|
|
472
|
+
type: string
|
|
473
|
+
format: date-time
|
|
474
|
+
example: 2014-12-20T21:17:56.891000Z
|
|
475
|
+
url:
|
|
476
|
+
type: string
|
|
477
|
+
example: https://swapi.dev/api/people/1/`;
|
package/src/actions.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
1
2
|
import axios from 'axios';
|
|
2
3
|
import { URL } from 'url';
|
|
3
4
|
import crypto from 'crypto';
|
|
@@ -12,6 +13,11 @@ export type ParametersSchema = {
|
|
|
12
13
|
required: string[];
|
|
13
14
|
};
|
|
14
15
|
|
|
16
|
+
export type OpenAPISchema = OpenAPIV3.SchemaObject &
|
|
17
|
+
ParametersSchema & {
|
|
18
|
+
items?: OpenAPIV3.ReferenceObject | OpenAPIV3.SchemaObject;
|
|
19
|
+
};
|
|
20
|
+
|
|
15
21
|
export type ApiKeyCredentials = {
|
|
16
22
|
api_key: string;
|
|
17
23
|
custom_auth_header?: string;
|
|
@@ -38,6 +44,70 @@ export function createURL(domain: string, path: string) {
|
|
|
38
44
|
return new URL(fullURL).toString();
|
|
39
45
|
}
|
|
40
46
|
|
|
47
|
+
const schemaTypeHandlers: Record<string, (schema: OpenAPISchema) => z.ZodTypeAny> = {
|
|
48
|
+
string: (schema) => {
|
|
49
|
+
if (schema.enum) {
|
|
50
|
+
return z.enum(schema.enum as [string, ...string[]]);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
let stringSchema = z.string();
|
|
54
|
+
if (schema.minLength !== undefined) {
|
|
55
|
+
stringSchema = stringSchema.min(schema.minLength);
|
|
56
|
+
}
|
|
57
|
+
if (schema.maxLength !== undefined) {
|
|
58
|
+
stringSchema = stringSchema.max(schema.maxLength);
|
|
59
|
+
}
|
|
60
|
+
return stringSchema;
|
|
61
|
+
},
|
|
62
|
+
number: (schema) => {
|
|
63
|
+
let numberSchema = z.number();
|
|
64
|
+
if (schema.minimum !== undefined) {
|
|
65
|
+
numberSchema = numberSchema.min(schema.minimum);
|
|
66
|
+
}
|
|
67
|
+
if (schema.maximum !== undefined) {
|
|
68
|
+
numberSchema = numberSchema.max(schema.maximum);
|
|
69
|
+
}
|
|
70
|
+
return numberSchema;
|
|
71
|
+
},
|
|
72
|
+
integer: (schema) => (schemaTypeHandlers.number(schema) as z.ZodNumber).int(),
|
|
73
|
+
boolean: () => z.boolean(),
|
|
74
|
+
array: (schema) => {
|
|
75
|
+
if (schema.items) {
|
|
76
|
+
const zodSchema = openAPISchemaToZod(schema.items as OpenAPISchema);
|
|
77
|
+
if (zodSchema) {
|
|
78
|
+
return z.array(zodSchema);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return z.array(z.unknown());
|
|
82
|
+
}
|
|
83
|
+
return z.array(z.unknown());
|
|
84
|
+
},
|
|
85
|
+
object: (schema) => {
|
|
86
|
+
const shape: { [key: string]: z.ZodTypeAny } = {};
|
|
87
|
+
if (schema.properties) {
|
|
88
|
+
Object.entries(schema.properties).forEach(([key, value]) => {
|
|
89
|
+
const zodSchema = openAPISchemaToZod(value as OpenAPISchema);
|
|
90
|
+
shape[key] = zodSchema || z.unknown();
|
|
91
|
+
if (schema.required && schema.required.includes(key)) {
|
|
92
|
+
shape[key] = shape[key].describe(value.description || '');
|
|
93
|
+
} else {
|
|
94
|
+
shape[key] = shape[key].optional().describe(value.description || '');
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
return z.object(shape);
|
|
99
|
+
},
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
function openAPISchemaToZod(schema: OpenAPISchema): z.ZodTypeAny | undefined {
|
|
103
|
+
if (schema.type === 'object' && Object.keys(schema.properties || {}).length === 0) {
|
|
104
|
+
return undefined;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const handler = schemaTypeHandlers[schema.type as string] || (() => z.unknown());
|
|
108
|
+
return handler(schema);
|
|
109
|
+
}
|
|
110
|
+
|
|
41
111
|
export class FunctionSignature {
|
|
42
112
|
name: string;
|
|
43
113
|
description: string;
|
|
@@ -46,11 +116,7 @@ export class FunctionSignature {
|
|
|
46
116
|
constructor(name: string, description: string, parameters: ParametersSchema) {
|
|
47
117
|
this.name = name;
|
|
48
118
|
this.description = description;
|
|
49
|
-
|
|
50
|
-
this.parameters = parameters.properties?.['requestBody'] as ParametersSchema;
|
|
51
|
-
} else {
|
|
52
|
-
this.parameters = parameters;
|
|
53
|
-
}
|
|
119
|
+
this.parameters = parameters;
|
|
54
120
|
}
|
|
55
121
|
|
|
56
122
|
toObjectTool(): FunctionTool {
|
|
@@ -219,14 +285,17 @@ function sanitizeOperationId(input: string) {
|
|
|
219
285
|
}
|
|
220
286
|
|
|
221
287
|
/** Function to convert OpenAPI spec to function signatures and request builders */
|
|
222
|
-
export function openapiToFunction(
|
|
288
|
+
export function openapiToFunction(
|
|
289
|
+
openapiSpec: OpenAPIV3.Document,
|
|
290
|
+
generateZodSchemas = false,
|
|
291
|
+
): {
|
|
223
292
|
functionSignatures: FunctionSignature[];
|
|
224
293
|
requestBuilders: Record<string, ActionRequest>;
|
|
294
|
+
zodSchemas?: Record<string, z.ZodTypeAny>;
|
|
225
295
|
} {
|
|
226
296
|
const functionSignatures: FunctionSignature[] = [];
|
|
227
297
|
const requestBuilders: Record<string, ActionRequest> = {};
|
|
228
|
-
|
|
229
|
-
// Base URL from OpenAPI spec servers
|
|
298
|
+
const zodSchemas: Record<string, z.ZodTypeAny> = {};
|
|
230
299
|
const baseUrl = openapiSpec.servers?.[0]?.url ?? '';
|
|
231
300
|
|
|
232
301
|
// Iterate over each path and method in the OpenAPI spec
|
|
@@ -241,19 +310,11 @@ export function openapiToFunction(openapiSpec: OpenAPIV3.Document): {
|
|
|
241
310
|
const operationId = operationObj.operationId || sanitizeOperationId(defaultOperationId);
|
|
242
311
|
const description = operationObj.summary || operationObj.description || '';
|
|
243
312
|
|
|
244
|
-
const parametersSchema:
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
const contentType = Object.keys(content)[0];
|
|
250
|
-
const schema = content[contentType]?.schema;
|
|
251
|
-
const resolvedSchema = resolveRef(
|
|
252
|
-
schema as OpenAPIV3.ReferenceObject | OpenAPIV3.SchemaObject,
|
|
253
|
-
openapiSpec.components,
|
|
254
|
-
);
|
|
255
|
-
parametersSchema.properties['requestBody'] = resolvedSchema;
|
|
256
|
-
}
|
|
313
|
+
const parametersSchema: OpenAPISchema = {
|
|
314
|
+
type: 'object',
|
|
315
|
+
properties: {},
|
|
316
|
+
required: [],
|
|
317
|
+
};
|
|
257
318
|
|
|
258
319
|
if (operationObj.parameters) {
|
|
259
320
|
for (const param of operationObj.parameters) {
|
|
@@ -266,9 +327,24 @@ export function openapiToFunction(openapiSpec: OpenAPIV3.Document): {
|
|
|
266
327
|
if (paramObj.required) {
|
|
267
328
|
parametersSchema.required.push(paramObj.name);
|
|
268
329
|
}
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
if (operationObj.requestBody) {
|
|
334
|
+
const requestBody = operationObj.requestBody as OpenAPIV3.RequestBodyObject;
|
|
335
|
+
const content = requestBody.content;
|
|
336
|
+
const contentType = Object.keys(content)[0];
|
|
337
|
+
const schema = content[contentType]?.schema;
|
|
338
|
+
const resolvedSchema = resolveRef(
|
|
339
|
+
schema as OpenAPIV3.ReferenceObject | OpenAPIV3.SchemaObject,
|
|
340
|
+
openapiSpec.components,
|
|
341
|
+
);
|
|
342
|
+
parametersSchema.properties = {
|
|
343
|
+
...parametersSchema.properties,
|
|
344
|
+
...resolvedSchema.properties,
|
|
345
|
+
};
|
|
346
|
+
if (resolvedSchema.required) {
|
|
347
|
+
parametersSchema.required.push(...resolvedSchema.required);
|
|
272
348
|
}
|
|
273
349
|
}
|
|
274
350
|
|
|
@@ -285,10 +361,17 @@ export function openapiToFunction(openapiSpec: OpenAPIV3.Document): {
|
|
|
285
361
|
);
|
|
286
362
|
|
|
287
363
|
requestBuilders[operationId] = actionRequest;
|
|
364
|
+
|
|
365
|
+
if (generateZodSchemas && Object.keys(parametersSchema.properties).length > 0) {
|
|
366
|
+
const schema = openAPISchemaToZod(parametersSchema);
|
|
367
|
+
if (schema) {
|
|
368
|
+
zodSchemas[operationId] = schema;
|
|
369
|
+
}
|
|
370
|
+
}
|
|
288
371
|
}
|
|
289
372
|
}
|
|
290
373
|
|
|
291
|
-
return { functionSignatures, requestBuilders };
|
|
374
|
+
return { functionSignatures, requestBuilders, zodSchemas };
|
|
292
375
|
}
|
|
293
376
|
|
|
294
377
|
export type ValidationResult = {
|