Confirmado que la factura nunca se creó: la lista de /app/facturas no filtra nada que pudiera esconderla. La herramienta falló y el modelo informó "FACTURA GUARDADA EXITOSAMENTE" igual. El commit anterior le prohíbe eso por prompt, pero un prompt es una sugerencia. Esto es la garantía: el código junta los errores que devolvieron las herramientas durante la conversación y los agrega a la respuesta. Si algo no se completó, se ve, diga lo que diga el modelo. No reemplaza al prompt — el modelo sigue debiendo explicar el fallo con sus palabras. Es la red por debajo, para que un "guardado exitosamente" sobre algo que no se guardó no pueda pasar desapercibido otra vez. Sigue sin saberse por qué falló aquella vez, porque hasta hoy el resultado de las herramientas no se registraba. Con el log y este aviso, el próximo intento lo va a decir en el momento. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
68 lines
2.4 KiB
Go
68 lines
2.4 KiB
Go
package services
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
// Registrar una factura dictando los datos no era posible: la única
|
|
// herramienta de venta era adjuntar_factura, que exige un archivo pendiente.
|
|
// Sin una alternativa, el modelo no tenía con qué guardar y el usuario recibía
|
|
// un "guardado exitosamente" sobre algo que nunca se creó.
|
|
func TestExisteHerramientaParaFacturaSinAdjunto(t *testing.T) {
|
|
tools := agentTools()
|
|
|
|
nombres := map[string]agentTool{}
|
|
for _, tl := range tools {
|
|
nombres[tl.Function.Name] = tl
|
|
}
|
|
|
|
crear, ok := nombres["crear_factura"]
|
|
if !ok {
|
|
t.Fatal("falta crear_factura: no habría forma de registrar una factura de venta sin archivo adjunto")
|
|
}
|
|
if _, ok := nombres["adjuntar_factura"]; !ok {
|
|
t.Error("adjuntar_factura debe seguir existiendo para el caso con documento")
|
|
}
|
|
|
|
// La descripción tiene que distinguirlas, o el modelo elige la equivocada
|
|
// y falla por falta de adjunto — que es justo el problema original.
|
|
d := strings.ToLower(crear.Function.Description)
|
|
if !strings.Contains(d, "sin archivo adjunto") {
|
|
t.Error("la descripción debe dejar claro que no necesita adjunto")
|
|
}
|
|
if !strings.Contains(d, "adjuntar_factura") {
|
|
t.Error("la descripción debe remitir a adjuntar_factura cuando sí hay documento")
|
|
}
|
|
|
|
// cliente_id y monto son obligatorios: sin ellos la factura no sirve.
|
|
req := strings.Join(crear.Function.Parameters.Required, ",")
|
|
for _, campo := range []string{"cliente_id", "monto"} {
|
|
if !strings.Contains(req, campo) {
|
|
t.Errorf("%q debería ser obligatorio en crear_factura", campo)
|
|
}
|
|
}
|
|
}
|
|
|
|
// El modelo puede informar éxito aunque la herramienta haya fallado — pasó con
|
|
// una factura que nunca se creó. El aviso lo escribe el código a partir del
|
|
// resultado real, así que no depende de que el modelo se porte bien.
|
|
func TestErrorDeTool(t *testing.T) {
|
|
casos := []struct {
|
|
nombre string
|
|
resultado string
|
|
esperado string
|
|
}{
|
|
{"error de la herramienta", `{"error": "cliente_id requerido"}`, "cliente_id requerido"},
|
|
{"resultado exitoso", `{"ok": true, "factura_id": 12}`, ""},
|
|
{"lista de datos", `{"items": [], "total": 0}`, ""},
|
|
{"resultado no JSON", `algo suelto`, ""},
|
|
{"error vacío no cuenta", `{"error": ""}`, ""},
|
|
}
|
|
for _, cas := range casos {
|
|
if got := errorDeTool(cas.resultado); got != cas.esperado {
|
|
t.Errorf("%s: errorDeTool(%s) = %q, esperaba %q", cas.nombre, cas.resultado, got, cas.esperado)
|
|
}
|
|
}
|
|
}
|