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

81 lines
2.6 KiB
Python

#!/usr/bin/env python3
"""Pone el cerebro en Ollama sobre GPU y mata el razonamiento.
Se ejecuta DENTRO del flatpak, con la app CERRADA (si no, al salir puede
reescribir los ajustes con lo que tenia en memoria):
flatpak run --command=python3 io.github.qwersyk.Newelle//master \
~/COFRE/CODERS/JARVIS/config/set_backend.py
Por que Ollama y no el llamacpp interno: el llama.cpp que empaqueta el
flatpak es el prebuilt de CPU, sin CUDA, asi que el modelo corria entero
en procesador. Ollama ya esta en GPU. Los dos son locales igual.
Medido el 10 ago 2026 con qwen3.5:4b, misma pregunta:
think=true 41.2 s 749 tokens (casi todos de monologo interno)
think=false 2.5 s 44 tokens
De paso deja sana la ruta de llamacpp por si se vuelve a ella: el mismo
razonamiento se apaga alli con --reasoning-budget 0, y el contexto por
defecto (0 = el del modelo) abria 262144 por slot, unos 10 GB de RAM.
"""
import json
import sys
from gi.repository import Gio
SCHEMA = "io.github.qwersyk.Newelle"
MODELO = "qwen3.5:4b"
AJUSTES_OLLAMA = {
"endpoint": "http://localhost:11434",
"model": MODELO,
"custom_model": False,
"thinking": False, # lo que baja de 41 s a 2.5 s
"native_tool_calling": True,
"serve": False, # el servicio del sistema ya esta levantado
}
AJUSTES_LLAMACPP = {
"custom_args": "--reasoning-budget 0",
"ctx": 8192,
}
def main():
settings = Gio.Settings.new(SCHEMA)
try:
llm = json.loads(settings.get_string("llm-settings") or "{}")
except json.JSONDecodeError:
llm = {}
# merge, no reemplazo: ahi viven los ajustes de todos los proveedores
llm.setdefault("ollama", {}).update(AJUSTES_OLLAMA)
llm.setdefault("llamacpp", {}).update(AJUSTES_LLAMACPP)
settings.set_string("llm-settings", json.dumps(llm))
settings.set_string("language-model", "ollama")
Gio.Settings.sync()
# releer de gsettings, no fiarse de lo que acabamos de escribir
guardado = json.loads(settings.get_string("llm-settings"))
motor = settings.get_string("language-model")
ollama = guardado.get("ollama", {})
print(f"language-model: {motor}")
print(f"modelo: {ollama.get('model')}")
print(f"thinking: {ollama.get('thinking')}")
print(f"llamacpp: {guardado.get('llamacpp', {}).get('custom_args')} "
f"· ctx {guardado.get('llamacpp', {}).get('ctx')}")
ok = (motor == "ollama"
and ollama.get("model") == MODELO
and ollama.get("thinking") is False)
print("verificado" if ok else "FALLO: no quedo como se pidio")
return 0 if ok else 1
if __name__ == "__main__":
sys.exit(main())