@drax/ai-back 3.0.0 → 3.16.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 (64) hide show
  1. package/dist/config/OpenAiConfig.js +1 -0
  2. package/dist/controllers/AILogController.js +18 -0
  3. package/dist/factory/OpenAiProviderFactory.js +2 -1
  4. package/dist/factory/services/AILogServiceFactory.js +30 -0
  5. package/dist/index.js +10 -1
  6. package/dist/interfaces/IAILog.js +1 -0
  7. package/dist/interfaces/IAILogRepository.js +1 -0
  8. package/dist/models/AILogModel.js +50 -0
  9. package/dist/permissions/AILogPermissions.js +10 -0
  10. package/dist/providers/OpenAiProvider.js +176 -26
  11. package/dist/repository/mongo/AILogMongoRepository.js +13 -0
  12. package/dist/repository/sqlite/AILogSqliteRepository.js +45 -0
  13. package/dist/routes/AILogRoutes.js +21 -0
  14. package/dist/schemas/AILogSchema.js +44 -0
  15. package/dist/services/AILogService.js +9 -0
  16. package/package.json +8 -2
  17. package/src/config/OpenAiConfig.ts +1 -0
  18. package/src/controllers/AILogController.ts +29 -0
  19. package/src/factory/OpenAiProviderFactory.ts +4 -2
  20. package/src/factory/services/AILogServiceFactory.ts +41 -0
  21. package/src/index.ts +39 -1
  22. package/src/interfaces/IAILogRepository.ts +11 -0
  23. package/src/interfaces/IAIProvider.ts +48 -2
  24. package/src/models/AILogModel.ts +65 -0
  25. package/src/permissions/AILogPermissions.ts +14 -0
  26. package/src/providers/OpenAiProvider.ts +231 -29
  27. package/src/repository/mongo/AILogMongoRepository.ts +22 -0
  28. package/src/repository/sqlite/AILogSqliteRepository.ts +53 -0
  29. package/src/routes/AILogRoutes.ts +38 -0
  30. package/src/schemas/AILogSchema.ts +52 -0
  31. package/src/services/AILogService.ts +20 -0
  32. package/test/OpenAiProvider.test.ts +91 -0
  33. package/tsconfig.tsbuildinfo +1 -1
  34. package/types/config/OpenAiConfig.d.ts +2 -1
  35. package/types/config/OpenAiConfig.d.ts.map +1 -1
  36. package/types/controllers/AILogController.d.ts +8 -0
  37. package/types/controllers/AILogController.d.ts.map +1 -0
  38. package/types/factory/OpenAiProviderFactory.d.ts.map +1 -1
  39. package/types/factory/services/AILogServiceFactory.d.ts +8 -0
  40. package/types/factory/services/AILogServiceFactory.d.ts.map +1 -0
  41. package/types/index.d.ts +13 -3
  42. package/types/index.d.ts.map +1 -1
  43. package/types/interfaces/IAILog.d.ts +77 -0
  44. package/types/interfaces/IAILog.d.ts.map +1 -0
  45. package/types/interfaces/IAILogRepository.d.ts +6 -0
  46. package/types/interfaces/IAILogRepository.d.ts.map +1 -0
  47. package/types/interfaces/IAIProvider.d.ts +32 -2
  48. package/types/interfaces/IAIProvider.d.ts.map +1 -1
  49. package/types/models/AILogModel.d.ts +15 -0
  50. package/types/models/AILogModel.d.ts.map +1 -0
  51. package/types/permissions/AILogPermissions.d.ts +10 -0
  52. package/types/permissions/AILogPermissions.d.ts.map +1 -0
  53. package/types/providers/OpenAiProvider.d.ts +71 -2
  54. package/types/providers/OpenAiProvider.d.ts.map +1 -1
  55. package/types/repository/mongo/AILogMongoRepository.d.ts +9 -0
  56. package/types/repository/mongo/AILogMongoRepository.d.ts.map +1 -0
  57. package/types/repository/sqlite/AILogSqliteRepository.d.ts +23 -0
  58. package/types/repository/sqlite/AILogSqliteRepository.d.ts.map +1 -0
  59. package/types/routes/AILogRoutes.d.ts +4 -0
  60. package/types/routes/AILogRoutes.d.ts.map +1 -0
  61. package/types/schemas/AILogSchema.d.ts +81 -0
  62. package/types/schemas/AILogSchema.d.ts.map +1 -0
  63. package/types/services/AILogService.d.ts +10 -0
  64. 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,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,14 @@
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
- export { OpenAiConfig, OpenAiProviderFactory, OpenAiProvider, KnowledgeService, };
10
+ import { AILogService } from "./services/AILogService.js";
11
+ import AILogPermissions from "./permissions/AILogPermissions.js";
12
+ import AILogController from "./controllers/AILogController.js";
13
+ import AILogRoutes from "./routes/AILogRoutes.js";
14
+ export { OpenAiConfig, AILogSchema, AILogBaseSchema, AILogModel, AILogMongoRepository, AILogSqliteRepository, OpenAiProviderFactory, AILogServiceFactory, OpenAiProvider, KnowledgeService, AILogService, AILogPermissions, AILogController, AILogRoutes, };
@@ -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;
@@ -1,7 +1,7 @@
1
1
  import OpenAI from "openai";
2
2
  import { zodResponseFormat } from "openai/helpers/zod";
3
3
  class OpenAiProvider {
4
- constructor(apiKey, model) {
4
+ constructor(apiKey, model, visionModel, aiLogService) {
5
5
  if (!apiKey) {
6
6
  throw new Error("OpenAI apiKey required");
7
7
  }
@@ -10,6 +10,8 @@ class OpenAiProvider {
10
10
  }
11
11
  this._apiKey = apiKey;
12
12
  this._model = model;
13
+ this._visionModel = visionModel;
14
+ this._aiLogService = aiLogService;
13
15
  }
14
16
  get model() {
15
17
  if (!this._model) {
@@ -25,6 +27,127 @@ class OpenAiProvider {
25
27
  }
26
28
  return this._client;
27
29
  }
30
+ get visionModel() {
31
+ return this._visionModel;
32
+ }
33
+ buildUserContent(input) {
34
+ if (input.userContent && input.userContent.length > 0) {
35
+ return this.mapContentParts(input.userContent);
36
+ }
37
+ if (input.userImages && input.userImages.length > 0) {
38
+ const content = [];
39
+ if (input.userInput) {
40
+ content.push({ type: 'text', text: input.userInput });
41
+ }
42
+ content.push(...input.userImages.map(image => ({
43
+ type: 'image_url',
44
+ image_url: {
45
+ url: image.url,
46
+ ...(image.detail ? { detail: image.detail } : {}),
47
+ }
48
+ })));
49
+ return content;
50
+ }
51
+ return input.userInput ?? "";
52
+ }
53
+ mapContentParts(content) {
54
+ return content.map(part => {
55
+ if (part.type === 'text') {
56
+ return {
57
+ type: 'text',
58
+ text: part.text
59
+ };
60
+ }
61
+ return {
62
+ type: 'image_url',
63
+ image_url: {
64
+ url: part.imageUrl,
65
+ ...(part.detail ? { detail: part.detail } : {}),
66
+ }
67
+ };
68
+ });
69
+ }
70
+ mapHistory(history = []) {
71
+ return history.map(message => ({
72
+ role: message.role,
73
+ content: typeof message.content === 'string'
74
+ ? message.content
75
+ : this.mapContentParts(message.content)
76
+ }));
77
+ }
78
+ hasImageInput(input) {
79
+ if (input.userImages && input.userImages.length > 0) {
80
+ return true;
81
+ }
82
+ if (input.userContent?.some(part => part.type === 'image')) {
83
+ return true;
84
+ }
85
+ return input.history?.some(message => Array.isArray(message.content) && message.content.some(part => part.type === 'image')) ?? false;
86
+ }
87
+ serializePromptInput(input, systemPrompt) {
88
+ return JSON.stringify({
89
+ systemPrompt,
90
+ history: input.history,
91
+ userInput: input.userInput,
92
+ userContent: input.userContent,
93
+ memory: input.memory,
94
+ knowledgeBase: input.knowledgeBase,
95
+ });
96
+ }
97
+ serializePromptOutput(output) {
98
+ if (typeof output === "string") {
99
+ return output;
100
+ }
101
+ if (output === null || output === undefined) {
102
+ return undefined;
103
+ }
104
+ return JSON.stringify(output);
105
+ }
106
+ buildLogPayload(input, params) {
107
+ return {
108
+ provider: "openai",
109
+ model: params.model,
110
+ operationTitle: input.operationTitle,
111
+ operationGroup: input.operationGroup,
112
+ ip: input.ip,
113
+ userAgent: input.userAgent,
114
+ input: this.serializePromptInput(input, params.systemPrompt),
115
+ inputImages: input.userImages?.map(image => ({
116
+ url: image.url,
117
+ })) ?? input.userContent
118
+ ?.filter(part => part.type === "image")
119
+ .map(part => ({
120
+ url: part.imageUrl,
121
+ })),
122
+ inputFiles: input.inputFiles,
123
+ inputTokens: params.inputTokens,
124
+ outputTokens: params.outputTokens,
125
+ tokens: params.tokens,
126
+ startedAt: params.startedAt,
127
+ endedAt: params.endedAt,
128
+ responseTime: params.endedAt ? `${params.endedAt.getTime() - params.startedAt.getTime()}ms` : undefined,
129
+ output: this.serializePromptOutput(params.output),
130
+ success: params.success,
131
+ errorMessage: params.errorMessage,
132
+ tenant: input.tenant,
133
+ user: input.user,
134
+ };
135
+ }
136
+ async registerPromptLog(input, params) {
137
+ if (!this._aiLogService) {
138
+ return;
139
+ }
140
+ try {
141
+ await this._aiLogService.create(this.buildLogPayload(input, params));
142
+ }
143
+ catch (e) {
144
+ console.error("Error registerPromptLog", {
145
+ name: e?.name,
146
+ message: e?.message,
147
+ stack: e?.stack,
148
+ });
149
+ }
150
+ }
28
151
  async generateEmbedding({ text, model = "text-embedding-ada-002" }) {
29
152
  const response = await this.client.embeddings.create({
30
153
  model: model,
@@ -36,7 +159,6 @@ class OpenAiProvider {
36
159
  if (!input.systemPrompt) {
37
160
  throw new Error("systemPrompt required");
38
161
  }
39
- const model = input.model ?? this.model;
40
162
  let systemPrompt = input.systemPrompt;
41
163
  if (input.memory && input.memory.length > 0) {
42
164
  systemPrompt += `\n\n ${input.memoryHeader ?? '[MEMORIA]'}\n ${input.memory.map(m => `${m.key}: ${m.value}`).join('\n')}`;
@@ -44,31 +166,59 @@ class OpenAiProvider {
44
166
  if (input.knowledgeBase && input.knowledgeBase.length > 0) {
45
167
  systemPrompt += `\n\n${input.knowledgeBaseHeader ?? '[BASE DE CONOCIMIENTO]'}\n ${input.knowledgeBase.join('\n')}`;
46
168
  }
47
- let userInput = input.userInput;
169
+ const userInput = this.buildUserContent(input);
170
+ const model = input.model ?? (this.hasImageInput(input) ? this.visionModel ?? this.model : this.model);
171
+ const startedAt = new Date();
48
172
  const startTime = performance.now();
49
- const chatCompletion = await this.client.chat.completions.create({
50
- messages: [
51
- { role: 'system', content: systemPrompt },
52
- ...(input.history ? input.history : []),
53
- { role: 'user', content: userInput },
54
- ],
55
- ...(input.zodSchema ? { response_format: zodResponseFormat(input.zodSchema, "event") } : {}),
56
- ...(input.jsonSchema ? { response_format: input.jsonSchema } : {}),
57
- model: model,
58
- });
59
- const output = chatCompletion.choices[0].message.content;
60
- const tokens = chatCompletion.usage.total_tokens;
61
- const inputTokens = chatCompletion.usage.prompt_tokens;
62
- const outputTokens = chatCompletion.usage.completion_tokens;
63
- const endTime = performance.now();
64
- const time = endTime - startTime;
65
- return {
66
- output,
67
- tokens,
68
- inputTokens,
69
- outputTokens,
70
- time
71
- };
173
+ try {
174
+ const chatCompletion = await this.client.chat.completions.create({
175
+ messages: [
176
+ { role: 'system', content: systemPrompt },
177
+ ...this.mapHistory(input.history),
178
+ { role: 'user', content: userInput },
179
+ ],
180
+ ...(input.zodSchema ? { response_format: zodResponseFormat(input.zodSchema, "event") } : {}),
181
+ ...(input.jsonSchema ? { response_format: input.jsonSchema } : {}),
182
+ model: model,
183
+ });
184
+ const output = chatCompletion.choices[0].message.content;
185
+ const tokens = chatCompletion.usage.total_tokens;
186
+ const inputTokens = chatCompletion.usage.prompt_tokens;
187
+ const outputTokens = chatCompletion.usage.completion_tokens;
188
+ const endTime = performance.now();
189
+ const time = endTime - startTime;
190
+ const endedAt = new Date();
191
+ await this.registerPromptLog(input, {
192
+ model,
193
+ systemPrompt,
194
+ startedAt,
195
+ endedAt,
196
+ inputTokens,
197
+ outputTokens,
198
+ tokens,
199
+ output,
200
+ success: true,
201
+ });
202
+ return {
203
+ output,
204
+ tokens,
205
+ inputTokens,
206
+ outputTokens,
207
+ time
208
+ };
209
+ }
210
+ catch (e) {
211
+ const endedAt = new Date();
212
+ await this.registerPromptLog(input, {
213
+ model,
214
+ systemPrompt,
215
+ startedAt,
216
+ endedAt,
217
+ success: false,
218
+ errorMessage: e?.message,
219
+ });
220
+ throw e;
221
+ }
72
222
  }
73
223
  }
74
224
  export default OpenAiProvider;
@@ -0,0 +1,13 @@
1
+ import { AbstractMongoRepository } from "@drax/crud-back";
2
+ import { AILogModel } from "../../models/AILogModel.js";
3
+ class AILogMongoRepository extends AbstractMongoRepository {
4
+ constructor() {
5
+ super();
6
+ this._model = AILogModel;
7
+ this._searchFields = ['provider', 'model', 'operationTitle', 'operationGroup', 'ip', 'userAgent', 'input', 'output', 'errorMessage'];
8
+ this._populateFields = ['tenant', 'user'];
9
+ this._lean = true;
10
+ }
11
+ }
12
+ export default AILogMongoRepository;
13
+ export { AILogMongoRepository };
@@ -0,0 +1,45 @@
1
+ import { AbstractSqliteRepository } from "@drax/crud-back";
2
+ class AILogSqliteRepository extends AbstractSqliteRepository {
3
+ constructor() {
4
+ super(...arguments);
5
+ this.tableName = 'AILog';
6
+ this.searchFields = ['provider', 'model', 'operationTitle', 'operationGroup', 'ip', 'userAgent', 'input', 'output', 'errorMessage'];
7
+ this.booleanFields = ['success'];
8
+ this.jsonFields = ['inputImages', 'inputFiles'];
9
+ this.identifier = '_id';
10
+ this.populateFields = [
11
+ { field: 'tenant', table: 'tenant', identifier: '_id' },
12
+ { field: 'user', table: 'user', identifier: '_id' }
13
+ ];
14
+ this.verbose = false;
15
+ this.tableFields = [
16
+ { name: "provider", type: "TEXT", unique: false, primary: false },
17
+ { name: "model", type: "TEXT", unique: false, primary: false },
18
+ { name: "operationTitle", type: "TEXT", unique: false, primary: false },
19
+ { name: "operationGroup", type: "TEXT", unique: false, primary: false },
20
+ { name: "ip", type: "TEXT", unique: false, primary: false },
21
+ { name: "userAgent", type: "TEXT", unique: false, primary: false },
22
+ { name: "input", type: "TEXT", unique: false, primary: false },
23
+ { name: "inputImages", type: "TEXT", unique: false, primary: false },
24
+ { name: "inputFiles", type: "TEXT", unique: false, primary: false },
25
+ { name: "inputTokens", type: "INTEGER", unique: false, primary: false },
26
+ { name: "inputTokens", type: "TEXT", unique: false, primary: false },
27
+ { name: "outputTokens", type: "INTEGER", unique: false, primary: false },
28
+ { name: "outputTokens", type: "TEXT", unique: false, primary: false },
29
+ { name: "tokens", type: "INTEGER", unique: false, primary: false },
30
+ { name: "tokens", type: "TEXT", unique: false, primary: false },
31
+ { name: "startedAt", type: "TEXT", unique: false, primary: false },
32
+ { name: "endedAt", type: "TEXT", unique: false, primary: false },
33
+ { name: "responseTime", type: "TEXT", unique: false, primary: false },
34
+ { name: "output", type: "TEXT", unique: false, primary: false },
35
+ { name: "success", type: "TEXT", unique: false, primary: false },
36
+ { name: "statusCode", type: "INTEGER", unique: false, primary: false },
37
+ { name: "statusCode", type: "TEXT", unique: false, primary: false },
38
+ { name: "errorMessage", type: "TEXT", unique: false, primary: false },
39
+ { name: "tenant", type: "TEXT", unique: false, primary: false },
40
+ { name: "user", type: "TEXT", unique: false, primary: false }
41
+ ];
42
+ }
43
+ }
44
+ export default AILogSqliteRepository;
45
+ export { AILogSqliteRepository };
@@ -0,0 +1,21 @@
1
+ import AILogController from "../controllers/AILogController.js";
2
+ import { CrudSchemaBuilder } from "@drax/crud-back";
3
+ import { AILogSchema, AILogBaseSchema } from '../schemas/AILogSchema.js';
4
+ async function AILogFastifyRoutes(fastify, options) {
5
+ const controller = new AILogController();
6
+ const schemas = new CrudSchemaBuilder(AILogSchema, AILogBaseSchema, AILogBaseSchema, 'AILog', 'openapi-3.0', ['ai']);
7
+ fastify.get('/api/ailog', { schema: schemas.paginateSchema }, (req, rep) => controller.paginate(req, rep));
8
+ fastify.get('/api/ailog/find', { schema: schemas.findSchema }, (req, rep) => controller.find(req, rep));
9
+ fastify.get('/api/ailog/search', { schema: schemas.searchSchema }, (req, rep) => controller.search(req, rep));
10
+ fastify.get('/api/ailog/:id', { schema: schemas.findByIdSchema }, (req, rep) => controller.findById(req, rep));
11
+ fastify.get('/api/ailog/find-one', { schema: schemas.findOneSchema }, (req, rep) => controller.findOne(req, rep));
12
+ fastify.get('/api/ailog/group-by', { schema: schemas.groupBySchema }, (req, rep) => controller.groupBy(req, rep));
13
+ fastify.post('/api/ailog', { schema: schemas.createSchema }, (req, rep) => controller.create(req, rep));
14
+ fastify.put('/api/ailog/:id', { schema: schemas.updateSchema }, (req, rep) => controller.update(req, rep));
15
+ fastify.patch('/api/ailog/:id', { schema: schemas.updateSchema }, (req, rep) => controller.updatePartial(req, rep));
16
+ fastify.delete('/api/ailog/:id', { schema: schemas.deleteSchema }, (req, rep) => controller.delete(req, rep));
17
+ fastify.get('/api/ailog/export', (req, rep) => controller.export(req, rep));
18
+ fastify.post('/api/ailog/import', (req, rep) => controller.import(req, rep));
19
+ }
20
+ export default AILogFastifyRoutes;
21
+ export { AILogFastifyRoutes };
@@ -0,0 +1,44 @@
1
+ import { z } from 'zod';
2
+ const AILogBaseSchema = z.object({
3
+ provider: z.string().optional(),
4
+ model: z.string().optional(),
5
+ operationTitle: z.string().optional(),
6
+ operationGroup: z.string().optional(),
7
+ ip: z.string().optional(),
8
+ userAgent: z.string().optional(),
9
+ input: z.string().optional(),
10
+ inputImages: z.array(z.object({
11
+ filename: z.string().optional(),
12
+ filepath: z.string().optional(),
13
+ size: z.number().nullable().optional(),
14
+ mimetype: z.string().optional(),
15
+ url: z.string().optional()
16
+ })).optional(),
17
+ inputFiles: z.array(z.object({
18
+ filename: z.string().optional(),
19
+ filepath: z.string().optional(),
20
+ size: z.number().nullable().optional(),
21
+ mimetype: z.string().optional(),
22
+ url: z.string().optional()
23
+ })).optional(),
24
+ inputTokens: z.number().nullable().optional(),
25
+ outputTokens: z.number().nullable().optional(),
26
+ tokens: z.number().nullable().optional(),
27
+ startedAt: z.coerce.date().nullable().optional(),
28
+ endedAt: z.coerce.date().nullable().optional(),
29
+ responseTime: z.string().optional(),
30
+ output: z.string().optional(),
31
+ success: z.boolean().optional(),
32
+ statusCode: z.number().nullable().optional(),
33
+ errorMessage: z.string().optional(),
34
+ tenant: z.coerce.string().optional().nullable(),
35
+ user: z.coerce.string().optional().nullable()
36
+ });
37
+ const AILogSchema = AILogBaseSchema
38
+ .extend({
39
+ _id: z.coerce.string(),
40
+ tenant: z.object({ _id: z.coerce.string(), name: z.string() }).nullable().optional(),
41
+ user: z.object({ _id: z.coerce.string(), username: z.string() }).nullable().optional()
42
+ });
43
+ export default AILogSchema;
44
+ export { AILogSchema, AILogBaseSchema };
@@ -0,0 +1,9 @@
1
+ import { AbstractService } from "@drax/crud-back";
2
+ class AILogService extends AbstractService {
3
+ constructor(AILogRepository, baseSchema, fullSchema) {
4
+ super(AILogRepository, baseSchema, fullSchema);
5
+ this._validateOutput = true;
6
+ }
7
+ }
8
+ export default AILogService;
9
+ export { AILogService };
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "3.0.0",
6
+ "version": "3.16.0",
7
7
  "description": "Ai utils",
8
8
  "main": "dist/index.js",
9
9
  "types": "types/index.d.ts",
@@ -17,6 +17,12 @@
17
17
  },
18
18
  "author": "Cristian Incarnato & Drax Team",
19
19
  "license": "ISC",
20
+ "dependencies": {
21
+ "@drax/ai-share": "^3.16.0",
22
+ "@drax/crud-back": "^3.15.0",
23
+ "mongoose": "^8.23.0",
24
+ "mongoose-paginate-v2": "^1.8.3"
25
+ },
20
26
  "peerDependencies": {
21
27
  "jsdom": "^26.0.0",
22
28
  "office-text-extractor": "^3.0.3",
@@ -38,5 +44,5 @@
38
44
  "typescript": "^5.9.3",
39
45
  "vitest": "^3.0.8"
40
46
  },
41
- "gitHead": "63ae718b24ea25ae80b1a9a5dfb84a3abbb95199"
47
+ "gitHead": "5254c8b1b2b59de46654f135122f38bd29ae5452"
42
48
  }
@@ -3,6 +3,7 @@ enum OpenAiConfig {
3
3
 
4
4
  OpenAiApiKey = "OPENAI_API_KEY",
5
5
  OpenAiModel = "OPENAI_MODEL",
6
+ OpenAiVisionModel = "OPENAI_VISION_MODEL",
6
7
 
7
8
  }
8
9
 
@@ -0,0 +1,29 @@
1
+
2
+ import AILogServiceFactory from "../factory/services/AILogServiceFactory.js";
3
+ import {AbstractFastifyController} from "@drax/crud-back";
4
+ import AILogPermissions from "../permissions/AILogPermissions.js";
5
+ import type {IAILog, IAILogBase} from "@drax/ai-share";
6
+
7
+ class AILogController extends AbstractFastifyController<IAILog, IAILogBase, IAILogBase> {
8
+
9
+ constructor() {
10
+ super(AILogServiceFactory.instance, AILogPermissions)
11
+ this.tenantField = "tenant";
12
+ this.userField = "user";
13
+
14
+ this.tenantFilter = true;
15
+ this.tenantSetter = true;
16
+ this.tenantAssert = true;
17
+
18
+ this.userFilter = true;
19
+ this.userSetter = true;
20
+ this.userAssert = true;
21
+ }
22
+
23
+ }
24
+
25
+ export default AILogController;
26
+ export {
27
+ AILogController
28
+ }
29
+
@@ -2,6 +2,7 @@ import {DraxConfig} from "@drax/common-back";
2
2
  import OpenAiConfig from "../config/OpenAiConfig.js";
3
3
  import type {IAIProvider} from "../interfaces/IAIProvider"
4
4
  import OpenAiProvider from "../providers/OpenAiProvider.js";
5
+ import AILogServiceFactory from "./services/AILogServiceFactory.js";
5
6
 
6
7
  class OpenAiProviderFactory {
7
8
  private static singleton: IAIProvider;
@@ -10,7 +11,9 @@ class OpenAiProviderFactory {
10
11
  if (!OpenAiProviderFactory.singleton) {
11
12
  OpenAiProviderFactory.singleton = new OpenAiProvider(
12
13
  DraxConfig.getOrLoad(OpenAiConfig.OpenAiApiKey),
13
- DraxConfig.getOrLoad(OpenAiConfig.OpenAiModel)
14
+ DraxConfig.getOrLoad(OpenAiConfig.OpenAiModel),
15
+ DraxConfig.getOrLoad(OpenAiConfig.OpenAiVisionModel),
16
+ AILogServiceFactory.instance
14
17
  );
15
18
  }
16
19
  return OpenAiProviderFactory.singleton;
@@ -21,4 +24,3 @@ export default OpenAiProviderFactory
21
24
  export {
22
25
  OpenAiProviderFactory
23
26
  }
24
-