feat(umind): los agentes también leen archivos, no solo audios e imágenes

Faltaba la tercera pata: audio pasaba por Whisper, imagen por OCR, y un PDF o
un Word adjunto se ignoraba en silencio. Ahora hay un interruptor por canal
—"Leer archivos adjuntos"— y los documentos que llegan por Telegram o WhatsApp
se convierten a texto antes de pasar al agente.

PDF se lee con stdlib cuando el documento es digital (facturas, cotizaciones,
lo exportado por cualquier programa) y cae al OCR si es un escaneo. Word .docx
es un zip con XML adentro; texto plano, CSV y JSON van directo.

El agente recibe el contenido con contexto ("el cliente adjuntó X, y escribió
Y"), no el chorizo pelado: sin eso el modelo contesta como si el cliente
hubiera tipeado una factura.

De paso la importación de plantillas gana PDF, que usa el mismo extractor.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Lizandro Guarnizo
2026-08-17 18:50:59 -05:00
co-authored by Claude Opus 5
parent e681f41639
commit c4b0305112
14 changed files with 384 additions and 124 deletions
@@ -16,15 +16,18 @@ type umindTgChat struct {
}
type umindTgFileRef struct {
FileID string `json:"file_id"`
FileID string `json:"file_id"`
FileName string `json:"file_name"` // solo lo traen los documentos
}
type umindTgMessage struct {
Chat umindTgChat `json:"chat"`
Text string `json:"text"`
Voice *umindTgFileRef `json:"voice"`
Audio *umindTgFileRef `json:"audio"`
Photo []umindTgFileRef `json:"photo"`
Chat umindTgChat `json:"chat"`
Text string `json:"text"`
Voice *umindTgFileRef `json:"voice"`
Audio *umindTgFileRef `json:"audio"`
Photo []umindTgFileRef `json:"photo"`
Document *umindTgFileRef `json:"document"`
Caption string `json:"caption"`
}
type umindTgUpdate struct {
@@ -55,6 +58,8 @@ func UmindTelegramWebhook(c *fiber.Ctx) error {
err = services.ProcesarMediaTelegramUmind(canal, msg.Chat.ID, msg.Audio.FileID, "audio")
case len(msg.Photo) > 0:
err = services.ProcesarMediaTelegramUmind(canal, msg.Chat.ID, msg.Photo[len(msg.Photo)-1].FileID, "image")
case msg.Document != nil && msg.Document.FileID != "":
err = services.ProcesarMediaTelegramUmindConNombre(canal, msg.Chat.ID, msg.Document.FileID, "document", msg.Document.FileName, msg.Caption)
default:
texto := strings.TrimSpace(msg.Text)
if texto == "" {
@@ -81,8 +86,13 @@ type umindWaMessage struct {
Text struct {
Body string `json:"body"`
} `json:"text"`
Image umindWaMediaRef `json:"image"`
Audio umindWaMediaRef `json:"audio"`
Image umindWaMediaRef `json:"image"`
Audio umindWaMediaRef `json:"audio"`
Document struct {
ID string `json:"id"`
Filename string `json:"filename"`
Caption string `json:"caption"`
} `json:"document"`
}
type umindWaValue struct {
@@ -172,6 +182,11 @@ func UmindWhatsAppWebhook(c *fiber.Ctx) error {
continue
}
err = services.ProcesarMediaWhatsAppUmind(canal, msg.From, msg.Image.ID, "image")
case "document":
if msg.Document.ID == "" {
continue
}
err = services.ProcesarMediaWhatsAppUmindConNombre(canal, msg.From, msg.Document.ID, "document", msg.Document.Filename, msg.Document.Caption)
default:
continue
}