JARVIS/nucleo/pruebas/prueba_dialogo.py
sito 8e4bc8ad94 JARVIS: asistente de voz local para Linux
Nucleo propio: oye con whisper.cpp, piensa con un modelo de Ollama, habla
con Piper, y hace RAG sobre los apuntes del usuario. 100% local, sin
cuentas ni claves.

Escrito bajo una restriccion dura, 4 GB de VRAM: el cerebro y whisper
comparten tarjeta y solo caben porque estan dimensionados para ello. El
RAG usa embeddings estaticos con busqueda hibrida; la voz clonada se sirve
de una cache de frases.

Incluye instalador (install.sh), requisitos, y documentacion del stack,
del manejo de root y de las acciones. Los apuntes indexados y el diario NO
se incluyen: son privados y el .gitignore los bloquea.
2026-08-16 15:34:37 +02:00

133 lines
5 KiB
Python
Executable file

#!/usr/bin/env python3
"""Pulsar una accion PREGUNTA lo que falta y la hace.
./venv/bin/python pruebas/prueba_dialogo.py
Antes, pulsar en "sabe hacer" recitaba la frase que dispara la accion. Eso es un
manual de instrucciones, y uno pulsa justo porque no quiere leerse el manual.
Lo que se comprueba, por orden de importancia:
1. que lo destructivo NO se dispare de un clic
2. que lo destructivo CON argumento pregunte las DOS cosas
3. que un "no" cancele de verdad
4. que la pregunta caduque, para que una frase suelta media conversacion
despues no se tome por la respuesta
"""
import os
import sys
import time
AQUI = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.insert(0, AQUI)
from cara.panel import Panel # noqa: E402
from manos import Manos # noqa: E402
from manos.preguntar import pregunta_por, es_si # noqa: E402
fallos = []
def afirma(condicion, queja):
print(f" {'OK ' if condicion else 'MAL'} {queja}")
if not condicion:
fallos.append(queja)
class CaraFalsa:
conectados = 1
def __init__(self): self.m = []
def manda(self, t, d): self.m.append((t, d))
class BocaMuda:
voz = "davefx"
def __init__(self): self.dicho = []
def di(self, t): self.dicho.append(t); return True
def di_si_libre(self, t): return self.di(t)
def disponibles(self): return ["davefx"]
def nuevo():
cara, boca = CaraFalsa(), BocaMuda()
return Panel(cara, boca, Manos(), candado=False), boca
def main() -> int:
print("\n las preguntas salen del catalogo")
manos = Manos()
conArg = [a for a in manos.catalogo.acciones if a.captura]
genericas = [a for a in conArg if pregunta_por(a) == "¿El que, señor?"]
afirma(len(conArg) >= 30, f"{len(conArg)} acciones piden argumento")
afirma(len(genericas) <= 10,
f"solo {len(genericas)} caen en la pregunta generica, de {len(conArg)}")
afirma(all(pregunta_por(a).endswith("señor?") for a in conArg),
"todas preguntan en el registro de la casa")
afirma("cierra" not in [pregunta_por(a).split()[0].strip("¿").lower()
for a in conArg],
"y ninguna empieza por el verbo de la orden")
print("\n con argumento: pregunta y ejecuta con lo que le digas")
p, boca = nuevo()
p.pulsado("accion", "explica:buscar_fichero"); time.sleep(0.2)
afirma(bool(p.esperando()), "se queda esperando respuesta")
afirma(any("fichero" in d for d in boca.dicho), f"pregunta cual: {boca.dicho}")
p.responde("informes"); time.sleep(1.5)
tareas = [t.titulo for t in p.tareas.todas()]
afirma(any("informes" in t for t in tareas), f"lo ejecuta con el argumento: {tareas}")
afirma(not p.esperando(), "y deja de esperar")
p.para()
print("\n lo destructivo NO se dispara de un clic")
p, boca = nuevo()
p.pulsado("accion", "explica:ff_firefox_cerrar"); time.sleep(0.3)
afirma(not p.tareas.todas(), "no ha lanzado nada al pulsar")
afirma(bool(p.esperando()), "esta esperando confirmacion")
p.responde("no"); time.sleep(0.4)
afirma(not p.tareas.todas(), "y con un 'no' sigue sin lanzar nada")
p.para()
print("\n destructivo CON argumento: las dos preguntas")
p, boca = nuevo()
p.pulsado("accion", "explica:proceso_matar"); time.sleep(0.2)
afirma(bool(p.esperando()), "pregunta el argumento")
p.responde("firefox"); time.sleep(0.3)
afirma(bool(p.esperando()),
"y DESPUES pide confirmacion, no ejecuta directo")
afirma(any("firefox" in d for d in boca.dicho),
"la confirmacion dice sobre que va, no un cheque en blanco")
p.responde("no"); time.sleep(0.4)
afirma(not p.tareas.todas(), "un 'no' lo cancela")
p.para()
print("\n lo que no pregunta nada, se hace")
p, boca = nuevo()
p.pulsado("accion", "explica:disco_libre"); time.sleep(1.5)
afirma(bool(p.tareas.todas()), "una accion sin argumento ni riesgo se ejecuta")
afirma(not p.esperando(), "sin preguntar nada")
p.para()
print("\n la pregunta caduca")
p, boca = nuevo()
p.ESPERA_S = 0.5
p.pulsado("accion", "explica:buscar_fichero"); time.sleep(0.2)
afirma(bool(p.esperando()), "recien preguntada, espera")
time.sleep(0.8)
afirma(not p.esperando(),
"pasado el plazo la olvida: una frase suelta media conversacion "
"despues no puede tomarse por la respuesta")
p.para()
print("\n el si y el no, como los dice la gente")
for t, esperado in [("si", True), ("Sí.", True), ("vale", True),
("claro que si", True), ("no", False), ("dejalo", False),
("no, mejor no", False), ("informes", None),
("el fichero de agosto", None)]:
afirma(es_si(t) is esperado, f"{t!r} -> {es_si(t)}")
print(f"\n {'todo en orden' if not fallos else str(len(fallos)) + ' fallan'}\n")
return 1 if fallos else 0
if __name__ == "__main__":
sys.exit(main())