Rama JARVIS-GREEN, independiente de master (el nucleo naranja queda intacto). - README propio del carril verde con diagrama de arquitectura y capturas del panel. - verde/: arranque, panel web, puente de voz (escucha local con faster-whisper + habla con la voz del naranja), pruebas (07 voz, 08 escucha), config y notas. - Integracion RAG: nucleo/saber/busca_cli.py + skill buscar-en-apuntes, para que el agente consulte el mismo indice que el nucleo. - Todo local (127.0.0.1); sin datos personales (rutas y modelo de GPU scrubeados).
136 lines
5.3 KiB
Bash
Executable file
136 lines
5.3 KiB
Bash
Executable file
#!/usr/bin/env bash
|
|
# La configuracion de Hermes, PARSEADA.
|
|
#
|
|
# Esta prueba existe por un fallo concreto que casi cuela. El config.yaml que
|
|
# genera el instalador declara `provider` y `base_url` DOS VECES dentro del
|
|
# bloque model:, una arriba y otra ~45 lineas mas abajo. En YAML gana la ultima.
|
|
# Al configurar Ollama en las de arriba el fichero PARECIA correcto leyendolo, y
|
|
# al parsearlo salia provider=auto, base_url=https://openrouter.ai/api/v1: el
|
|
# carril habria salido por OpenRouter creyendo nosotros que iba a localhost.
|
|
#
|
|
# Con 1890 lineas y 121 activas, leer el fichero no demuestra nada. Aqui todo se
|
|
# comprueba con yaml.safe_load, que es lo mismo que hace Hermes al arrancar.
|
|
|
|
cd "$(dirname "$0")" && . ./comun.sh
|
|
|
|
titulo "El cerebro"
|
|
|
|
afirma "existe $CONFIG" test -f "$CONFIG"
|
|
|
|
comprueba "el modelo por defecto es el verde" "$(config_model default)" "$MODELO"
|
|
|
|
# Sin esto Hermes se cree lo que dice /api/show, que son 262144 —el maximo del
|
|
# modelo— y no los 24576 configurados. El sintoma serian respuestas truncadas y
|
|
# llamadas rotas, y la conclusion natural ("el 4B no vale") seria falsa.
|
|
CTX_YAML=$(config_model context_length)
|
|
CTX_REAL=$(parametro "$MODELO" num_ctx)
|
|
comprueba "Hermes sabe el contexto REAL, no el que reporta /api/show" \
|
|
"$CTX_YAML" "$CTX_REAL"
|
|
|
|
# Los dos numeros tienen que ser iguales Y llegar al suelo de 64k. Poner aqui
|
|
# 65536 con el modelo a 24576 pasaria el arranque y romperia en marcha: Hermes
|
|
# no comprimiria hasta acercarse a 64k y ollama iria tirando los tokens mas
|
|
# viejos, que son el prompt de sistema y los esquemas de las herramientas. El
|
|
# agente dejaria de llamar herramientas a mitad de sesion, sin un solo error.
|
|
mayor_que "y ese contexto llega al suelo de Hermes" "${CTX_YAML:-0}" 63999
|
|
|
|
BASE=$(config_model base_url)
|
|
case "$BASE" in
|
|
http://127.0.0.1:*|http://localhost:*)
|
|
echo " $(verde OK) el cerebro apunta a esta maquina ($BASE)"; _ok=$((_ok + 1)) ;;
|
|
*)
|
|
echo " $(rojo FALLO) base_url NO es local: $BASE"; _fallo=$((_fallo + 1)) ;;
|
|
esac
|
|
|
|
titulo "Cero nube"
|
|
|
|
# Se recorre el YAML entero, no solo el bloque model:. Cualquier http(s) que no
|
|
# apunte al propio equipo es una salida potencial y tiene que estar justificada.
|
|
FUERA=$(python3 - "$CONFIG" <<'PY'
|
|
import re, sys, yaml
|
|
|
|
LOCAL = re.compile(r'^https?://(127\.0\.0\.1|localhost|0\.0\.0\.0|\[::1\])')
|
|
URL = re.compile(r'https?://[^\s"\']+')
|
|
fuera = []
|
|
|
|
def anda(nodo, ruta=""):
|
|
if isinstance(nodo, dict):
|
|
for k, v in nodo.items():
|
|
anda(v, f"{ruta}.{k}" if ruta else str(k))
|
|
elif isinstance(nodo, list):
|
|
for i, v in enumerate(nodo):
|
|
anda(v, f"{ruta}[{i}]")
|
|
elif isinstance(nodo, str):
|
|
for u in URL.findall(nodo):
|
|
if not LOCAL.match(u):
|
|
fuera.append(f"{ruta} = {u}")
|
|
|
|
anda(yaml.safe_load(open(sys.argv[1])))
|
|
for f in fuera:
|
|
print(f)
|
|
PY
|
|
)
|
|
if [ -z "$FUERA" ]; then
|
|
echo " $(verde OK) ninguna URL de nube activa en el config"; _ok=$((_ok + 1))
|
|
else
|
|
echo " $(rojo FALLO) hay URLs que salen de esta maquina:"
|
|
printf '%s\n' "$FUERA" | sed 's/^/ /'
|
|
_fallo=$((_fallo + 1))
|
|
fi
|
|
|
|
titulo "Desde donde mira el agente"
|
|
|
|
# `cwd: "."` de fabrica NO es "el directorio desde el que lanzas hermes", por
|
|
# mucho que lo diga su documentacion: el agente arranca en $HOME. Con la raiz
|
|
# ahi, "lee verde/README.md" acaba leyendo $HOME/README.md —el de
|
|
# Gitleaks— y resumiendolo perfectamente. Ningun error, respuesta inutil.
|
|
CWD_AGENTE=$(python3 -c "
|
|
import yaml
|
|
print(yaml.safe_load(open('$CONFIG')).get('terminal', {}).get('cwd', ''))" 2>/dev/null)
|
|
comprueba "el agente arranca en la raiz del proyecto" "$CWD_AGENTE" "$RAIZ"
|
|
|
|
titulo "El lanzador entra donde debe"
|
|
|
|
# Un `cd` antes de llamar a hermes NO basta: Hermes restaura el directorio que
|
|
# tenga apuntado en la sesion. El sintoma no parece de rutas — se le pide "lee
|
|
# verde/README.md y resumelo" y devuelve un resumen impecable de OTRO fichero,
|
|
# sin un solo error — asi que se comprueba estaticamente.
|
|
if grep -q -- '--in "\$RAIZ"' "$VERDE/verde-launcher.sh"; then
|
|
echo " $(verde OK) verde-launcher.sh pasa --in y no confia en el cd"; _ok=$((_ok + 1))
|
|
else
|
|
echo " $(rojo FALLO) verde-launcher.sh no pasa --in: Hermes abrira en otro sitio"
|
|
_fallo=$((_fallo + 1))
|
|
fi
|
|
|
|
titulo "La voz apunta a la del naranja"
|
|
|
|
# Sin esto la comparacion de voz entre carriles no diria nada: Hermes se
|
|
# descargaria su propia voz de Piper (la GPL de OHF-Voice) en vez de usar el
|
|
# binario MIT y el .onnx que ya tiene el naranja.
|
|
ORDEN=$(python3 -c "
|
|
import yaml
|
|
t = yaml.safe_load(open('$CONFIG')).get('tts', {})
|
|
p = t.get('providers', {}).get(t.get('provider', ''), {})
|
|
print(p.get('command', ''))
|
|
" 2>/dev/null)
|
|
case "$ORDEN" in
|
|
*/verde/voz-verde.sh*)
|
|
echo " $(verde OK) TTS por comando -> voz-verde.sh"; _ok=$((_ok + 1)) ;;
|
|
*)
|
|
echo " $(rojo FALLO) el TTS no apunta al adaptador: $ORDEN"; _fallo=$((_fallo + 1)) ;;
|
|
esac
|
|
|
|
stt_local() { # $1 = clave dentro de stt.local
|
|
python3 -c "
|
|
import yaml
|
|
print(yaml.safe_load(open('$CONFIG')).get('stt', {}).get('local', {}).get('$1', ''))
|
|
" 2>/dev/null | tr -d '\n'
|
|
}
|
|
|
|
comprueba "la escucha es la local de Hermes" "$(stt_local model)" "small"
|
|
|
|
# 839 MiB de pico contra una tarjeta con 3301 MiB de modelo dentro: whisper
|
|
# tiene que soltar la VRAM cuando calla, o la siguiente frase da OOM.
|
|
menor_que "whisper suelta la VRAM tras callar (s)" "$(stt_local unload_after_idle_seconds)" 601
|
|
|
|
resumen
|