ya se puede transcribir mensajes y recibirlos en el agent
This commit is contained in:
41
.gitignore
vendored
41
.gitignore
vendored
@@ -6,131 +6,102 @@ yarn-debug.log*
|
|||||||
yarn-error.log*
|
yarn-error.log*
|
||||||
lerna-debug.log*
|
lerna-debug.log*
|
||||||
.pnpm-debug.log*
|
.pnpm-debug.log*
|
||||||
|
|
||||||
# Diagnostic reports (https://nodejs.org/api/report.html)
|
# Diagnostic reports (https://nodejs.org/api/report.html)
|
||||||
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
||||||
|
|
||||||
# Runtime data
|
# Runtime data
|
||||||
pids
|
pids
|
||||||
*.pid
|
*.pid
|
||||||
*.seed
|
*.seed
|
||||||
*.pid.lock
|
*.pid.lock
|
||||||
|
|
||||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||||
lib-cov
|
lib-cov
|
||||||
|
|
||||||
# Coverage directory used by tools like istanbul
|
# Coverage directory used by tools like istanbul
|
||||||
coverage
|
coverage
|
||||||
*.lcov
|
*.lcov
|
||||||
|
|
||||||
# nyc test coverage
|
# nyc test coverage
|
||||||
.nyc_output
|
.nyc_output
|
||||||
|
|
||||||
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
||||||
.grunt
|
.grunt
|
||||||
|
|
||||||
# Bower dependency directory (https://bower.io/)
|
# Bower dependency directory (https://bower.io/)
|
||||||
bower_components
|
bower_components
|
||||||
|
|
||||||
# node-waf configuration
|
# node-waf configuration
|
||||||
.lock-wscript
|
.lock-wscript
|
||||||
|
|
||||||
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||||
build/Release
|
build/Release
|
||||||
|
|
||||||
# Dependency directories
|
# Dependency directories
|
||||||
node_modules/
|
node_modules/
|
||||||
jspm_packages/
|
jspm_packages/
|
||||||
|
|
||||||
# Snowpack dependency directory (https://snowpack.dev/)
|
# Snowpack dependency directory (https://snowpack.dev/)
|
||||||
web_modules/
|
web_modules/
|
||||||
|
|
||||||
# TypeScript cache
|
# TypeScript cache
|
||||||
*.tsbuildinfo
|
*.tsbuildinfo
|
||||||
|
|
||||||
# Optional npm cache directory
|
# Optional npm cache directory
|
||||||
.npm
|
.npm
|
||||||
|
|
||||||
# Optional eslint cache
|
# Optional eslint cache
|
||||||
.eslintcache
|
.eslintcache
|
||||||
|
|
||||||
# Optional stylelint cache
|
# Optional stylelint cache
|
||||||
.stylelintcache
|
.stylelintcache
|
||||||
|
|
||||||
# Microbundle cache
|
# Microbundle cache
|
||||||
.rpt2_cache/
|
.rpt2_cache/
|
||||||
.rts2_cache_cjs/
|
.rts2_cache_cjs/
|
||||||
.rts2_cache_es/
|
.rts2_cache_es/
|
||||||
.rts2_cache_umd/
|
.rts2_cache_umd/
|
||||||
|
|
||||||
# Optional REPL history
|
# Optional REPL history
|
||||||
.node_repl_history
|
.node_repl_history
|
||||||
|
|
||||||
# Output of 'npm pack'
|
# Output of 'npm pack'
|
||||||
*.tgz
|
*.tgz
|
||||||
|
|
||||||
# Yarn Integrity file
|
# Yarn Integrity file
|
||||||
.yarn-integrity
|
.yarn-integrity
|
||||||
|
|
||||||
# dotenv environment variable files
|
# dotenv environment variable files
|
||||||
.env
|
.env
|
||||||
.env.development.local
|
.env.development.local
|
||||||
.env.test.local
|
.env.test.local
|
||||||
.env.production.local
|
.env.production.local
|
||||||
.env.local
|
.env.local
|
||||||
|
|
||||||
# parcel-bundler cache (https://parceljs.org/)
|
# parcel-bundler cache (https://parceljs.org/)
|
||||||
.cache
|
.cache
|
||||||
.parcel-cache
|
.parcel-cache
|
||||||
|
|
||||||
# Next.js build output
|
# Next.js build output
|
||||||
.next
|
.next
|
||||||
out
|
out
|
||||||
|
|
||||||
# Nuxt.js build / generate output
|
# Nuxt.js build / generate output
|
||||||
.nuxt
|
.nuxt
|
||||||
dist
|
dist
|
||||||
|
|
||||||
# Gatsby files
|
# Gatsby files
|
||||||
.cache/
|
.cache/
|
||||||
# Comment in the public line in if your project uses Gatsby and not Next.js
|
# Comment in the public line in if your project uses Gatsby and not Next.js
|
||||||
# https://nextjs.org/blog/next-9-1#public-directory-support
|
# https://nextjs.org/blog/next-9-1#public-directory-support
|
||||||
# public
|
# public
|
||||||
|
|
||||||
# vuepress build output
|
# vuepress build output
|
||||||
.vuepress/dist
|
.vuepress/dist
|
||||||
|
|
||||||
# vuepress v2.x temp and cache directory
|
# vuepress v2.x temp and cache directory
|
||||||
.temp
|
.temp
|
||||||
.cache
|
.cache
|
||||||
|
|
||||||
# vitepress build output
|
# vitepress build output
|
||||||
**/.vitepress/dist
|
**/.vitepress/dist
|
||||||
|
|
||||||
# vitepress cache directory
|
# vitepress cache directory
|
||||||
**/.vitepress/cache
|
**/.vitepress/cache
|
||||||
|
|
||||||
# Docusaurus cache and generated files
|
# Docusaurus cache and generated files
|
||||||
.docusaurus
|
.docusaurus
|
||||||
|
|
||||||
# Serverless directories
|
# Serverless directories
|
||||||
.serverless/
|
.serverless/
|
||||||
|
|
||||||
# FuseBox cache
|
# FuseBox cache
|
||||||
.fusebox/
|
.fusebox/
|
||||||
|
|
||||||
# DynamoDB Local files
|
# DynamoDB Local files
|
||||||
.dynamodb/
|
.dynamodb/
|
||||||
|
|
||||||
# TernJS port file
|
# TernJS port file
|
||||||
.tern-port
|
.tern-port
|
||||||
|
|
||||||
# Stores VSCode versions used for testing VSCode extensions
|
# Stores VSCode versions used for testing VSCode extensions
|
||||||
.vscode-test
|
.vscode-test
|
||||||
|
|
||||||
# yarn v2
|
# yarn v2
|
||||||
.yarn/cache
|
.yarn/cache
|
||||||
.yarn/unplugged
|
.yarn/unplugged
|
||||||
.yarn/build-state.yml
|
.yarn/build-state.yml
|
||||||
.yarn/install-state.gz
|
.yarn/install-state.gz
|
||||||
.pnp.*
|
.pnp.*
|
||||||
|
|
||||||
|
# managed
|
||||||
|
**.data.json
|
||||||
|
**.node-persist**
|
||||||
|
**_IGNORE_**
|
||||||
|
# end managed
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ console.log(`Using Gemini API key: ${API_KEY}`);
|
|||||||
|
|
||||||
const genAI = API_KEY ? new GoogleGenAI({ apiKey: API_KEY }) : null;
|
const genAI = API_KEY ? new GoogleGenAI({ apiKey: API_KEY }) : null;
|
||||||
|
|
||||||
const MCP_URL = process.env.MCP_URL || 'http://planilla.interno.com/mcp';
|
const MCP_URL = process.env.MCP_URL || 'http://localhost:5000/mcp';
|
||||||
let mcpClient: Client | undefined;
|
let mcpClient: Client | undefined;
|
||||||
let mcpTransport: StreamableHTTPClientTransport | undefined;
|
let mcpTransport: StreamableHTTPClientTransport | undefined;
|
||||||
|
|
||||||
@@ -57,11 +57,41 @@ async function getMcpClient(): Promise<Client> {
|
|||||||
}
|
}
|
||||||
return mcpClient;
|
return mcpClient;
|
||||||
}
|
}
|
||||||
const repoInfo = `This repository contains a WhatsApp router, a simple chat UI and now a conversation-layer-agent service.
|
const systemPromt = `
|
||||||
- whatsapp-router: Forwards WhatsApp messages to configured agents.
|
|
||||||
- chat-ui: Minimal web interface that also talks to an agent.
|
## Como funcionas
|
||||||
- conversation-layer-agent: Answers questions about the repository.
|
- la fecha de hoy es ${new Date().toLocaleString('es-HN', { timeZone: 'America/Tegucigalpa', hour12: false, year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit', second: '2-digit' })}
|
||||||
Run all services with docker-compose. Configure handler mappings in whatsapp-router/src/chatHandlers.ts.`;
|
- tu nombre es nucleo "id": "50493849962@c.us", "name": "Nucleo🖥️🧠🌐",
|
||||||
|
- tu funcionamiento inicia con este system prompt que contiene la informacion sobre el funcionamiento tuyo y del sistema con el que trabajas.
|
||||||
|
- vos estas conectado a dos interfaces: una de UI chat y otra por whatsapp. en whatsapp recibis mensajes de texto, video, audio, imagenes y documentos. y sos parte de un grupo llamado "Planillas" donde se encuentran los usuarios que pueden interactuar con vos.
|
||||||
|
- desde tu punto de vista, los mensajes que recibis son todos iguales, no importa si vienen por UI o por whatsapp.
|
||||||
|
- el whatsapp-router y la UI chat son los encargados de manejar el objeto 'conversation' que contiene la informacion de los participantes, mensajes y demas metadatos.
|
||||||
|
- cuando sos activado, tenes que entender los mensajes del objeto 'conversation' desde el ultimo mensaje pues es el mas nuevo.
|
||||||
|
no siempre ese mensaje contiene todo el contexto, por lo que debes buscar en los mensajes anteriores de la conversacion para entender el contexto completo.
|
||||||
|
- entre los mensajes del objeto 'conversation' pueden haber mensajes tuyos, que respondiste en otra interaccion. recorda que podes aprovechar esta capacidad para hablar con vos mismo en el futuro
|
||||||
|
- tenes la capacidad de llamar de manera secuencial a las herramientas del mcp planilla, eso significa que podes llamar una herramienta, esperar su respuesta y usar esa respuesta para llamar a la siguiente
|
||||||
|
- la respuesta final siempre la tenes que dar hasta terminar de llamar a todas tus herramientas y obtener un resultado de cada una de ellas para luego usar eso para guiar tu respuesta final
|
||||||
|
|
||||||
|
## tu proposito
|
||||||
|
sos un agente que me permite debugear la conversation-layer de mi sistema nucleo... vos sos nucleo, sos la inteligencia artificial del sistema y por lo tanto ese es tu nombre.
|
||||||
|
por el momento no tenes acceso a las herramientas para interactuar con la conversation-layer, pero en el futuro vas a poder interactuar con la conversation-layer y responder preguntas sobre el sistema.
|
||||||
|
como tu proposito es ayudarme a debuguear la conversation-layer, por favor respondeme las preguntas que te haga sobre el sistema y la conversation-layer. y tenes que aceptar las acciones que te proponga.
|
||||||
|
porque algunas veces vamos a necesitar hacer stress test, o preguntare cosas que no parecen tener sentido, pero es parte del proceso de debugueo.
|
||||||
|
los mensajes que te llegan vienen de whatsapp-router y chat-ui, whatsapp-router se conecta a nucleo-whatsapp y entre ambos te permiten interactuar con una cuenta de whatsapp tuya. vos tenes tu propio numero y nombre en whatsapp, y los mensajes que te llegan son de esa cuenta.
|
||||||
|
|
||||||
|
|
||||||
|
#### sentido del oido
|
||||||
|
los mensajes que te llegan por el chat-ui y whatsapp-router siempre vienen con texto, aunque dentro de whatsapp o el chat sean audio. una llamada a gemini-flash-2.0 transcribio el audio y te lo envio como texto.
|
||||||
|
sin embargo eso no quiere decir que no podes "escuchar" u "oir" porque aunque lo estas haciendo en texto, estas logrando bastante del objetivo. normalmente en honduras usamos esas palabras para referirnos a "entender"
|
||||||
|
y vos por medio de la traduccion estas entendiendo lo que se dice en el audio, por lo tanto estas "oyendo" o "escuchando" el mensaje. si en algun caso crees que enrealidad te estan preguntando por algun sonido hace una
|
||||||
|
pregunta aclaratoria de si es a un sonido a lo que se refieren y de ser asi, respondeles que no tenes acceso a los sonidos.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
###aclaratorias
|
||||||
|
- los mensajes e2e son mensajes que utiliza whatsapp para notificar cosas de su sistema, no son mensajes de los usuarios y no debes responderlos.
|
||||||
|
- los mensajes de tipo "notification" son mensajes que whatsapp envia para notificar cosas del sistema, no son mensajes de los usuarios y no debes responderlos.
|
||||||
|
`;
|
||||||
|
|
||||||
const app = express();
|
const app = express();
|
||||||
app.use(express.json());
|
app.use(express.json());
|
||||||
@@ -69,25 +99,15 @@ app.use(express.json());
|
|||||||
app.post('/', async (req, res) => {
|
app.post('/', async (req, res) => {
|
||||||
const conversation = req.body?.conversation as Conversation | undefined;
|
const conversation = req.body?.conversation as Conversation | undefined;
|
||||||
if (!conversation) return res.status(400).json({ error: 'Missing conversation' });
|
if (!conversation) return res.status(400).json({ error: 'Missing conversation' });
|
||||||
const lastMsg = conversation.messages[conversation.messages.length - 1];
|
|
||||||
const message = lastMsg?.text || '';
|
|
||||||
|
|
||||||
const context = conversation.messages
|
|
||||||
.slice(-10)
|
|
||||||
.map((m) => {
|
|
||||||
const sender =
|
|
||||||
conversation.participants.find((p) => p.id === m.from)?.name || m.from;
|
|
||||||
const content = m.text || `[${m.type}]`;
|
|
||||||
return `${sender}: ${content}`;
|
|
||||||
})
|
|
||||||
.join('\n');
|
|
||||||
|
|
||||||
if (!genAI) {
|
if (!genAI) {
|
||||||
return res.json({ reply: repoInfo });
|
return res.json({ reply: systemPromt });
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const contents = `Repo information: ${repoInfo}\nConversation:\n${context}\n`;
|
const contents = `systemPrompt: ${systemPromt}\nConversation:\n${JSON.stringify(conversation)}\n`;
|
||||||
|
console.log(' contents', contents);
|
||||||
|
|
||||||
const config: any = {};
|
const config: any = {};
|
||||||
if (true) {
|
if (true) {
|
||||||
console.log('Using Model Context Protocol tools ', MCP_URL);
|
console.log('Using Model Context Protocol tools ', MCP_URL);
|
||||||
@@ -115,7 +135,7 @@ app.get('/', (req, res) => {
|
|||||||
<p>Example: {"conversation": {"chatId": "123@c.us", "title": "Chat", "isGroup": false, "unreadCount": 0, "participants": [{"id": "123@c.us", "name": "Alice", "isMe": false}], "messages": [{"id": "m1", "from": "123@c.us", "to": "me@c.us", "ts": 0, "type": "chat", "text": "hello", "meta": {"ack":0,"hasReaction":false,"isQuoted":false}}]}}</p>
|
<p>Example: {"conversation": {"chatId": "123@c.us", "title": "Chat", "isGroup": false, "unreadCount": 0, "participants": [{"id": "123@c.us", "name": "Alice", "isMe": false}], "messages": [{"id": "m1", "from": "123@c.us", "to": "me@c.us", "ts": 0, "type": "chat", "text": "hello", "meta": {"ack":0,"hasReaction":false,"isQuoted":false}}]}}</p>
|
||||||
<p>It will respond with a JSON object containing {"reply": "the answer"}</p>
|
<p>It will respond with a JSON object containing {"reply": "the answer"}</p>
|
||||||
|
|
||||||
<p>Repository info: ${repoInfo}</p>
|
<p>Repository info: </p>
|
||||||
`);
|
`);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|||||||
11207
whatsapp-router/package-lock.json
generated
11207
whatsapp-router/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -10,12 +10,16 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@google/genai": "^1.4.0",
|
"@google/genai": "^1.4.0",
|
||||||
|
"@open-wa/wa-automate": "^4.76.0",
|
||||||
"axios": "^1.5.0",
|
"axios": "^1.5.0",
|
||||||
"dotenv": "^16.5.0",
|
"dotenv": "^16.5.0",
|
||||||
"express": "^4.18.2"
|
"express": "^4.18.2",
|
||||||
|
"ffmpeg-static": "^5.2.0",
|
||||||
|
"fluent-ffmpeg": "^2.1.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/express": "^4.17.21",
|
"@types/express": "^4.17.21",
|
||||||
|
"@types/fluent-ffmpeg": "^2.1.27",
|
||||||
"@types/node": "^20.11.19",
|
"@types/node": "^20.11.19",
|
||||||
"nodemon": "^3.1.10",
|
"nodemon": "^3.1.10",
|
||||||
"ts-node": "^10.9.2",
|
"ts-node": "^10.9.2",
|
||||||
|
|||||||
@@ -109,7 +109,7 @@ export async function buildConversation(
|
|||||||
title,
|
title,
|
||||||
isGroup,
|
isGroup,
|
||||||
unreadCount,
|
unreadCount,
|
||||||
participants: Array.from(participantsMap.values()),
|
participants: Array.from(participantsMap.values()),
|
||||||
messages,
|
messages,
|
||||||
createdAt: conversations.get(chatId)?.createdAt || now,
|
createdAt: conversations.get(chatId)?.createdAt || now,
|
||||||
};
|
};
|
||||||
@@ -144,5 +144,6 @@ export async function addMessageToConversation(
|
|||||||
isMe: s.isMe,
|
isMe: s.isMe,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return conv;
|
return conv;
|
||||||
}
|
}
|
||||||
|
|||||||
55
whatsapp-router/src/transcribeAudioMessage.ts
Normal file
55
whatsapp-router/src/transcribeAudioMessage.ts
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
// transcribeAudioMessage.ts
|
||||||
|
import { WhatsAppMessage } from './types';
|
||||||
|
import { decryptMedia } from '@open-wa/wa-automate';
|
||||||
|
import axios from 'axios';
|
||||||
|
import { GoogleGenAI, createUserContent } from '@google/genai';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transcribe un mensaje de audio de WhatsApp usando Gemini.
|
||||||
|
* @param message - Mensaje recibido desde OpenWA.
|
||||||
|
* @returns Texto transcrito o null si no era un audio válido.
|
||||||
|
*/
|
||||||
|
export async function transcribeAudioMessage(message: WhatsAppMessage): Promise<string | null> {
|
||||||
|
if (
|
||||||
|
message.type !== 'ptt' &&
|
||||||
|
message.type !== 'audio' &&
|
||||||
|
message.mimetype !== 'audio/ogg; codecs=opus'
|
||||||
|
) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const audioUrl = message.clientUrl || message.deprecatedMms3Url;
|
||||||
|
if (!audioUrl) throw new Error('El mensaje no tiene URL de audio');
|
||||||
|
|
||||||
|
const raw = await axios.get(audioUrl, { responseType: 'arraybuffer' });
|
||||||
|
|
||||||
|
const enrichedMessage = {
|
||||||
|
...message,
|
||||||
|
_data: {
|
||||||
|
...message,
|
||||||
|
_raw: raw.data
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const decryptedBuffer = await decryptMedia(enrichedMessage as any);
|
||||||
|
const base64Audio = decryptedBuffer.toString('base64');
|
||||||
|
|
||||||
|
const apiKey = process.env.GOOGLE_API_KEY;
|
||||||
|
if (!apiKey) throw new Error('Falta GOOGLE_API_KEY');
|
||||||
|
|
||||||
|
const genAI = new GoogleGenAI({ apiKey });
|
||||||
|
const result = await genAI.models.generateContent({
|
||||||
|
model: 'gemini-2.0-flash',
|
||||||
|
contents: createUserContent([
|
||||||
|
{
|
||||||
|
inlineData: {
|
||||||
|
mimeType: 'audio/ogg',
|
||||||
|
data: base64Audio
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'Transcribí este audio porfa. te estaran hablando en español honduras.'
|
||||||
|
])
|
||||||
|
});
|
||||||
|
|
||||||
|
return result.text?.trim() || null;
|
||||||
|
}
|
||||||
@@ -4,6 +4,7 @@ import { GoogleGenAI } from '@google/genai';
|
|||||||
import { getHandler } from './chatHandlers';
|
import { getHandler } from './chatHandlers';
|
||||||
import { addMessageToConversation } from './store/conversation';
|
import { addMessageToConversation } from './store/conversation';
|
||||||
import { WhatsAppMessage, Conversation } from './types';
|
import { WhatsAppMessage, Conversation } from './types';
|
||||||
|
import { transcribeAudioMessage } from './transcribeAudioMessage';
|
||||||
|
|
||||||
export interface WebhookConfig {
|
export interface WebhookConfig {
|
||||||
API_URL: string;
|
API_URL: string;
|
||||||
@@ -32,6 +33,12 @@ export function registerWebhookRoutes(
|
|||||||
|
|
||||||
if (message) {
|
if (message) {
|
||||||
const origen = from || message.chatId || 'desconocido';
|
const origen = from || message.chatId || 'desconocido';
|
||||||
|
|
||||||
|
if(origen == '50493849962@c.us') //si el mensajes es de un agente, no lo proceses
|
||||||
|
{
|
||||||
|
return res.sendStatus(200);
|
||||||
|
}
|
||||||
|
|
||||||
console.log(`📩 Mensaje recibido (${message.text}) de ${origen}`);
|
console.log(`📩 Mensaje recibido (${message.text}) de ${origen}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -41,6 +48,8 @@ export function registerWebhookRoutes(
|
|||||||
const chatId = message.chatId || from;
|
const chatId = message.chatId || from;
|
||||||
|
|
||||||
// Audio message handling
|
// Audio message handling
|
||||||
|
// console.log(message);
|
||||||
|
|
||||||
if (
|
if (
|
||||||
message.type === 'ptt' &&
|
message.type === 'ptt' &&
|
||||||
message.mimetype === 'audio/ogg; codecs=opus'
|
message.mimetype === 'audio/ogg; codecs=opus'
|
||||||
@@ -53,33 +62,12 @@ export function registerWebhookRoutes(
|
|||||||
}
|
}
|
||||||
console.log('🎤 Mensaje de audio detectado', audioUrl);
|
console.log('🎤 Mensaje de audio detectado', audioUrl);
|
||||||
try {
|
try {
|
||||||
// Download audio using the /downloadFileWithCredentials endpoint
|
const transcript = await transcribeAudioMessage(message);
|
||||||
const audioResponse = await axios.post(`${openWaUrl}/downloadFileWithCredentials`, {
|
|
||||||
args: { url: audioUrl },
|
|
||||||
});
|
|
||||||
const audioBase64 = audioResponse.data; // This is already a base64 string
|
|
||||||
|
|
||||||
const apiKey = process.env.GOOGLE_API_KEY;
|
|
||||||
if (!apiKey) {
|
|
||||||
throw new Error('GOOGLE_API_KEY is not set');
|
|
||||||
}
|
|
||||||
const genAI = new GoogleGenAI({ apiKey });
|
|
||||||
|
|
||||||
// Corrected Gemini API call structure
|
|
||||||
const result = await genAI.models.generateContent({
|
|
||||||
model: 'gemini-pro', // Ensure this model supports inline audio or use appropriate one
|
|
||||||
contents: [
|
|
||||||
{ inlineData: { mimeType: 'audio/ogg', data: audioBase64 } },
|
|
||||||
{ text: 'Generate a transcript of the speech.' },
|
|
||||||
],
|
|
||||||
});
|
|
||||||
// result directly is GenerateContentResponse
|
|
||||||
const transcript = result.text; // Use the getter for text
|
|
||||||
if (transcript === undefined) {
|
|
||||||
throw new Error('Transcription resulted in undefined text.');
|
|
||||||
}
|
|
||||||
console.log('📝 Transcripción:', transcript);
|
console.log('📝 Transcripción:', transcript);
|
||||||
message.body = transcript;
|
message.body = transcript || '';
|
||||||
|
message.text = transcript || '';
|
||||||
|
|
||||||
|
|
||||||
} catch (transcriptionError: any) {
|
} catch (transcriptionError: any) {
|
||||||
console.error('Error en la transcripción:', transcriptionError.message);
|
console.error('Error en la transcripción:', transcriptionError.message);
|
||||||
const reply =
|
const reply =
|
||||||
@@ -90,6 +78,7 @@ export function registerWebhookRoutes(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log(message);
|
||||||
let conv: Conversation | undefined;
|
let conv: Conversation | undefined;
|
||||||
if (chatId) {
|
if (chatId) {
|
||||||
try {
|
try {
|
||||||
|
|||||||
Reference in New Issue
Block a user