@drax/ai-back 3.0.0 → 3.17.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/dist/config/OpenAiConfig.js +1 -0
- package/dist/controllers/AIController.js +150 -0
- package/dist/controllers/AICrudController.js +150 -0
- package/dist/controllers/AIGenericController.js +83 -0
- package/dist/controllers/AILogController.js +18 -0
- package/dist/factory/AiProviderFactory.js +1 -1
- package/dist/factory/OpenAiProviderFactory.js +2 -1
- package/dist/factory/services/AILogServiceFactory.js +30 -0
- package/dist/index.js +14 -1
- package/dist/interfaces/IAILog.js +1 -0
- package/dist/interfaces/IAILogRepository.js +1 -0
- package/dist/models/AILogModel.js +50 -0
- package/dist/permissions/AILogPermissions.js +10 -0
- package/dist/permissions/AIPermissions.js +7 -0
- package/dist/providers/OpenAiProvider.js +176 -26
- package/dist/repository/mongo/AILogMongoRepository.js +13 -0
- package/dist/repository/sqlite/AILogSqliteRepository.js +45 -0
- package/dist/routes/AILogRoutes.js +21 -0
- package/dist/routes/AIRoutes.js +10 -0
- package/dist/schemas/AILogSchema.js +44 -0
- package/dist/services/AILogService.js +9 -0
- package/package.json +8 -2
- package/src/config/OpenAiConfig.ts +1 -0
- package/src/controllers/AICrudController.ts +168 -0
- package/src/controllers/AIGenericController.ts +97 -0
- package/src/controllers/AILogController.ts +29 -0
- package/src/factory/AiProviderFactory.ts +1 -1
- package/src/factory/OpenAiProviderFactory.ts +4 -2
- package/src/factory/services/AILogServiceFactory.ts +41 -0
- package/src/index.ts +47 -1
- package/src/interfaces/IAILogRepository.ts +11 -0
- package/src/interfaces/IAIProvider.ts +48 -2
- package/src/models/AILogModel.ts +65 -0
- package/src/permissions/AILogPermissions.ts +14 -0
- package/src/permissions/AIPermissions.ts +11 -0
- package/src/providers/OpenAiProvider.ts +231 -29
- package/src/repository/mongo/AILogMongoRepository.ts +22 -0
- package/src/repository/sqlite/AILogSqliteRepository.ts +53 -0
- package/src/routes/AILogRoutes.ts +38 -0
- package/src/routes/AIRoutes.ts +18 -0
- package/src/schemas/AILogSchema.ts +52 -0
- package/src/services/AILogService.ts +20 -0
- package/test/OpenAiProvider.test.ts +91 -0
- package/tsconfig.tsbuildinfo +1 -1
- package/types/config/OpenAiConfig.d.ts +2 -1
- package/types/config/OpenAiConfig.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/AICrudController.d.ts +25 -0
- package/types/controllers/AICrudController.d.ts.map +1 -0
- package/types/controllers/AIGenericController.d.ts +7 -0
- package/types/controllers/AIGenericController.d.ts.map +1 -0
- package/types/controllers/AILogController.d.ts +8 -0
- package/types/controllers/AILogController.d.ts.map +1 -0
- package/types/factory/AiProviderFactory.d.ts +1 -1
- package/types/factory/AiProviderFactory.d.ts.map +1 -1
- package/types/factory/OpenAiProviderFactory.d.ts.map +1 -1
- package/types/factory/services/AILogServiceFactory.d.ts +8 -0
- package/types/factory/services/AILogServiceFactory.d.ts.map +1 -0
- package/types/index.d.ts +17 -3
- 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/IAILogRepository.d.ts +6 -0
- package/types/interfaces/IAILogRepository.d.ts.map +1 -0
- package/types/interfaces/IAIProvider.d.ts +32 -2
- package/types/interfaces/IAIProvider.d.ts.map +1 -1
- package/types/models/AILogModel.d.ts +15 -0
- package/types/models/AILogModel.d.ts.map +1 -0
- package/types/permissions/AILogPermissions.d.ts +10 -0
- package/types/permissions/AILogPermissions.d.ts.map +1 -0
- package/types/permissions/AIPermissions.d.ts +7 -0
- package/types/permissions/AIPermissions.d.ts.map +1 -0
- package/types/providers/OpenAiProvider.d.ts +71 -2
- package/types/providers/OpenAiProvider.d.ts.map +1 -1
- package/types/repository/mongo/AILogMongoRepository.d.ts +9 -0
- package/types/repository/mongo/AILogMongoRepository.d.ts.map +1 -0
- package/types/repository/sqlite/AILogSqliteRepository.d.ts +23 -0
- package/types/repository/sqlite/AILogSqliteRepository.d.ts.map +1 -0
- package/types/routes/AILogRoutes.d.ts +4 -0
- package/types/routes/AILogRoutes.d.ts.map +1 -0
- package/types/routes/AIRoutes.d.ts +4 -0
- package/types/routes/AIRoutes.d.ts.map +1 -0
- package/types/schemas/AILogSchema.d.ts +81 -0
- package/types/schemas/AILogSchema.d.ts.map +1 -0
- package/types/services/AILogService.d.ts +10 -0
- package/types/services/AILogService.d.ts.map +1 -0
|
@@ -2,6 +2,7 @@ var OpenAiConfig;
|
|
|
2
2
|
(function (OpenAiConfig) {
|
|
3
3
|
OpenAiConfig["OpenAiApiKey"] = "OPENAI_API_KEY";
|
|
4
4
|
OpenAiConfig["OpenAiModel"] = "OPENAI_MODEL";
|
|
5
|
+
OpenAiConfig["OpenAiVisionModel"] = "OPENAI_VISION_MODEL";
|
|
5
6
|
})(OpenAiConfig || (OpenAiConfig = {}));
|
|
6
7
|
export default OpenAiConfig;
|
|
7
8
|
export { OpenAiConfig };
|
|
@@ -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 };
|
|
@@ -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 AICrudController 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 AICrudController;
|
|
150
|
+
export { AICrudController };
|
|
@@ -0,0 +1,83 @@
|
|
|
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 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 PromptMessageSchema = z.object({
|
|
21
|
+
role: z.enum(["user", "assistant", "system"]),
|
|
22
|
+
content: z.union([
|
|
23
|
+
z.string(),
|
|
24
|
+
z.array(PromptContentPartSchema),
|
|
25
|
+
]),
|
|
26
|
+
});
|
|
27
|
+
const PromptMemorySchema = z.object({
|
|
28
|
+
key: z.string().min(1),
|
|
29
|
+
value: z.string(),
|
|
30
|
+
});
|
|
31
|
+
const PromptInputFileSchema = z.object({
|
|
32
|
+
filename: z.string().optional(),
|
|
33
|
+
filepath: z.string().optional(),
|
|
34
|
+
size: z.number().nullable().optional(),
|
|
35
|
+
mimetype: z.string().optional(),
|
|
36
|
+
url: z.string().optional(),
|
|
37
|
+
});
|
|
38
|
+
const GenericPromptRequestSchema = z.object({
|
|
39
|
+
systemPrompt: z.string().min(1),
|
|
40
|
+
userInput: z.string().optional(),
|
|
41
|
+
userImages: z.array(PromptImageSchema).optional(),
|
|
42
|
+
inputFiles: z.array(PromptInputFileSchema).optional(),
|
|
43
|
+
userContent: z.array(PromptContentPartSchema).optional(),
|
|
44
|
+
history: z.array(PromptMessageSchema).optional(),
|
|
45
|
+
memory: z.array(PromptMemorySchema).optional(),
|
|
46
|
+
memoryHeader: z.string().optional(),
|
|
47
|
+
knowledgeBase: z.array(z.string()).optional(),
|
|
48
|
+
knowledgeBaseHeader: z.string().optional(),
|
|
49
|
+
jsonSchema: z.record(z.string(), z.any()).or(z.array(z.any())).optional(),
|
|
50
|
+
model: z.string().optional(),
|
|
51
|
+
operationTitle: z.string().optional(),
|
|
52
|
+
operationGroup: z.string().optional(),
|
|
53
|
+
});
|
|
54
|
+
class AIGenericController extends CommonController {
|
|
55
|
+
async prompt(request, reply) {
|
|
56
|
+
try {
|
|
57
|
+
request.rbac.assertPermission(AIPermissions.Prompt);
|
|
58
|
+
const input = GenericPromptRequestSchema.parse(request.body ?? {});
|
|
59
|
+
const aiProvider = AiProviderFactory.instance();
|
|
60
|
+
const promptInput = {
|
|
61
|
+
...input,
|
|
62
|
+
operationTitle: input.operationTitle ?? "generic-ai-prompt",
|
|
63
|
+
operationGroup: input.operationGroup ?? "generic-ai-prompt",
|
|
64
|
+
ip: request.ip,
|
|
65
|
+
userAgent: request.headers["user-agent"],
|
|
66
|
+
tenant: request.rbac?.tenantId ?? null,
|
|
67
|
+
user: request.rbac?.userId ?? null,
|
|
68
|
+
};
|
|
69
|
+
const response = await aiProvider.prompt(promptInput);
|
|
70
|
+
return reply.send(response);
|
|
71
|
+
}
|
|
72
|
+
catch (e) {
|
|
73
|
+
if (e?.name === "ZodError") {
|
|
74
|
+
return reply.status(400).send({
|
|
75
|
+
message: e?.message || "AI prompt validation error",
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
this.handleError(e, reply);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
export default AIGenericController;
|
|
83
|
+
export { AIGenericController };
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import AILogServiceFactory from "../factory/services/AILogServiceFactory.js";
|
|
2
|
+
import { AbstractFastifyController } from "@drax/crud-back";
|
|
3
|
+
import AILogPermissions from "../permissions/AILogPermissions.js";
|
|
4
|
+
class AILogController extends AbstractFastifyController {
|
|
5
|
+
constructor() {
|
|
6
|
+
super(AILogServiceFactory.instance, AILogPermissions);
|
|
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 AILogController;
|
|
18
|
+
export { AILogController };
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { DraxConfig } from "@drax/common-back";
|
|
2
2
|
import OpenAiConfig from "../config/OpenAiConfig.js";
|
|
3
3
|
import OpenAiProvider from "../providers/OpenAiProvider.js";
|
|
4
|
+
import AILogServiceFactory from "./services/AILogServiceFactory.js";
|
|
4
5
|
class OpenAiProviderFactory {
|
|
5
6
|
static instance() {
|
|
6
7
|
if (!OpenAiProviderFactory.singleton) {
|
|
7
|
-
OpenAiProviderFactory.singleton = new OpenAiProvider(DraxConfig.getOrLoad(OpenAiConfig.OpenAiApiKey), DraxConfig.getOrLoad(OpenAiConfig.OpenAiModel));
|
|
8
|
+
OpenAiProviderFactory.singleton = new OpenAiProvider(DraxConfig.getOrLoad(OpenAiConfig.OpenAiApiKey), DraxConfig.getOrLoad(OpenAiConfig.OpenAiModel), DraxConfig.getOrLoad(OpenAiConfig.OpenAiVisionModel), AILogServiceFactory.instance);
|
|
8
9
|
}
|
|
9
10
|
return OpenAiProviderFactory.singleton;
|
|
10
11
|
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import AILogMongoRepository from '../../repository/mongo/AILogMongoRepository.js';
|
|
2
|
+
import AILogSqliteRepository from '../../repository/sqlite/AILogSqliteRepository.js';
|
|
3
|
+
import { AILogService } from '../../services/AILogService.js';
|
|
4
|
+
import { AILogBaseSchema, AILogSchema } from "../../schemas/AILogSchema.js";
|
|
5
|
+
import { COMMON, CommonConfig, DraxConfig } from "@drax/common-back";
|
|
6
|
+
class AILogServiceFactory {
|
|
7
|
+
static get instance() {
|
|
8
|
+
if (!AILogServiceFactory.service) {
|
|
9
|
+
let repository;
|
|
10
|
+
switch (DraxConfig.getOrLoad(CommonConfig.DbEngine)) {
|
|
11
|
+
case COMMON.DB_ENGINES.MONGODB:
|
|
12
|
+
repository = new AILogMongoRepository();
|
|
13
|
+
break;
|
|
14
|
+
case COMMON.DB_ENGINES.SQLITE:
|
|
15
|
+
const dbFile = DraxConfig.getOrLoad(CommonConfig.SqliteDbFile);
|
|
16
|
+
repository = new AILogSqliteRepository(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 = AILogBaseSchema;
|
|
23
|
+
const fullSchema = AILogSchema;
|
|
24
|
+
AILogServiceFactory.service = new AILogService(repository, baseSchema, fullSchema);
|
|
25
|
+
}
|
|
26
|
+
return AILogServiceFactory.service;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
export default AILogServiceFactory;
|
|
30
|
+
export { AILogServiceFactory };
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
import { OpenAiConfig } from "./config/OpenAiConfig.js";
|
|
2
|
+
import { AILogSchema, AILogBaseSchema } from "./schemas/AILogSchema.js";
|
|
3
|
+
import AILogModel from "./models/AILogModel.js";
|
|
4
|
+
import AILogMongoRepository from "./repository/mongo/AILogMongoRepository.js";
|
|
5
|
+
import AILogSqliteRepository from "./repository/sqlite/AILogSqliteRepository.js";
|
|
2
6
|
import { OpenAiProviderFactory } from "./factory/OpenAiProviderFactory.js";
|
|
7
|
+
import AILogServiceFactory from "./factory/services/AILogServiceFactory.js";
|
|
3
8
|
import { OpenAiProvider } from "./providers/OpenAiProvider.js";
|
|
4
9
|
import { KnowledgeService } from "./services/KnowledgeService.js";
|
|
5
|
-
|
|
10
|
+
import { AILogService } from "./services/AILogService.js";
|
|
11
|
+
import AILogPermissions from "./permissions/AILogPermissions.js";
|
|
12
|
+
import AIPermissions from "./permissions/AIPermissions.js";
|
|
13
|
+
import AILogController from "./controllers/AILogController.js";
|
|
14
|
+
import AICrudController from "./controllers/AICrudController.js";
|
|
15
|
+
import AIGenericController from "./controllers/AIGenericController.js";
|
|
16
|
+
import AILogRoutes from "./routes/AILogRoutes.js";
|
|
17
|
+
import AIRoutes from "./routes/AIRoutes.js";
|
|
18
|
+
export { OpenAiConfig, AILogSchema, AILogBaseSchema, AILogModel, AILogMongoRepository, AILogSqliteRepository, OpenAiProviderFactory, AILogServiceFactory, OpenAiProvider, KnowledgeService, AILogService, AILogPermissions, AIPermissions, AILogController, AICrudController, AIGenericController, AILogRoutes, AIRoutes };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { mongoose } from '@drax/common-back';
|
|
2
|
+
import uniqueValidator from 'mongoose-unique-validator';
|
|
3
|
+
import mongoosePaginate from 'mongoose-paginate-v2';
|
|
4
|
+
const AILogSchema = new mongoose.Schema({
|
|
5
|
+
provider: { type: String, required: false, index: true, unique: false },
|
|
6
|
+
model: { type: String, required: false, index: true, unique: false },
|
|
7
|
+
operationTitle: { type: String, required: false, index: false, unique: false },
|
|
8
|
+
operationGroup: { type: String, required: false, index: false, unique: false },
|
|
9
|
+
ip: { type: String, required: false, index: false, unique: false },
|
|
10
|
+
userAgent: { type: String, required: false, index: false, unique: false },
|
|
11
|
+
input: { type: String, required: false, index: false, unique: false },
|
|
12
|
+
inputImages: [{
|
|
13
|
+
filename: { type: String, required: false, index: false, unique: false },
|
|
14
|
+
filepath: { type: String, required: false, index: false, unique: false },
|
|
15
|
+
size: { type: Number, required: false, index: false, unique: false },
|
|
16
|
+
mimetype: { type: String, required: false, index: false, unique: false },
|
|
17
|
+
url: { type: String, required: false, index: false, unique: false }
|
|
18
|
+
}],
|
|
19
|
+
inputFiles: [{
|
|
20
|
+
filename: { type: String, required: false, index: false, unique: false },
|
|
21
|
+
filepath: { type: String, required: false, index: false, unique: false },
|
|
22
|
+
size: { type: Number, required: false, index: false, unique: false },
|
|
23
|
+
mimetype: { type: String, required: false, index: false, unique: false },
|
|
24
|
+
url: { type: String, required: false, index: false, unique: false }
|
|
25
|
+
}],
|
|
26
|
+
inputTokens: { type: Number, required: false, index: false, unique: false },
|
|
27
|
+
outputTokens: { type: Number, required: false, index: false, unique: false },
|
|
28
|
+
tokens: { type: Number, required: false, index: false, unique: false },
|
|
29
|
+
startedAt: { type: Date, required: false, index: false, unique: false },
|
|
30
|
+
endedAt: { type: Date, required: false, index: false, unique: false },
|
|
31
|
+
responseTime: { type: String, required: false, index: false, unique: false },
|
|
32
|
+
output: { type: String, required: false, index: false, unique: false },
|
|
33
|
+
success: { type: Boolean, required: false, index: false, unique: false },
|
|
34
|
+
statusCode: { type: Number, required: false, index: false, unique: false },
|
|
35
|
+
errorMessage: { type: String, required: false, index: false, unique: false },
|
|
36
|
+
tenant: { type: mongoose.Schema.Types.ObjectId, ref: 'Tenant', required: false, index: false, unique: false },
|
|
37
|
+
user: { type: mongoose.Schema.Types.ObjectId, ref: 'User', required: false, index: false, unique: false }
|
|
38
|
+
}, { timestamps: true });
|
|
39
|
+
AILogSchema.plugin(uniqueValidator, { message: 'validation.unique' });
|
|
40
|
+
AILogSchema.plugin(mongoosePaginate);
|
|
41
|
+
AILogSchema.virtual("id").get(function () {
|
|
42
|
+
return this._id.toString();
|
|
43
|
+
});
|
|
44
|
+
AILogSchema.set('toJSON', { getters: true, virtuals: true });
|
|
45
|
+
AILogSchema.set('toObject', { getters: true, virtuals: true });
|
|
46
|
+
const MODEL_NAME = 'AILog';
|
|
47
|
+
const COLLECTION_NAME = 'AILog';
|
|
48
|
+
const AILogModel = mongoose.model(MODEL_NAME, AILogSchema, COLLECTION_NAME);
|
|
49
|
+
export { AILogSchema, AILogModel };
|
|
50
|
+
export default AILogModel;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
var AILogPermissions;
|
|
2
|
+
(function (AILogPermissions) {
|
|
3
|
+
AILogPermissions["Create"] = "ailog:create";
|
|
4
|
+
AILogPermissions["Update"] = "ailog:update";
|
|
5
|
+
AILogPermissions["Delete"] = "ailog:delete";
|
|
6
|
+
AILogPermissions["View"] = "ailog:view";
|
|
7
|
+
AILogPermissions["Manage"] = "ailog:manage";
|
|
8
|
+
})(AILogPermissions || (AILogPermissions = {}));
|
|
9
|
+
export { AILogPermissions };
|
|
10
|
+
export default AILogPermissions;
|