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.
60 lines
1.8 KiB
Python
60 lines
1.8 KiB
Python
#!/usr/bin/env python3
|
|
"""Endurece la escucha para que no se dispare con ruido de fuera.
|
|
|
|
flatpak run --command=python3 io.github.qwersyk.Newelle//master \
|
|
~/COFRE/CODERS/JARVIS/config/set_ruido.py [--suave]
|
|
|
|
NO es reconocimiento de hablante: Newelle no distingue quien habla, no hay
|
|
nada de eso en el codigo. Esto solo sube el liston de que se considera voz.
|
|
Para que responda unicamente a una persona haria falta un modelo de huella
|
|
vocal (ECAPA, Resemblyzer) delante del STT, que es otro proyecto.
|
|
|
|
Las tres capas, de menos a mas efectiva:
|
|
|
|
1. la palabra clave, que ya filtra casi todo
|
|
2. estos umbrales
|
|
3. supresion de ruido en PipeWire (RNNoise), antes de que el audio llegue
|
|
a la app; esa vale para todo el sistema y no cuesta CPU a Newelle
|
|
|
|
Valores por defecto de Newelle: energia 300, VAD 1.
|
|
"""
|
|
import sys
|
|
|
|
from gi.repository import Gio
|
|
|
|
SCHEMA = "io.github.qwersyk.Newelle"
|
|
|
|
# vad-aggressiveness va de 0 a 3: cuanto descarta lo que no suena a voz.
|
|
DURO = {
|
|
"wakeword-energy-threshold": 800, # por debajo de esto ni escucha
|
|
"wakeword-vad-aggressiveness": 3,
|
|
}
|
|
SUAVE = {
|
|
"wakeword-energy-threshold": 500,
|
|
"wakeword-vad-aggressiveness": 2,
|
|
}
|
|
|
|
|
|
def main():
|
|
perfil = SUAVE if "--suave" in sys.argv else DURO
|
|
settings = Gio.Settings.new(SCHEMA)
|
|
|
|
print("antes:")
|
|
for clave in perfil:
|
|
print(f" {clave} = {settings.get_int(clave)}")
|
|
|
|
for clave, valor in perfil.items():
|
|
settings.set_int(clave, valor)
|
|
Gio.Settings.sync()
|
|
|
|
print("despues:")
|
|
for clave in perfil:
|
|
print(f" {clave} = {settings.get_int(clave)}")
|
|
|
|
ok = all(settings.get_int(c) == v for c, v in perfil.items())
|
|
print("verificado" if ok else "FALLO: no quedo como se pidio")
|
|
return 0 if ok else 1
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main())
|