@drax/ai-back 3.29.0 → 3.32.0

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.
Files changed (103) hide show
  1. package/dist/agents/ChatbotTaskService.js +143 -0
  2. package/dist/agents/ChatbotTaskTools.js +756 -0
  3. package/dist/agents/DraxAgent.js +450 -0
  4. package/dist/controllers/AgentSessionController.js +18 -0
  5. package/dist/controllers/DraxAgentController.js +114 -0
  6. package/dist/factory/services/AgentSessionServiceFactory.js +30 -0
  7. package/dist/index.js +15 -1
  8. package/dist/interfaces/IAgentSession.js +1 -0
  9. package/dist/interfaces/IAgentSessionRepository.js +1 -0
  10. package/dist/interfaces/IBuilderTool.js +1 -0
  11. package/dist/interfaces/IDraxAgent.js +1 -0
  12. package/dist/interfaces/IDraxAgentController.js +1 -0
  13. package/dist/interfaces/IDraxAgentRoutes.js +1 -0
  14. package/dist/models/AgentSessionModel.js +31 -0
  15. package/dist/permissions/AgentPermissions.js +6 -0
  16. package/dist/permissions/AgentSessionPermissions.js +10 -0
  17. package/dist/repository/mongo/AgentSessionMongoRepository.js +13 -0
  18. package/dist/repository/sqlite/AgentSessionSqliteRepository.js +34 -0
  19. package/dist/routes/AgentSessionRoutes.js +21 -0
  20. package/dist/routes/ChatbotTaskRoutes.js +8 -0
  21. package/dist/routes/DraxAgentRoutes.js +9 -0
  22. package/dist/schemas/AgentSessionSchema.js +25 -0
  23. package/dist/services/AgentSessionService.js +9 -0
  24. package/dist/tools/BuilderTool.js +248 -0
  25. package/dist/tools/ToolBuilder.js +243 -0
  26. package/package.json +4 -4
  27. package/src/agents/DraxAgent.ts +574 -0
  28. package/src/controllers/AgentSessionController.ts +29 -0
  29. package/src/controllers/DraxAgentController.ts +135 -0
  30. package/src/factory/services/AgentSessionServiceFactory.ts +41 -0
  31. package/src/index.ts +58 -1
  32. package/src/interfaces/IAIProvider.ts +8 -0
  33. package/src/interfaces/IAgentSession.ts +44 -0
  34. package/src/interfaces/IAgentSessionRepository.ts +11 -0
  35. package/src/interfaces/IBuilderTool.ts +51 -0
  36. package/src/interfaces/IDraxAgent.ts +108 -0
  37. package/src/interfaces/IDraxAgentController.ts +5 -0
  38. package/src/interfaces/IDraxAgentRoutes.ts +7 -0
  39. package/src/models/AgentSessionModel.ts +46 -0
  40. package/src/permissions/AgentPermissions.ts +10 -0
  41. package/src/permissions/AgentSessionPermissions.ts +14 -0
  42. package/src/repository/mongo/AgentSessionMongoRepository.ts +22 -0
  43. package/src/repository/sqlite/AgentSessionSqliteRepository.ts +42 -0
  44. package/src/routes/AgentSessionRoutes.ts +38 -0
  45. package/src/routes/DraxAgentRoutes.ts +12 -0
  46. package/src/schemas/AgentSessionSchema.ts +30 -0
  47. package/src/services/AgentSessionService.ts +20 -0
  48. package/src/tools/BuilderTool.ts +289 -0
  49. package/test/DraxAgent.test.ts +221 -0
  50. package/test/ToolBuilder.test.ts +90 -0
  51. package/tsconfig.tsbuildinfo +1 -1
  52. package/types/agents/ChatbotTaskService.d.ts +42 -0
  53. package/types/agents/ChatbotTaskService.d.ts.map +1 -0
  54. package/types/agents/ChatbotTaskTools.d.ts +54 -0
  55. package/types/agents/ChatbotTaskTools.d.ts.map +1 -0
  56. package/types/agents/DraxAgent.d.ts +55 -0
  57. package/types/agents/DraxAgent.d.ts.map +1 -0
  58. package/types/controllers/AgentSessionController.d.ts +8 -0
  59. package/types/controllers/AgentSessionController.d.ts.map +1 -0
  60. package/types/controllers/DraxAgentController.d.ts +16 -0
  61. package/types/controllers/DraxAgentController.d.ts.map +1 -0
  62. package/types/factory/services/AgentSessionServiceFactory.d.ts +8 -0
  63. package/types/factory/services/AgentSessionServiceFactory.d.ts.map +1 -0
  64. package/types/index.d.ts +14 -2
  65. package/types/index.d.ts.map +1 -1
  66. package/types/interfaces/IAIProvider.d.ts +7 -1
  67. package/types/interfaces/IAIProvider.d.ts.map +1 -1
  68. package/types/interfaces/IAgentSession.d.ts +39 -0
  69. package/types/interfaces/IAgentSession.d.ts.map +1 -0
  70. package/types/interfaces/IAgentSessionRepository.d.ts +6 -0
  71. package/types/interfaces/IAgentSessionRepository.d.ts.map +1 -0
  72. package/types/interfaces/IBuilderTool.d.ts +26 -0
  73. package/types/interfaces/IBuilderTool.d.ts.map +1 -0
  74. package/types/interfaces/IDraxAgent.d.ts +74 -0
  75. package/types/interfaces/IDraxAgent.d.ts.map +1 -0
  76. package/types/interfaces/IDraxAgentController.d.ts +5 -0
  77. package/types/interfaces/IDraxAgentController.d.ts.map +1 -0
  78. package/types/interfaces/IDraxAgentRoutes.d.ts +6 -0
  79. package/types/interfaces/IDraxAgentRoutes.d.ts.map +1 -0
  80. package/types/models/AgentSessionModel.d.ts +15 -0
  81. package/types/models/AgentSessionModel.d.ts.map +1 -0
  82. package/types/permissions/AgentPermissions.d.ts +6 -0
  83. package/types/permissions/AgentPermissions.d.ts.map +1 -0
  84. package/types/permissions/AgentSessionPermissions.d.ts +10 -0
  85. package/types/permissions/AgentSessionPermissions.d.ts.map +1 -0
  86. package/types/repository/mongo/AgentSessionMongoRepository.d.ts +9 -0
  87. package/types/repository/mongo/AgentSessionMongoRepository.d.ts.map +1 -0
  88. package/types/repository/sqlite/AgentSessionSqliteRepository.d.ts +23 -0
  89. package/types/repository/sqlite/AgentSessionSqliteRepository.d.ts.map +1 -0
  90. package/types/routes/AgentSessionRoutes.d.ts +4 -0
  91. package/types/routes/AgentSessionRoutes.d.ts.map +1 -0
  92. package/types/routes/ChatbotTaskRoutes.d.ts +4 -0
  93. package/types/routes/ChatbotTaskRoutes.d.ts.map +1 -0
  94. package/types/routes/DraxAgentRoutes.d.ts +4 -0
  95. package/types/routes/DraxAgentRoutes.d.ts.map +1 -0
  96. package/types/schemas/AgentSessionSchema.d.ts +51 -0
  97. package/types/schemas/AgentSessionSchema.d.ts.map +1 -0
  98. package/types/services/AgentSessionService.d.ts +10 -0
  99. package/types/services/AgentSessionService.d.ts.map +1 -0
  100. package/types/tools/BuilderTool.d.ts +35 -0
  101. package/types/tools/BuilderTool.d.ts.map +1 -0
  102. package/types/tools/ToolBuilder.d.ts +47 -0
  103. package/types/tools/ToolBuilder.d.ts.map +1 -0
@@ -0,0 +1,31 @@
1
+ import { mongoose } from '@drax/common-back';
2
+ import uniqueValidator from 'mongoose-unique-validator';
3
+ import mongoosePaginate from 'mongoose-paginate-v2';
4
+ const AgentSessionSchema = new mongoose.Schema({
5
+ sessionId: { type: String, required: true, index: true, unique: false },
6
+ title: { type: String, required: false, index: false, unique: false },
7
+ lastMessageAt: { type: Date, required: false, index: true, unique: false },
8
+ messages: [{
9
+ role: { type: String, enum: ['user', 'assistant', 'system'], required: true, index: false, unique: false },
10
+ content: { type: String, required: true, index: false, unique: false },
11
+ createdAt: { type: Date, required: false, index: false, unique: false }
12
+ }],
13
+ messageCount: { type: Number, required: false, index: false, unique: false },
14
+ inputTokens: { type: Number, required: false, index: false, unique: false },
15
+ outputTokens: { type: Number, required: false, index: false, unique: false },
16
+ tokens: { type: Number, required: false, index: false, unique: false },
17
+ tenant: { type: mongoose.Schema.Types.ObjectId, ref: 'Tenant', required: false, index: true, unique: false },
18
+ user: { type: mongoose.Schema.Types.ObjectId, ref: 'User', required: false, index: true, unique: false }
19
+ }, { timestamps: true });
20
+ AgentSessionSchema.plugin(uniqueValidator, { message: 'validation.unique' });
21
+ AgentSessionSchema.plugin(mongoosePaginate);
22
+ AgentSessionSchema.virtual("id").get(function () {
23
+ return this._id.toString();
24
+ });
25
+ AgentSessionSchema.set('toJSON', { getters: true, virtuals: true });
26
+ AgentSessionSchema.set('toObject', { getters: true, virtuals: true });
27
+ const MODEL_NAME = 'AgentSession';
28
+ const COLLECTION_NAME = 'AgentSession';
29
+ const AgentSessionModel = mongoose.model(MODEL_NAME, AgentSessionSchema, COLLECTION_NAME);
30
+ export { AgentSessionSchema, AgentSessionModel };
31
+ export default AgentSessionModel;
@@ -0,0 +1,6 @@
1
+ var AgentPermissions;
2
+ (function (AgentPermissions) {
3
+ AgentPermissions["Session"] = "agent:session";
4
+ })(AgentPermissions || (AgentPermissions = {}));
5
+ export { AgentPermissions };
6
+ export default AgentPermissions;
@@ -0,0 +1,10 @@
1
+ var AgentSessionPermissions;
2
+ (function (AgentSessionPermissions) {
3
+ AgentSessionPermissions["Create"] = "agentsession:create";
4
+ AgentSessionPermissions["Update"] = "agentsession:update";
5
+ AgentSessionPermissions["Delete"] = "agentsession:delete";
6
+ AgentSessionPermissions["View"] = "agentsession:view";
7
+ AgentSessionPermissions["Manage"] = "agentsession:manage";
8
+ })(AgentSessionPermissions || (AgentSessionPermissions = {}));
9
+ export { AgentSessionPermissions };
10
+ export default AgentSessionPermissions;
@@ -0,0 +1,13 @@
1
+ import { AbstractMongoRepository } from "@drax/crud-back";
2
+ import { AgentSessionModel } from "../../models/AgentSessionModel.js";
3
+ class AgentSessionMongoRepository extends AbstractMongoRepository {
4
+ constructor() {
5
+ super();
6
+ this._model = AgentSessionModel;
7
+ this._searchFields = ['sessionId', 'title'];
8
+ this._populateFields = ['tenant', 'user'];
9
+ this._lean = true;
10
+ }
11
+ }
12
+ export default AgentSessionMongoRepository;
13
+ export { AgentSessionMongoRepository };
@@ -0,0 +1,34 @@
1
+ import { AbstractSqliteRepository } from "@drax/crud-back";
2
+ class AgentSessionSqliteRepository extends AbstractSqliteRepository {
3
+ constructor() {
4
+ super(...arguments);
5
+ this.tableName = 'AgentSession';
6
+ this.searchFields = ['sessionId', 'title'];
7
+ this.booleanFields = [];
8
+ this.jsonFields = ['messages'];
9
+ this.identifier = '_id';
10
+ this.populateFields = [
11
+ { field: 'tenant', table: 'tenant', identifier: '_id' },
12
+ { field: 'user', table: 'user', identifier: '_id' }
13
+ ];
14
+ this.verbose = false;
15
+ this.tableFields = [
16
+ { name: "sessionId", type: "TEXT", unique: false, primary: false },
17
+ { name: "title", type: "TEXT", unique: false, primary: false },
18
+ { name: "lastMessageAt", type: "TEXT", unique: false, primary: false },
19
+ { name: "messages", type: "TEXT", unique: false, primary: false },
20
+ { name: "messageCount", type: "NUMERIC", unique: false, primary: false },
21
+ { name: "messageCount", type: "TEXT", unique: false, primary: false },
22
+ { name: "inputTokens", type: "NUMERIC", unique: false, primary: false },
23
+ { name: "inputTokens", type: "TEXT", unique: false, primary: false },
24
+ { name: "outputTokens", type: "NUMERIC", unique: false, primary: false },
25
+ { name: "outputTokens", type: "TEXT", unique: false, primary: false },
26
+ { name: "tokens", type: "NUMERIC", unique: false, primary: false },
27
+ { name: "tokens", type: "TEXT", unique: false, primary: false },
28
+ { name: "tenant", type: "TEXT", unique: false, primary: false },
29
+ { name: "user", type: "TEXT", unique: false, primary: false }
30
+ ];
31
+ }
32
+ }
33
+ export default AgentSessionSqliteRepository;
34
+ export { AgentSessionSqliteRepository };
@@ -0,0 +1,21 @@
1
+ import AgentSessionController from "../controllers/AgentSessionController.js";
2
+ import { CrudSchemaBuilder } from "@drax/crud-back";
3
+ import { AgentSessionSchema, AgentSessionBaseSchema } from '../schemas/AgentSessionSchema.js';
4
+ async function AgentSessionRoutes(fastify, options) {
5
+ const controller = new AgentSessionController();
6
+ const schemas = new CrudSchemaBuilder(AgentSessionSchema, AgentSessionBaseSchema, AgentSessionBaseSchema, 'AgentSession', 'openapi-3.0', ['ai']);
7
+ fastify.get('/api/agentsession', { schema: schemas.paginateSchema }, (req, rep) => controller.paginate(req, rep));
8
+ fastify.get('/api/agentsession/find', { schema: schemas.findSchema }, (req, rep) => controller.find(req, rep));
9
+ fastify.get('/api/agentsession/search', { schema: schemas.searchSchema }, (req, rep) => controller.search(req, rep));
10
+ fastify.get('/api/agentsession/:id', { schema: schemas.findByIdSchema }, (req, rep) => controller.findById(req, rep));
11
+ fastify.get('/api/agentsession/find-one', { schema: schemas.findOneSchema }, (req, rep) => controller.findOne(req, rep));
12
+ fastify.get('/api/agentsession/group-by', { schema: schemas.groupBySchema }, (req, rep) => controller.groupBy(req, rep));
13
+ fastify.post('/api/agentsession', { schema: schemas.createSchema }, (req, rep) => controller.create(req, rep));
14
+ fastify.put('/api/agentsession/:id', { schema: schemas.updateSchema }, (req, rep) => controller.update(req, rep));
15
+ fastify.patch('/api/agentsession/:id', { schema: schemas.updateSchema }, (req, rep) => controller.updatePartial(req, rep));
16
+ fastify.delete('/api/agentsession/:id', { schema: schemas.deleteSchema }, (req, rep) => controller.delete(req, rep));
17
+ fastify.get('/api/agentsession/export', (req, rep) => controller.export(req, rep));
18
+ fastify.post('/api/agentsession/import', (req, rep) => controller.import(req, rep));
19
+ }
20
+ export default AgentSessionRoutes;
21
+ export { AgentSessionRoutes };
@@ -0,0 +1,8 @@
1
+ import ChatbotTaskController from "../controllers/ChatbotTaskController.js";
2
+ async function ChatbotTaskFastifyRoutes(fastify, options) {
3
+ const controller = new ChatbotTaskController();
4
+ fastify.post("/api/chatbot/session", (req, rep) => controller.startSession(req, rep));
5
+ fastify.post("/api/chatbot/message", (req, rep) => controller.message(req, rep));
6
+ }
7
+ export default ChatbotTaskFastifyRoutes;
8
+ export { ChatbotTaskFastifyRoutes };
@@ -0,0 +1,9 @@
1
+ import DraxAgentController from "../controllers/DraxAgentController.js";
2
+ async function DraxAgentRoutes(fastify, options) {
3
+ const controller = new DraxAgentController();
4
+ const prefix = "/api/ai/agent";
5
+ fastify.post(`${prefix}/session`, (req, rep) => controller.startSession(req, rep));
6
+ fastify.post(`${prefix}/message`, (req, rep) => controller.message(req, rep));
7
+ }
8
+ export default DraxAgentRoutes;
9
+ export { DraxAgentRoutes };
@@ -0,0 +1,25 @@
1
+ import { z } from 'zod';
2
+ const AgentSessionBaseSchema = z.object({
3
+ sessionId: z.string().min(1, 'validation.required'),
4
+ title: z.string().optional(),
5
+ lastMessageAt: z.coerce.date().nullable().optional(),
6
+ messages: z.array(z.object({
7
+ role: z.enum(['user', 'assistant', 'system']),
8
+ content: z.string().min(1, 'validation.required'),
9
+ createdAt: z.coerce.date().nullable().optional()
10
+ })).optional(),
11
+ messageCount: z.number().nullable().optional(),
12
+ inputTokens: z.number().nullable().optional(),
13
+ outputTokens: z.number().nullable().optional(),
14
+ tokens: z.number().nullable().optional(),
15
+ tenant: z.coerce.string().optional().nullable(),
16
+ user: z.coerce.string().optional().nullable()
17
+ });
18
+ const AgentSessionSchema = AgentSessionBaseSchema
19
+ .extend({
20
+ _id: z.coerce.string(),
21
+ tenant: z.object({ _id: z.coerce.string(), name: z.string().optional() }).nullable(),
22
+ user: z.object({ _id: z.coerce.string(), username: z.string().optional() }).nullable()
23
+ });
24
+ export default AgentSessionSchema;
25
+ export { AgentSessionSchema, AgentSessionBaseSchema };
@@ -0,0 +1,9 @@
1
+ import { AbstractService } from "@drax/crud-back";
2
+ class AgentSessionService extends AbstractService {
3
+ constructor(AgentSessionRepository, baseSchema, fullSchema) {
4
+ super(AgentSessionRepository, baseSchema, fullSchema);
5
+ this._validateOutput = true;
6
+ }
7
+ }
8
+ export default AgentSessionService;
9
+ export { AgentSessionService };
@@ -0,0 +1,248 @@
1
+ import { z } from "zod";
2
+ const emptyParameters = {
3
+ type: "object",
4
+ properties: {},
5
+ additionalProperties: false,
6
+ };
7
+ const idSchema = z.object({
8
+ id: z.string().describe("Entity identifier"),
9
+ });
10
+ const filtersSchema = z.array(z.object({
11
+ field: z.string(),
12
+ operator: z.string(),
13
+ value: z.any(),
14
+ orGroup: z.string().optional(),
15
+ })).optional().describe("Optional Drax field filters");
16
+ const toolDefinitions = {
17
+ create: {
18
+ description: entityName => `Crear un registro de ${entityName}`,
19
+ parameters: builder => builder.objectParameters(z.object({
20
+ data: builder.inputSchema.describe("Data for the entity to create"),
21
+ })),
22
+ execute: (service, args) => service.create?.(args.data),
23
+ },
24
+ update: {
25
+ description: entityName => `Reemplazar un registro de ${entityName} por id`,
26
+ parameters: builder => builder.objectParameters(z.object({
27
+ id: z.string().describe("Entity identifier"),
28
+ data: builder.inputSchema.describe("Complete replacement data"),
29
+ })),
30
+ execute: (service, args) => service.update?.(args.id, args.data),
31
+ },
32
+ updatePartial: {
33
+ description: entityName => `Actualizar parcialmente un registro de ${entityName} por id`,
34
+ parameters: builder => builder.objectParameters(z.object({
35
+ id: z.string().describe("Entity identifier"),
36
+ data: builder.partialInputSchema.describe("Partial data to update"),
37
+ })),
38
+ execute: (service, args) => service.updatePartial?.(args.id, args.data),
39
+ },
40
+ delete: {
41
+ description: entityName => `Eliminar un registro de ${entityName} por id`,
42
+ parameters: builder => builder.objectParameters(idSchema),
43
+ execute: (service, args) => service.delete?.(args.id),
44
+ },
45
+ findById: {
46
+ description: entityName => `Buscar un registro de ${entityName} por id`,
47
+ parameters: builder => builder.objectParameters(idSchema),
48
+ execute: (service, args) => service.findById?.(args.id),
49
+ },
50
+ findByIds: {
51
+ description: entityName => `Buscar multiples registros de ${entityName} por ids`,
52
+ parameters: builder => builder.objectParameters(z.object({
53
+ ids: z.array(z.string()).describe("Entity identifiers"),
54
+ })),
55
+ execute: (service, args) => service.findByIds?.(args.ids),
56
+ },
57
+ findOneBy: {
58
+ description: entityName => `Buscar un registro de ${entityName} por valor de campo`,
59
+ parameters: builder => builder.objectParameters(z.object({
60
+ field: z.string(),
61
+ value: z.any(),
62
+ filters: filtersSchema,
63
+ })),
64
+ execute: (service, args) => service.findOneBy?.(args.field, args.value, args.filters ?? []),
65
+ },
66
+ findOne: {
67
+ description: entityName => `Buscar el primer registro de ${entityName} que coincida con busqueda y filtros`,
68
+ parameters: builder => builder.objectParameters(z.object({
69
+ search: z.string().optional(),
70
+ filters: filtersSchema,
71
+ })),
72
+ execute: (service, args) => service.findOne?.({
73
+ search: args.search ?? "",
74
+ filters: args.filters ?? [],
75
+ }),
76
+ },
77
+ findBy: {
78
+ description: entityName => `Buscar registros de ${entityName} por valor de campo`,
79
+ parameters: builder => builder.objectParameters(z.object({
80
+ field: z.string(),
81
+ value: z.any(),
82
+ limit: z.number().optional(),
83
+ filters: filtersSchema,
84
+ })),
85
+ execute: (service, args) => service.findBy?.(args.field, args.value, args.limit ?? 1000, args.filters ?? []),
86
+ },
87
+ fetchAll: {
88
+ description: entityName => `Obtener todos los registros de ${entityName}`,
89
+ parameters: () => emptyParameters,
90
+ execute: service => service.fetchAll?.(),
91
+ },
92
+ search: {
93
+ description: entityName => `Buscar registros de ${entityName} por texto`,
94
+ parameters: builder => builder.objectParameters(z.object({
95
+ value: z.string().describe("Search text"),
96
+ limit: z.number().optional(),
97
+ filters: filtersSchema,
98
+ })),
99
+ execute: (service, args) => service.search?.(args.value, args.limit ?? 1000, args.filters ?? []),
100
+ },
101
+ find: {
102
+ description: entityName => `Buscar registros de ${entityName} usando opciones de listado`,
103
+ parameters: builder => builder.objectParameters(z.object({
104
+ orderBy: z.string().optional(),
105
+ order: z.union([z.enum(["asc", "desc"]), z.boolean()]).optional(),
106
+ search: z.string().optional(),
107
+ filters: filtersSchema,
108
+ limit: z.number().optional(),
109
+ })),
110
+ execute: (service, args) => service.find?.({
111
+ orderBy: args.orderBy ?? "",
112
+ order: args.order ?? false,
113
+ search: args.search ?? "",
114
+ filters: args.filters ?? [],
115
+ limit: args.limit ?? 0,
116
+ }),
117
+ },
118
+ paginate: {
119
+ description: entityName => `Paginar registros de ${entityName}`,
120
+ parameters: builder => builder.objectParameters(z.object({
121
+ page: z.number().optional(),
122
+ limit: z.number().optional(),
123
+ orderBy: z.string().optional(),
124
+ order: z.enum(["asc", "desc"]).optional(),
125
+ search: z.string().optional(),
126
+ filters: filtersSchema,
127
+ })),
128
+ execute: (service, args) => service.paginate({
129
+ page: args.page ?? 1,
130
+ limit: args.limit ?? 10,
131
+ orderBy: args.orderBy,
132
+ order: args.order ?? "asc",
133
+ search: args.search ?? "",
134
+ filters: args.filters ?? [],
135
+ }),
136
+ },
137
+ groupBy: {
138
+ description: entityName => `Agrupar registros de ${entityName} por campos`,
139
+ parameters: builder => builder.objectParameters(z.object({
140
+ fields: z.array(z.string()).optional(),
141
+ filters: filtersSchema,
142
+ dateFormat: z.enum(["year", "month", "day", "hour", "minute", "second"]).optional(),
143
+ })),
144
+ execute: (service, args) => service.groupBy?.({
145
+ fields: args.fields ?? [],
146
+ filters: args.filters ?? [],
147
+ dateFormat: args.dateFormat ?? "day",
148
+ }),
149
+ },
150
+ };
151
+ class BuilderTool {
152
+ constructor(options) {
153
+ this.options = options;
154
+ this._inputSchema = this.schemaAdapter(options.schema);
155
+ this._partialInputSchema = this._inputSchema.partial();
156
+ this._outputSchema = options.outputSchema ? this.schemaAdapter(options.outputSchema) : undefined;
157
+ }
158
+ get inputSchema() {
159
+ return this._inputSchema;
160
+ }
161
+ get partialInputSchema() {
162
+ return this._partialInputSchema;
163
+ }
164
+ get outputSchema() {
165
+ return this._outputSchema;
166
+ }
167
+ getTools() {
168
+ return this.options.methods.map(method => this.buildTool(method));
169
+ }
170
+ getSystemPromptSection() {
171
+ const entityDescription = this.options.entityDescription
172
+ ? `\n${this.options.entityDescription}`
173
+ : "";
174
+ const tools = this.options.methods
175
+ .map(method => {
176
+ const definition = toolDefinitions[method];
177
+ return `- ${this.getToolName(method)}: ${definition.description(this.options.entityName)}`;
178
+ })
179
+ .join("\n");
180
+ const entitySchema = JSON.stringify(this.toJsonSchema(this._outputSchema ?? this._inputSchema));
181
+ return [
182
+ `[ENTIDAD: ${this.options.entityName}]${entityDescription}`,
183
+ `Schema JSON de la entidad: ${entitySchema}`,
184
+ "Tools disponibles:",
185
+ tools,
186
+ ].join("\n");
187
+ }
188
+ objectParameters(schema) {
189
+ return this.toJsonSchema(this.schemaAdapter(schema));
190
+ }
191
+ buildTool(method) {
192
+ const definition = toolDefinitions[method];
193
+ const serviceMethod = this.options.service[method];
194
+ if (typeof serviceMethod !== "function") {
195
+ throw new Error(`Tool method not available on service: ${method}`);
196
+ }
197
+ return {
198
+ name: this.getToolName(method),
199
+ description: definition.description(this.options.entityName),
200
+ parameters: definition.parameters(this),
201
+ navigation: {
202
+ entityName: this.options.entityName,
203
+ method,
204
+ basePath: this.options.navigationBasePath,
205
+ },
206
+ execute: async (args) => definition.execute(this.options.service, args),
207
+ };
208
+ }
209
+ getToolName(method) {
210
+ return `${this.options.toolNamePrefix ?? this.options.entityName}_${method}`;
211
+ }
212
+ toJsonSchema(schema) {
213
+ return z.toJSONSchema(schema, { target: "openAi" });
214
+ }
215
+ getTypeName(field) {
216
+ return field?.constructor?.name;
217
+ }
218
+ fieldAdapter(field) {
219
+ const f = field;
220
+ const typeName = this.getTypeName(f);
221
+ if (typeof f?.unwrap === "function" && typeName === "ZodOptional") {
222
+ return this.fieldAdapter(f.unwrap()).optional();
223
+ }
224
+ if (typeof f?.unwrap === "function" && typeName === "ZodNullable") {
225
+ return this.fieldAdapter(f.unwrap()).nullable();
226
+ }
227
+ if (typeName === "ZodArray" && f?.element) {
228
+ return z.array(this.fieldAdapter(f.element));
229
+ }
230
+ if (typeName === "ZodObject" && f?.shape) {
231
+ return this.schemaAdapter(f);
232
+ }
233
+ if (typeName === "ZodDate") {
234
+ return z.iso.datetime();
235
+ }
236
+ return f;
237
+ }
238
+ schemaAdapter(schema) {
239
+ const shape = schema.shape;
240
+ const newShape = {};
241
+ for (const key of Object.keys(shape)) {
242
+ newShape[key] = this.fieldAdapter(shape[key]);
243
+ }
244
+ return z.object(newShape);
245
+ }
246
+ }
247
+ export default BuilderTool;
248
+ export { BuilderTool };
@@ -0,0 +1,243 @@
1
+ import { z } from "zod";
2
+ const emptyParameters = {
3
+ type: "object",
4
+ properties: {},
5
+ additionalProperties: false,
6
+ };
7
+ const idSchema = z.object({
8
+ id: z.string().describe("Entity identifier"),
9
+ });
10
+ const filtersSchema = z.array(z.object({
11
+ field: z.string(),
12
+ operator: z.string(),
13
+ value: z.any(),
14
+ orGroup: z.string().optional(),
15
+ })).optional().describe("Optional Drax field filters");
16
+ const toolDefinitions = {
17
+ create: {
18
+ description: entityName => `Crear un registro de ${entityName}`,
19
+ parameters: builder => builder.objectParameters(z.object({
20
+ data: builder.inputSchema.describe("Data for the entity to create"),
21
+ })),
22
+ execute: (service, args) => service.create?.(args.data),
23
+ },
24
+ update: {
25
+ description: entityName => `Reemplazar un registro de ${entityName} por id`,
26
+ parameters: builder => builder.objectParameters(z.object({
27
+ id: z.string().describe("Entity identifier"),
28
+ data: builder.inputSchema.describe("Complete replacement data"),
29
+ })),
30
+ execute: (service, args) => service.update?.(args.id, args.data),
31
+ },
32
+ updatePartial: {
33
+ description: entityName => `Actualizar parcialmente un registro de ${entityName} por id`,
34
+ parameters: builder => builder.objectParameters(z.object({
35
+ id: z.string().describe("Entity identifier"),
36
+ data: builder.partialInputSchema.describe("Partial data to update"),
37
+ })),
38
+ execute: (service, args) => service.updatePartial?.(args.id, args.data),
39
+ },
40
+ delete: {
41
+ description: entityName => `Eliminar un registro de ${entityName} por id`,
42
+ parameters: builder => builder.objectParameters(idSchema),
43
+ execute: (service, args) => service.delete?.(args.id),
44
+ },
45
+ findById: {
46
+ description: entityName => `Buscar un registro de ${entityName} por id`,
47
+ parameters: builder => builder.objectParameters(idSchema),
48
+ execute: (service, args) => service.findById?.(args.id),
49
+ },
50
+ findByIds: {
51
+ description: entityName => `Buscar multiples registros de ${entityName} por ids`,
52
+ parameters: builder => builder.objectParameters(z.object({
53
+ ids: z.array(z.string()).describe("Entity identifiers"),
54
+ })),
55
+ execute: (service, args) => service.findByIds?.(args.ids),
56
+ },
57
+ findOneBy: {
58
+ description: entityName => `Buscar un registro de ${entityName} por valor de campo`,
59
+ parameters: builder => builder.objectParameters(z.object({
60
+ field: z.string(),
61
+ value: z.any(),
62
+ filters: filtersSchema,
63
+ })),
64
+ execute: (service, args) => service.findOneBy?.(args.field, args.value, args.filters ?? []),
65
+ },
66
+ findOne: {
67
+ description: entityName => `Buscar el primer registro de ${entityName} que coincida con busqueda y filtros`,
68
+ parameters: builder => builder.objectParameters(z.object({
69
+ search: z.string().optional(),
70
+ filters: filtersSchema,
71
+ })),
72
+ execute: (service, args) => service.findOne?.({
73
+ search: args.search ?? "",
74
+ filters: args.filters ?? [],
75
+ }),
76
+ },
77
+ findBy: {
78
+ description: entityName => `Buscar registros de ${entityName} por valor de campo`,
79
+ parameters: builder => builder.objectParameters(z.object({
80
+ field: z.string(),
81
+ value: z.any(),
82
+ limit: z.number().optional(),
83
+ filters: filtersSchema,
84
+ })),
85
+ execute: (service, args) => service.findBy?.(args.field, args.value, args.limit ?? 1000, args.filters ?? []),
86
+ },
87
+ fetchAll: {
88
+ description: entityName => `Obtener todos los registros de ${entityName}`,
89
+ parameters: () => emptyParameters,
90
+ execute: service => service.fetchAll?.(),
91
+ },
92
+ search: {
93
+ description: entityName => `Buscar registros de ${entityName} por texto`,
94
+ parameters: builder => builder.objectParameters(z.object({
95
+ value: z.string().describe("Search text"),
96
+ limit: z.number().optional(),
97
+ filters: filtersSchema,
98
+ })),
99
+ execute: (service, args) => service.search?.(args.value, args.limit ?? 1000, args.filters ?? []),
100
+ },
101
+ find: {
102
+ description: entityName => `Buscar registros de ${entityName} usando opciones de listado`,
103
+ parameters: builder => builder.objectParameters(z.object({
104
+ orderBy: z.string().optional(),
105
+ order: z.union([z.enum(["asc", "desc"]), z.boolean()]).optional(),
106
+ search: z.string().optional(),
107
+ filters: filtersSchema,
108
+ limit: z.number().optional(),
109
+ })),
110
+ execute: (service, args) => service.find?.({
111
+ orderBy: args.orderBy ?? "",
112
+ order: args.order ?? false,
113
+ search: args.search ?? "",
114
+ filters: args.filters ?? [],
115
+ limit: args.limit ?? 0,
116
+ }),
117
+ },
118
+ paginate: {
119
+ description: entityName => `Paginar registros de ${entityName}`,
120
+ parameters: builder => builder.objectParameters(z.object({
121
+ page: z.number().optional(),
122
+ limit: z.number().optional(),
123
+ orderBy: z.string().optional(),
124
+ order: z.enum(["asc", "desc"]).optional(),
125
+ search: z.string().optional(),
126
+ filters: filtersSchema,
127
+ })),
128
+ execute: (service, args) => service.paginate({
129
+ page: args.page ?? 1,
130
+ limit: args.limit ?? 10,
131
+ orderBy: args.orderBy,
132
+ order: args.order ?? "asc",
133
+ search: args.search ?? "",
134
+ filters: args.filters ?? [],
135
+ }),
136
+ },
137
+ groupBy: {
138
+ description: entityName => `Agrupar registros de ${entityName} por campos`,
139
+ parameters: builder => builder.objectParameters(z.object({
140
+ fields: z.array(z.string()).optional(),
141
+ filters: filtersSchema,
142
+ dateFormat: z.enum(["year", "month", "day", "hour", "minute", "second"]).optional(),
143
+ })),
144
+ execute: (service, args) => service.groupBy?.({
145
+ fields: args.fields ?? [],
146
+ filters: args.filters ?? [],
147
+ dateFormat: args.dateFormat ?? "day",
148
+ }),
149
+ },
150
+ };
151
+ class ToolBuilder {
152
+ constructor(options) {
153
+ this.options = options;
154
+ this._inputSchema = this.schemaAdapter(options.schema);
155
+ this._partialInputSchema = this._inputSchema.partial();
156
+ this._outputSchema = options.outputSchema ? this.schemaAdapter(options.outputSchema) : undefined;
157
+ }
158
+ get inputSchema() {
159
+ return this._inputSchema;
160
+ }
161
+ get partialInputSchema() {
162
+ return this._partialInputSchema;
163
+ }
164
+ get outputSchema() {
165
+ return this._outputSchema;
166
+ }
167
+ getTools() {
168
+ return this.options.methods.map(method => this.buildTool(method));
169
+ }
170
+ getSystemPromptSection() {
171
+ const entityDescription = this.options.entityDescription
172
+ ? `\n${this.options.entityDescription}`
173
+ : "";
174
+ const tools = this.options.methods
175
+ .map(method => {
176
+ const definition = toolDefinitions[method];
177
+ return `- ${this.getToolName(method)}: ${definition.description(this.options.entityName)}`;
178
+ })
179
+ .join("\n");
180
+ const entitySchema = JSON.stringify(this.toJsonSchema(this._outputSchema ?? this._inputSchema));
181
+ return [
182
+ `[ENTIDAD: ${this.options.entityName}]${entityDescription}`,
183
+ `Schema JSON de la entidad: ${entitySchema}`,
184
+ "Tools disponibles:",
185
+ tools,
186
+ ].join("\n");
187
+ }
188
+ objectParameters(schema) {
189
+ return this.toJsonSchema(this.schemaAdapter(schema));
190
+ }
191
+ buildTool(method) {
192
+ const definition = toolDefinitions[method];
193
+ const serviceMethod = this.options.service[method];
194
+ if (typeof serviceMethod !== "function") {
195
+ throw new Error(`Tool method not available on service: ${method}`);
196
+ }
197
+ return {
198
+ name: this.getToolName(method),
199
+ description: definition.description(this.options.entityName),
200
+ parameters: definition.parameters(this),
201
+ execute: async (args) => definition.execute(this.options.service, args),
202
+ };
203
+ }
204
+ getToolName(method) {
205
+ return `${this.options.toolNamePrefix ?? this.options.entityName}_${method}`;
206
+ }
207
+ toJsonSchema(schema) {
208
+ return z.toJSONSchema(schema, { target: "openAi" });
209
+ }
210
+ getTypeName(field) {
211
+ return field?.constructor?.name;
212
+ }
213
+ fieldAdapter(field) {
214
+ const f = field;
215
+ const typeName = this.getTypeName(f);
216
+ if (typeof f?.unwrap === "function" && typeName === "ZodOptional") {
217
+ return this.fieldAdapter(f.unwrap()).optional();
218
+ }
219
+ if (typeof f?.unwrap === "function" && typeName === "ZodNullable") {
220
+ return this.fieldAdapter(f.unwrap()).nullable();
221
+ }
222
+ if (typeName === "ZodArray" && f?.element) {
223
+ return z.array(this.fieldAdapter(f.element));
224
+ }
225
+ if (typeName === "ZodObject" && f?.shape) {
226
+ return this.schemaAdapter(f);
227
+ }
228
+ if (typeName === "ZodDate") {
229
+ return z.iso.datetime();
230
+ }
231
+ return f;
232
+ }
233
+ schemaAdapter(schema) {
234
+ const shape = schema.shape;
235
+ const newShape = {};
236
+ for (const key of Object.keys(shape)) {
237
+ newShape[key] = this.fieldAdapter(shape[key]);
238
+ }
239
+ return z.object(newShape);
240
+ }
241
+ }
242
+ export default ToolBuilder;
243
+ export { ToolBuilder };