@drax/ai-back 3.41.0 → 3.42.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 (100) hide show
  1. package/dist/agents/DraxAgent.js +1 -1
  2. package/dist/config/ElevenLabsTTSConfig.js +10 -0
  3. package/dist/controllers/AICrudController.js +1 -1
  4. package/dist/controllers/AIGenericController.js +1 -1
  5. package/dist/controllers/TTSGenericController.js +61 -0
  6. package/dist/factory/ElevenLabsTTSProviderFactory.js +13 -0
  7. package/dist/factory/TTSProviderFactory.js +27 -0
  8. package/dist/factory/ai/AiProviderFactory.js +30 -0
  9. package/dist/factory/ai/DeepSeekAiProviderFactory.js +14 -0
  10. package/dist/factory/ai/GoogleAiProviderFactory.js +14 -0
  11. package/dist/factory/ai/OllamaAiProviderFactory.js +14 -0
  12. package/dist/factory/ai/OpenAiProviderFactory.js +14 -0
  13. package/dist/factory/tts/ElevenLabsTTSProviderFactory.js +13 -0
  14. package/dist/factory/tts/TTSProviderFactory.js +27 -0
  15. package/dist/index.js +22 -13
  16. package/dist/interfaces/ITTSProvider.js +1 -0
  17. package/dist/permissions/TTSPermissions.js +6 -0
  18. package/dist/providers/ElevenLabsTTSProvider.js +108 -0
  19. package/dist/providers/ai/DeepSeekAiProvider.js +34 -0
  20. package/dist/providers/ai/GoogleAiProvider.js +367 -0
  21. package/dist/providers/ai/OllamaAiProvider.js +342 -0
  22. package/dist/providers/ai/OpenAiProvider.js +302 -0
  23. package/dist/providers/tts/ElevenLabsTTSProvider.js +108 -0
  24. package/dist/routes/TTSRoutes.js +8 -0
  25. package/dist/schemas/TTSRequestSchema.js +24 -0
  26. package/dist/services/TTSGenericService.js +21 -0
  27. package/package.json +2 -2
  28. package/src/agents/DraxAgent.ts +1 -1
  29. package/src/config/ElevenLabsTTSConfig.ts +13 -0
  30. package/src/controllers/AICrudController.ts +1 -1
  31. package/src/controllers/AIGenericController.ts +1 -1
  32. package/src/controllers/TTSGenericController.ts +70 -0
  33. package/src/factory/{AiProviderFactory.ts → ai/AiProviderFactory.ts} +3 -3
  34. package/src/factory/ai/DeepSeekAiProviderFactory.ts +27 -0
  35. package/src/factory/{GoogleAiProviderFactory.ts → ai/GoogleAiProviderFactory.ts} +4 -4
  36. package/src/factory/{OllamaAiProviderFactory.ts → ai/OllamaAiProviderFactory.ts} +4 -4
  37. package/src/factory/{OpenAiProviderFactory.ts → ai/OpenAiProviderFactory.ts} +4 -4
  38. package/src/factory/tts/ElevenLabsTTSProviderFactory.ts +26 -0
  39. package/src/factory/tts/TTSProviderFactory.ts +42 -0
  40. package/src/index.ts +52 -11
  41. package/src/interfaces/ITTSProvider.ts +47 -0
  42. package/src/permissions/AIPermissions.ts +0 -1
  43. package/src/permissions/TTSPermissions.ts +8 -0
  44. package/src/providers/{DeepSeekProvider.ts → ai/DeepSeekAiProvider.ts} +5 -5
  45. package/src/providers/{GoogleAiProvider.ts → ai/GoogleAiProvider.ts} +2 -2
  46. package/src/providers/{OllamaAiProvider.ts → ai/OllamaAiProvider.ts} +2 -2
  47. package/src/providers/{OpenAiProvider.ts → ai/OpenAiProvider.ts} +2 -2
  48. package/src/providers/tts/ElevenLabsTTSProvider.ts +132 -0
  49. package/src/routes/TTSRoutes.ts +13 -0
  50. package/src/schemas/TTSRequestSchema.ts +38 -0
  51. package/src/services/TTSGenericService.ts +41 -0
  52. package/test/DeepSeekProvider.test.ts +4 -4
  53. package/tsconfig.tsbuildinfo +1 -1
  54. package/types/config/ElevenLabsTTSConfig.d.ts +10 -0
  55. package/types/config/ElevenLabsTTSConfig.d.ts.map +1 -0
  56. package/types/controllers/TTSGenericController.d.ts +11 -0
  57. package/types/controllers/TTSGenericController.d.ts.map +1 -0
  58. package/types/factory/ElevenLabsTTSProviderFactory.d.ts +8 -0
  59. package/types/factory/ElevenLabsTTSProviderFactory.d.ts.map +1 -0
  60. package/types/factory/TTSProviderFactory.d.ts +15 -0
  61. package/types/factory/TTSProviderFactory.d.ts.map +1 -0
  62. package/types/factory/ai/AiProviderFactory.d.ts +8 -0
  63. package/types/factory/ai/AiProviderFactory.d.ts.map +1 -0
  64. package/types/factory/ai/DeepSeekAiProviderFactory.d.ts +8 -0
  65. package/types/factory/ai/DeepSeekAiProviderFactory.d.ts.map +1 -0
  66. package/types/factory/ai/GoogleAiProviderFactory.d.ts +8 -0
  67. package/types/factory/ai/GoogleAiProviderFactory.d.ts.map +1 -0
  68. package/types/factory/ai/OllamaAiProviderFactory.d.ts +8 -0
  69. package/types/factory/ai/OllamaAiProviderFactory.d.ts.map +1 -0
  70. package/types/factory/ai/OpenAiProviderFactory.d.ts +8 -0
  71. package/types/factory/ai/OpenAiProviderFactory.d.ts.map +1 -0
  72. package/types/factory/tts/ElevenLabsTTSProviderFactory.d.ts +8 -0
  73. package/types/factory/tts/ElevenLabsTTSProviderFactory.d.ts.map +1 -0
  74. package/types/factory/tts/TTSProviderFactory.d.ts +15 -0
  75. package/types/factory/tts/TTSProviderFactory.d.ts.map +1 -0
  76. package/types/index.d.ts +24 -11
  77. package/types/index.d.ts.map +1 -1
  78. package/types/interfaces/ITTSProvider.d.ts +39 -0
  79. package/types/interfaces/ITTSProvider.d.ts.map +1 -0
  80. package/types/permissions/TTSPermissions.d.ts +6 -0
  81. package/types/permissions/TTSPermissions.d.ts.map +1 -0
  82. package/types/providers/ElevenLabsTTSProvider.d.ts +38 -0
  83. package/types/providers/ElevenLabsTTSProvider.d.ts.map +1 -0
  84. package/types/providers/ai/DeepSeekAiProvider.d.ts +24 -0
  85. package/types/providers/ai/DeepSeekAiProvider.d.ts.map +1 -0
  86. package/types/providers/ai/GoogleAiProvider.d.ts +63 -0
  87. package/types/providers/ai/GoogleAiProvider.d.ts.map +1 -0
  88. package/types/providers/ai/OllamaAiProvider.d.ts +78 -0
  89. package/types/providers/ai/OllamaAiProvider.d.ts.map +1 -0
  90. package/types/providers/ai/OpenAiProvider.d.ts +97 -0
  91. package/types/providers/ai/OpenAiProvider.d.ts.map +1 -0
  92. package/types/providers/tts/ElevenLabsTTSProvider.d.ts +38 -0
  93. package/types/providers/tts/ElevenLabsTTSProvider.d.ts.map +1 -0
  94. package/types/routes/TTSRoutes.d.ts +4 -0
  95. package/types/routes/TTSRoutes.d.ts.map +1 -0
  96. package/types/schemas/TTSRequestSchema.d.ts +37 -0
  97. package/types/schemas/TTSRequestSchema.d.ts.map +1 -0
  98. package/types/services/TTSGenericService.d.ts +17 -0
  99. package/types/services/TTSGenericService.d.ts.map +1 -0
  100. package/src/factory/DeepSeekProviderFactory.ts +0 -27
@@ -0,0 +1,302 @@
1
+ import OpenAI from "openai";
2
+ import { zodResponseFormat } from "openai/helpers/zod";
3
+ class OpenAiProvider {
4
+ constructor(apiKey, model, visionModel, aiLogService) {
5
+ if (!apiKey) {
6
+ throw new Error("OpenAI apiKey required");
7
+ }
8
+ if (!model) {
9
+ throw new Error("OpenAI model required");
10
+ }
11
+ this._apiKey = apiKey;
12
+ this._model = model;
13
+ this._visionModel = visionModel;
14
+ this._aiLogService = aiLogService;
15
+ }
16
+ get model() {
17
+ if (!this._model) {
18
+ throw new Error("OpenAI model not found");
19
+ }
20
+ return this._model;
21
+ }
22
+ get client() {
23
+ if (!this._client) {
24
+ this._client = new OpenAI({
25
+ apiKey: this._apiKey,
26
+ });
27
+ }
28
+ return this._client;
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
+ tools: input.tools?.map(tool => ({
96
+ name: tool.name,
97
+ description: tool.description,
98
+ parameters: tool.parameters,
99
+ })),
100
+ });
101
+ }
102
+ serializePromptOutput(output) {
103
+ if (typeof output === "string") {
104
+ return output;
105
+ }
106
+ if (output === null || output === undefined) {
107
+ return undefined;
108
+ }
109
+ return JSON.stringify(output);
110
+ }
111
+ buildLogPayload(input, params) {
112
+ return {
113
+ provider: "openai",
114
+ model: params.model,
115
+ operationTitle: input.operationTitle,
116
+ operationGroup: input.operationGroup,
117
+ ip: input.ip,
118
+ userAgent: input.userAgent,
119
+ input: this.serializePromptInput(input, params.systemPrompt),
120
+ inputImages: input.userImages?.map(image => ({
121
+ url: image.url,
122
+ })) ?? input.userContent
123
+ ?.filter(part => part.type === "image")
124
+ .map(part => ({
125
+ url: part.imageUrl,
126
+ })),
127
+ inputFiles: input.inputFiles,
128
+ inputTokens: params.inputTokens,
129
+ outputTokens: params.outputTokens,
130
+ tokens: params.tokens,
131
+ startedAt: params.startedAt,
132
+ endedAt: params.endedAt,
133
+ responseTime: params.endedAt ? `${params.endedAt.getTime() - params.startedAt.getTime()}ms` : undefined,
134
+ output: this.serializePromptOutput(params.output),
135
+ success: params.success,
136
+ errorMessage: params.errorMessage,
137
+ tenant: input.tenant,
138
+ user: input.user,
139
+ };
140
+ }
141
+ async registerPromptLog(input, params) {
142
+ if (!this._aiLogService) {
143
+ return;
144
+ }
145
+ try {
146
+ await this._aiLogService.create(this.buildLogPayload(input, params));
147
+ }
148
+ catch (e) {
149
+ console.error("Error registerPromptLog", {
150
+ name: e?.name,
151
+ message: e?.message,
152
+ stack: e?.stack,
153
+ });
154
+ }
155
+ }
156
+ async generateEmbedding({ text, model = "text-embedding-ada-002" }) {
157
+ const response = await this.client.embeddings.create({
158
+ model: model,
159
+ input: text,
160
+ });
161
+ return response.data[0].embedding;
162
+ }
163
+ mapTools(tools = []) {
164
+ return tools.map(tool => ({
165
+ type: "function",
166
+ function: {
167
+ name: tool.name,
168
+ description: tool.description,
169
+ parameters: tool.parameters ?? {
170
+ type: "object",
171
+ properties: {},
172
+ additionalProperties: false,
173
+ },
174
+ },
175
+ }));
176
+ }
177
+ parseToolArguments(args) {
178
+ if (!args) {
179
+ return {};
180
+ }
181
+ try {
182
+ return JSON.parse(args);
183
+ }
184
+ catch (e) {
185
+ throw new Error(`Invalid tool arguments: ${args}`);
186
+ }
187
+ }
188
+ serializeToolOutput(output) {
189
+ if (typeof output === "string") {
190
+ return output;
191
+ }
192
+ if (output === undefined) {
193
+ return "";
194
+ }
195
+ return JSON.stringify(output);
196
+ }
197
+ async buildToolMessages(toolCalls = [], tools = []) {
198
+ const toolMessages = [];
199
+ for (const toolCall of toolCalls) {
200
+ const toolName = toolCall.function?.name;
201
+ const tool = tools.find(t => t.name === toolName);
202
+ if (!tool) {
203
+ throw new Error(`Tool not found: ${toolName}`);
204
+ }
205
+ const args = this.parseToolArguments(toolCall.function?.arguments);
206
+ const output = await tool.execute(args);
207
+ toolMessages.push({
208
+ role: "tool",
209
+ tool_call_id: toolCall.id,
210
+ content: this.serializeToolOutput(output),
211
+ });
212
+ }
213
+ return toolMessages;
214
+ }
215
+ async prompt(input) {
216
+ if (!input.systemPrompt) {
217
+ throw new Error("systemPrompt required");
218
+ }
219
+ let systemPrompt = input.systemPrompt;
220
+ if (input.memory && input.memory.length > 0) {
221
+ systemPrompt += `\n\n ${input.memoryHeader ?? '[MEMORIA]'}\n ${input.memory.map(m => `${m.key}: ${m.value}`).join('\n')}`;
222
+ }
223
+ if (input.knowledgeBase && input.knowledgeBase.length > 0) {
224
+ systemPrompt += `\n\n${input.knowledgeBaseHeader ?? '[BASE DE CONOCIMIENTO]'}\n ${input.knowledgeBase.join('\n')}`;
225
+ }
226
+ const userInput = this.buildUserContent(input);
227
+ const model = input.model ?? (this.hasImageInput(input) ? this.visionModel ?? this.model : this.model);
228
+ const startedAt = new Date();
229
+ const startTime = performance.now();
230
+ let tokens = 0;
231
+ let inputTokens = 0;
232
+ let outputTokens = 0;
233
+ try {
234
+ const messages = [
235
+ { role: 'system', content: systemPrompt },
236
+ ...this.mapHistory(input.history),
237
+ { role: 'user', content: userInput },
238
+ ];
239
+ const tools = input.tools ?? [];
240
+ const maxIterations = input.toolMaxIterations ?? 5;
241
+ let output;
242
+ for (let iteration = 0; iteration < maxIterations; iteration++) {
243
+ const chatCompletion = await this.client.chat.completions.create({
244
+ messages,
245
+ ...(input.zodSchema ? { response_format: zodResponseFormat(input.zodSchema, "event") } : {}),
246
+ ...(input.jsonSchema ? { response_format: input.jsonSchema } : {}),
247
+ ...(tools.length > 0 ? { tools: this.mapTools(tools) } : {}),
248
+ model: model,
249
+ });
250
+ tokens += chatCompletion.usage?.total_tokens ?? 0;
251
+ inputTokens += chatCompletion.usage?.prompt_tokens ?? 0;
252
+ outputTokens += chatCompletion.usage?.completion_tokens ?? 0;
253
+ const message = chatCompletion.choices[0].message;
254
+ const toolCalls = message.tool_calls ?? [];
255
+ if (toolCalls.length === 0) {
256
+ output = message.content;
257
+ break;
258
+ }
259
+ messages.push(message);
260
+ messages.push(...await this.buildToolMessages(toolCalls, tools));
261
+ }
262
+ if (output === undefined) {
263
+ throw new Error(`Tool max iterations reached: ${maxIterations}`);
264
+ }
265
+ const endTime = performance.now();
266
+ const time = endTime - startTime;
267
+ const endedAt = new Date();
268
+ await this.registerPromptLog(input, {
269
+ model,
270
+ systemPrompt,
271
+ startedAt,
272
+ endedAt,
273
+ inputTokens,
274
+ outputTokens,
275
+ tokens,
276
+ output,
277
+ success: true,
278
+ });
279
+ return {
280
+ output,
281
+ tokens,
282
+ inputTokens,
283
+ outputTokens,
284
+ time
285
+ };
286
+ }
287
+ catch (e) {
288
+ const endedAt = new Date();
289
+ await this.registerPromptLog(input, {
290
+ model,
291
+ systemPrompt,
292
+ startedAt,
293
+ endedAt,
294
+ success: false,
295
+ errorMessage: e?.message,
296
+ });
297
+ throw e;
298
+ }
299
+ }
300
+ }
301
+ export default OpenAiProvider;
302
+ export { OpenAiProvider };
@@ -0,0 +1,108 @@
1
+ class ElevenLabsTTSProvider {
2
+ constructor(apiKey, model, voiceId, baseUrl = "https://api.elevenlabs.io", outputFormat) {
3
+ if (!apiKey) {
4
+ throw new Error("ElevenLabs apiKey required");
5
+ }
6
+ if (!model) {
7
+ throw new Error("ElevenLabs model required");
8
+ }
9
+ if (!voiceId) {
10
+ throw new Error("ElevenLabs voiceId required");
11
+ }
12
+ this._apiKey = apiKey;
13
+ this._model = model;
14
+ this._voiceId = voiceId;
15
+ this._baseUrl = baseUrl.replace(/\/+$/, "");
16
+ this._outputFormat = outputFormat;
17
+ }
18
+ get model() {
19
+ if (!this._model) {
20
+ throw new Error("ElevenLabs model not found");
21
+ }
22
+ return this._model;
23
+ }
24
+ get voiceId() {
25
+ if (!this._voiceId) {
26
+ throw new Error("ElevenLabs voiceId not found");
27
+ }
28
+ return this._voiceId;
29
+ }
30
+ mapContentType(outputFormat) {
31
+ if (!outputFormat) {
32
+ return "audio/mpeg";
33
+ }
34
+ if (outputFormat.startsWith("mp3")) {
35
+ return "audio/mpeg";
36
+ }
37
+ if (outputFormat.startsWith("opus")) {
38
+ return "audio/ogg";
39
+ }
40
+ if (outputFormat.startsWith("pcm")) {
41
+ return "audio/wav";
42
+ }
43
+ if (outputFormat.startsWith("ulaw") || outputFormat.startsWith("alaw")) {
44
+ return "audio/basic";
45
+ }
46
+ return "application/octet-stream";
47
+ }
48
+ mapVoiceSettings(voiceSettings) {
49
+ if (!voiceSettings) {
50
+ return undefined;
51
+ }
52
+ return {
53
+ stability: voiceSettings.stability,
54
+ similarity_boost: voiceSettings.similarityBoost,
55
+ style: voiceSettings.style,
56
+ use_speaker_boost: voiceSettings.useSpeakerBoost,
57
+ speed: voiceSettings.speed,
58
+ };
59
+ }
60
+ buildBody(input, model) {
61
+ return {
62
+ text: input.text,
63
+ model_id: model,
64
+ ...(input.voiceSettings ? { voice_settings: this.mapVoiceSettings(input.voiceSettings) } : {}),
65
+ ...(input.previousText ? { previous_text: input.previousText } : {}),
66
+ ...(input.nextText ? { next_text: input.nextText } : {}),
67
+ ...(input.languageCode ? { language_code: input.languageCode } : {}),
68
+ ...(input.seed !== undefined ? { seed: input.seed } : {}),
69
+ };
70
+ }
71
+ async textToSpeech(input) {
72
+ const startedAt = Date.now();
73
+ const model = input.model ?? this.model;
74
+ const voiceId = input.voiceId ?? this.voiceId;
75
+ const outputFormat = input.outputFormat ?? this._outputFormat;
76
+ const url = new URL(`${this._baseUrl}/v1/text-to-speech/${encodeURIComponent(voiceId)}`);
77
+ if (outputFormat) {
78
+ url.searchParams.set("output_format", outputFormat);
79
+ }
80
+ const response = await fetch(url, {
81
+ method: "POST",
82
+ headers: {
83
+ "Accept": this.mapContentType(outputFormat),
84
+ "Content-Type": "application/json",
85
+ "xi-api-key": this._apiKey,
86
+ },
87
+ body: JSON.stringify(this.buildBody(input, model)),
88
+ });
89
+ if (!response.ok) {
90
+ const errorText = await response.text();
91
+ throw new Error(`ElevenLabs TTS request failed (${response.status}): ${errorText}`);
92
+ }
93
+ const audio = Buffer.from(await response.arrayBuffer());
94
+ const contentType = response.headers.get("content-type") ?? this.mapContentType(outputFormat);
95
+ return {
96
+ audio,
97
+ contentType,
98
+ size: audio.byteLength,
99
+ time: Date.now() - startedAt,
100
+ provider: "elevenlabs",
101
+ model,
102
+ voiceId,
103
+ outputFormat,
104
+ };
105
+ }
106
+ }
107
+ export default ElevenLabsTTSProvider;
108
+ export { ElevenLabsTTSProvider };
@@ -0,0 +1,8 @@
1
+ import TTSGenericController from "../controllers/TTSGenericController.js";
2
+ async function TTSFastifyRoutes(fastify, options) {
3
+ const genericController = new TTSGenericController();
4
+ fastify.get('/api/tts/providers', (req, rep) => genericController.availableProviders(req, rep));
5
+ fastify.post('/api/tts', (req, rep) => genericController.textToSpeech(req, rep));
6
+ }
7
+ export default TTSFastifyRoutes;
8
+ export { TTSFastifyRoutes };
@@ -0,0 +1,24 @@
1
+ import { z } from "zod";
2
+ const TTSVoiceSettingsSchema = z.object({
3
+ stability: z.number().min(0).max(1).optional(),
4
+ similarityBoost: z.number().min(0).max(1).optional(),
5
+ style: z.number().min(0).max(1).optional(),
6
+ useSpeakerBoost: z.boolean().optional(),
7
+ speed: z.number().positive().optional(),
8
+ });
9
+ const TTSRequestSchema = z.object({
10
+ text: z.string().min(1),
11
+ provider: z.string().default("ElevenLabs"),
12
+ voiceId: z.string().optional(),
13
+ model: z.string().optional(),
14
+ outputFormat: z.string().optional(),
15
+ voiceSettings: TTSVoiceSettingsSchema.optional(),
16
+ previousText: z.string().optional(),
17
+ nextText: z.string().optional(),
18
+ languageCode: z.string().optional(),
19
+ seed: z.number().int().optional(),
20
+ responseFormat: z.enum(["audio", "base64"]).default("audio"),
21
+ operationTitle: z.string().optional(),
22
+ operationGroup: z.string().optional(),
23
+ });
24
+ export { TTSRequestSchema, TTSVoiceSettingsSchema, };
@@ -0,0 +1,21 @@
1
+ import TTSProviderFactory from "../factory/tts/TTSProviderFactory.js";
2
+ class TTSGenericService {
3
+ availableProviders() {
4
+ return TTSProviderFactory.availableProviders();
5
+ }
6
+ async textToSpeech(input, context = {}) {
7
+ const ttsProvider = TTSProviderFactory.instance(input.provider);
8
+ const ttsInput = {
9
+ ...input,
10
+ operationTitle: input.operationTitle ?? "generic-tts",
11
+ operationGroup: input.operationGroup ?? "generic-tts",
12
+ ip: context.ip,
13
+ userAgent: context.userAgent,
14
+ tenant: context.tenant ?? null,
15
+ user: context.user ?? null,
16
+ };
17
+ return ttsProvider.textToSpeech(ttsInput);
18
+ }
19
+ }
20
+ export default TTSGenericService;
21
+ export { TTSGenericService, };
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "3.41.0",
6
+ "version": "3.42.0",
7
7
  "description": "Ai utils",
8
8
  "main": "dist/index.js",
9
9
  "types": "types/index.d.ts",
@@ -46,5 +46,5 @@
46
46
  "typescript": "^5.9.3",
47
47
  "vitest": "^3.0.8"
48
48
  },
49
- "gitHead": "21f6bdb9f6cc6da6c74dee8aef275d8f88755dd4"
49
+ "gitHead": "30b8ca8c49adb16b4617e61c497b554389c49dd1"
50
50
  }
@@ -14,7 +14,7 @@ import type {
14
14
  } from "../interfaces/IDraxAgent.js";
15
15
  import type {IPromptMessage} from "../interfaces/IAIProvider.js";
16
16
  import type {IAgentSession, IAgentSessionBase} from "../interfaces/IAgentSession.js";
17
- import AiProviderFactory from "../factory/AiProviderFactory.js";
17
+ import AiProviderFactory from "../factory/ai/AiProviderFactory.js";
18
18
  import AgentSessionServiceFactory from "../factory/services/AgentSessionServiceFactory.js";
19
19
  import type {AgentSessionService} from "../services/AgentSessionService.js";
20
20
 
@@ -0,0 +1,13 @@
1
+ enum ElevenLabsTTSConfig {
2
+
3
+ ElevenLabsApiKey = "ELEVENLABS_API_KEY",
4
+ ElevenLabsBaseUrl = "ELEVENLABS_BASE_URL",
5
+ ElevenLabsModel = "ELEVENLABS_MODEL",
6
+ ElevenLabsVoiceId = "ELEVENLABS_VOICE_ID",
7
+ ElevenLabsOutputFormat = "ELEVENLABS_OUTPUT_FORMAT",
8
+
9
+ }
10
+
11
+ export default ElevenLabsTTSConfig;
12
+
13
+ export {ElevenLabsTTSConfig}
@@ -1,6 +1,6 @@
1
1
  import {z} from "zod";
2
2
  import {CommonController} from "@drax/common-back";
3
- import AiProviderFactory from "../factory/AiProviderFactory.js";
3
+ import AiProviderFactory from "../factory/ai/AiProviderFactory.js";
4
4
  import AIPermissions from "../permissions/AIPermissions.js";
5
5
 
6
6
  const CrudAiFieldSchema: z.ZodType<any> = z.lazy(() => z.object({
@@ -1,6 +1,6 @@
1
1
  import {z} from "zod";
2
2
  import {CommonController} from "@drax/common-back";
3
- import AiProviderFactory from "../factory/AiProviderFactory.js";
3
+ import AiProviderFactory from "../factory/ai/AiProviderFactory.js";
4
4
  import AIPermissions from "../permissions/AIPermissions.js";
5
5
  import type {IPromptParams} from "../interfaces/IAIProvider.js";
6
6
 
@@ -0,0 +1,70 @@
1
+ import {CommonController} from "@drax/common-back";
2
+ import TTSPermissions from "../permissions/TTSPermissions.js";
3
+ import {TTSRequestSchema} from "../schemas/TTSRequestSchema.js";
4
+ import TTSGenericService from "../services/TTSGenericService.js";
5
+
6
+ class TTSGenericController extends CommonController {
7
+ protected service: TTSGenericService
8
+
9
+ constructor(service: TTSGenericService = new TTSGenericService()) {
10
+ super()
11
+ this.service = service
12
+ }
13
+
14
+ async availableProviders(request, reply) {
15
+ try {
16
+ request.rbac.assertPermission(TTSPermissions.TextToSpeech)
17
+
18
+ return reply.send({
19
+ providers: this.service.availableProviders(),
20
+ })
21
+ } catch (e: any) {
22
+ this.handleError(e, reply)
23
+ }
24
+ }
25
+
26
+ async textToSpeech(request, reply) {
27
+ try {
28
+ request.rbac.assertPermission(TTSPermissions.TextToSpeech)
29
+
30
+ const input = TTSRequestSchema.parse(request.body ?? {})
31
+ const response = await this.service.textToSpeech(input, {
32
+ ip: request.ip,
33
+ userAgent: request.headers["user-agent"],
34
+ tenant: request.rbac?.tenantId ?? null,
35
+ user: request.rbac?.userId ?? null,
36
+ })
37
+
38
+ if (input.responseFormat === "base64") {
39
+ return reply.send({
40
+ audio: response.audio.toString("base64"),
41
+ contentType: response.contentType,
42
+ meta: {
43
+ provider: response.provider,
44
+ model: response.model,
45
+ voiceId: response.voiceId,
46
+ outputFormat: response.outputFormat,
47
+ size: response.size,
48
+ time: response.time,
49
+ },
50
+ })
51
+ }
52
+
53
+ return reply
54
+ .header("Content-Type", response.contentType)
55
+ .header("Content-Length", response.size)
56
+ .send(response.audio)
57
+ } catch (e: any) {
58
+ if (e?.name === "ZodError") {
59
+ return reply.status(400).send({
60
+ message: e?.message || "TTS validation error",
61
+ })
62
+ }
63
+
64
+ this.handleError(e, reply)
65
+ }
66
+ }
67
+ }
68
+
69
+ export default TTSGenericController;
70
+ export {TTSGenericController};
@@ -1,8 +1,8 @@
1
- import type {IAIProvider} from "../interfaces/IAIProvider"
1
+ import type {IAIProvider} from "../../interfaces/IAIProvider.js"
2
2
  import OpenAiProviderFactory from "./OpenAiProviderFactory.js";
3
3
  import GoogleAiProviderFactory from "./GoogleAiProviderFactory.js";
4
4
  import OllamaAiProviderFactory from "./OllamaAiProviderFactory.js";
5
- import DeepSeekProviderFactory from "./DeepSeekProviderFactory.js";
5
+ import DeepSeekAiProviderFactory from "./DeepSeekAiProviderFactory.js";
6
6
 
7
7
  class AiProviderFactory {
8
8
  private static singletons: Record<string, IAIProvider> = {};
@@ -20,7 +20,7 @@ class AiProviderFactory {
20
20
  AiProviderFactory.singletons[provider] = OllamaAiProviderFactory.instance()
21
21
  break;
22
22
  case 'DeepSeek':
23
- AiProviderFactory.singletons[provider] = DeepSeekProviderFactory.instance()
23
+ AiProviderFactory.singletons[provider] = DeepSeekAiProviderFactory.instance()
24
24
  break;
25
25
  default:
26
26
  throw new Error(`Unsupported AI provider: ${provider}`);
@@ -0,0 +1,27 @@
1
+ import {DraxConfig} from "@drax/common-back";
2
+ import DeepSeekConfig from "../../config/DeepSeekConfig.js";
3
+ import type {IAIProvider} from "../../interfaces/IAIProvider.js"
4
+ import DeepSeekAiProvider from "../../providers/ai/DeepSeekAiProvider.js";
5
+ import AILogServiceFactory from "../services/AILogServiceFactory.js";
6
+
7
+ class DeepSeekAiProviderFactory {
8
+ private static singleton: IAIProvider;
9
+
10
+ public static instance(): IAIProvider {
11
+ if (!DeepSeekAiProviderFactory.singleton) {
12
+ DeepSeekAiProviderFactory.singleton = new DeepSeekAiProvider(
13
+ DraxConfig.getOrLoad(DeepSeekConfig.DeepSeekApiKey),
14
+ DraxConfig.getOrLoad(DeepSeekConfig.DeepSeekModel),
15
+ DraxConfig.getOrLoad(DeepSeekConfig.DeepSeekBaseUrl, "string", "https://api.deepseek.com"),
16
+ DraxConfig.getOrLoad(DeepSeekConfig.DeepSeekVisionModel),
17
+ AILogServiceFactory.instance
18
+ );
19
+ }
20
+ return DeepSeekAiProviderFactory.singleton;
21
+ }
22
+ }
23
+
24
+ export default DeepSeekAiProviderFactory
25
+ export {
26
+ DeepSeekAiProviderFactory
27
+ }
@@ -1,8 +1,8 @@
1
1
  import {DraxConfig} from "@drax/common-back";
2
- import GoogleAiConfig from "../config/GoogleAiConfig.js";
3
- import type {IAIProvider} from "../interfaces/IAIProvider"
4
- import GoogleAiProvider from "../providers/GoogleAiProvider.js";
5
- import AILogServiceFactory from "./services/AILogServiceFactory.js";
2
+ import GoogleAiConfig from "../../config/GoogleAiConfig.js";
3
+ import type {IAIProvider} from "../../interfaces/IAIProvider.js"
4
+ import GoogleAiProvider from "../../providers/ai/GoogleAiProvider.js";
5
+ import AILogServiceFactory from "../services/AILogServiceFactory.js";
6
6
 
7
7
  class GoogleAiProviderFactory {
8
8
  private static singleton: IAIProvider;
@@ -1,8 +1,8 @@
1
1
  import {DraxConfig} from "@drax/common-back";
2
- import OllamaAiConfig from "../config/OllamaAiConfig.js";
3
- import type {IAIProvider} from "../interfaces/IAIProvider"
4
- import OllamaAiProvider from "../providers/OllamaAiProvider.js";
5
- import AILogServiceFactory from "./services/AILogServiceFactory.js";
2
+ import OllamaAiConfig from "../../config/OllamaAiConfig.js";
3
+ import type {IAIProvider} from "../../interfaces/IAIProvider.js"
4
+ import OllamaAiProvider from "../../providers/ai/OllamaAiProvider.js";
5
+ import AILogServiceFactory from "../services/AILogServiceFactory.js";
6
6
 
7
7
  class OllamaAiProviderFactory {
8
8
  private static singleton: IAIProvider;