@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.
- package/dist/config/OpenAiConfig.js +1 -0
- package/dist/controllers/AILogController.js +18 -0
- package/dist/factory/OpenAiProviderFactory.js +2 -1
- package/dist/factory/services/AILogServiceFactory.js +30 -0
- package/dist/index.js +10 -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/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/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/AILogController.ts +29 -0
- package/src/factory/OpenAiProviderFactory.ts +4 -2
- package/src/factory/services/AILogServiceFactory.ts +41 -0
- package/src/index.ts +39 -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/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/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/AILogController.d.ts +8 -0
- package/types/controllers/AILogController.d.ts.map +1 -0
- 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 +13 -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/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/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,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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
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.
|
|
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": "
|
|
47
|
+
"gitHead": "5254c8b1b2b59de46654f135122f38bd29ae5452"
|
|
42
48
|
}
|
|
@@ -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
|
-
|