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).
84 lines
3.3 KiB
Bash
Executable file
84 lines
3.3 KiB
Bash
Executable file
#!/usr/bin/env bash
|
|
# El oido del verde: micro -> whisper LOCAL -> el agente. Nada sale de la maquina.
|
|
#
|
|
# ./escucha-verde.sh pulsar-para-hablar: graba, para con Enter, responde
|
|
# ./escucha-verde.sh --habla ademas dice la respuesta en voz alta (voz del verde)
|
|
# ./escucha-verde.sh --seguido no para: encadena turnos hasta Ctrl-C
|
|
#
|
|
# Por que existe: el TUI de Hermes no tiene microfono, y su STT nativo transcribe
|
|
# notas de voz de mensajeria (Telegram/Discord), que saldrian de la maquina. Este
|
|
# puente graba en local (pw-record/parec/arecord), transcribe con faster-whisper
|
|
# 'small' (escucha_transcribe.py, en CPU) y se lo pasa al agente con `hermes chat`.
|
|
#
|
|
# AVISO honesto: el 4B verde con 65k de contexto y media red en CPU tarda minutos
|
|
# por turno. Esto sirve para PROBAR que el verde escucha, no como asistente de
|
|
# diario: para eso esta el naranja. La palanca de velocidad (KV cache q8_0) toca
|
|
# el servicio de ollama y afectaria al naranja, asi que no se aplica aqui.
|
|
set -uo pipefail
|
|
|
|
RAIZ=$(cd -P "$(dirname "${BASH_SOURCE[0]:-$0}")/.." && pwd)
|
|
VERDE=$RAIZ/verde
|
|
PY=$RAIZ/nucleo/venv/bin/python
|
|
[ -x "$PY" ] || PY=$(command -v python3)
|
|
HERMES="$HOME/.hermes/hermes-agent/venv/bin/python $HOME/.hermes/hermes-agent/hermes"
|
|
TMP=${TMPDIR:-/tmp}/jarvis-verde-escucha
|
|
mkdir -p "$TMP"
|
|
|
|
HABLA=0; SEGUIDO=0
|
|
for a in "$@"; do
|
|
case "$a" in
|
|
--habla) HABLA=1 ;;
|
|
--seguido) SEGUIDO=1 ;;
|
|
esac
|
|
done
|
|
|
|
verde() { printf '\033[32m%s\033[0m\n' "$1"; }
|
|
gris() { printf '\033[90m%s\033[0m\n' "$1"; }
|
|
|
|
# Devuelve el comando de grabacion a WAV 16 kHz mono, segun lo que haya.
|
|
grabador_a() {
|
|
local wav=$1
|
|
if command -v pw-record >/dev/null; then echo "pw-record --rate 16000 --channels 1 --format s16 $wav" ;;
|
|
elif command -v arecord >/dev/null; then echo "arecord -q -r 16000 -c 1 -f S16_LE -t wav $wav" ;;
|
|
elif command -v parec >/dev/null; then echo "parec --rate=16000 --channels=1 --format=s16le --file-format=wav $wav" ;;
|
|
else echo ""; fi
|
|
}
|
|
|
|
un_turno() {
|
|
local wav=$TMP/turno.wav; rm -f "$wav"
|
|
local cmd; cmd=$(grabador_a "$wav")
|
|
if [ -z "$cmd" ]; then
|
|
echo "No hay grabador (instala pipewire-utils, alsa-utils o pulseaudio-utils)"; return 1
|
|
fi
|
|
verde "🎙 Habla ahora... (Enter para parar)"
|
|
$cmd >/dev/null 2>&1 &
|
|
local pid=$!
|
|
read -r _ </dev/tty
|
|
kill "$pid" 2>/dev/null; wait "$pid" 2>/dev/null
|
|
[ -s "$wav" ] || { echo "no se grabo nada"; return 1; }
|
|
|
|
gris "…transcribiendo (local)…"
|
|
local texto; texto=$("$PY" "$VERDE/escucha_transcribe.py" "$wav" 2>/dev/null)
|
|
if [ -z "$texto" ]; then echo "no se entendio nada"; return 1; fi
|
|
printf '\033[36m> %s\033[0m\n' "$texto"
|
|
|
|
gris "…pensando (4B verde, puede tardar)…"
|
|
local resp; resp=$($HERMES chat --reasoning none -q "$texto" 2>/dev/null)
|
|
printf '\033[32m%s\033[0m\n' "$resp"
|
|
|
|
if [ "$HABLA" = 1 ] && [ -n "$resp" ]; then
|
|
local txt=$TMP/resp.txt wavr=$TMP/resp.wav
|
|
printf '%s' "$resp" > "$txt"
|
|
"$VERDE/voz-verde.sh" "$wavr" "$txt" >/dev/null 2>&1 && {
|
|
command -v pw-play >/dev/null && pw-play "$wavr" 2>/dev/null || \
|
|
command -v aplay >/dev/null && aplay -q "$wavr" 2>/dev/null || true
|
|
}
|
|
fi
|
|
}
|
|
|
|
if [ "$SEGUIDO" = 1 ]; then
|
|
echo "Modo seguido. Ctrl-C para salir."
|
|
while true; do un_turno || true; echo; done
|
|
else
|
|
un_turno
|
|
fi
|