fix(whisper): acepta la transcripción en texto plano y permite descargarla

El servicio devolvía la transcripción correcta y la reportábamos como
error: "respuesta inesperada del servicio de transcripción: <la
transcripción completa>". La causa es que mandábamos response_format=json,
que es la convención de la API de OpenAI — whisper-asr-webservice usa el
query param `output`, así que ignoró el campo y respondió en su formato
por defecto (txt), y el parser exigía JSON.

- textoDeRespuestaWhisper acepta las dos formas (JSON {"text":...} o texto
  pelado) en vez de adivinar qué variante corre del otro lado. Con test:
  8 casos, incluidos JSON sin campo text y un texto que empieza con "{".
- Whisper y OCR: la caja de resultado ahora muestra el conteo de
  caracteres, scrollea si es largo, y tiene Copiar y Descargar .txt (Blob
  + <a download>, el texto ya está en el navegador).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Lizandro Guarnizo
2026-08-14 20:49:08 -05:00
co-authored by Claude Sonnet 5
parent 7146539f86
commit 6817ad1e6c
4 changed files with 122 additions and 8 deletions
+27 -4
View File
@@ -7,6 +7,7 @@ import (
"io"
"mime/multipart"
"net/http"
"strings"
"time"
"github.com/sujit-baniya/fiber-boilerplate/pkg/models"
@@ -63,12 +64,34 @@ func TranscribirAudioSelfHosted(agenteID uint, audioBytes []byte, filename strin
return "", fmt.Errorf("el servicio de transcripción respondió %d: %s", resp.StatusCode, string(raw))
}
texto, err := textoDeRespuestaWhisper(raw)
if err != nil {
return "", err
}
RegistrarUso(agenteID, models.UsoTipoWhisper, 1, "transcripcion")
return texto, nil
}
// textoDeRespuestaWhisper acepta las dos formas en que puede volver una
// transcripción, en vez de adivinar cuál variante corre del otro lado:
//
// - JSON {"text": "..."} — las APIs compatibles con OpenAI.
// - El texto pelado — whisper-asr-webservice devuelve txt por defecto: su
// parámetro es `output` (query), no `response_format` (form), así que
// ignora el que mandamos.
//
// Exigir JSON hacía que una transcripción buena se reportara como "respuesta
// inesperada", con el texto correcto adentro del mensaje de error.
func textoDeRespuestaWhisper(raw []byte) (string, error) {
var out struct {
Text string `json:"text"`
}
if err := json.Unmarshal(raw, &out); err != nil {
return "", fmt.Errorf("respuesta inesperada del servicio de transcripción: %s", string(raw))
if err := json.Unmarshal(raw, &out); err == nil && strings.TrimSpace(out.Text) != "" {
return strings.TrimSpace(out.Text), nil
}
RegistrarUso(agenteID, models.UsoTipoWhisper, 1, "transcripcion")
return out.Text, nil
texto := strings.TrimSpace(string(raw))
if texto == "" {
return "", fmt.Errorf("el servicio de transcripción devolvió una respuesta vacía")
}
return texto, nil
}