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.
This commit is contained in:
commit
8e4bc8ad94
125 changed files with 25033 additions and 0 deletions
65
config/apuntar_modelo_jarvis.py
Normal file
65
config/apuntar_modelo_jarvis.py
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
#!/usr/bin/env python3
|
||||
"""Apunta el cerebro de Newelle al modelo qwen3.5:4b-jarvis.
|
||||
|
||||
Es la variante con num_gpu 24 (ver qwen3.5-4b-jarvis.Modelfile): deja 1540 MiB
|
||||
libres en la tarjeta para que whisper pueda transcribir sin morir de OOM.
|
||||
|
||||
Toca solo la clave llm-settings, y dentro solo el bloque "ollama": el modelo
|
||||
elegido, la lista de modelos y la biblioteca que se ve en la interfaz.
|
||||
"""
|
||||
import json
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
APP = "io.github.qwersyk.Newelle"
|
||||
RAMA = "master"
|
||||
MODELO = "qwen3.5:4b-jarvis"
|
||||
|
||||
GSET = ["flatpak", "run", f"--command=gsettings", f"{APP}//{RAMA}"]
|
||||
|
||||
|
||||
def leer(clave):
|
||||
out = subprocess.check_output(GSET + ["get", APP, clave], text=True).strip()
|
||||
# gsettings devuelve el string entre comillas simples, con \\ escapados
|
||||
return json.loads(out[1:-1].replace("\\\\", "\\").replace("\\'", "'"))
|
||||
|
||||
|
||||
def escribir(clave, valor):
|
||||
subprocess.check_call(GSET + ["set", APP, clave, json.dumps(valor)])
|
||||
|
||||
|
||||
def main():
|
||||
ajustes = leer("llm-settings")
|
||||
ollama = ajustes.setdefault("ollama", {})
|
||||
|
||||
anterior = ollama.get("model")
|
||||
if anterior == MODELO:
|
||||
print(f"ya apuntaba a {MODELO}; nada que hacer.")
|
||||
return 0
|
||||
|
||||
ollama["model"] = MODELO
|
||||
|
||||
# la lista de modelos del desplegable: pares [clave, titulo]
|
||||
modelos = json.loads(ollama.get("models", "[]"))
|
||||
if not any(m[0] == MODELO for m in modelos):
|
||||
modelos.insert(0, [MODELO, MODELO])
|
||||
ollama["models"] = json.dumps(modelos)
|
||||
|
||||
# la biblioteca que pinta la interfaz, para que salga con descripcion
|
||||
biblioteca = ollama.get("model-library", [])
|
||||
if not any(m.get("key") == MODELO for m in biblioteca):
|
||||
biblioteca.insert(0, {
|
||||
"key": MODELO,
|
||||
"title": "qwen3.5 4b (JARVIS)",
|
||||
"description": "qwen3.5:4b con 24 de 32 capas en GPU, para dejar "
|
||||
"sitio a whisper en la tarjeta de 4 GB.",
|
||||
})
|
||||
ollama["model-library"] = biblioteca
|
||||
|
||||
escribir("llm-settings", ajustes)
|
||||
print(f"cerebro: {anterior} -> {MODELO}")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
Loading…
Add table
Add a link
Reference in a new issue