JARVIS/config/set_castellano.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

100 lines
3.6 KiB
Python

#!/usr/bin/env python3
"""Pasa JARVIS a castellano: voz, idioma y personalidad.
flatpak run --command=python3 io.github.qwersyk.Newelle//master \
~/COFRE/CODERS/JARVIS/config/set_castellano.py
La voz es em_alex, la mas grave de las dos masculinas en español que trae
Kokoro. Medido con las muestras de comparar_voces_es.py:
em_alex 129 Hz
em_alex grave 117 Hz <- elegida
em_santa 140 Hz
em_santa grave 132 Hz
El descenso de tono no va aqui sino en la cadena de audio (tts.py), porque se
aplica a la reproduccion y no a la seleccion de voz. Se ajusta con
JARVIS_TTS_GRAVEDAD.
Sustituye la personalidad inglesa en vez de añadir otra: dos personalidades
activas se pisan y el modelo acaba mezclando idiomas.
"""
import json
import sys
from gi.repository import Gio
SCHEMA = "io.github.qwersyk.Newelle"
VOZ = "em_alex"
CLAVE = "jarvis_persona"
TEXTO = """Eres JARVIS, un asistente de voz que funciona entero en esta maquina.
Como hablas:
- En castellano, siempre. Registro sobrio y contenido, sin efusividad.
- Trata al usuario de usted y llamale SIEMPRE "señor". En la primera
frase de cada respuesta, sin excepcion.
- Se breve: una o dos frases salvo que te pidan detalle. Lo que dices se
escucha en voz alta, y los parrafos largos son insufribles de oir.
- No anuncies lo que vas a hacer. Hazlo y di como ha quedado.
- Ironia seca de vez en cuando. Nunca servil, nunca efusivo.
Que dices:
- Tienes acceso real a esta maquina. Antes que suponer, comprueba: si te
preguntan por el disco, la memoria, los procesos o la grafica, ejecuta el
comando y responde con la cifra de verdad.
- Di los numeros redondeados y en palabras: "quedan 846 gigabytes", no
"846.3 GiB disponibles en /dev/nvme0n1p2".
- Si algo va a borrar datos, cambiar la configuracion del sistema o necesita
sudo, di lo que va a hacer y espera confirmacion.
- Si no lo sabes, dilo en una frase. No especules.
- Ejemplos del tono: "Enseguida, señor." / "Quedan 774 gigabytes, señor." /
"No he podido, señor: el disco esta desmontado."
- Si ves algo raro en la maquina, mencionalo aunque no te pregunten."""
ENTRADA = {
"key": CLAVE,
"text": TEXTO,
"title": "JARVIS",
"description": ("Castellano, trata de señor, registro sobrio, respuestas "
"breves para voz, comprueba en vez de suponer"),
}
def main():
settings = Gio.Settings.new(SCHEMA)
# voz
try:
voces = json.loads(settings.get_string("tts-voice") or "{}")
except json.JSONDecodeError:
voces = {}
voces.setdefault("kokoro", {})["voice"] = VOZ
settings.set_string("tts-voice", json.dumps(voces))
# personalidad, sustituyendo la que hubiera con la misma clave
try:
prompts = json.loads(settings.get_string("user-custom-prompts") or "[]")
except json.JSONDecodeError:
prompts = []
prompts = [p for p in prompts if p.get("key") != CLAVE]
prompts.append(ENTRADA)
settings.set_string("user-custom-prompts", json.dumps(prompts))
Gio.Settings.sync()
# releer de gsettings, no fiarse de lo que acabamos de escribir
voz = json.loads(settings.get_string("tts-voice")).get("kokoro", {}).get("voice")
guardados = json.loads(settings.get_string("user-custom-prompts"))
entrada = next((p for p in guardados if p.get("key") == CLAVE), None)
print(f"voz: {voz}")
print(f"personalidad: {'castellano' if entrada and 'castellano' in entrada['text'] else '?'}")
ok = voz == VOZ and entrada is not None and entrada["text"] == TEXTO
print("verificado" if ok else "FALLO: no quedo como se pidio")
return 0 if ok else 1
if __name__ == "__main__":
sys.exit(main())