@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,450 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import AiProviderFactory from "../factory/AiProviderFactory.js";
3
+ import AgentSessionServiceFactory from "../factory/services/AgentSessionServiceFactory.js";
4
+ class DraxAgent {
5
+ constructor() {
6
+ this.sessions = new Map();
7
+ this.config = {
8
+ systemPrompt: "Sos un asistente del sistema. Responde de forma clara, breve y util.",
9
+ };
10
+ }
11
+ static instance() {
12
+ if (!DraxAgent.singleton) {
13
+ DraxAgent.singleton = new DraxAgent();
14
+ }
15
+ return DraxAgent.singleton;
16
+ }
17
+ configure(config) {
18
+ this.config = {
19
+ ...this.config,
20
+ ...config,
21
+ };
22
+ return this;
23
+ }
24
+ setProvider(provider) {
25
+ return this.configure({ provider });
26
+ }
27
+ setSystemPrompt(systemPrompt) {
28
+ return this.configure({ systemPrompt });
29
+ }
30
+ setToolBuilders(toolBuilders) {
31
+ return this.configure({ toolBuilders });
32
+ }
33
+ setTools(tools) {
34
+ return this.configure({ tools });
35
+ }
36
+ clearSessions() {
37
+ this.sessions.clear();
38
+ return this;
39
+ }
40
+ async startSession(input = {}) {
41
+ if (input.sessionId) {
42
+ const existingSession = await this.resolveSession(input);
43
+ if (existingSession) {
44
+ return existingSession;
45
+ }
46
+ }
47
+ return this.createSession(input);
48
+ }
49
+ getSession(input) {
50
+ if (!input.sessionId) {
51
+ return null;
52
+ }
53
+ return this.sessions.get(this.getSessionKey(input.userId, input.tenantId, input.sessionId)) ?? null;
54
+ }
55
+ async sendMessage(input) {
56
+ const session = await this.resolveSession(input);
57
+ const context = { session, input };
58
+ const toolBuilders = await this.resolveToolBuilders(context);
59
+ const navigationState = { path: null };
60
+ const tools = this.prepareTools([
61
+ ...toolBuilders.flatMap(builder => builder.getTools()),
62
+ ...await this.resolveTools(context),
63
+ ], session.id, navigationState);
64
+ const systemPrompt = await this.buildSystemPrompt(context, toolBuilders);
65
+ const response = await this.getProvider().prompt({
66
+ systemPrompt,
67
+ userInput: input.message,
68
+ userImages: input.userImages,
69
+ userContent: input.userContent,
70
+ inputFiles: input.inputFiles,
71
+ history: session.messages.slice(-(this.config.historyLimit ?? 20)),
72
+ memory: input.memory,
73
+ memoryHeader: input.memoryHeader,
74
+ knowledgeBase: input.knowledgeBase,
75
+ knowledgeBaseHeader: input.knowledgeBaseHeader,
76
+ tools,
77
+ toolMaxIterations: input.toolMaxIterations ?? this.config.toolMaxIterations,
78
+ model: input.model,
79
+ operationTitle: input.operationTitle ?? this.config.operationTitle ?? "drax-agent-message",
80
+ operationGroup: input.operationGroup ?? this.config.operationGroup ?? "drax-agent",
81
+ ip: input.ip,
82
+ userAgent: input.userAgent,
83
+ tenant: input.tenantId ?? session.tenantId ?? null,
84
+ user: input.userId ?? session.userId ?? null,
85
+ });
86
+ const assistantMessage = this.normalizeOutput(response.output);
87
+ const now = new Date();
88
+ session.messages.push({ role: "user", content: input.message });
89
+ session.messages.push({ role: "assistant", content: assistantMessage });
90
+ session.updatedAt = new Date();
91
+ await this.persistSessionUpdate(session, {
92
+ lastMessageAt: now,
93
+ tokens: response.tokens,
94
+ inputTokens: response.inputTokens,
95
+ outputTokens: response.outputTokens,
96
+ });
97
+ return {
98
+ sessionId: session.id,
99
+ message: assistantMessage,
100
+ navigationPath: navigationState.path,
101
+ output: response.output,
102
+ tokens: response.tokens,
103
+ inputTokens: response.inputTokens,
104
+ outputTokens: response.outputTokens,
105
+ time: response.time,
106
+ };
107
+ }
108
+ async createSession(input = {}) {
109
+ const session = {
110
+ id: input.sessionId ?? randomUUID(),
111
+ userId: input.userId ?? null,
112
+ tenantId: input.tenantId ?? null,
113
+ messages: [],
114
+ createdAt: new Date(),
115
+ updatedAt: new Date(),
116
+ };
117
+ this.sessions.set(this.getSessionKey(session.userId, session.tenantId, session.id), session);
118
+ await this.persistSessionCreate(session);
119
+ return session;
120
+ }
121
+ async resolveSession(input) {
122
+ if (!input.sessionId) {
123
+ return this.startSession(input);
124
+ }
125
+ const existingSession = this.getSession(input);
126
+ if (existingSession) {
127
+ return existingSession;
128
+ }
129
+ const persistedSession = await this.findPersistedSession(input);
130
+ if (persistedSession) {
131
+ const session = this.hydrateSession(persistedSession, input);
132
+ this.sessions.set(this.getSessionKey(session.userId, session.tenantId, session.id), session);
133
+ return session;
134
+ }
135
+ return this.createSession(input);
136
+ }
137
+ async persistSessionCreate(session) {
138
+ const sessionService = this.getSessionService();
139
+ if (!sessionService) {
140
+ return;
141
+ }
142
+ const record = await sessionService.create(this.buildSessionPayload(session, {
143
+ messageCount: 0,
144
+ inputTokens: 0,
145
+ outputTokens: 0,
146
+ tokens: 0,
147
+ }));
148
+ session.recordId = this.stringifyRecordId(record);
149
+ }
150
+ async persistSessionUpdate(session, usage) {
151
+ const sessionService = this.getSessionService();
152
+ if (!sessionService) {
153
+ return;
154
+ }
155
+ if (!session.recordId) {
156
+ const record = await this.findPersistedSession({
157
+ sessionId: session.id,
158
+ userId: session.userId,
159
+ tenantId: session.tenantId,
160
+ });
161
+ session.recordId = this.stringifyRecordId(record);
162
+ }
163
+ if (!session.recordId) {
164
+ await this.persistSessionCreate(session);
165
+ }
166
+ if (!session.recordId) {
167
+ return;
168
+ }
169
+ const storedUsage = await this.resolveStoredUsage(session.recordId);
170
+ await sessionService.updatePartial(session.recordId, this.buildSessionPayload(session, {
171
+ lastMessageAt: usage.lastMessageAt,
172
+ messageCount: session.messages.length,
173
+ inputTokens: storedUsage.inputTokens + (usage.inputTokens ?? 0),
174
+ outputTokens: storedUsage.outputTokens + (usage.outputTokens ?? 0),
175
+ tokens: storedUsage.tokens + (usage.tokens ?? 0),
176
+ }));
177
+ }
178
+ buildSessionPayload(session, overrides = {}) {
179
+ return {
180
+ sessionId: session.id,
181
+ title: this.resolveSessionTitle(session.messages),
182
+ lastMessageAt: session.messages.length > 0 ? session.updatedAt : null,
183
+ messages: session.messages.map(message => ({
184
+ role: message.role,
185
+ content: this.stringifyMessageContent(message.content),
186
+ createdAt: session.updatedAt,
187
+ })),
188
+ messageCount: session.messages.length,
189
+ inputTokens: 0,
190
+ outputTokens: 0,
191
+ tokens: 0,
192
+ tenant: session.tenantId ?? null,
193
+ user: session.userId ?? null,
194
+ ...overrides,
195
+ };
196
+ }
197
+ async findPersistedSession(input) {
198
+ if (!input.sessionId) {
199
+ return null;
200
+ }
201
+ const sessionService = this.getSessionService();
202
+ if (!sessionService) {
203
+ return null;
204
+ }
205
+ const filters = [
206
+ ...(input.tenantId ? [{ field: "tenant", operator: "eq", value: input.tenantId }] : []),
207
+ ...(input.userId ? [{ field: "user", operator: "eq", value: input.userId }] : []),
208
+ ];
209
+ const sessions = await sessionService.findBy("sessionId", input.sessionId, 1, filters);
210
+ return sessions?.[0] ?? null;
211
+ }
212
+ hydrateSession(record, input) {
213
+ const session = {
214
+ id: record.sessionId,
215
+ recordId: this.stringifyRecordId(record),
216
+ userId: input.userId ?? this.stringifyRelationId(record.user) ?? null,
217
+ tenantId: input.tenantId ?? this.stringifyRelationId(record.tenant) ?? null,
218
+ messages: this.normalizeStoredMessages(record.messages ?? []),
219
+ createdAt: record.createdAt ? new Date(record.createdAt) : new Date(),
220
+ updatedAt: record.updatedAt ? new Date(record.updatedAt) : new Date(),
221
+ };
222
+ return session;
223
+ }
224
+ normalizeStoredMessages(messages) {
225
+ return (messages ?? [])
226
+ .filter(message => message.role === "user" || message.role === "assistant" || message.role === "system")
227
+ .map(message => ({
228
+ role: message.role,
229
+ content: message.content,
230
+ }));
231
+ }
232
+ async resolveStoredUsage(recordId) {
233
+ const sessionService = this.getSessionService();
234
+ if (!sessionService) {
235
+ return { inputTokens: 0, outputTokens: 0, tokens: 0 };
236
+ }
237
+ const record = await sessionService.findById(recordId);
238
+ return {
239
+ inputTokens: this.normalizeNumber(record?.inputTokens),
240
+ outputTokens: this.normalizeNumber(record?.outputTokens),
241
+ tokens: this.normalizeNumber(record?.tokens),
242
+ };
243
+ }
244
+ normalizeNumber(value) {
245
+ return typeof value === "number" && Number.isFinite(value) ? value : 0;
246
+ }
247
+ resolveSessionTitle(messages) {
248
+ const firstUserMessage = messages.find(message => message.role === "user");
249
+ if (!firstUserMessage) {
250
+ return undefined;
251
+ }
252
+ const title = this.stringifyMessageContent(firstUserMessage.content).trim();
253
+ return title.length > 80 ? `${title.slice(0, 77)}...` : title;
254
+ }
255
+ stringifyMessageContent(content) {
256
+ return typeof content === "string" ? content : JSON.stringify(content);
257
+ }
258
+ stringifyRecordId(record) {
259
+ return this.stringifyNavigationId(record?._id ?? record?.id);
260
+ }
261
+ stringifyRelationId(value) {
262
+ return this.stringifyNavigationId(value?._id ?? value?.id ?? value);
263
+ }
264
+ getSessionService() {
265
+ if (this.config.sessionService === false) {
266
+ return null;
267
+ }
268
+ return this.config.sessionService ?? AgentSessionServiceFactory.instance;
269
+ }
270
+ getSessionKey(userId, tenantId, sessionId) {
271
+ return `${tenantId ?? "global"}:${userId ?? "anonymous"}:${sessionId}`;
272
+ }
273
+ async buildSystemPrompt(context, toolBuilders) {
274
+ const systemPrompt = this.config.systemPrompt ?? "";
275
+ const basePrompt = typeof systemPrompt === "function"
276
+ ? await systemPrompt(context)
277
+ : systemPrompt;
278
+ const rbacSection = this.buildRbacContextSection(context);
279
+ const toolSections = toolBuilders
280
+ .map(builder => builder.getSystemPromptSection())
281
+ .filter(section => section.trim().length > 0);
282
+ return [
283
+ basePrompt,
284
+ rbacSection,
285
+ toolSections.length > 0
286
+ ? [
287
+ "[ENTIDADES Y TOOLS]",
288
+ ...toolSections,
289
+ ].join("\n")
290
+ : "",
291
+ ].filter(section => section.trim().length > 0).join("\n\n");
292
+ }
293
+ buildRbacContextSection(context) {
294
+ const userId = context.input?.userId ?? context.session.userId ?? null;
295
+ const tenantId = context.input?.tenantId ?? context.session.tenantId ?? null;
296
+ if (!userId && !tenantId) {
297
+ return "";
298
+ }
299
+ const lines = [
300
+ "[CONTEXTO RBAC]",
301
+ `tenantId: ${tenantId ?? "null"}`,
302
+ `userId: ${userId ?? "null"}`,
303
+ "Cuando necesites completar campos de pertenencia o auditoria como tenant, tenantId, user, userId o createdBy, usa estos valores del usuario autenticado. No los solicites al usuario ni inventes otros valores.",
304
+ ];
305
+ return lines.join("\n");
306
+ }
307
+ async resolveToolBuilders(context) {
308
+ if (!this.config.toolBuilders) {
309
+ return [];
310
+ }
311
+ return typeof this.config.toolBuilders === "function"
312
+ ? await this.config.toolBuilders(context)
313
+ : this.config.toolBuilders;
314
+ }
315
+ async resolveTools(context) {
316
+ if (!this.config.tools) {
317
+ return [];
318
+ }
319
+ return typeof this.config.tools === "function"
320
+ ? await this.config.tools(context)
321
+ : this.config.tools;
322
+ }
323
+ prepareTools(tools, sessionId, navigationState) {
324
+ return tools.map(tool => ({
325
+ ...tool,
326
+ execute: async (args) => {
327
+ if (this.config.logToolExecution) {
328
+ console.log("[drax-agent] tool:start", {
329
+ sessionId,
330
+ tool: tool.name,
331
+ args,
332
+ });
333
+ }
334
+ try {
335
+ const result = await tool.execute(args);
336
+ navigationState.path = this.resolveNavigationPath(tool.navigation, args, result) ?? navigationState.path;
337
+ if (this.config.logToolExecution) {
338
+ console.log("[drax-agent] tool:success", {
339
+ sessionId,
340
+ tool: tool.name,
341
+ navigationPath: navigationState.path,
342
+ });
343
+ }
344
+ return result;
345
+ }
346
+ catch (error) {
347
+ if (this.config.logToolExecution) {
348
+ console.error("[drax-agent] tool:error", {
349
+ sessionId,
350
+ tool: tool.name,
351
+ error,
352
+ });
353
+ }
354
+ throw error;
355
+ }
356
+ },
357
+ }));
358
+ }
359
+ resolveNavigationPath(navigation, args, result) {
360
+ if (!navigation) {
361
+ return null;
362
+ }
363
+ const mode = this.resolveNavigationMode(navigation.method);
364
+ if (!mode) {
365
+ return null;
366
+ }
367
+ const id = this.resolveNavigationId(navigation.method, args, result);
368
+ if (!id) {
369
+ return null;
370
+ }
371
+ const basePath = navigation.basePath ?? `/crud/${encodeURIComponent(navigation.entityName)}`;
372
+ const query = new URLSearchParams({
373
+ mode,
374
+ id,
375
+ });
376
+ return `${basePath}?${query.toString()}`;
377
+ }
378
+ resolveNavigationMode(method) {
379
+ if (["create", "update", "updatePartial"].includes(method)) {
380
+ return "edit";
381
+ }
382
+ if (["findById", "findOneBy", "findOne", "findBy", "search", "find", "paginate"].includes(method)) {
383
+ return "view";
384
+ }
385
+ return null;
386
+ }
387
+ resolveNavigationId(method, args, result) {
388
+ if (["update", "updatePartial", "findById"].includes(method)) {
389
+ return this.stringifyNavigationId(args?.id) ?? this.extractRecordId(result);
390
+ }
391
+ return this.extractRecordId(result);
392
+ }
393
+ extractRecordId(result) {
394
+ const record = this.resolveSingleRecord(result);
395
+ if (!record || typeof record !== "object") {
396
+ return null;
397
+ }
398
+ return this.stringifyNavigationId(record.id)
399
+ ?? this.stringifyNavigationId(record._id)
400
+ ?? this.stringifyNavigationId(record.uuid);
401
+ }
402
+ resolveSingleRecord(result) {
403
+ if (Array.isArray(result)) {
404
+ return result.length === 1 ? result[0] : null;
405
+ }
406
+ if (Array.isArray(result?.docs)) {
407
+ return result.docs.length === 1 ? result.docs[0] : null;
408
+ }
409
+ if (Array.isArray(result?.items)) {
410
+ return result.items.length === 1 ? result.items[0] : null;
411
+ }
412
+ if (Array.isArray(result?.data)) {
413
+ return result.data.length === 1 ? result.data[0] : null;
414
+ }
415
+ return result;
416
+ }
417
+ stringifyNavigationId(id) {
418
+ if (id === null || id === undefined || id === "") {
419
+ return null;
420
+ }
421
+ if (typeof id === "string" || typeof id === "number" || typeof id === "boolean") {
422
+ return String(id);
423
+ }
424
+ if (typeof id?.toString === "function") {
425
+ const value = id.toString();
426
+ return value && value !== "[object Object]" ? value : null;
427
+ }
428
+ return null;
429
+ }
430
+ normalizeOutput(output) {
431
+ if (this.config.normalizeOutput) {
432
+ return this.config.normalizeOutput(output);
433
+ }
434
+ if (typeof output === "string") {
435
+ return output;
436
+ }
437
+ if (output?.message && typeof output.message === "string") {
438
+ return output.message;
439
+ }
440
+ return JSON.stringify(output);
441
+ }
442
+ getProvider() {
443
+ if (!this.config.provider) {
444
+ this.config.provider = AiProviderFactory.instance();
445
+ }
446
+ return this.config.provider;
447
+ }
448
+ }
449
+ export default DraxAgent;
450
+ export { DraxAgent };
@@ -0,0 +1,18 @@
1
+ import AgentSessionServiceFactory from "../factory/services/AgentSessionServiceFactory.js";
2
+ import { AbstractFastifyController } from "@drax/crud-back";
3
+ import AgentSessionPermissions from "../permissions/AgentSessionPermissions.js";
4
+ class AgentSessionController extends AbstractFastifyController {
5
+ constructor() {
6
+ super(AgentSessionServiceFactory.instance, AgentSessionPermissions);
7
+ this.tenantField = "tenant";
8
+ this.userField = "user";
9
+ this.tenantFilter = true;
10
+ this.tenantSetter = true;
11
+ this.tenantAssert = true;
12
+ this.userFilter = true;
13
+ this.userSetter = true;
14
+ this.userAssert = true;
15
+ }
16
+ }
17
+ export default AgentSessionController;
18
+ export { AgentSessionController };
@@ -0,0 +1,114 @@
1
+ import { z } from "zod";
2
+ import { CommonController } from "@drax/common-back";
3
+ import { DraxAgent } from "../agents/DraxAgent.js";
4
+ import { AgentPermissions } from "../permissions/AgentPermissions.js";
5
+ const PromptImageSchema = z.object({
6
+ url: z.string().min(1),
7
+ detail: z.enum(["auto", "low", "high"]).optional(),
8
+ });
9
+ const PromptContentPartSchema = z.discriminatedUnion("type", [
10
+ z.object({
11
+ type: z.literal("text"),
12
+ text: z.string(),
13
+ }),
14
+ z.object({
15
+ type: z.literal("image"),
16
+ imageUrl: z.string().min(1),
17
+ detail: z.enum(["auto", "low", "high"]).optional(),
18
+ }),
19
+ ]);
20
+ const PromptMemorySchema = z.object({
21
+ key: z.string().min(1),
22
+ value: z.string(),
23
+ });
24
+ const PromptInputFileSchema = z.object({
25
+ filename: z.string().optional(),
26
+ filepath: z.string().optional(),
27
+ size: z.number().nullable().optional(),
28
+ mimetype: z.string().optional(),
29
+ url: z.string().optional(),
30
+ });
31
+ const AgentSessionRequestSchema = z.object({
32
+ sessionId: z.string().optional(),
33
+ userId: z.string().optional().nullable(),
34
+ tenantId: z.string().optional().nullable(),
35
+ });
36
+ const AgentMessageRequestSchema = AgentSessionRequestSchema.extend({
37
+ message: z.string().min(1),
38
+ userImages: z.array(PromptImageSchema).optional(),
39
+ userContent: z.array(PromptContentPartSchema).optional(),
40
+ inputFiles: z.array(PromptInputFileSchema).optional(),
41
+ memory: z.array(PromptMemorySchema).optional(),
42
+ memoryHeader: z.string().optional(),
43
+ knowledgeBase: z.array(z.string()).optional(),
44
+ knowledgeBaseHeader: z.string().optional(),
45
+ model: z.string().optional(),
46
+ toolMaxIterations: z.number().optional(),
47
+ operationTitle: z.string().optional(),
48
+ operationGroup: z.string().optional(),
49
+ });
50
+ class DraxAgentController extends CommonController {
51
+ constructor() {
52
+ super();
53
+ this.permission = AgentPermissions.Session;
54
+ this.agent = DraxAgent.instance();
55
+ }
56
+ async startSession(request, reply) {
57
+ try {
58
+ this.assertAccess(request);
59
+ const input = AgentSessionRequestSchema.parse(request.body ?? {});
60
+ const session = await this.agent.startSession({
61
+ sessionId: input.sessionId,
62
+ userId: this.resolveUserId(request, input.userId),
63
+ tenantId: this.resolveTenantId(request, input.tenantId),
64
+ });
65
+ return reply.send({
66
+ sessionId: session.id,
67
+ createdAt: session.createdAt,
68
+ updatedAt: session.updatedAt,
69
+ });
70
+ }
71
+ catch (e) {
72
+ this.handleControllerError(e, reply);
73
+ }
74
+ }
75
+ async message(request, reply) {
76
+ try {
77
+ this.assertAccess(request);
78
+ const input = AgentMessageRequestSchema.parse(request.body ?? {});
79
+ const response = await this.agent.sendMessage({
80
+ ...input,
81
+ userId: this.resolveUserId(request, input.userId),
82
+ tenantId: this.resolveTenantId(request, input.tenantId),
83
+ ip: request.ip,
84
+ userAgent: request.headers["user-agent"],
85
+ });
86
+ return reply.send(response);
87
+ }
88
+ catch (e) {
89
+ this.handleControllerError(e, reply);
90
+ }
91
+ }
92
+ assertAccess(request) {
93
+ if (this.permission === false) {
94
+ return;
95
+ }
96
+ request.rbac.assertPermission(this.permission);
97
+ }
98
+ resolveUserId(request, userId) {
99
+ return request.rbac?.userId ?? userId ?? null;
100
+ }
101
+ resolveTenantId(request, tenantId) {
102
+ return request.rbac?.tenantId ?? tenantId ?? null;
103
+ }
104
+ handleControllerError(e, reply) {
105
+ if (e?.name === "ZodError") {
106
+ return reply.status(400).send({
107
+ message: e?.message || "Drax agent validation error",
108
+ });
109
+ }
110
+ this.handleError(e, reply);
111
+ }
112
+ }
113
+ export default DraxAgentController;
114
+ export { DraxAgentController };
@@ -0,0 +1,30 @@
1
+ import AgentSessionMongoRepository from '../../repository/mongo/AgentSessionMongoRepository.js';
2
+ import AgentSessionSqliteRepository from '../../repository/sqlite/AgentSessionSqliteRepository.js';
3
+ import { AgentSessionService } from '../../services/AgentSessionService.js';
4
+ import { AgentSessionBaseSchema, AgentSessionSchema } from "../../schemas/AgentSessionSchema.js";
5
+ import { COMMON, CommonConfig, DraxConfig } from "@drax/common-back";
6
+ class AgentSessionServiceFactory {
7
+ static get instance() {
8
+ if (!AgentSessionServiceFactory.service) {
9
+ let repository;
10
+ switch (DraxConfig.getOrLoad(CommonConfig.DbEngine)) {
11
+ case COMMON.DB_ENGINES.MONGODB:
12
+ repository = new AgentSessionMongoRepository();
13
+ break;
14
+ case COMMON.DB_ENGINES.SQLITE:
15
+ const dbFile = DraxConfig.getOrLoad(CommonConfig.SqliteDbFile);
16
+ repository = new AgentSessionSqliteRepository(dbFile, false);
17
+ repository.build();
18
+ break;
19
+ default:
20
+ throw new Error("DraxConfig.DB_ENGINE must be one of " + Object.values(COMMON.DB_ENGINES).join(", "));
21
+ }
22
+ const baseSchema = AgentSessionBaseSchema;
23
+ const fullSchema = AgentSessionSchema;
24
+ AgentSessionServiceFactory.service = new AgentSessionService(repository, baseSchema, fullSchema);
25
+ }
26
+ return AgentSessionServiceFactory.service;
27
+ }
28
+ }
29
+ export default AgentSessionServiceFactory;
30
+ export { AgentSessionServiceFactory };
package/dist/index.js CHANGED
@@ -7,13 +7,27 @@ import { OpenAiProviderFactory } from "./factory/OpenAiProviderFactory.js";
7
7
  import { AiProviderFactory } from "./factory/AiProviderFactory.js";
8
8
  import AILogServiceFactory from "./factory/services/AILogServiceFactory.js";
9
9
  import { OpenAiProvider } from "./providers/OpenAiProvider.js";
10
+ import { BuilderTool } from "./tools/BuilderTool.js";
10
11
  import { KnowledgeService } from "./services/KnowledgeService.js";
11
12
  import { AILogService } from "./services/AILogService.js";
12
13
  import AILogPermissions from "./permissions/AILogPermissions.js";
14
+ import AgentPermissions from "./permissions/AgentPermissions.js";
15
+ import AgentSessionPermissions from "./permissions/AgentSessionPermissions.js";
13
16
  import AIPermissions from "./permissions/AIPermissions.js";
14
17
  import AILogController from "./controllers/AILogController.js";
15
18
  import AICrudController from "./controllers/AICrudController.js";
16
19
  import AIGenericController from "./controllers/AIGenericController.js";
20
+ import DraxAgentController from "./controllers/DraxAgentController.js";
21
+ import AgentSessionController from "./controllers/AgentSessionController.js";
17
22
  import AILogRoutes from "./routes/AILogRoutes.js";
18
23
  import AIRoutes from "./routes/AIRoutes.js";
19
- export { OpenAiConfig, AILogSchema, AILogBaseSchema, AILogModel, AILogMongoRepository, AILogSqliteRepository, OpenAiProviderFactory, AiProviderFactory, AILogServiceFactory, OpenAiProvider, KnowledgeService, AILogService, AILogPermissions, AIPermissions, AILogController, AICrudController, AIGenericController, AILogRoutes, AIRoutes };
24
+ import DraxAgentRoutes from "./routes/DraxAgentRoutes.js";
25
+ import AgentSessionRoutes from "./routes/AgentSessionRoutes.js";
26
+ import { DraxAgent } from "./agents/DraxAgent.js";
27
+ export { OpenAiConfig, AILogSchema, AILogBaseSchema, AILogModel, AILogMongoRepository, AILogSqliteRepository, OpenAiProviderFactory, AiProviderFactory, AILogServiceFactory, OpenAiProvider, BuilderTool,
28
+ //Service
29
+ KnowledgeService, AILogService,
30
+ //Permissions
31
+ AILogPermissions, AgentPermissions, AIPermissions, AgentSessionPermissions,
32
+ //Controllers
33
+ AILogController, AICrudController, AIGenericController, DraxAgentController, AgentSessionController, DraxAgent, AILogRoutes, AIRoutes, DraxAgentRoutes, AgentSessionRoutes };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};