@runnerpro/backend 1.17.3 → 1.17.5

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.
@@ -10,9 +10,9 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
10
10
  };
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
12
  exports.reprocessRecentMedia = exports.processMediaFile = exports.describeImage = exports.transcribeAudio = void 0;
13
- const vertexai_1 = require("@google-cloud/vertexai");
14
- const index_1 = require("../db/index");
15
- const index_2 = require("../err/index");
13
+ const index_1 = require("../prompt/index");
14
+ const index_2 = require("../db/index");
15
+ const index_3 = require("../err/index");
16
16
  // ✅ PROMPTS PARA PROCESAMIENTO DE ARCHIVOS MULTIMEDIA
17
17
  const AUDIO_TRANSCRIPTION_PROMPT = `Transcribe el audio de forma literal y completa en español.
18
18
  Si el audio está en otro idioma, tradúcelo al español.
@@ -41,51 +41,15 @@ Si la imagen no está relacionada con deporte/fitness, descríbela brevemente.`;
41
41
  * ```
42
42
  */
43
43
  const transcribeAudio = (fileBuffer, mimetype) => __awaiter(void 0, void 0, void 0, function* () {
44
- var _a, _b, _c, _d;
45
44
  try {
46
- const vertex_ai = new vertexai_1.VertexAI({
47
- project: process.env.PROJECT_ID,
48
- location: 'europe-west8',
45
+ const result = yield (0, index_1.sendPrompt)(AUDIO_TRANSCRIPTION_PROMPT, 'PRO', undefined, {
46
+ buffer: fileBuffer,
47
+ mimetype,
49
48
  });
50
- // Usamos gemini-2.5-flash para balance entre velocidad y precisión
51
- const generativeModel = vertex_ai.preview.getGenerativeModel({
52
- model: 'gemini-2.5-flash',
53
- generationConfig: {
54
- temperature: 0.1, // Baja temperatura para transcripción precisa
55
- topP: 0.8,
56
- topK: 20,
57
- },
58
- });
59
- const base64Audio = fileBuffer.toString('base64');
60
- // @ts-ignore - Vertex AI types son complejos para contenido multimodal
61
- const resp = yield generativeModel.generateContent({
62
- contents: [
63
- {
64
- role: 'user',
65
- parts: [
66
- {
67
- inlineData: {
68
- mimeType: mimetype,
69
- data: base64Audio,
70
- },
71
- },
72
- {
73
- text: AUDIO_TRANSCRIPTION_PROMPT,
74
- },
75
- ],
76
- },
77
- ],
78
- });
79
- const candidate = (_a = resp.response.candidates) === null || _a === void 0 ? void 0 : _a[0];
80
- if (!candidate)
81
- return '[Error al procesar audio]';
82
- const parts = (_b = candidate.content) === null || _b === void 0 ? void 0 : _b.parts;
83
- if (!parts || parts.length === 0)
84
- return '[Error al procesar audio]';
85
- return ((_d = (_c = parts[0]) === null || _c === void 0 ? void 0 : _c.text) === null || _d === void 0 ? void 0 : _d.trim()) || '[Audio vacío]';
49
+ return result || '[Audio vacío]';
86
50
  }
87
51
  catch (error) {
88
- (0, index_2.err)(null, null, error, null);
52
+ (0, index_3.err)(null, null, error, null);
89
53
  return '[Error al transcribir audio]';
90
54
  }
91
55
  });
@@ -105,51 +69,15 @@ exports.transcribeAudio = transcribeAudio;
105
69
  * ```
106
70
  */
107
71
  const describeImage = (fileBuffer, mimetype) => __awaiter(void 0, void 0, void 0, function* () {
108
- var _e, _f, _g, _h;
109
72
  try {
110
- const vertex_ai = new vertexai_1.VertexAI({
111
- project: process.env.PROJECT_ID,
112
- location: 'europe-west8',
113
- });
114
- // ⭐ Usamos gemini-2.5-flash para balance entre velocidad y precisión
115
- const generativeModel = vertex_ai.preview.getGenerativeModel({
116
- model: 'gemini-2.5-flash',
117
- generationConfig: {
118
- temperature: 0.3,
119
- topP: 0.8,
120
- topK: 20,
121
- },
122
- });
123
- const base64Image = fileBuffer.toString('base64');
124
- // @ts-ignore - Vertex AI types son complejos para contenido multimodal
125
- const resp = yield generativeModel.generateContent({
126
- contents: [
127
- {
128
- role: 'user',
129
- parts: [
130
- {
131
- inlineData: {
132
- mimeType: mimetype,
133
- data: base64Image,
134
- },
135
- },
136
- {
137
- text: IMAGE_DESCRIPTION_PROMPT,
138
- },
139
- ],
140
- },
141
- ],
73
+ const result = yield (0, index_1.sendPrompt)(IMAGE_DESCRIPTION_PROMPT, 'PRO', undefined, {
74
+ buffer: fileBuffer,
75
+ mimetype,
142
76
  });
143
- const candidate = (_e = resp.response.candidates) === null || _e === void 0 ? void 0 : _e[0];
144
- if (!candidate)
145
- return '[Error al procesar imagen]';
146
- const parts = (_f = candidate.content) === null || _f === void 0 ? void 0 : _f.parts;
147
- if (!parts || parts.length === 0)
148
- return '[Error al procesar imagen]';
149
- return ((_h = (_g = parts[0]) === null || _g === void 0 ? void 0 : _g.text) === null || _h === void 0 ? void 0 : _h.trim()) || '[Imagen no analizable]';
77
+ return result || '[Imagen no analizable]';
150
78
  }
151
79
  catch (error) {
152
- (0, index_2.err)(null, null, error, null);
80
+ (0, index_3.err)(null, null, error, null);
153
81
  return '[Error al describir imagen]';
154
82
  }
155
83
  });
@@ -183,10 +111,10 @@ const processMediaFile = (idMessage, fileBuffer, mimetype) => __awaiter(void 0,
183
111
  return;
184
112
  }
185
113
  // ✅ Guardar el resultado en la base de datos
186
- yield (0, index_1.query)('UPDATE [CHAT MESSAGE] SET [FILE TEXT] = ? WHERE [ID] = ?', [fileText, idMessage]);
114
+ yield (0, index_2.query)('UPDATE [CHAT MESSAGE] SET [FILE TEXT] = ? WHERE [ID] = ?', [fileText, idMessage]);
187
115
  }
188
116
  catch (error) {
189
- (0, index_2.err)(null, null, error, null);
117
+ (0, index_3.err)(null, null, error, null);
190
118
  }
191
119
  });
192
120
  exports.processMediaFile = processMediaFile;
@@ -208,7 +136,7 @@ const reprocessRecentMedia = (bucket) => __awaiter(void 0, void 0, void 0, funct
208
136
  let errors = 0;
209
137
  try {
210
138
  // ✅ Buscar mensajes de los últimos 2 días con MIMETYPE de audio/imagen y sin FILE TEXT
211
- const messages = yield (0, index_1.query)(`SELECT "ID", "MIMETYPE" FROM "CHAT MESSAGE"
139
+ const messages = yield (0, index_2.query)(`SELECT "ID", "MIMETYPE" FROM "CHAT MESSAGE"
212
140
  WHERE "DATE" >= NOW() - INTERVAL '2 days'
213
141
  AND "FILE TEXT" IS NULL
214
142
  AND ("MIMETYPE" LIKE 'audio/%' OR "MIMETYPE" LIKE 'image/%')
@@ -235,7 +163,7 @@ const reprocessRecentMedia = (bucket) => __awaiter(void 0, void 0, void 0, funct
235
163
  fileText = yield describeImage(fileBuffer, message.mimetype);
236
164
  }
237
165
  // ⭐ Guardar en BD
238
- yield (0, index_1.query)('UPDATE [CHAT MESSAGE] SET [FILE TEXT] = ? WHERE [ID] = ?', [fileText, message.id]);
166
+ yield (0, index_2.query)('UPDATE [CHAT MESSAGE] SET [FILE TEXT] = ? WHERE [ID] = ?', [fileText, message.id]);
239
167
  // eslint-disable-next-line no-console
240
168
  console.log(`[reprocessRecentMedia] ✅ Procesado mensaje ${message.id}: ${fileText.substring(0, 50)}...`);
241
169
  processed++;
@@ -248,7 +176,7 @@ const reprocessRecentMedia = (bucket) => __awaiter(void 0, void 0, void 0, funct
248
176
  }
249
177
  }
250
178
  catch (error) {
251
- (0, index_2.err)(null, null, error, null);
179
+ (0, index_3.err)(null, null, error, null);
252
180
  }
253
181
  return { processed, errors };
254
182
  });
@@ -11,36 +11,98 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
12
  exports.sendPrompt = void 0;
13
13
  const vertexai_1 = require("@google-cloud/vertexai");
14
- function sendPrompt(prompt, model = 'FLASH', systemPrompt = undefined) {
15
- var _a, _b, _c;
14
+ /**
15
+ * Envía un prompt a Gemini (Vertex AI) y retorna la respuesta
16
+ * Soporta contenido multimodal (texto + audio/imagen)
17
+ *
18
+ * @param prompt - Texto del prompt a enviar
19
+ * @param model - Modelo a usar: 'LITE', 'FLASH' (default), 'PRO'
20
+ * @param systemPrompt - Instrucciones de sistema opcionales
21
+ * @param media - Contenido multimedia opcional (audio o imagen)
22
+ * @returns Promise<string> - Respuesta generada por la IA
23
+ *
24
+ * @example
25
+ * ```typescript
26
+ * // Solo texto
27
+ * const response = await sendPrompt('Traduce esto al inglés: Hola mundo');
28
+ *
29
+ * // Con imagen
30
+ * const description = await sendPrompt(
31
+ * 'Describe esta imagen',
32
+ * 'FLASH',
33
+ * undefined,
34
+ * { buffer: imageBuffer, mimetype: 'image/jpeg' }
35
+ * );
36
+ *
37
+ * // Con audio
38
+ * const transcription = await sendPrompt(
39
+ * 'Transcribe este audio en español',
40
+ * 'FLASH',
41
+ * undefined,
42
+ * { buffer: audioBuffer, mimetype: 'audio/mpeg' }
43
+ * );
44
+ * ```
45
+ */
46
+ function sendPrompt(prompt, model = 'FLASH', systemPrompt = undefined, media = undefined) {
47
+ var _a, _b, _c, _d;
16
48
  return __awaiter(this, void 0, void 0, function* () {
17
49
  // Inicializa el cliente de Vertex AI
18
50
  const vertex_ai = new vertexai_1.VertexAI({
19
- project: process.env.PROJECT_ID, // Reemplaza con tu ID de proyecto de Google Cloud
20
- location: 'europe-west8', // Reemplaza con tu región
51
+ project: process.env.PROJECT_ID,
52
+ location: 'europe-west8',
21
53
  });
22
- // Selecciona el modelo de Gemini más rápido
54
+ // Selecciona el modelo de Gemini
23
55
  let modelGemini;
24
56
  if (model === 'LITE')
25
57
  modelGemini = 'gemini-2.5-flash-lite';
26
58
  else if (model === 'PRO')
27
- modelGemini = 'gemini-2.5-pro';
59
+ modelGemini = 'gemini-3-pro-preview';
28
60
  else
29
61
  modelGemini = 'gemini-2.5-flash';
30
- // Configura el modelo generativo con parámetros optimizados para velocidad
62
+ // Configura el modelo generativo
31
63
  const generativeModel = vertex_ai.preview.getGenerativeModel({
32
64
  model: modelGemini,
33
65
  generationConfig: {
34
- temperature: 0.3,
66
+ temperature: media ? 0.1 : 0.3, // Menor temperatura para transcripciones
35
67
  topP: 0.8,
36
68
  topK: 20,
37
69
  },
38
- systemInstruction: systemPrompt ? {
39
- role: 'system',
40
- parts: [{ text: systemPrompt }],
41
- } : undefined,
70
+ systemInstruction: systemPrompt
71
+ ? {
72
+ role: 'system',
73
+ parts: [{ text: systemPrompt }],
74
+ }
75
+ : undefined,
42
76
  });
43
- const resp = yield generativeModel.generateContent(prompt);
77
+ // Construir contenido según si hay media o no
78
+ let content;
79
+ if (media) {
80
+ // Contenido multimodal (audio/imagen + texto)
81
+ content = {
82
+ contents: [
83
+ {
84
+ role: 'user',
85
+ parts: [
86
+ {
87
+ inlineData: {
88
+ mimeType: media.mimetype,
89
+ data: media.buffer.toString('base64'),
90
+ },
91
+ },
92
+ {
93
+ text: prompt,
94
+ },
95
+ ],
96
+ },
97
+ ],
98
+ };
99
+ }
100
+ else {
101
+ // Solo texto
102
+ content = prompt;
103
+ }
104
+ // @ts-ignore - Vertex AI types son complejos para contenido multimodal
105
+ const resp = yield generativeModel.generateContent(content);
44
106
  // Early return si no hay candidates
45
107
  const candidate = (_a = resp.response.candidates) === null || _a === void 0 ? void 0 : _a[0];
46
108
  if (!candidate)
@@ -50,7 +112,7 @@ function sendPrompt(prompt, model = 'FLASH', systemPrompt = undefined) {
50
112
  if (!parts || parts.length === 0)
51
113
  return '';
52
114
  // Return del texto si existe
53
- return ((_c = parts[0]) === null || _c === void 0 ? void 0 : _c.text) || '';
115
+ return ((_d = (_c = parts[0]) === null || _c === void 0 ? void 0 : _c.text) === null || _d === void 0 ? void 0 : _d.trim()) || '';
54
116
  });
55
117
  }
56
118
  exports.sendPrompt = sendPrompt;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/mediaProcessing/index.ts"],"names":[],"mappings":";AAqBA;;;;;;;;;;;;GAYG;AACH,QAAA,MAAM,eAAe,eAAsB,MAAM,YAAY,MAAM,KAAG,QAAQ,MAAM,CAkDnF,CAAC;AAEF;;;;;;;;;;;;;GAaG;AACH,QAAA,MAAM,aAAa,eAAsB,MAAM,YAAY,MAAM,KAAG,QAAQ,MAAM,CAkDjF,CAAC;AAEF;;;;;;;;;;;;;;GAcG;AACH,QAAA,MAAM,gBAAgB,cAAqB,MAAM,cAAc,MAAM,YAAY,MAAM,KAAG,QAAQ,IAAI,CAkBrG,CAAC;AAEF;;;;;;;;;;;;GAYG;AACH,QAAA,MAAM,oBAAoB,WAAkB,GAAG;eAAwB,MAAM;YAAU,MAAM;EAwD5F,CAAC;AAEF,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/mediaProcessing/index.ts"],"names":[],"mappings":";AAqBA;;;;;;;;;;;;GAYG;AACH,QAAA,MAAM,eAAe,eAAsB,MAAM,YAAY,MAAM,KAAG,QAAQ,MAAM,CAWnF,CAAC;AAEF;;;;;;;;;;;;;GAaG;AACH,QAAA,MAAM,aAAa,eAAsB,MAAM,YAAY,MAAM,KAAG,QAAQ,MAAM,CAWjF,CAAC;AAEF;;;;;;;;;;;;;;GAcG;AACH,QAAA,MAAM,gBAAgB,cAAqB,MAAM,cAAc,MAAM,YAAY,MAAM,KAAG,QAAQ,IAAI,CAkBrG,CAAC;AAEF;;;;;;;;;;;;GAYG;AACH,QAAA,MAAM,oBAAoB,WAAkB,GAAG;eAAwB,MAAM;YAAU,MAAM;EAwD5F,CAAC;AAEF,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,CAAC"}
@@ -1,3 +1,45 @@
1
- declare function sendPrompt(prompt: string, model?: string, systemPrompt?: string | undefined): Promise<string>;
1
+ /// <reference types="node" />
2
+ /**
3
+ * Contenido multimedia opcional para enviar junto con el prompt
4
+ */
5
+ interface MediaContent {
6
+ /** Buffer del archivo (audio o imagen) */
7
+ buffer: Buffer;
8
+ /** Tipo MIME del archivo (ej: 'audio/mpeg', 'image/jpeg') */
9
+ mimetype: string;
10
+ }
11
+ /**
12
+ * Envía un prompt a Gemini (Vertex AI) y retorna la respuesta
13
+ * Soporta contenido multimodal (texto + audio/imagen)
14
+ *
15
+ * @param prompt - Texto del prompt a enviar
16
+ * @param model - Modelo a usar: 'LITE', 'FLASH' (default), 'PRO'
17
+ * @param systemPrompt - Instrucciones de sistema opcionales
18
+ * @param media - Contenido multimedia opcional (audio o imagen)
19
+ * @returns Promise<string> - Respuesta generada por la IA
20
+ *
21
+ * @example
22
+ * ```typescript
23
+ * // Solo texto
24
+ * const response = await sendPrompt('Traduce esto al inglés: Hola mundo');
25
+ *
26
+ * // Con imagen
27
+ * const description = await sendPrompt(
28
+ * 'Describe esta imagen',
29
+ * 'FLASH',
30
+ * undefined,
31
+ * { buffer: imageBuffer, mimetype: 'image/jpeg' }
32
+ * );
33
+ *
34
+ * // Con audio
35
+ * const transcription = await sendPrompt(
36
+ * 'Transcribe este audio en español',
37
+ * 'FLASH',
38
+ * undefined,
39
+ * { buffer: audioBuffer, mimetype: 'audio/mpeg' }
40
+ * );
41
+ * ```
42
+ */
43
+ declare function sendPrompt(prompt: string, model?: string, systemPrompt?: string | undefined, media?: MediaContent | undefined): Promise<string>;
2
44
  export { sendPrompt };
3
45
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/prompt/index.ts"],"names":[],"mappings":"AAEA,iBAAe,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,GAAE,MAAgB,EAAE,YAAY,GAAE,MAAM,GAAG,SAAqB,mBAuC9G;AAED,OAAO,EAAE,UAAU,EAAE,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/prompt/index.ts"],"names":[],"mappings":";AAEA;;GAEG;AACH,UAAU,YAAY;IACpB,0CAA0C;IAC1C,MAAM,EAAE,MAAM,CAAC;IACf,6DAA6D;IAC7D,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,iBAAe,UAAU,CACvB,MAAM,EAAE,MAAM,EACd,KAAK,GAAE,MAAgB,EACvB,YAAY,GAAE,MAAM,GAAG,SAAqB,EAC5C,KAAK,GAAE,YAAY,GAAG,SAAqB,mBAsE5C;AAED,OAAO,EAAE,UAAU,EAAE,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@runnerpro/backend",
3
- "version": "1.17.3",
3
+ "version": "1.17.5",
4
4
  "description": "A collection of common backend functions",
5
5
  "exports": {
6
6
  ".": "./lib/cjs/index.js"