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:
co-authored by
Claude Sonnet 5
parent
7146539f86
commit
6817ad1e6c
@@ -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
|
||||
}
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
package services
|
||||
|
||||
import "testing"
|
||||
|
||||
// El bug real: whisper-asr-webservice devuelve texto plano (su parámetro es
|
||||
// `output`, no `response_format`), y exigir JSON hacía que una transcripción
|
||||
// buena se reportara como error con el texto correcto dentro del mensaje.
|
||||
func TestTextoDeRespuestaWhisper(t *testing.T) {
|
||||
casos := []struct {
|
||||
nombre string
|
||||
cuerpo string
|
||||
esperado string
|
||||
falla bool
|
||||
}{
|
||||
{"texto plano", "Buenas tardes, respecto al punto uno...", "Buenas tardes, respecto al punto uno...", false},
|
||||
{"texto plano con espacios", " hola mundo \n", "hola mundo", false},
|
||||
{"json compatible con openai", `{"text":"hola mundo"}`, "hola mundo", false},
|
||||
{"json con espacios en el texto", `{"text":" hola "}`, "hola", false},
|
||||
{"json sin campo text cae a crudo", `{"resultado":"x"}`, `{"resultado":"x"}`, false},
|
||||
{"json con text vacío cae a crudo", `{"text":""}`, `{"text":""}`, false},
|
||||
{"respuesta vacía es error", " ", "", true},
|
||||
// Un texto que empieza con { pero no es JSON no debe romper el parseo.
|
||||
{"texto que parece json roto", `{esto no es json`, `{esto no es json`, false},
|
||||
}
|
||||
|
||||
for _, cas := range casos {
|
||||
t.Run(cas.nombre, func(t *testing.T) {
|
||||
got, err := textoDeRespuestaWhisper([]byte(cas.cuerpo))
|
||||
if cas.falla {
|
||||
if err == nil {
|
||||
t.Fatalf("esperaba error para %q", cas.cuerpo)
|
||||
}
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatalf("no esperaba error: %v", err)
|
||||
}
|
||||
if got != cas.esperado {
|
||||
t.Errorf("textoDeRespuestaWhisper(%q) = %q, esperaba %q", cas.cuerpo, got, cas.esperado)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user