Mensajes de voz

Una nota de voz llega convertida en texto. La transcripción ocurre a la entrada, antes de que el agente entre en juego.

Mensajes de voz

Manda una nota de voz por Telegram o WhatsApp, o por Discord (como adjunto de un comando de barra, o hablada directo en un canal que el bot escuche), y lo que recibe el agente es texto. El audio se transcribe al llegar, antes de que exista una sesión y antes de tomar cualquier decisión de enrutado, así que al agente le llega un mensaje corriente y silvestre.

No siempre funcionó así. Antes, el gateway guardaba el archivo en el workspace del agente y le pasaba la ruta, dejando que el agente resolviera por su cuenta cómo “escucharlo”: buscar un transcriptor, instalarlo, correrlo, leer la salida. Cada nota de voz se volvía un pequeño proyecto de investigación. Era lento, salía distinto cada vez, y encima gastaba un aviso de permiso solo por el hecho de leer el mensaje recién llegado.

No hay nada que configurar

Si ya tienes una conexión de modelo con OpenAI o con Groq, la transcripción ya funciona sola. Pepe reutiliza esa misma credencial y le pide al proveedor su modelo de transcripción (whisper-1 en OpenAI, whisper-large-v3-turbo en Groq) en vez del modelo de chat con el que configuraste esa conexión. Manda una nota de voz y te responde, sin ningún ajuste previo.

En qué canales funciona

Telegram, WhatsApp y Discord hacen pasar un adjunto por la misma puerta: el audio se convierte en transcripción, un documento en texto, y una imagen llega como imagen a un modelo con visión. Los ajustes de esta página valen para los tres, así que configurar la transcripción una vez alcanza para todos.

Dos detalles que conviene tener presentes:

Slack, Microsoft Teams y Google Chat siguen recibiendo solo texto.

Cómo elige la ruta

Pepe prueba en este orden, y cualquiera de estas opciones puede faltar:

  1. media.audio.model: una conexión de modelo, referida por su nombre. La cadena de fallbacks propia de esa conexión también aplica aquí, así que el failover no cuesta nada extra.
  2. media.audio.command: un comando local, como whisper-cli -f {file}, donde {file} se reemplaza por la ruta del audio. Este se intenta antes que la detección automática, y es a propósito: si alguien configuró un transcriptor local fue justamente para que el audio no saliera de la máquina, y saltárselo para llamar a un proveedor externo echaría por tierra ese propósito.
  3. Detección automática: la ruta sin configuración que se describe arriba.
  4. Nada disponible: el archivo pasa directo al agente, que se las arregla con las herramientas que tenga. Esta vía se mantiene como red de seguridad, no como la entrada normal.

Configurarlo

Puedes apuntar la transcripción a una conexión de modelo específica, o a un comando local, desde la CLI:

pepe media audio --model groq --language es --echo true
pepe media audio --command "whisper-cli -f {file}"   # mantiene el audio dentro de la máquina
pepe media audio off                                 # vuelve a la detección automática

--echo true devuelve la transcripción al chat, así quien habló puede confirmar que se entendió bien. Los mismos ajustes están en la página Config del panel, y en pepe setup bajo Media.

Por qué importa transcribir primero

Como el texto ya existe antes de que corra el enrutado, el enrutado puede leerlo. De ahí salen dos cosas que antes eran imposibles, cuando la transcripción solo aparecía dentro del turno del agente:

El audio se convierte en texto; la foto se convierte en algo que se ve. El habla se transcribe justo a la entrada. Una foto se le manda al modelo como una imagen que de verdad puede ver (en un modelo con visión, más abajo). Un documento se extrae a texto.

Responder también con voz

Puedes responder a una nota de voz con otra nota de voz. Viene apagado por defecto; apunta media.tts a una conexión de modelo que sirva un /audio/speech compatible con OpenAI y se activa:

pepe media tts --model openai --voice nova
pepe media tts off

El registro que queda guardado sigue siendo la respuesta en texto. El audio es un añadido, con un límite de duración para que una respuesta larga jamás se convierta en un clip de cinco minutos. Si el TTS falla, falla en silencio: la respuesta en texto ya se mandó, así que no se pierde nada, simplemente ese turno se queda sin voz. Los mismos ajustes están en la página Config del panel, y en pepe setup bajo Media.

Fotos

Manda una foto por Telegram, WhatsApp o Discord y, si el modelo tiene capacidad de visión, el agente ve la imagen de verdad, no solo un nombre de archivo. Antes recibía apenas una línea de texto (“el usuario mandó una foto, guardada en …”) mientras que la imagen en sí nunca llegaba al modelo, así que el agente terminaba adivinando, o directamente inventando, qué había en ella. Ahora la imagen viaja junto con el mensaje.

Está desactivado a menos que le digas explícitamente al sistema que el modelo puede ver. No todos los endpoints compatibles con OpenAI aceptan imágenes, y mandarle una a un modelo de solo texto es un error, así que la visión se activa por conexión:

{
  "models": {
    "gpt4o": {
      "base_url": "https://api.openai.com/v1",
      "api_key": "${OPENAI_API_KEY}",
      "model": "gpt-4o",
      "vision": true
    }
  }
}

Con vision activado, una foto (con o sin pie de foto) le llega al modelo como imagen en el mismo turno en que se recibe. Funciona igual en conexiones compatibles con OpenAI, Anthropic y Responses/Codex. La imagen viaja solo en ese turno puntual: igual que con una transcripción, lo que queda como registro es la respuesta del agente sobre ella, no los bytes en sí, así que nunca infla la sesión ni se reenvía turno tras turno. Un modelo sin vision cae de vuelta al comportamiento anterior (la ruta del archivo en el prompt, para que el agente la abra con sus propias herramientas).

Telegram ya manda cada foto en varios tamaños preescalados, así que Pepe elige la más grande que quepa dentro del límite de bytes, sin necesitar ninguna biblioteca de procesamiento de imágenes. Un álbum de fotos se manda como varias imágenes juntas. Los límites viven bajo media.image, y ambos tienen valores por defecto:

{
  "media": {
    "image": { "max_mb": 5, "max_parts": 4 }
  }
}

Configuración

Todas las claves son opcionales y viven bajo media.audio, dentro de ~/.pepe/config.json:

{
  "media": {
    "audio": {
      "model": "groq",
      "language": "es",
      "max_mb": 20,
      "timeout": 60,
      "echo": true
    }
  }
}

Para mantener el audio dentro de la máquina, usa un comando en lugar de una conexión:

{
  "media": {
    "audio": {
      "command": "whisper-cli -f {file}",
      "timeout": 120
    }
  }
}

Resguardos