@drax/ai-back 3.51.0 → 3.52.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/controllers/AILogController.js +2 -2
- package/dist/models/AILogModel.js +1 -0
- package/dist/providers/ai/AbstractAiProvider.js +90 -0
- package/dist/providers/ai/DeepSeekAiProvider.js +1 -7
- package/dist/providers/ai/GoogleAiProvider.js +3 -80
- package/dist/providers/ai/OllamaAiProvider.js +3 -80
- package/dist/providers/ai/OpenAiProvider.js +4 -81
- package/dist/repository/sqlite/AILogSqliteRepository.js +1 -0
- package/dist/routes/AILogRoutes.js +5 -5
- package/dist/schemas/AILogSchema.js +4 -1
- package/package.json +3 -3
- package/src/controllers/AILogController.ts +2 -2
- package/src/models/AILogModel.ts +1 -0
- package/src/providers/ai/AbstractAiProvider.ts +129 -0
- package/src/providers/ai/DeepSeekAiProvider.ts +1 -20
- package/src/providers/ai/GoogleAiProvider.ts +4 -116
- package/src/providers/ai/OllamaAiProvider.ts +4 -116
- package/src/providers/ai/OpenAiProvider.ts +5 -117
- package/src/repository/sqlite/AILogSqliteRepository.ts +1 -1
- package/src/routes/AILogRoutes.ts +14 -14
- package/src/schemas/AILogSchema.ts +4 -1
- package/tsconfig.tsbuildinfo +1 -1
- package/types/models/AILogModel.d.ts.map +1 -1
- package/types/providers/ai/AbstractAiProvider.d.ts +29 -0
- package/types/providers/ai/AbstractAiProvider.d.ts.map +1 -0
- package/types/providers/ai/DeepSeekAiProvider.d.ts +0 -14
- package/types/providers/ai/DeepSeekAiProvider.d.ts.map +1 -1
- package/types/providers/ai/GoogleAiProvider.d.ts +3 -31
- package/types/providers/ai/GoogleAiProvider.d.ts.map +1 -1
- package/types/providers/ai/OllamaAiProvider.d.ts +3 -31
- package/types/providers/ai/OllamaAiProvider.d.ts.map +1 -1
- package/types/providers/ai/OpenAiProvider.d.ts +4 -32
- package/types/providers/ai/OpenAiProvider.d.ts.map +1 -1
- package/types/repository/sqlite/AILogSqliteRepository.d.ts.map +1 -1
- package/types/schemas/AILogSchema.d.ts +4 -0
- package/types/schemas/AILogSchema.d.ts.map +1 -1
|
@@ -9,9 +9,9 @@ class AILogController extends AbstractFastifyController {
|
|
|
9
9
|
this.tenantFilter = true;
|
|
10
10
|
this.tenantSetter = true;
|
|
11
11
|
this.tenantAssert = true;
|
|
12
|
-
this.userFilter =
|
|
12
|
+
this.userFilter = false;
|
|
13
13
|
this.userSetter = true;
|
|
14
|
-
this.userAssert =
|
|
14
|
+
this.userAssert = false;
|
|
15
15
|
}
|
|
16
16
|
}
|
|
17
17
|
export default AILogController;
|
|
@@ -29,6 +29,7 @@ const AILogSchema = new mongoose.Schema({
|
|
|
29
29
|
startedAt: { type: Date, required: false, index: false, unique: false },
|
|
30
30
|
endedAt: { type: Date, required: false, index: false, unique: false },
|
|
31
31
|
responseTime: { type: String, required: false, index: false, unique: false },
|
|
32
|
+
responseTimeMS: { type: Number, required: false, index: false, unique: false },
|
|
32
33
|
output: { type: String, required: false, index: false, unique: false },
|
|
33
34
|
success: { type: Boolean, required: false, index: false, unique: false },
|
|
34
35
|
statusCode: { type: Number, required: false, index: false, unique: false },
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
class AbstractAiProvider {
|
|
2
|
+
constructor(providerName, aiLogService) {
|
|
3
|
+
this.providerName = providerName;
|
|
4
|
+
this._aiLogService = aiLogService;
|
|
5
|
+
}
|
|
6
|
+
hasImageInput(input) {
|
|
7
|
+
if (input.userImages && input.userImages.length > 0) {
|
|
8
|
+
return true;
|
|
9
|
+
}
|
|
10
|
+
if (input.userContent?.some(part => part.type === 'image')) {
|
|
11
|
+
return true;
|
|
12
|
+
}
|
|
13
|
+
return input.history?.some(message => Array.isArray(message.content) && message.content.some(part => part.type === 'image')) ?? false;
|
|
14
|
+
}
|
|
15
|
+
serializePromptInput(input, systemPrompt) {
|
|
16
|
+
return JSON.stringify({
|
|
17
|
+
systemPrompt,
|
|
18
|
+
history: input.history,
|
|
19
|
+
userInput: input.userInput,
|
|
20
|
+
userContent: input.userContent,
|
|
21
|
+
memory: input.memory,
|
|
22
|
+
knowledgeBase: input.knowledgeBase,
|
|
23
|
+
tools: input.tools?.map(tool => ({
|
|
24
|
+
name: tool.name,
|
|
25
|
+
description: tool.description,
|
|
26
|
+
parameters: tool.parameters,
|
|
27
|
+
})),
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
serializePromptOutput(output) {
|
|
31
|
+
if (typeof output === "string") {
|
|
32
|
+
return output;
|
|
33
|
+
}
|
|
34
|
+
if (output === null || output === undefined) {
|
|
35
|
+
return undefined;
|
|
36
|
+
}
|
|
37
|
+
return JSON.stringify(output);
|
|
38
|
+
}
|
|
39
|
+
buildLogPayload(input, params) {
|
|
40
|
+
const responseTimeMS = params.endedAt
|
|
41
|
+
? params.endedAt.getTime() - params.startedAt.getTime()
|
|
42
|
+
: undefined;
|
|
43
|
+
return {
|
|
44
|
+
provider: this.providerName,
|
|
45
|
+
model: params.model,
|
|
46
|
+
operationTitle: input.operationTitle,
|
|
47
|
+
operationGroup: input.operationGroup,
|
|
48
|
+
ip: input.ip,
|
|
49
|
+
userAgent: input.userAgent,
|
|
50
|
+
input: this.serializePromptInput(input, params.systemPrompt),
|
|
51
|
+
inputImages: input.userImages?.map(image => ({
|
|
52
|
+
url: image.url,
|
|
53
|
+
})) ?? input.userContent
|
|
54
|
+
?.filter(part => part.type === "image")
|
|
55
|
+
.map(part => ({
|
|
56
|
+
url: part.imageUrl,
|
|
57
|
+
})),
|
|
58
|
+
inputFiles: input.inputFiles,
|
|
59
|
+
inputTokens: params.inputTokens,
|
|
60
|
+
outputTokens: params.outputTokens,
|
|
61
|
+
tokens: params.tokens,
|
|
62
|
+
startedAt: params.startedAt,
|
|
63
|
+
endedAt: params.endedAt,
|
|
64
|
+
responseTime: responseTimeMS !== undefined ? `${responseTimeMS}ms` : undefined,
|
|
65
|
+
responseTimeMS,
|
|
66
|
+
output: this.serializePromptOutput(params.output),
|
|
67
|
+
success: params.success,
|
|
68
|
+
errorMessage: params.errorMessage,
|
|
69
|
+
tenant: input.tenant,
|
|
70
|
+
user: input.user,
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
async registerPromptLog(input, params) {
|
|
74
|
+
if (!this._aiLogService) {
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
try {
|
|
78
|
+
await this._aiLogService.create(this.buildLogPayload(input, params));
|
|
79
|
+
}
|
|
80
|
+
catch (e) {
|
|
81
|
+
console.error("Error registerPromptLog", {
|
|
82
|
+
name: e?.name,
|
|
83
|
+
message: e?.message,
|
|
84
|
+
stack: e?.stack,
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
export default AbstractAiProvider;
|
|
90
|
+
export { AbstractAiProvider };
|
|
@@ -8,7 +8,7 @@ class DeepSeekAiProvider extends OpenAiProvider {
|
|
|
8
8
|
if (!model) {
|
|
9
9
|
throw new Error("DeepSeek model required");
|
|
10
10
|
}
|
|
11
|
-
super(apiKey, model, visionModel, aiLogService);
|
|
11
|
+
super(apiKey, model, visionModel, aiLogService, "deepseek");
|
|
12
12
|
if (!baseUrl) {
|
|
13
13
|
throw new Error("DeepSeek baseUrl required");
|
|
14
14
|
}
|
|
@@ -23,12 +23,6 @@ class DeepSeekAiProvider extends OpenAiProvider {
|
|
|
23
23
|
}
|
|
24
24
|
return this._client;
|
|
25
25
|
}
|
|
26
|
-
buildLogPayload(input, params) {
|
|
27
|
-
return {
|
|
28
|
-
...super.buildLogPayload(input, params),
|
|
29
|
-
provider: "deepseek",
|
|
30
|
-
};
|
|
31
|
-
}
|
|
32
26
|
}
|
|
33
27
|
export default DeepSeekAiProvider;
|
|
34
28
|
export { DeepSeekAiProvider };
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { GoogleGenAI } from "@google/genai";
|
|
2
2
|
import { toJSONSchema } from "zod";
|
|
3
3
|
import PromptAudioService from "../../services/PromptAudioService.js";
|
|
4
|
-
|
|
4
|
+
import AbstractAiProvider from "./AbstractAiProvider.js";
|
|
5
|
+
class GoogleAiProvider extends AbstractAiProvider {
|
|
5
6
|
constructor(apiKey, model, visionModel, aiLogService) {
|
|
6
7
|
if (!apiKey) {
|
|
7
8
|
throw new Error("Google AI apiKey required");
|
|
@@ -9,10 +10,10 @@ class GoogleAiProvider {
|
|
|
9
10
|
if (!model) {
|
|
10
11
|
throw new Error("Google AI model required");
|
|
11
12
|
}
|
|
13
|
+
super("googleai", aiLogService);
|
|
12
14
|
this._apiKey = apiKey;
|
|
13
15
|
this._model = model;
|
|
14
16
|
this._visionModel = visionModel;
|
|
15
|
-
this._aiLogService = aiLogService;
|
|
16
17
|
}
|
|
17
18
|
get model() {
|
|
18
19
|
if (!this._model) {
|
|
@@ -120,84 +121,6 @@ class GoogleAiProvider {
|
|
|
120
121
|
};
|
|
121
122
|
});
|
|
122
123
|
}
|
|
123
|
-
hasImageInput(input) {
|
|
124
|
-
if (input.userImages && input.userImages.length > 0) {
|
|
125
|
-
return true;
|
|
126
|
-
}
|
|
127
|
-
if (input.userContent?.some(part => part.type === 'image')) {
|
|
128
|
-
return true;
|
|
129
|
-
}
|
|
130
|
-
return input.history?.some(message => Array.isArray(message.content) && message.content.some(part => part.type === 'image')) ?? false;
|
|
131
|
-
}
|
|
132
|
-
serializePromptInput(input, systemPrompt) {
|
|
133
|
-
return JSON.stringify({
|
|
134
|
-
systemPrompt,
|
|
135
|
-
history: input.history,
|
|
136
|
-
userInput: input.userInput,
|
|
137
|
-
userContent: input.userContent,
|
|
138
|
-
memory: input.memory,
|
|
139
|
-
knowledgeBase: input.knowledgeBase,
|
|
140
|
-
tools: input.tools?.map(tool => ({
|
|
141
|
-
name: tool.name,
|
|
142
|
-
description: tool.description,
|
|
143
|
-
parameters: tool.parameters,
|
|
144
|
-
})),
|
|
145
|
-
});
|
|
146
|
-
}
|
|
147
|
-
serializePromptOutput(output) {
|
|
148
|
-
if (typeof output === "string") {
|
|
149
|
-
return output;
|
|
150
|
-
}
|
|
151
|
-
if (output === null || output === undefined) {
|
|
152
|
-
return undefined;
|
|
153
|
-
}
|
|
154
|
-
return JSON.stringify(output);
|
|
155
|
-
}
|
|
156
|
-
buildLogPayload(input, params) {
|
|
157
|
-
return {
|
|
158
|
-
provider: "googleai",
|
|
159
|
-
model: params.model,
|
|
160
|
-
operationTitle: input.operationTitle,
|
|
161
|
-
operationGroup: input.operationGroup,
|
|
162
|
-
ip: input.ip,
|
|
163
|
-
userAgent: input.userAgent,
|
|
164
|
-
input: this.serializePromptInput(input, params.systemPrompt),
|
|
165
|
-
inputImages: input.userImages?.map(image => ({
|
|
166
|
-
url: image.url,
|
|
167
|
-
})) ?? input.userContent
|
|
168
|
-
?.filter(part => part.type === "image")
|
|
169
|
-
.map(part => ({
|
|
170
|
-
url: part.imageUrl,
|
|
171
|
-
})),
|
|
172
|
-
inputFiles: input.inputFiles,
|
|
173
|
-
inputTokens: params.inputTokens,
|
|
174
|
-
outputTokens: params.outputTokens,
|
|
175
|
-
tokens: params.tokens,
|
|
176
|
-
startedAt: params.startedAt,
|
|
177
|
-
endedAt: params.endedAt,
|
|
178
|
-
responseTime: params.endedAt ? `${params.endedAt.getTime() - params.startedAt.getTime()}ms` : undefined,
|
|
179
|
-
output: this.serializePromptOutput(params.output),
|
|
180
|
-
success: params.success,
|
|
181
|
-
errorMessage: params.errorMessage,
|
|
182
|
-
tenant: input.tenant,
|
|
183
|
-
user: input.user,
|
|
184
|
-
};
|
|
185
|
-
}
|
|
186
|
-
async registerPromptLog(input, params) {
|
|
187
|
-
if (!this._aiLogService) {
|
|
188
|
-
return;
|
|
189
|
-
}
|
|
190
|
-
try {
|
|
191
|
-
await this._aiLogService.create(this.buildLogPayload(input, params));
|
|
192
|
-
}
|
|
193
|
-
catch (e) {
|
|
194
|
-
console.error("Error registerPromptLog", {
|
|
195
|
-
name: e?.name,
|
|
196
|
-
message: e?.message,
|
|
197
|
-
stack: e?.stack,
|
|
198
|
-
});
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
124
|
async generateEmbedding({ text, model = "text-embedding-004" }) {
|
|
202
125
|
const response = await this.client.models.embedContent({
|
|
203
126
|
model,
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { toJSONSchema } from "zod";
|
|
2
2
|
import PromptAudioService from "../../services/PromptAudioService.js";
|
|
3
|
-
|
|
3
|
+
import AbstractAiProvider from "./AbstractAiProvider.js";
|
|
4
|
+
class OllamaAiProvider extends AbstractAiProvider {
|
|
4
5
|
constructor(baseUrl, model, visionModel, embeddingModel, aiLogService) {
|
|
5
6
|
if (!baseUrl) {
|
|
6
7
|
throw new Error("Ollama AI baseUrl required");
|
|
@@ -8,11 +9,11 @@ class OllamaAiProvider {
|
|
|
8
9
|
if (!model) {
|
|
9
10
|
throw new Error("Ollama AI model required");
|
|
10
11
|
}
|
|
12
|
+
super("ollamaai", aiLogService);
|
|
11
13
|
this._baseUrl = baseUrl.replace(/\/+$/, "");
|
|
12
14
|
this._model = model;
|
|
13
15
|
this._visionModel = visionModel;
|
|
14
16
|
this._embeddingModel = embeddingModel;
|
|
15
|
-
this._aiLogService = aiLogService;
|
|
16
17
|
}
|
|
17
18
|
get model() {
|
|
18
19
|
if (!this._model) {
|
|
@@ -98,84 +99,6 @@ class OllamaAiProvider {
|
|
|
98
99
|
}
|
|
99
100
|
return messages;
|
|
100
101
|
}
|
|
101
|
-
hasImageInput(input) {
|
|
102
|
-
if (input.userImages && input.userImages.length > 0) {
|
|
103
|
-
return true;
|
|
104
|
-
}
|
|
105
|
-
if (input.userContent?.some(part => part.type === 'image')) {
|
|
106
|
-
return true;
|
|
107
|
-
}
|
|
108
|
-
return input.history?.some(message => Array.isArray(message.content) && message.content.some(part => part.type === 'image')) ?? false;
|
|
109
|
-
}
|
|
110
|
-
serializePromptInput(input, systemPrompt) {
|
|
111
|
-
return JSON.stringify({
|
|
112
|
-
systemPrompt,
|
|
113
|
-
history: input.history,
|
|
114
|
-
userInput: input.userInput,
|
|
115
|
-
userContent: input.userContent,
|
|
116
|
-
memory: input.memory,
|
|
117
|
-
knowledgeBase: input.knowledgeBase,
|
|
118
|
-
tools: input.tools?.map(tool => ({
|
|
119
|
-
name: tool.name,
|
|
120
|
-
description: tool.description,
|
|
121
|
-
parameters: tool.parameters,
|
|
122
|
-
})),
|
|
123
|
-
});
|
|
124
|
-
}
|
|
125
|
-
serializePromptOutput(output) {
|
|
126
|
-
if (typeof output === "string") {
|
|
127
|
-
return output;
|
|
128
|
-
}
|
|
129
|
-
if (output === null || output === undefined) {
|
|
130
|
-
return undefined;
|
|
131
|
-
}
|
|
132
|
-
return JSON.stringify(output);
|
|
133
|
-
}
|
|
134
|
-
buildLogPayload(input, params) {
|
|
135
|
-
return {
|
|
136
|
-
provider: "ollamaai",
|
|
137
|
-
model: params.model,
|
|
138
|
-
operationTitle: input.operationTitle,
|
|
139
|
-
operationGroup: input.operationGroup,
|
|
140
|
-
ip: input.ip,
|
|
141
|
-
userAgent: input.userAgent,
|
|
142
|
-
input: this.serializePromptInput(input, params.systemPrompt),
|
|
143
|
-
inputImages: input.userImages?.map(image => ({
|
|
144
|
-
url: image.url,
|
|
145
|
-
})) ?? input.userContent
|
|
146
|
-
?.filter(part => part.type === "image")
|
|
147
|
-
.map(part => ({
|
|
148
|
-
url: part.imageUrl,
|
|
149
|
-
})),
|
|
150
|
-
inputFiles: input.inputFiles,
|
|
151
|
-
inputTokens: params.inputTokens,
|
|
152
|
-
outputTokens: params.outputTokens,
|
|
153
|
-
tokens: params.tokens,
|
|
154
|
-
startedAt: params.startedAt,
|
|
155
|
-
endedAt: params.endedAt,
|
|
156
|
-
responseTime: params.endedAt ? `${params.endedAt.getTime() - params.startedAt.getTime()}ms` : undefined,
|
|
157
|
-
output: this.serializePromptOutput(params.output),
|
|
158
|
-
success: params.success,
|
|
159
|
-
errorMessage: params.errorMessage,
|
|
160
|
-
tenant: input.tenant,
|
|
161
|
-
user: input.user,
|
|
162
|
-
};
|
|
163
|
-
}
|
|
164
|
-
async registerPromptLog(input, params) {
|
|
165
|
-
if (!this._aiLogService) {
|
|
166
|
-
return;
|
|
167
|
-
}
|
|
168
|
-
try {
|
|
169
|
-
await this._aiLogService.create(this.buildLogPayload(input, params));
|
|
170
|
-
}
|
|
171
|
-
catch (e) {
|
|
172
|
-
console.error("Error registerPromptLog", {
|
|
173
|
-
name: e?.name,
|
|
174
|
-
message: e?.message,
|
|
175
|
-
stack: e?.stack,
|
|
176
|
-
});
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
102
|
async generateEmbedding({ text, model }) {
|
|
180
103
|
const response = await this.post("/api/embed", {
|
|
181
104
|
model: model ?? this.embeddingModel,
|
|
@@ -1,18 +1,19 @@
|
|
|
1
1
|
import OpenAI from "openai";
|
|
2
2
|
import { zodResponseFormat } from "openai/helpers/zod";
|
|
3
3
|
import PromptAudioService from "../../services/PromptAudioService.js";
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
import AbstractAiProvider from "./AbstractAiProvider.js";
|
|
5
|
+
class OpenAiProvider extends AbstractAiProvider {
|
|
6
|
+
constructor(apiKey, model, visionModel, aiLogService, providerName = "openai") {
|
|
6
7
|
if (!apiKey) {
|
|
7
8
|
throw new Error("OpenAI apiKey required");
|
|
8
9
|
}
|
|
9
10
|
if (!model) {
|
|
10
11
|
throw new Error("OpenAI model required");
|
|
11
12
|
}
|
|
13
|
+
super(providerName, aiLogService);
|
|
12
14
|
this._apiKey = apiKey;
|
|
13
15
|
this._model = model;
|
|
14
16
|
this._visionModel = visionModel;
|
|
15
|
-
this._aiLogService = aiLogService;
|
|
16
17
|
}
|
|
17
18
|
get model() {
|
|
18
19
|
if (!this._model) {
|
|
@@ -76,84 +77,6 @@ class OpenAiProvider {
|
|
|
76
77
|
: this.mapContentParts(message.content)
|
|
77
78
|
}));
|
|
78
79
|
}
|
|
79
|
-
hasImageInput(input) {
|
|
80
|
-
if (input.userImages && input.userImages.length > 0) {
|
|
81
|
-
return true;
|
|
82
|
-
}
|
|
83
|
-
if (input.userContent?.some(part => part.type === 'image')) {
|
|
84
|
-
return true;
|
|
85
|
-
}
|
|
86
|
-
return input.history?.some(message => Array.isArray(message.content) && message.content.some(part => part.type === 'image')) ?? false;
|
|
87
|
-
}
|
|
88
|
-
serializePromptInput(input, systemPrompt) {
|
|
89
|
-
return JSON.stringify({
|
|
90
|
-
systemPrompt,
|
|
91
|
-
history: input.history,
|
|
92
|
-
userInput: input.userInput,
|
|
93
|
-
userContent: input.userContent,
|
|
94
|
-
memory: input.memory,
|
|
95
|
-
knowledgeBase: input.knowledgeBase,
|
|
96
|
-
tools: input.tools?.map(tool => ({
|
|
97
|
-
name: tool.name,
|
|
98
|
-
description: tool.description,
|
|
99
|
-
parameters: tool.parameters,
|
|
100
|
-
})),
|
|
101
|
-
});
|
|
102
|
-
}
|
|
103
|
-
serializePromptOutput(output) {
|
|
104
|
-
if (typeof output === "string") {
|
|
105
|
-
return output;
|
|
106
|
-
}
|
|
107
|
-
if (output === null || output === undefined) {
|
|
108
|
-
return undefined;
|
|
109
|
-
}
|
|
110
|
-
return JSON.stringify(output);
|
|
111
|
-
}
|
|
112
|
-
buildLogPayload(input, params) {
|
|
113
|
-
return {
|
|
114
|
-
provider: "openai",
|
|
115
|
-
model: params.model,
|
|
116
|
-
operationTitle: input.operationTitle,
|
|
117
|
-
operationGroup: input.operationGroup,
|
|
118
|
-
ip: input.ip,
|
|
119
|
-
userAgent: input.userAgent,
|
|
120
|
-
input: this.serializePromptInput(input, params.systemPrompt),
|
|
121
|
-
inputImages: input.userImages?.map(image => ({
|
|
122
|
-
url: image.url,
|
|
123
|
-
})) ?? input.userContent
|
|
124
|
-
?.filter(part => part.type === "image")
|
|
125
|
-
.map(part => ({
|
|
126
|
-
url: part.imageUrl,
|
|
127
|
-
})),
|
|
128
|
-
inputFiles: input.inputFiles,
|
|
129
|
-
inputTokens: params.inputTokens,
|
|
130
|
-
outputTokens: params.outputTokens,
|
|
131
|
-
tokens: params.tokens,
|
|
132
|
-
startedAt: params.startedAt,
|
|
133
|
-
endedAt: params.endedAt,
|
|
134
|
-
responseTime: params.endedAt ? `${params.endedAt.getTime() - params.startedAt.getTime()}ms` : undefined,
|
|
135
|
-
output: this.serializePromptOutput(params.output),
|
|
136
|
-
success: params.success,
|
|
137
|
-
errorMessage: params.errorMessage,
|
|
138
|
-
tenant: input.tenant,
|
|
139
|
-
user: input.user,
|
|
140
|
-
};
|
|
141
|
-
}
|
|
142
|
-
async registerPromptLog(input, params) {
|
|
143
|
-
if (!this._aiLogService) {
|
|
144
|
-
return;
|
|
145
|
-
}
|
|
146
|
-
try {
|
|
147
|
-
await this._aiLogService.create(this.buildLogPayload(input, params));
|
|
148
|
-
}
|
|
149
|
-
catch (e) {
|
|
150
|
-
console.error("Error registerPromptLog", {
|
|
151
|
-
name: e?.name,
|
|
152
|
-
message: e?.message,
|
|
153
|
-
stack: e?.stack,
|
|
154
|
-
});
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
80
|
async generateEmbedding({ text, model = "text-embedding-ada-002" }) {
|
|
158
81
|
const response = await this.client.embeddings.create({
|
|
159
82
|
model: model,
|
|
@@ -31,6 +31,7 @@ class AILogSqliteRepository extends AbstractSqliteRepository {
|
|
|
31
31
|
{ name: "startedAt", type: "TEXT", unique: false, primary: false },
|
|
32
32
|
{ name: "endedAt", type: "TEXT", unique: false, primary: false },
|
|
33
33
|
{ name: "responseTime", type: "TEXT", unique: false, primary: false },
|
|
34
|
+
{ name: "responseTimeMS", type: "REAL", unique: false, primary: false },
|
|
34
35
|
{ name: "output", type: "TEXT", unique: false, primary: false },
|
|
35
36
|
{ name: "success", type: "TEXT", unique: false, primary: false },
|
|
36
37
|
{ name: "statusCode", type: "INTEGER", unique: false, primary: false },
|
|
@@ -10,12 +10,12 @@ async function AILogFastifyRoutes(fastify, options) {
|
|
|
10
10
|
fastify.get('/api/ailog/:id', { schema: schemas.findByIdSchema }, (req, rep) => controller.findById(req, rep));
|
|
11
11
|
fastify.get('/api/ailog/find-one', { schema: schemas.findOneSchema }, (req, rep) => controller.findOne(req, rep));
|
|
12
12
|
fastify.get('/api/ailog/group-by', { schema: schemas.groupBySchema }, (req, rep) => controller.groupBy(req, rep));
|
|
13
|
-
fastify.post('/api/ailog', {
|
|
14
|
-
fastify.put('/api/ailog/:id', {
|
|
15
|
-
fastify.patch('/api/ailog/:id', {
|
|
16
|
-
fastify.delete('/api/ailog/:id', {
|
|
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
17
|
fastify.get('/api/ailog/export', (req, rep) => controller.export(req, rep));
|
|
18
|
-
fastify.post('/api/ailog/import', (req,
|
|
18
|
+
// fastify.post('/api/ailog/import', (req,rep) => controller.import(req,rep))
|
|
19
19
|
}
|
|
20
20
|
export default AILogFastifyRoutes;
|
|
21
21
|
export { AILogFastifyRoutes };
|
|
@@ -27,6 +27,7 @@ const AILogBaseSchema = z.object({
|
|
|
27
27
|
startedAt: z.coerce.date().nullable().optional(),
|
|
28
28
|
endedAt: z.coerce.date().nullable().optional(),
|
|
29
29
|
responseTime: z.string().optional(),
|
|
30
|
+
responseTimeMS: z.number().nullable().optional(),
|
|
30
31
|
output: z.string().optional(),
|
|
31
32
|
success: z.boolean().optional(),
|
|
32
33
|
statusCode: z.number().nullable().optional(),
|
|
@@ -38,7 +39,9 @@ const AILogSchema = AILogBaseSchema
|
|
|
38
39
|
.extend({
|
|
39
40
|
_id: z.coerce.string(),
|
|
40
41
|
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
|
+
user: z.object({ _id: z.coerce.string(), username: z.string() }).nullable().optional(),
|
|
43
|
+
createdAt: z.coerce.date().nullable().optional(),
|
|
44
|
+
updatedAt: z.coerce.date().nullable().optional(),
|
|
42
45
|
});
|
|
43
46
|
export default AILogSchema;
|
|
44
47
|
export { AILogSchema, AILogBaseSchema };
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"publishConfig": {
|
|
4
4
|
"access": "public"
|
|
5
5
|
},
|
|
6
|
-
"version": "3.
|
|
6
|
+
"version": "3.52.0",
|
|
7
7
|
"description": "Ai utils",
|
|
8
8
|
"main": "dist/index.js",
|
|
9
9
|
"types": "types/index.d.ts",
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
"author": "Cristian Incarnato & Drax Team",
|
|
19
19
|
"license": "ISC",
|
|
20
20
|
"dependencies": {
|
|
21
|
-
"@drax/ai-share": "^3.
|
|
21
|
+
"@drax/ai-share": "^3.52.0",
|
|
22
22
|
"@drax/crud-back": "^3.51.0",
|
|
23
23
|
"mongoose": "^8.23.0",
|
|
24
24
|
"mongoose-paginate-v2": "^1.8.3"
|
|
@@ -46,5 +46,5 @@
|
|
|
46
46
|
"typescript": "^5.9.3",
|
|
47
47
|
"vitest": "^3.0.8"
|
|
48
48
|
},
|
|
49
|
-
"gitHead": "
|
|
49
|
+
"gitHead": "3c78a6b0da9e2b69b231c57730c450964d532978"
|
|
50
50
|
}
|
|
@@ -15,9 +15,9 @@ class AILogController extends AbstractFastifyController<IAILog, IAILogBase, IAIL
|
|
|
15
15
|
this.tenantSetter = true;
|
|
16
16
|
this.tenantAssert = true;
|
|
17
17
|
|
|
18
|
-
this.userFilter =
|
|
18
|
+
this.userFilter = false;
|
|
19
19
|
this.userSetter = true;
|
|
20
|
-
this.userAssert =
|
|
20
|
+
this.userAssert = false;
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
}
|
package/src/models/AILogModel.ts
CHANGED
|
@@ -33,6 +33,7 @@ const AILogSchema = new mongoose.Schema<IAILog>({
|
|
|
33
33
|
startedAt: {type: Date, required: false, index: false, unique: false },
|
|
34
34
|
endedAt: {type: Date, required: false, index: false, unique: false },
|
|
35
35
|
responseTime: {type: String, required: false, index: false, unique: false },
|
|
36
|
+
responseTimeMS: {type: Number, required: false, index: false, unique: false },
|
|
36
37
|
output: {type: String, required: false, index: false, unique: false },
|
|
37
38
|
success: {type: Boolean, required: false, index: false, unique: false },
|
|
38
39
|
statusCode: {type: Number, required: false, index: false, unique: false },
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import type {IAILogBase} from "@drax/ai-share";
|
|
2
|
+
import type {
|
|
3
|
+
IAIProvider,
|
|
4
|
+
IPromptParams,
|
|
5
|
+
IPromptResponse,
|
|
6
|
+
} from "../../interfaces/IAIProvider.js";
|
|
7
|
+
import type {AILogService} from "../../services/AILogService.js";
|
|
8
|
+
|
|
9
|
+
type PromptLogParams = {
|
|
10
|
+
model: string,
|
|
11
|
+
systemPrompt: string,
|
|
12
|
+
startedAt: Date,
|
|
13
|
+
endedAt?: Date,
|
|
14
|
+
inputTokens?: number,
|
|
15
|
+
outputTokens?: number,
|
|
16
|
+
tokens?: number,
|
|
17
|
+
output?: unknown,
|
|
18
|
+
success: boolean,
|
|
19
|
+
errorMessage?: string,
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
abstract class AbstractAiProvider implements IAIProvider {
|
|
23
|
+
protected readonly providerName: string
|
|
24
|
+
protected _aiLogService?: AILogService
|
|
25
|
+
|
|
26
|
+
protected constructor(providerName: string, aiLogService?: AILogService) {
|
|
27
|
+
this.providerName = providerName
|
|
28
|
+
this._aiLogService = aiLogService
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
abstract prompt(input: IPromptParams): Promise<IPromptResponse>
|
|
32
|
+
|
|
33
|
+
protected hasImageInput(input: IPromptParams){
|
|
34
|
+
if(input.userImages && input.userImages.length > 0){
|
|
35
|
+
return true
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if(input.userContent?.some(part => part.type === 'image')){
|
|
39
|
+
return true
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return input.history?.some(message =>
|
|
43
|
+
Array.isArray(message.content) && message.content.some(part => part.type === 'image')
|
|
44
|
+
) ?? false
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
protected serializePromptInput(input: IPromptParams, systemPrompt: string){
|
|
48
|
+
return JSON.stringify({
|
|
49
|
+
systemPrompt,
|
|
50
|
+
history: input.history,
|
|
51
|
+
userInput: input.userInput,
|
|
52
|
+
userContent: input.userContent,
|
|
53
|
+
memory: input.memory,
|
|
54
|
+
knowledgeBase: input.knowledgeBase,
|
|
55
|
+
tools: input.tools?.map(tool => ({
|
|
56
|
+
name: tool.name,
|
|
57
|
+
description: tool.description,
|
|
58
|
+
parameters: tool.parameters,
|
|
59
|
+
})),
|
|
60
|
+
})
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
protected serializePromptOutput(output: unknown){
|
|
64
|
+
if (typeof output === "string") {
|
|
65
|
+
return output
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (output === null || output === undefined) {
|
|
69
|
+
return undefined
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return JSON.stringify(output)
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
protected buildLogPayload(input: IPromptParams, params: PromptLogParams): IAILogBase {
|
|
76
|
+
const responseTimeMS = params.endedAt
|
|
77
|
+
? params.endedAt.getTime() - params.startedAt.getTime()
|
|
78
|
+
: undefined
|
|
79
|
+
|
|
80
|
+
return {
|
|
81
|
+
provider: this.providerName,
|
|
82
|
+
model: params.model,
|
|
83
|
+
operationTitle: input.operationTitle,
|
|
84
|
+
operationGroup: input.operationGroup,
|
|
85
|
+
ip: input.ip,
|
|
86
|
+
userAgent: input.userAgent,
|
|
87
|
+
input: this.serializePromptInput(input, params.systemPrompt),
|
|
88
|
+
inputImages: input.userImages?.map(image => ({
|
|
89
|
+
url: image.url,
|
|
90
|
+
})) ?? input.userContent
|
|
91
|
+
?.filter(part => part.type === "image")
|
|
92
|
+
.map(part => ({
|
|
93
|
+
url: part.imageUrl,
|
|
94
|
+
})),
|
|
95
|
+
inputFiles: input.inputFiles,
|
|
96
|
+
inputTokens: params.inputTokens,
|
|
97
|
+
outputTokens: params.outputTokens,
|
|
98
|
+
tokens: params.tokens,
|
|
99
|
+
startedAt: params.startedAt,
|
|
100
|
+
endedAt: params.endedAt,
|
|
101
|
+
responseTime: responseTimeMS !== undefined ? `${responseTimeMS}ms` : undefined,
|
|
102
|
+
responseTimeMS,
|
|
103
|
+
output: this.serializePromptOutput(params.output),
|
|
104
|
+
success: params.success,
|
|
105
|
+
errorMessage: params.errorMessage,
|
|
106
|
+
tenant: input.tenant,
|
|
107
|
+
user: input.user,
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
protected async registerPromptLog(input: IPromptParams, params: PromptLogParams){
|
|
112
|
+
if(!this._aiLogService){
|
|
113
|
+
return
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
try{
|
|
117
|
+
await this._aiLogService.create(this.buildLogPayload(input, params))
|
|
118
|
+
}catch(e: any){
|
|
119
|
+
console.error("Error registerPromptLog", {
|
|
120
|
+
name: e?.name,
|
|
121
|
+
message: e?.message,
|
|
122
|
+
stack: e?.stack,
|
|
123
|
+
})
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export default AbstractAiProvider
|
|
129
|
+
export {AbstractAiProvider}
|