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:
co-authored by
Claude Opus 5
parent
e681f41639
commit
c4b0305112
@@ -198,7 +198,7 @@ async function copiarWidget() {
|
||||
function canalVacio() {
|
||||
return {
|
||||
tipo: 'telegram', bot_token: '', phone_number_id: '', access_token: '', app_secret: '', verify_token: '',
|
||||
usar_whisper_audio: false, usar_ocr_imagenes: false,
|
||||
usar_whisper_audio: false, usar_ocr_imagenes: false, usar_archivos_docs: false,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -226,6 +226,7 @@ async function guardarCanal() {
|
||||
await api.post(apiUmind('/umind/canales'), {
|
||||
agente_id: agenteIdNum.value, tipo: canalForm.value.tipo, credenciales, activo: true,
|
||||
usar_whisper_audio: canalForm.value.usar_whisper_audio, usar_ocr_imagenes: canalForm.value.usar_ocr_imagenes,
|
||||
usar_archivos_docs: canalForm.value.usar_archivos_docs,
|
||||
})
|
||||
showCanalForm.value = false
|
||||
await cargarCanales()
|
||||
@@ -234,29 +235,24 @@ async function guardarCanal() {
|
||||
}
|
||||
}
|
||||
|
||||
async function toggleCanal(c) {
|
||||
// El PUT de canales manda los tres interruptores siempre: si alguno faltara, el
|
||||
// backend lo tomaría como false y lo apagaría sin que nadie lo pidiera.
|
||||
async function guardarInterruptores(c, cambios) {
|
||||
await api.put(apiUmind(`/umind/canales/${c.ID}`), {
|
||||
activo: !c.activo, credenciales: {},
|
||||
usar_whisper_audio: c.usar_whisper_audio, usar_ocr_imagenes: c.usar_ocr_imagenes,
|
||||
activo: c.activo,
|
||||
credenciales: {},
|
||||
usar_whisper_audio: c.usar_whisper_audio,
|
||||
usar_ocr_imagenes: c.usar_ocr_imagenes,
|
||||
usar_archivos_docs: c.usar_archivos_docs,
|
||||
...cambios,
|
||||
})
|
||||
await cargarCanales()
|
||||
}
|
||||
|
||||
async function toggleCanalWhisper(c) {
|
||||
await api.put(apiUmind(`/umind/canales/${c.ID}`), {
|
||||
activo: c.activo, credenciales: {},
|
||||
usar_whisper_audio: !c.usar_whisper_audio, usar_ocr_imagenes: c.usar_ocr_imagenes,
|
||||
})
|
||||
await cargarCanales()
|
||||
}
|
||||
|
||||
async function toggleCanalOcr(c) {
|
||||
await api.put(apiUmind(`/umind/canales/${c.ID}`), {
|
||||
activo: c.activo, credenciales: {},
|
||||
usar_whisper_audio: c.usar_whisper_audio, usar_ocr_imagenes: !c.usar_ocr_imagenes,
|
||||
})
|
||||
await cargarCanales()
|
||||
}
|
||||
const toggleCanal = (c) => guardarInterruptores(c, { activo: !c.activo })
|
||||
const toggleCanalWhisper = (c) => guardarInterruptores(c, { usar_whisper_audio: !c.usar_whisper_audio })
|
||||
const toggleCanalOcr = (c) => guardarInterruptores(c, { usar_ocr_imagenes: !c.usar_ocr_imagenes })
|
||||
const toggleCanalArchivos = (c) => guardarInterruptores(c, { usar_archivos_docs: !c.usar_archivos_docs })
|
||||
|
||||
async function eliminarCanal(c) {
|
||||
if (!confirm(`¿Eliminar el canal ${c.tipo}?`)) return
|
||||
@@ -585,6 +581,10 @@ watch(
|
||||
<input type="checkbox" :checked="c.usar_ocr_imagenes" @change="toggleCanalOcr(c)" class="rounded border-borde text-brand focus:ring-brand" />
|
||||
Leer texto de imágenes (OCR)
|
||||
</label>
|
||||
<label class="flex items-center gap-1.5 text-texto cursor-pointer">
|
||||
<input type="checkbox" :checked="c.usar_archivos_docs" @change="toggleCanalArchivos(c)" class="rounded border-borde text-brand focus:ring-brand" />
|
||||
Leer archivos adjuntos (PDF, Word, texto)
|
||||
</label>
|
||||
</div>
|
||||
<p class="label mt-1 break-all">
|
||||
Webhook: <code class="bg-elevado px-1 rounded">{{ c.webhook_url }}</code>
|
||||
@@ -640,6 +640,10 @@ watch(
|
||||
<input type="checkbox" v-model="canalForm.usar_ocr_imagenes" class="rounded border-borde text-brand focus:ring-brand" />
|
||||
Leer texto de imágenes con OCR
|
||||
</label>
|
||||
<label class="flex items-center gap-2 text-sm text-texto cursor-pointer">
|
||||
<input type="checkbox" v-model="canalForm.usar_archivos_docs" class="rounded border-borde text-brand focus:ring-brand" />
|
||||
Leer archivos adjuntos (PDF, Word, texto)
|
||||
</label>
|
||||
</div>
|
||||
<div class="flex justify-end gap-2 pt-2">
|
||||
<button type="button" class="btn-ghost" @click="showCanalForm = false">Cancelar</button>
|
||||
|
||||
Reference in New Issue
Block a user