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.
133 lines
5 KiB
Python
Executable file
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())
|