#!/usr/bin/env bash
# LA DECISIVA: ¿emite el 4B llamadas a herramienta bien formadas?
#
# Es la duda que dejo abierta todo el carril. La guia de Hermes dice que para
# trabajo agentico completo hace falta gemma4:31b y marca SIN tool calling todo
# lo de 9B para abajo. qwen3.5 no sale en su tabla y si lo hace —el commit
# "el cerebro con tool calling nativo" lo demuestra— pero lo demuestra en el
# arnes propio, que inyecta muchas menos definiciones por vuelta.
#
# Se pregunta al ENDPOINT directamente, sin Hermes de por medio, a proposito.
# Junta, estas dos preguntas se contestan mal:
#
#     ¿sabe el modelo emitir una llamada?      <- esto mide esta prueba
#     ¿funciona Hermes?                        <- eso es la ESCALERA
#
# Si esto falla, no hay nada que depurar en Hermes. Si esto pasa y el TUI se
# atraganta, el fallo esta en el arnes y se busca alli.
#
# Tres casos, no uno: uno que PIDE herramienta, uno que NO la necesita, y uno de
# dos argumentos. Un modelo que llama siempre es tan inutil como uno que no
# llama nunca, y con una sola pregunta no se distingue.

cd "$(dirname "$0")" && . ./comun.sh

titulo "Llamadas a herramienta"

HERRAMIENTAS='[
  {"type":"function","function":{
     "name":"ejecuta_comando",
     "description":"Ejecuta un comando de shell y devuelve su salida",
     "parameters":{"type":"object","properties":{"comando":{"type":"string"}},"required":["comando"]}}},
  {"type":"function","function":{
     "name":"escribe_fichero",
     "description":"Escribe texto en un fichero del disco",
     "parameters":{"type":"object","properties":{
        "ruta":{"type":"string"},"contenido":{"type":"string"}},"required":["ruta","contenido"]}}}
]'

# pregunta <descripcion> <texto> <herramienta esperada, o "" si no debe llamar>
pregunta() {
    local desc=$1 texto=$2 espera=$3 cuerpo r
    cuerpo=$(python3 -c "
import json, sys
print(json.dumps({
    'model': '$MODELO',
    'messages': [{'role': 'user', 'content': sys.argv[1]}],
    'tools': json.loads(sys.argv[2]),
}))" "$texto" "$HERRAMIENTAS")

    r=$(curl -s --max-time 300 "$OLLAMA/v1/chat/completions" \
        -H 'Content-Type: application/json' -d "$cuerpo" 2>/dev/null)

    local salida
    salida=$(printf '%s' "$r" | python3 -c "
import json, sys
try:
    d = json.load(sys.stdin)
except Exception:
    print('ILEGIBLE|'); raise SystemExit
if 'error' in d:
    print('ERROR|' + str(d['error'])[:120]); raise SystemExit
m = d['choices'][0]['message']
tc = m.get('tool_calls') or []
if not tc:
    print('PROSA|' + (m.get('content') or '')[:90].replace('\n', ' ')); raise SystemExit
f = tc[0]['function']
try:
    args = json.loads(f['arguments'])
except Exception:
    print('ROTO|' + str(f['arguments'])[:90]); raise SystemExit
print(f\"{f['name']}|\" + json.dumps(args, ensure_ascii=False)[:90])
")
    local nombre=${salida%%|*} detalle=${salida#*|}

    if [ -z "$espera" ]; then
        # aqui lo correcto es NO llamar
        if [ "$nombre" = PROSA ]; then
            printf '  %s %s\n' "$(verde OK)" "$desc"
            printf '        %s\n' "$(gris "contesto: $detalle")"
            _ok=$((_ok + 1))
        else
            printf '  %s %s\n' "$(rojo FALLO)" "$desc"
            printf '        llamo a %s cuando no hacia falta: %s\n' "$nombre" "$detalle"
            _fallo=$((_fallo + 1))
        fi
        return
    fi

    if [ "$nombre" = "$espera" ]; then
        printf '  %s %s\n' "$(verde OK)" "$desc"
        printf '        %s\n' "$(gris "$nombre $detalle")"
        _ok=$((_ok + 1))
    else
        printf '  %s %s\n' "$(rojo FALLO)" "$desc"
        printf '        esperado: %s\n        obtenido: %s %s\n' "$espera" "$nombre" "$detalle"
        _fallo=$((_fallo + 1))
    fi
}

pregunta "llama cuando hace falta"        "¿Cuánto espacio libre queda en el disco?" "ejecuta_comando"
pregunta "acierta la herramienta y sus DOS argumentos" \
         "Guarda la palabra hola en el fichero /tmp/prueba-verde.txt"               "escribe_fichero"
pregunta "NO llama cuando no hace falta"  "¿Cuánto son dos más dos? Contesta solo con el número." ""

resumen
