@drax/ai-back 3.35.1 → 3.37.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.
- package/.env +4 -0
- package/dist/agents/ChatbotTaskService.js +143 -0
- package/dist/agents/ChatbotTaskTools.js +756 -0
- package/dist/agents/DraxAgent.js +4 -7
- package/dist/controllers/AIController.js +150 -0
- package/dist/controllers/DraxAgentController.js +29 -6
- package/dist/factory/DraxAgentFactory.js +15 -0
- package/dist/index.js +2 -1
- package/dist/interfaces/IAILog.js +1 -0
- package/dist/routes/ChatbotTaskRoutes.js +8 -0
- package/dist/routes/DraxAgentRoutes.js +2 -1
- package/dist/tools/ToolBuilder.js +243 -0
- package/dist/vectors/ChromaVector.js +65 -0
- package/package.json +3 -3
- package/src/agents/DraxAgent.ts +5 -11
- package/src/controllers/DraxAgentController.ts +34 -6
- package/src/factory/DraxAgentFactory.ts +22 -0
- package/src/index.ts +2 -0
- package/src/interfaces/IDraxAgent.ts +2 -0
- package/src/interfaces/IDraxAgentController.ts +2 -0
- package/src/routes/DraxAgentRoutes.ts +2 -1
- package/test/DraxAgent.test.ts +26 -5
- package/tsconfig.tsbuildinfo +1 -1
- package/types/agents/ChatbotTaskService.d.ts +42 -0
- package/types/agents/ChatbotTaskService.d.ts.map +1 -0
- package/types/agents/ChatbotTaskTools.d.ts +54 -0
- package/types/agents/ChatbotTaskTools.d.ts.map +1 -0
- package/types/agents/DraxAgent.d.ts +3 -3
- package/types/agents/DraxAgent.d.ts.map +1 -1
- package/types/controllers/AIController.d.ts +25 -0
- package/types/controllers/AIController.d.ts.map +1 -0
- package/types/controllers/DraxAgentController.d.ts +6 -2
- package/types/controllers/DraxAgentController.d.ts.map +1 -1
- package/types/factory/DraxAgentFactory.d.ts +9 -0
- package/types/factory/DraxAgentFactory.d.ts.map +1 -0
- package/types/index.d.ts +3 -2
- package/types/index.d.ts.map +1 -1
- package/types/interfaces/IAILog.d.ts +77 -0
- package/types/interfaces/IAILog.d.ts.map +1 -0
- package/types/interfaces/IDraxAgent.d.ts +2 -0
- package/types/interfaces/IDraxAgent.d.ts.map +1 -1
- package/types/interfaces/IDraxAgentController.d.ts +2 -0
- package/types/interfaces/IDraxAgentController.d.ts.map +1 -1
- package/types/routes/ChatbotTaskRoutes.d.ts +4 -0
- package/types/routes/ChatbotTaskRoutes.d.ts.map +1 -0
- package/types/routes/DraxAgentRoutes.d.ts.map +1 -1
- package/types/tools/ToolBuilder.d.ts +47 -0
- package/types/tools/ToolBuilder.d.ts.map +1 -0
- package/types/vectors/ChromaVector.d.ts +21 -0
- package/types/vectors/ChromaVector.d.ts.map +1 -0
package/dist/agents/DraxAgent.js
CHANGED
|
@@ -2,18 +2,14 @@ import { randomUUID } from "node:crypto";
|
|
|
2
2
|
import AiProviderFactory from "../factory/AiProviderFactory.js";
|
|
3
3
|
import AgentSessionServiceFactory from "../factory/services/AgentSessionServiceFactory.js";
|
|
4
4
|
class DraxAgent {
|
|
5
|
-
constructor() {
|
|
5
|
+
constructor(identifier = "default", description = "") {
|
|
6
|
+
this.identifier = identifier;
|
|
7
|
+
this.description = description;
|
|
6
8
|
this.sessions = new Map();
|
|
7
9
|
this.config = {
|
|
8
10
|
systemPrompt: "Sos un asistente del sistema. Responde de forma clara, breve y util.",
|
|
9
11
|
};
|
|
10
12
|
}
|
|
11
|
-
static instance() {
|
|
12
|
-
if (!DraxAgent.singleton) {
|
|
13
|
-
DraxAgent.singleton = new DraxAgent();
|
|
14
|
-
}
|
|
15
|
-
return DraxAgent.singleton;
|
|
16
|
-
}
|
|
17
13
|
configure(config) {
|
|
18
14
|
this.config = {
|
|
19
15
|
...this.config,
|
|
@@ -95,6 +91,7 @@ class DraxAgent {
|
|
|
95
91
|
outputTokens: response.outputTokens,
|
|
96
92
|
});
|
|
97
93
|
return {
|
|
94
|
+
agentIdentifier: this.identifier,
|
|
98
95
|
sessionId: session.id,
|
|
99
96
|
message: assistantMessage,
|
|
100
97
|
navigationPath: navigationState.path,
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { CommonController } from "@drax/common-back";
|
|
3
|
+
import AiProviderFactory from "../factory/AiProviderFactory.js";
|
|
4
|
+
import AIPermissions from "../permissions/AIPermissions.js";
|
|
5
|
+
const CrudAiFieldSchema = z.lazy(() => z.object({
|
|
6
|
+
name: z.string(),
|
|
7
|
+
type: z.string(),
|
|
8
|
+
label: z.string(),
|
|
9
|
+
hint: z.string().nullable().default(null),
|
|
10
|
+
placeholder: z.string().nullable().default(null),
|
|
11
|
+
readonly: z.boolean().nullable().default(null),
|
|
12
|
+
default: z.any().nullable().default(null),
|
|
13
|
+
enum: z.array(z.string()).nullable().default(null),
|
|
14
|
+
items: z.array(z.object({
|
|
15
|
+
title: z.string().nullable().default(null),
|
|
16
|
+
value: z.any().nullable().default(null),
|
|
17
|
+
})).nullable().default(null),
|
|
18
|
+
ref: z.string().nullable().default(null),
|
|
19
|
+
refDisplay: z.string().nullable().default(null),
|
|
20
|
+
objectFields: z.array(CrudAiFieldSchema).nullable().default(null),
|
|
21
|
+
}));
|
|
22
|
+
const PromptRequestSchema = z.object({
|
|
23
|
+
prompt: z.string().min(1),
|
|
24
|
+
operation: z.enum(["create", "edit"]).default("create"),
|
|
25
|
+
entity: z.object({
|
|
26
|
+
name: z.string(),
|
|
27
|
+
identifier: z.string().optional().nullable(),
|
|
28
|
+
}),
|
|
29
|
+
currentValues: z.record(z.string(), z.any()).default({}),
|
|
30
|
+
fields: z.array(CrudAiFieldSchema).min(1),
|
|
31
|
+
});
|
|
32
|
+
class AIController extends CommonController {
|
|
33
|
+
buildFieldValueSchema(field) {
|
|
34
|
+
switch (field.type) {
|
|
35
|
+
case "number":
|
|
36
|
+
return z.number().nullable().default(null);
|
|
37
|
+
case "boolean":
|
|
38
|
+
return z.boolean().nullable().default(null);
|
|
39
|
+
case "array.string":
|
|
40
|
+
case "array.enum":
|
|
41
|
+
case "array.ref":
|
|
42
|
+
return z.array(z.string()).nullable().default(null);
|
|
43
|
+
case "array.number":
|
|
44
|
+
return z.array(z.number()).nullable().default(null);
|
|
45
|
+
case "object":
|
|
46
|
+
if (field.objectFields && Array.isArray(field.objectFields) && field.objectFields.length > 0) {
|
|
47
|
+
return z.object(this.buildFieldShape(field.objectFields)).nullable().default(null);
|
|
48
|
+
}
|
|
49
|
+
return z.string().nullable().default(null);
|
|
50
|
+
case "record":
|
|
51
|
+
case "array.object":
|
|
52
|
+
case "array.record":
|
|
53
|
+
case "array.fullFile":
|
|
54
|
+
case "file":
|
|
55
|
+
case "fullFile":
|
|
56
|
+
return z.string().nullable().default(null);
|
|
57
|
+
case "id":
|
|
58
|
+
case "string":
|
|
59
|
+
case "longString":
|
|
60
|
+
case "date":
|
|
61
|
+
case "ref":
|
|
62
|
+
case "enum":
|
|
63
|
+
case "select":
|
|
64
|
+
case "password":
|
|
65
|
+
default:
|
|
66
|
+
return z.string().nullable().default(null);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
buildFieldShape(fields) {
|
|
70
|
+
return fields.reduce((acc, field) => {
|
|
71
|
+
acc[field.name] = this.buildFieldValueSchema(field);
|
|
72
|
+
return acc;
|
|
73
|
+
}, {});
|
|
74
|
+
}
|
|
75
|
+
buildSystemPrompt(input) {
|
|
76
|
+
return [
|
|
77
|
+
"Sos un asistente de formularios para un sistema CRUD.",
|
|
78
|
+
"Tu tarea es proponer valores JSON para completar o editar una entidad.",
|
|
79
|
+
"Debes respetar exactamente los nombres de campo entregados.",
|
|
80
|
+
"No inventes campos adicionales.",
|
|
81
|
+
"Si un campo no tiene una propuesta razonable, omitilo.",
|
|
82
|
+
"Usa tipos compatibles con JSON.",
|
|
83
|
+
"Para campos enum o select, elegí solamente valores válidos de la lista provista.",
|
|
84
|
+
"Para campos record, object sin estructura fija, array.object o array.record, devolve un string JSON serializado valido.",
|
|
85
|
+
"La respuesta debe describir sugerencias concretas, breves y aplicables.",
|
|
86
|
+
`Operacion actual: ${input.operation}.`,
|
|
87
|
+
`Entidad actual: ${input.entity.name}.`,
|
|
88
|
+
].join("\n");
|
|
89
|
+
}
|
|
90
|
+
buildUserInput(input) {
|
|
91
|
+
return JSON.stringify({
|
|
92
|
+
task: input.prompt,
|
|
93
|
+
operation: input.operation,
|
|
94
|
+
entity: input.entity,
|
|
95
|
+
currentValues: input.currentValues,
|
|
96
|
+
fields: input.fields,
|
|
97
|
+
expectedResponse: {
|
|
98
|
+
message: "string",
|
|
99
|
+
suggestions: "object with proposed values indexed by field name",
|
|
100
|
+
warnings: ["optional string warnings"],
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
async prompt(req, rep) {
|
|
105
|
+
try {
|
|
106
|
+
req.rbac.assertPermission(AIPermissions.PromptCrud);
|
|
107
|
+
const input = PromptRequestSchema.parse(req.body ?? {});
|
|
108
|
+
const aiProvider = AiProviderFactory.instance();
|
|
109
|
+
const responseSchema = z.object({
|
|
110
|
+
message: z.string().nullable().default(null),
|
|
111
|
+
suggestions: z.object(this.buildFieldShape(input.fields)).strict().default({}),
|
|
112
|
+
warnings: z.array(z.string()).default([]),
|
|
113
|
+
});
|
|
114
|
+
const response = await aiProvider.prompt({
|
|
115
|
+
systemPrompt: this.buildSystemPrompt(input),
|
|
116
|
+
userInput: this.buildUserInput(input),
|
|
117
|
+
zodSchema: responseSchema,
|
|
118
|
+
operationTitle: `crud-${input.operation}-assistant`,
|
|
119
|
+
operationGroup: "crud-form-assistant",
|
|
120
|
+
ip: req.ip,
|
|
121
|
+
userAgent: req.headers["user-agent"],
|
|
122
|
+
tenant: req.rbac?.tenantId ?? null,
|
|
123
|
+
user: req.rbac?.userId ?? null,
|
|
124
|
+
});
|
|
125
|
+
const parsedOutput = responseSchema.parse(typeof response.output === "string"
|
|
126
|
+
? JSON.parse(response.output)
|
|
127
|
+
: response.output);
|
|
128
|
+
return rep.send({
|
|
129
|
+
message: parsedOutput.message,
|
|
130
|
+
suggestions: parsedOutput.suggestions,
|
|
131
|
+
warnings: parsedOutput.warnings,
|
|
132
|
+
meta: {
|
|
133
|
+
tokens: response.tokens,
|
|
134
|
+
inputTokens: response.inputTokens,
|
|
135
|
+
outputTokens: response.outputTokens,
|
|
136
|
+
time: response.time,
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
catch (e) {
|
|
141
|
+
console.error("AIController.prompt error", e);
|
|
142
|
+
const statusCode = e?.name === "ZodError" ? 400 : 500;
|
|
143
|
+
return rep.status(statusCode).send({
|
|
144
|
+
message: e?.message || "AI prompt error",
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
export default AIController;
|
|
150
|
+
export { AIController };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { CommonController } from "@drax/common-back";
|
|
3
|
-
import
|
|
3
|
+
import DraxAgentFactory from "../factory/DraxAgentFactory.js";
|
|
4
4
|
import { AgentPermissions } from "../permissions/AgentPermissions.js";
|
|
5
5
|
const PromptImageSchema = z.object({
|
|
6
6
|
url: z.string().min(1),
|
|
@@ -29,6 +29,7 @@ const PromptInputFileSchema = z.object({
|
|
|
29
29
|
url: z.string().optional(),
|
|
30
30
|
});
|
|
31
31
|
const AgentSessionRequestSchema = z.object({
|
|
32
|
+
identifier: z.string().min(1).optional(),
|
|
32
33
|
sessionId: z.string().optional(),
|
|
33
34
|
userId: z.string().optional().nullable(),
|
|
34
35
|
tenantId: z.string().optional().nullable(),
|
|
@@ -48,21 +49,39 @@ const AgentMessageRequestSchema = AgentSessionRequestSchema.extend({
|
|
|
48
49
|
operationGroup: z.string().optional(),
|
|
49
50
|
});
|
|
50
51
|
class DraxAgentController extends CommonController {
|
|
51
|
-
constructor() {
|
|
52
|
+
constructor(options = {}) {
|
|
52
53
|
super();
|
|
53
|
-
this.permission = AgentPermissions.Session;
|
|
54
|
-
this.
|
|
54
|
+
this.permission = options.permission ?? AgentPermissions.Session;
|
|
55
|
+
this.defaultAgentIdentifier = options.agentIdentifier ?? "default";
|
|
56
|
+
this.defaultAgentDescription = options.agentDescription ?? "";
|
|
57
|
+
DraxAgentFactory.instance(this.defaultAgentIdentifier, this.defaultAgentDescription);
|
|
58
|
+
}
|
|
59
|
+
async agents(request, reply) {
|
|
60
|
+
try {
|
|
61
|
+
this.assertAccess(request);
|
|
62
|
+
return reply.send({
|
|
63
|
+
agents: DraxAgentFactory.agents().map(agent => ({
|
|
64
|
+
identifier: agent.identifier,
|
|
65
|
+
description: agent.description,
|
|
66
|
+
})),
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
catch (e) {
|
|
70
|
+
this.handleControllerError(e, reply);
|
|
71
|
+
}
|
|
55
72
|
}
|
|
56
73
|
async startSession(request, reply) {
|
|
57
74
|
try {
|
|
58
75
|
this.assertAccess(request);
|
|
59
76
|
const input = AgentSessionRequestSchema.parse(request.body ?? {});
|
|
60
|
-
const
|
|
77
|
+
const agent = this.resolveAgent(input.identifier);
|
|
78
|
+
const session = await agent.startSession({
|
|
61
79
|
sessionId: input.sessionId,
|
|
62
80
|
userId: this.resolveUserId(request, input.userId),
|
|
63
81
|
tenantId: this.resolveTenantId(request, input.tenantId),
|
|
64
82
|
});
|
|
65
83
|
return reply.send({
|
|
84
|
+
agentIdentifier: agent.identifier,
|
|
66
85
|
sessionId: session.id,
|
|
67
86
|
createdAt: session.createdAt,
|
|
68
87
|
updatedAt: session.updatedAt,
|
|
@@ -76,7 +95,8 @@ class DraxAgentController extends CommonController {
|
|
|
76
95
|
try {
|
|
77
96
|
this.assertAccess(request);
|
|
78
97
|
const input = AgentMessageRequestSchema.parse(request.body ?? {});
|
|
79
|
-
const
|
|
98
|
+
const agent = this.resolveAgent(input.identifier);
|
|
99
|
+
const response = await agent.sendMessage({
|
|
80
100
|
...input,
|
|
81
101
|
userId: this.resolveUserId(request, input.userId),
|
|
82
102
|
tenantId: this.resolveTenantId(request, input.tenantId),
|
|
@@ -89,6 +109,9 @@ class DraxAgentController extends CommonController {
|
|
|
89
109
|
this.handleControllerError(e, reply);
|
|
90
110
|
}
|
|
91
111
|
}
|
|
112
|
+
resolveAgent(identifier) {
|
|
113
|
+
return DraxAgentFactory.instance(identifier ?? this.defaultAgentIdentifier);
|
|
114
|
+
}
|
|
92
115
|
assertAccess(request) {
|
|
93
116
|
if (this.permission === false) {
|
|
94
117
|
return;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import DraxAgent from "../agents/DraxAgent.js";
|
|
2
|
+
class DraxAgentFactory {
|
|
3
|
+
static instance(identifier = "default", description = "") {
|
|
4
|
+
if (!DraxAgentFactory.singletons[identifier]) {
|
|
5
|
+
DraxAgentFactory.singletons[identifier] = new DraxAgent(identifier, description);
|
|
6
|
+
}
|
|
7
|
+
return DraxAgentFactory.singletons[identifier];
|
|
8
|
+
}
|
|
9
|
+
static agents() {
|
|
10
|
+
return Object.values(DraxAgentFactory.singletons);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
DraxAgentFactory.singletons = {};
|
|
14
|
+
export default DraxAgentFactory;
|
|
15
|
+
export { DraxAgentFactory, };
|
package/dist/index.js
CHANGED
|
@@ -9,6 +9,7 @@ import { OpenAiProviderFactory } from "./factory/OpenAiProviderFactory.js";
|
|
|
9
9
|
import { GoogleAiProviderFactory } from "./factory/GoogleAiProviderFactory.js";
|
|
10
10
|
import { OllamaAiProviderFactory } from "./factory/OllamaAiProviderFactory.js";
|
|
11
11
|
import { AiProviderFactory } from "./factory/AiProviderFactory.js";
|
|
12
|
+
import { DraxAgentFactory } from "./factory/DraxAgentFactory.js";
|
|
12
13
|
import AILogServiceFactory from "./factory/services/AILogServiceFactory.js";
|
|
13
14
|
import { OpenAiProvider } from "./providers/OpenAiProvider.js";
|
|
14
15
|
import { GoogleAiProvider } from "./providers/GoogleAiProvider.js";
|
|
@@ -30,7 +31,7 @@ import AIRoutes from "./routes/AIRoutes.js";
|
|
|
30
31
|
import DraxAgentRoutes from "./routes/DraxAgentRoutes.js";
|
|
31
32
|
import AgentSessionRoutes from "./routes/AgentSessionRoutes.js";
|
|
32
33
|
import { DraxAgent } from "./agents/DraxAgent.js";
|
|
33
|
-
export { OpenAiConfig, GoogleAiConfig, OllamaAiConfig, AILogSchema, AILogBaseSchema, AILogModel, AILogMongoRepository, AILogSqliteRepository, OpenAiProviderFactory, GoogleAiProviderFactory, OllamaAiProviderFactory, AiProviderFactory, AILogServiceFactory, OpenAiProvider, GoogleAiProvider, OllamaAiProvider, BuilderTool,
|
|
34
|
+
export { OpenAiConfig, GoogleAiConfig, OllamaAiConfig, AILogSchema, AILogBaseSchema, AILogModel, AILogMongoRepository, AILogSqliteRepository, OpenAiProviderFactory, GoogleAiProviderFactory, OllamaAiProviderFactory, AiProviderFactory, DraxAgentFactory, AILogServiceFactory, OpenAiProvider, GoogleAiProvider, OllamaAiProvider, BuilderTool,
|
|
34
35
|
//Service
|
|
35
36
|
KnowledgeService, AILogService,
|
|
36
37
|
//Permissions
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -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 };
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import DraxAgentController from "../controllers/DraxAgentController.js";
|
|
2
2
|
async function DraxAgentRoutes(fastify, options) {
|
|
3
|
-
const controller = new DraxAgentController();
|
|
3
|
+
const controller = new DraxAgentController(options);
|
|
4
4
|
const prefix = "/api/ai/agent";
|
|
5
|
+
fastify.get(`${prefix}`, (req, rep) => controller.agents(req, rep));
|
|
5
6
|
fastify.post(`${prefix}/session`, (req, rep) => controller.startSession(req, rep));
|
|
6
7
|
fastify.post(`${prefix}/message`, (req, rep) => controller.message(req, rep));
|
|
7
8
|
}
|
|
@@ -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 };
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { ChromaClient } from 'chromadb';
|
|
2
|
+
class ChromaVector {
|
|
3
|
+
constructor(path = 'http://localhost:8000', database) {
|
|
4
|
+
this.client = new ChromaClient({ path, database });
|
|
5
|
+
this.collections = new Map();
|
|
6
|
+
}
|
|
7
|
+
async initializeCollection(collectionName) {
|
|
8
|
+
if (!this.collections.has(collectionName)) {
|
|
9
|
+
const collection = await this.client.createCollection({
|
|
10
|
+
name: collectionName,
|
|
11
|
+
// embeddingFunction: embeddingFunction
|
|
12
|
+
});
|
|
13
|
+
this.collections.set(collectionName, collection);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
async addDocument(collectionName, document) {
|
|
17
|
+
await this.initializeCollection(collectionName);
|
|
18
|
+
const collection = this.collections.get(collectionName);
|
|
19
|
+
if (collection) {
|
|
20
|
+
await collection.add({
|
|
21
|
+
ids: [document.id],
|
|
22
|
+
documents: [document.content],
|
|
23
|
+
metadatas: [document.metadata || {}],
|
|
24
|
+
embeddings: document.embedding ? [document.embedding] : undefined, // Add this line
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
throw new Error(`Collection ${collectionName} not found`);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
async queryCollection(collectionName, query, topK = 5) {
|
|
32
|
+
await this.initializeCollection(collectionName);
|
|
33
|
+
const collection = this.collections.get(collectionName);
|
|
34
|
+
if (collection) {
|
|
35
|
+
const results = await collection.query({
|
|
36
|
+
queryTexts: [query],
|
|
37
|
+
nResults: topK,
|
|
38
|
+
});
|
|
39
|
+
return results.documents[0] || [];
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
throw new Error(`Collection ${collectionName} not found`);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
async deleteDocument(collectionName, documentId) {
|
|
46
|
+
const collection = this.collections.get(collectionName);
|
|
47
|
+
if (collection) {
|
|
48
|
+
await collection.delete({
|
|
49
|
+
ids: [documentId],
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
throw new Error(`Collection ${collectionName} not found`);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
async listCollections() {
|
|
57
|
+
return await this.client.listCollections();
|
|
58
|
+
}
|
|
59
|
+
async deleteCollection(collectionName) {
|
|
60
|
+
await this.client.deleteCollection({ name: collectionName });
|
|
61
|
+
this.collections.delete(collectionName);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
export default ChromaVector;
|
|
65
|
+
export { ChromaVector };
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"publishConfig": {
|
|
4
4
|
"access": "public"
|
|
5
5
|
},
|
|
6
|
-
"version": "3.
|
|
6
|
+
"version": "3.37.0",
|
|
7
7
|
"description": "Ai utils",
|
|
8
8
|
"main": "dist/index.js",
|
|
9
9
|
"types": "types/index.d.ts",
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
"license": "ISC",
|
|
20
20
|
"dependencies": {
|
|
21
21
|
"@drax/ai-share": "^3.31.0",
|
|
22
|
-
"@drax/crud-back": "^3.
|
|
22
|
+
"@drax/crud-back": "^3.36.0",
|
|
23
23
|
"mongoose": "^8.23.0",
|
|
24
24
|
"mongoose-paginate-v2": "^1.8.3"
|
|
25
25
|
},
|
|
@@ -46,5 +46,5 @@
|
|
|
46
46
|
"typescript": "^5.9.3",
|
|
47
47
|
"vitest": "^3.0.8"
|
|
48
48
|
},
|
|
49
|
-
"gitHead": "
|
|
49
|
+
"gitHead": "eb4c24e68c7409b77d724d24f40b1e71b7d6d187"
|
|
50
50
|
}
|