JARVIS elige modelo, backend Wayland, y roadmap para la comunidad

- Seleccion de modelo: JARVIS cambia su cerebro en caliente (elegir_modelo).
- Ventanas en Wayland: backend para Sway e Hyprland (ventanas-wayland.sh).
- ROADMAP.md y CONTRIBUTING.md: tareas acotadas y como empezar, para animar
  commits (mas gestores, mas acciones, probar otros modelos, el RAG).
- docs/rag.md: tabla explicita de que se publica y que no del RAG; nota de
  que un clon recien hecho ya tiene RAG util (man pages + glosario) sin datos
  privados.
- README enlaza el roadmap y menciona la eleccion de modelo.
This commit is contained in:
sito 2026-08-16 16:58:41 +02:00
parent fde0fc1c72
commit 6a81cef3be
8 changed files with 375 additions and 9 deletions

87
CONTRIBUTING.md Normal file
View file

@ -0,0 +1,87 @@
# Contribuir
JARVIS es software libre (GPL-3.0) y se agradecen los commits. Esta guía dice
cómo está montado, cómo probarlo y cómo añadir lo más común. Las ideas acotadas
están en [ROADMAP.md](ROADMAP.md).
## Cómo está montado
```
nucleo/jarvis.py el bucle: oír → pensar → hacer → hablar
nucleo/oido/ whisper.cpp (STT) y la captura del micrófono
nucleo/cerebro/ Ollama, tool-calling, el RAG y la elección de modelo
nucleo/boca/ Piper / voz clonada (TTS)
nucleo/manos/ las acciones, el diario, el sudo, la fonética
nucleo/cara/ el panel web (servidor WebSocket + HUD en cara/hud/)
nucleo/saber/ el RAG: indexa, busca, y enriquece/ para mejorarlo
config/ scripts: modelo, voz, whisper, ventanas
```
Nada de esto necesita compilar: es Python, bash y una página web. Se edita y se
prueba en el sitio.
## Probarlo
```bash
./install.sh # entorno, modelo de Ollama, lanzador de escritorio
./nucleo/arranca.sh # el asistente y el panel en 127.0.0.1
```
El panel se puede abrir **sin arrancar nada**, para trabajar el diseño:
`nucleo/cara/hud/index.html` en un navegador se llena solo con datos de ejemplo.
Hay dos suites de comprobaciones (`nucleo/pruebas/`) y un eval del RAG
(`nucleo/saber/enriquece/evalua.py`, mide `hit@k`). Si tocas el RAG, corre el
eval y pon los números en el commit.
## Añadir una acción
Una acción es una entrada en `nucleo/datos/acciones.json`:
```json
{
"id": "brillo_sube",
"grupo": "sistema",
"frases": ["sube el brillo", "más brillo", "más luz"],
"acuse": "Voy, señor.",
"comando": "brightnessctl set +10%",
"host": true,
"respuesta": "Brillo al {campo1}, señor.",
"confirmar": false,
"captura": false
}
```
- `frases`: cómo se dice, con variantes. Se emparejan ANTES de llamar al modelo,
así que una acción del catálogo es instantánea.
- `comando`: shell. `{campoN}` en `respuesta` es el enésimo campo de la salida.
- `confirmar: true` para lo que borra o cambia; `captura: true` si necesita un
argumento hablado ("busca el fichero **X**").
Si el comando depende de una herramienta que puede no estar, que degrade con
gracia (mira `config/ventanas.sh`).
## Probar con otro modelo
```bash
JARVIS_MODELO=qwen3.5:9b ./nucleo/arranca.sh
```
O, en marcha, dile a JARVIS "usa un modelo más potente" (usa la herramienta
`elegir_modelo`). Para medir si un modelo hace bien el tool-calling, mira el
patrón del eval del RAG y adáptalo.
## Un backend de ventanas nuevo (Wayland u otro)
`config/ventanas-wayland.sh` es el ejemplo: detecta el compositor y traduce los
verbos (maximiza, izquierda, centro...) a sus órdenes. Para KDE, GNOME Wayland u
otro, se copia el patrón. Actúa sobre la ventana ACTIVA (en Wayland no se mueven
ventanas ajenas). Prueba cada orden en tu compositor antes del commit.
## Estilo
- Registro técnico y conciso, en castellano. Los comentarios explican el
**porqué**, no el qué.
- Los commits describen la decisión, no solo el cambio.
- Sin datos personales en el repo: los apuntes indexados, el diario y la voz de
referencia son del usuario (ver [PRIVACIDAD.md](PRIVACIDAD.md)).

View file

@ -104,6 +104,10 @@ preguntas de pentesting, la recuperación sube de **hit@1 48 % → 82 %** y
**hit@5 77 % → 95 %**. El método, el pipeline y esos números están en **hit@5 77 % → 95 %**. El método, el pipeline y esos números están en
[docs/rag.md](docs/rag.md). [docs/rag.md](docs/rag.md).
Y **JARVIS elige su propio cerebro**: si una pregunta es más dura de lo normal
puede cambiar a un modelo más potente (`elegir_modelo`), y volver al rápido para
lo simple. Dile "usa un modelo más potente" o fija uno con `JARVIS_MODELO`.
## Órdenes con root — importante ## Órdenes con root — importante
Algunas órdenes que gestionan el sistema (instalar un paquete, reiniciar un Algunas órdenes que gestionan el sistema (instalar un paquete, reiniciar un
@ -148,6 +152,14 @@ Proyecto en desarrollo. En el radar: acabar de generalizar los scripts de `voz/`
**Wayland** (hoy solo X11), y el fine-tuning de `entrena/`, pendiente de un **Wayland** (hoy solo X11), y el fine-tuning de `entrena/`, pendiente de un
ajuste para modelos multimodales. ajuste para modelos multimodales.
## Contribuir
Se agradecen los commits: es software libre y hay mucho por hacer. Las ideas
acotadas están en [ROADMAP.md](ROADMAP.md) (más gestores de ventanas, más
acciones, probar otros modelos, el RAG...) y cómo empezar en
[CONTRIBUTING.md](CONTRIBUTING.md). No hay que compilar nada: es Python, bash y
una página web.
## Licencia ## Licencia
[GPL-3.0](LICENSE). Software libre y copyleft: si lo modificas y lo distribuyes, [GPL-3.0](LICENSE). Software libre y copyleft: si lo modificas y lo distribuyes,

80
ROADMAP.md Normal file
View file

@ -0,0 +1,80 @@
# Roadmap
Cosas por hacer, ordenadas por dónde aporta más un commit. No es una lista de
deseos: cada punto está acotado para que se pueda coger, hacer y medir. Si algo
te interesa, [CONTRIBUTING.md](CONTRIBUTING.md) dice cómo probarlo.
## Ventanas en más gestores
El motor de ventanas es X11 (`config/ventanas.sh`, con `xdotool`). En Wayland ya
hay un backend para **Sway e Hyprland** (`config/ventanas-wayland.sh`), pero está
a medias:
- **Terminar Sway/Hyprland.** `maximiza`, `restaura` y `minimiza` son fiables;
las mitades (izquierda/derecha/centro) están escritas de la documentación y
**necesitan probarse** en un equipo real —el flotante y las unidades (`ppt`,
`%`) cambian por versión—. Es un primer commit ideal si usas uno de los dos.
- **KDE Plasma (Wayland).** Vía `kdotool` o scripting de KWin. Falta el backend.
- **GNOME Wayland.** No hay API general para mover ventanas ajenas (es su modelo
de seguridad); requiere una extensión. Documentar la limitación o escribir el
puente a una extensión.
- **Mover entre pantallas y a escritorios virtuales** en Wayland: el X11 lo hace,
los backends de Wayland aún no.
## Más acciones
El catálogo son 150 órdenes (`nucleo/datos/acciones.json`). Cada una es JSON:
frases que la disparan, un comando de shell, y una plantilla de respuesta. Añadir
una es un commit pequeño y satisfactorio. Ideas que faltan:
- **Multimedia**: play/pausa, siguiente, volumen (`playerctl`, `wpctl`).
- **Brillo** de pantalla (`brightnessctl`).
- **Portapapeles**: leer/copiar (`wl-clipboard` / `xclip`).
- **Capturas** de pantalla (`grim`, `spectacle`, `gnome-screenshot`).
- **Energía**: bloquear, suspender, apagar (con confirmación).
- **Red**: a qué wifi conectar (`nmcli`).
- **Notificaciones** recientes, calendario, temporizadores.
Al añadir acciones que dependan de una herramienta, degradar con gracia si no
está (como hacen las de ventanas).
## Probar con otros modelos
JARVIS ya puede **elegir su modelo** en caliente (herramienta `elegir_modelo`, y
`JARVIS_MODELO` para fijarlo). Lo que falta es saber cuáles van bien:
- **Medir el tool-calling** de más modelos locales (llama, mistral, gemma,
phi...) con el mismo criterio del RAG (`nucleo/saber/enriquece/eval_set.jsonl`
da la idea). Cuáles emiten `tool_calls` fiables y cuáles no.
- **Auto-escalado**: que suba a un modelo potente solo cuando la pregunta lo
pida, y vuelva al rápido. Hoy lo decide el propio modelo; se puede afinar.
- **Un modelo pensado para voz**: respuestas cortas, sin divagar.
## El RAG
- **Reranker**: un cross-encoder sobre los primeros k. Medido que un embedder
transformer no compensa (ver [docs/rag.md](docs/rag.md)); un reranker es la
otra palanca sin probar.
- **Corpus de demostración público** para poder ver el RAG funcionando sin los
apuntes privados de nadie (man pages, docs de una herramienta libre).
- **Troceado que mantenga el bloque de comandos con su encabezado** (hoy separa
algún comando de su contexto).
## Voz
- **Más idiomas.** Hoy es castellano de punta a punta (STT, prompt, TTS). Piper
y whisper tienen modelos de otros idiomas; falta parametrizarlo.
- **Fine-tuning de la voz** (Piper/VITS) para clonar mejor con poco audio.
## Fine-tuning del modelo
`entrena/` tiene el pipeline QLoRA, bloqueado porque `qwen3.5` es multimodal y
`FastLanguageModel` lo mete por el camino de solo texto. El arreglo apunta a
`FastVisionModel` con `finetune_vision_layers=False`. Sin probar.
## Empaquetado
- **Perfiles de hardware** en `install.sh` (4 GB / 12 GB / solo-CPU) que ajusten
el modelo y el contexto solos.
- **Detección de rutas** más fina en los scripts de `config/` y `voz/`, que aún
asumen parte de la disposición del autor.

57
config/ventanas-wayland.sh Executable file
View file

@ -0,0 +1,57 @@
#!/usr/bin/env bash
# Backend de ventanas para Wayland (Sway e Hyprland). ventanas.sh delega aqui
# cuando detecta uno de esos compositores.
#
# Por que un fichero aparte y no dentro de ventanas.sh: aquel es X11 puro
# (xdotool, xrandr) y esta probado; mezclar aqui lo romperia. Cada backend en su
# sitio, y este se puede iterar sin tocar el que ya funciona.
#
# En Wayland NO se pueden mover ventanas AJENAS sin permisos del compositor, asi
# que esto actua sobre la ventana ACTIVA (la que tiene el foco), que es lo que
# el compositor si deja hacer. Por eso ignora el <ventana> que si usa el X11.
#
# ── ESTADO ──────────────────────────────────────────────────────────────────
#
# maximiza / restaura / minimiza: confiables (pantalla completa y scratchpad son
# ordenes de una linea, bien documentadas en los dos compositores).
# izquierda / derecha / centro: escritas de la documentacion, A PROBAR en un
# equipo real —el flotante y las unidades (ppt, %) varian por version—. Es un
# primer commit ideal para quien use Sway o Hyprland. Ver ROADMAP.md.
set -uo pipefail
verbo=${1:-}
if command -v swaymsg >/dev/null 2>&1 && swaymsg -t get_version >/dev/null 2>&1; then
COMP=sway
elif command -v hyprctl >/dev/null 2>&1 && hyprctl version >/dev/null 2>&1; then
COMP=hypr
else
echo "no encuentro Sway ni Hyprland en marcha"; exit 1
fi
sway() { swaymsg "$@" >/dev/null 2>&1; }
hypr() { hyprctl dispatch "$@" >/dev/null 2>&1; }
case "$verbo:$COMP" in
# --- pantalla completa: confiable ---
maximiza:sway) sway fullscreen enable; echo "Maximizada, señor" ;;
maximiza:hypr) hypr fullscreen 1; echo "Maximizada, señor" ;;
restaura:sway) sway fullscreen disable; echo "Restaurada, señor" ;;
restaura:hypr) hypr fullscreen 0; echo "Restaurada, señor" ;;
minimiza:sway) sway move scratchpad; echo "Minimizada, señor" ;;
minimiza:hypr) hypr movetoworkspacesilent special; echo "Minimizada, señor" ;;
# --- mitades y centro: A PROBAR (unidades/flotante dependen de la version) ---
izquierda:sway) sway floating enable; sway resize set width 50 ppt height 100 ppt; sway move absolute position 0 0; echo "A la izquierda, señor" ;;
derecha:sway) sway floating enable; sway resize set width 50 ppt height 100 ppt; sway move absolute position 50 ppt 0; echo "A la derecha, señor" ;;
centro:sway) sway floating enable; sway resize set width 70 ppt height 80 ppt; sway move absolute position center; echo "Al centro, señor" ;;
izquierda:hypr) hypr setfloating; hypr resizeactive exact 50% 100%; hypr moveactive exact 0 0; echo "A la izquierda, señor" ;;
derecha:hypr) hypr setfloating; hypr resizeactive exact 50% 100%; hypr moveactive exact 50% 0; echo "A la derecha, señor" ;;
centro:hypr) hypr setfloating; hypr resizeactive exact 70% 80%; hypr centerwindow; echo "Al centro, señor" ;;
*)
echo "en $COMP todavia no esta esa orden de ventanas. Implementadas:" \
"maximiza, restaura, minimiza, izquierda, derecha, centro." \
"Las demas (mover entre pantallas, escritorios) estan en el roadmap."
exit 1 ;;
esac

View file

@ -33,12 +33,24 @@ ALIAS_FICHERO=${JARVIS_ALIAS_PANTALLAS:-$HOME/.config/jarvis-pantallas.json}
hay_x11() { [ -n "${DISPLAY:-}" ] && command -v xdotool >/dev/null 2>&1; } hay_x11() { [ -n "${DISPLAY:-}" ] && command -v xdotool >/dev/null 2>&1; }
if ! hay_x11; then if ! hay_x11; then
# Diagnostico segun el entorno, no un "no X11" seco. en_wayland() { [ "${XDG_SESSION_TYPE:-}" = "wayland" ] || [ -n "${WAYLAND_DISPLAY:-}" ]; }
if [ "${XDG_SESSION_TYPE:-}" = "wayland" ] || [ -n "${WAYLAND_DISPLAY:-}" ]; then # En Wayland con Sway o Hyprland, delega en el backend propio (actua sobre la
echo "las acciones de ventanas usan xdotool (X11) y estas en Wayland" \ # ventana activa). El verbo va en $1 y para las ordenes de ventana el destino
"(${XDG_CURRENT_DESKTOP:-tu compositor}). En Wayland mover ventanas" \ # (izquierda/derecha/centro/maximiza...) suele venir en $3; se le pasa el que
"ajenas depende del compositor; por ahora estas ordenes quedan fuera." \ # tenga sentido.
"El resto de JARVIS funciona igual." if en_wayland && (command -v swaymsg >/dev/null 2>&1 || command -v hyprctl >/dev/null 2>&1); then
BACKEND="$(dirname "${BASH_SOURCE[0]:-$0}")/ventanas-wayland.sh"
case "${1:-}" in
zona) exec bash "$BACKEND" "${3:-centro}" ;; # zona <ventana> <izquierda|derecha|centro>
maximiza|restaura|minimiza) exec bash "$BACKEND" "$1" ;;
*) echo "en Wayland de momento van: maximiza, restaura, minimiza," \
"y mitades (izquierda/derecha/centro). El resto, en el roadmap."; exit 1 ;;
esac
fi
if en_wayland; then
echo "estas en Wayland (${XDG_CURRENT_DESKTOP:-tu compositor}) sin Sway ni" \
"Hyprland: mover ventanas ajenas no tiene via universal ahi. El resto" \
"de JARVIS funciona igual. Soporte por compositor: ver ROADMAP.md."
elif [ -n "${DISPLAY:-}" ]; then elif [ -n "${DISPLAY:-}" ]; then
echo "estas en X11 pero falta xdotool. Instalalo con tu gestor de paquetes." echo "estas en X11 pero falta xdotool. Instalalo con tu gestor de paquetes."
else else

View file

@ -80,3 +80,19 @@ reproduce con `enriquece/experimento_embedder.py`.
conocimiento personal; `diario.jsonl` es lo que se le dice al asistente. Son del conocimiento personal; `diario.jsonl` es lo que se le dice al asistente. Son del
usuario. El `.gitignore` los bloquea. Lo reproducible —el método, el código y la usuario. El `.gitignore` los bloquea. Lo reproducible —el método, el código y la
forma de medirlo— sí está; el corpus lo pone cada quien con sus notas. forma de medirlo— sí está; el corpus lo pone cada quien con sus notas.
## Qué se publica y qué no, en concreto
| | En el repo |
|---|---|
| El código: `indexa`, `busca`, `glosario`, `sistema`, `enriquece/` | **sí** |
| El eval (`enriquece/eval_set.jsonl`) | **sí** |
| El glosario de tareas de Linux en castellano (`glosario.py`) | **sí** (es conocimiento genérico, no personal) |
| El índice (`saber.jsonl`) y sus copias | **no** — los apuntes |
| El diario (`diario.jsonl`) | **no** — las conversaciones |
| Lo derivado (`cosecha.jsonl`, `sintesis.jsonl`) | **no** — salen de los apuntes |
Un detalle útil: `indexa.py` en un equipo recién clonado ya construye un índice
**útil sin ningún dato privado** —de las *man pages* del sistema y del glosario—,
así que se puede ver el RAG funcionando de inmediato; los apuntes de COFRE solo
lo enriquecen si los tienes.

View file

@ -262,9 +262,16 @@ class Panel:
self.manda_controles() self.manda_controles()
self.cara.manda("voz", {"nombre": self.boca.voz, "id": self.boca.voz}) self.cara.manda("voz", {"nombre": self.boca.voz, "id": self.boca.voz})
if self.cerebro: if self.cerebro:
self.cara.manda("cerebro", {"nombre": "local", "id": "local", self._manda_cerebro(self.cerebro.modelo)
"detalle": self.cerebro.modelo, # Cuando JARVIS elige otro modelo, el rotulo del boton "cerebro" se
"fuera": False}) # actualiza solo: si no, dice uno y piensa con otro.
self.cerebro.al_cambiar_modelo = self._manda_cerebro
def _manda_cerebro(self, modelo):
corto = modelo.partition(":")
corto = corto[0] if corto[2] in ("", "latest") else corto[2]
self.cara.manda("cerebro", {"nombre": corto, "id": modelo,
"detalle": modelo, "fuera": False})
def _bucle_telemetria(self): def _bucle_telemetria(self):
tic = 0 tic = 0

View file

@ -43,6 +43,16 @@ import urllib.request
ENDPOINT = os.environ.get("JARVIS_OLLAMA", "http://127.0.0.1:11434") ENDPOINT = os.environ.get("JARVIS_OLLAMA", "http://127.0.0.1:11434")
MODELO = os.environ.get("JARVIS_MODELO", "qwen3.5:4b-jarvis") MODELO = os.environ.get("JARVIS_MODELO", "qwen3.5:4b-jarvis")
def modelos_disponibles(endpoint=ENDPOINT) -> list:
"""Los modelos que tiene Ollama en disco, para poder elegir entre ellos."""
try:
with urllib.request.urlopen(endpoint + "/api/tags", timeout=5) as r:
datos = json.load(r)
return sorted(m["name"] for m in datos.get("models", []))
except Exception:
return []
# Cuantas vueltas de "llama a una herramienta -> toma el resultado" se permiten # Cuantas vueltas de "llama a una herramienta -> toma el resultado" se permiten
# antes de cortar. Sin tope, un modelo que se atasca pidiendo lo mismo deja al # antes de cortar. Sin tope, un modelo que se atasca pidiendo lo mismo deja al
# usuario esperando para siempre. # usuario esperando para siempre.
@ -75,6 +85,12 @@ usa buscar_en_apuntes con palabras clave ANTES de contestar. Ahi estan los
comandos que el usuario ya ha probado; es mejor buscarlos que inventarlos. Si la comandos que el usuario ya ha probado; es mejor buscarlos que inventarlos. Si la
primera busqueda no da, reformula y prueba otra vez. primera busqueda no da, reformula y prueba otra vez.
Eliges tu propio cerebro. Si una pregunta es mas dificil de lo normal razonar,
codigo, varios pasos cambia a un modelo mas potente con elegir_modelo("potente")
antes de contestarla, y para lo simple vuelve a elegir_modelo("rapido"). Si el
usuario pide un modelo concreto, cambialo. Avisa en una frase de que has
cambiado.
Tu respuesta se convierte en AUDIO y se escucha en voz alta: Tu respuesta se convierte en AUDIO y se escucha en voz alta:
- Castellano siempre. Trata al usuario de usted y llamale "señor" en la primera - Castellano siempre. Trata al usuario de usted y llamale "señor" en la primera
frase. frase.
@ -169,6 +185,32 @@ def herramientas(catalogo) -> list:
}, },
}, },
}, },
{
"type": "function",
"function": {
"name": "elegir_modelo",
"description": (
"Cambia el modelo con el que piensas. Usalo cuando una "
"pregunta sea mas dificil de lo normal (razonar, codigo, "
"varios pasos) y quieras uno mas potente, o cuando el usuario "
"pida expresamente cambiar de modelo. Valores: 'rapido' "
"(ligero, para lo simple), 'potente' (mas grande, mas lento), "
"'razonador' (piensa en voz alta antes de responder), o el "
"nombre exacto de un modelo de Ollama. El cambio dura hasta "
"que lo vuelvas a cambiar. Contesta breve tras cambiar."
),
"parameters": {
"type": "object",
"properties": {
"cual": {
"type": "string",
"description": "rapido | potente | razonador | nombre exacto",
}
},
"required": ["cual"],
},
},
},
{ {
"type": "function", "type": "function",
"function": { "function": {
@ -203,6 +245,57 @@ class Cerebro:
self.capas = 0 if en_ram else None self.capas = 0 if en_ram else None
self.manos = manos # a quien pedirle que ejecute self.manos = manos # a quien pedirle que ejecute
self.historia = [] self.historia = []
# A quien avisar cuando JARVIS cambia de modelo, para que el panel
# actualice el rotulo del boton "cerebro". Lo conecta el panel.
self.al_cambiar_modelo = None
# Atajos hablados -> familia de modelo. El modelo elige por INTENCION
# ("potente") y aqui se traduce a lo que hay instalado, para no obligarle a
# saberse los nombres exactos. Se resuelve contra lo que de verdad hay en
# Ollama, cogiendo el mayor/menor de la familia qwen que exista.
def _resuelve_modelo(self, cual: str) -> str | None:
cual = (cual or "").strip().lower()
hay = modelos_disponibles(self.endpoint)
if not hay:
return None
# nombre exacto o casi (permite "9b" -> "qwen3.5:9b")
for m in hay:
if cual == m.lower() or cual == m.lower().split(":")[-1]:
return m
qwen = sorted(m for m in hay if "qwen" in m.lower())
def por_tam(sufijo):
return next((m for m in qwen if m.lower().endswith(sufijo)), None)
if cual in ("rapido", "ligero", "pequeno", "pequeño"):
return por_tam(":2b") or por_tam(":4b-jarvis") or (qwen[0] if qwen else None)
if cual in ("potente", "grande", "mejor", "fuerte"):
return por_tam(":9b") or (qwen[-1] if qwen else None)
if cual in ("razonador", "razona", "piensa", "deepseek"):
return next((m for m in hay if "deepseek" in m.lower() or "-r1" in m.lower()), None)
# por defecto, el de trabajo del naranja si existe
return por_tam(":4b-jarvis") or (qwen[0] if qwen else None)
@staticmethod
def _nombre_corto(m: str) -> str:
# "qwen3.5:9b" -> "9b"; "deepseek-r1:latest" -> "deepseek-r1"
base, _, tag = m.partition(":")
return base if tag in ("", "latest") else tag
def cambia_modelo(self, cual: str) -> str:
"""Cambia el cerebro en caliente. Devuelve lo que decir tras cambiar."""
nuevo = self._resuelve_modelo(cual)
if not nuevo:
return f"no tengo ningun modelo que encaje con «{cual}», señor."
if nuevo == self.modelo:
return f"ya estoy pensando con {self._nombre_corto(nuevo)}, señor."
self.modelo = nuevo
if self.al_cambiar_modelo:
try:
self.al_cambiar_modelo(nuevo)
except Exception:
pass
# Ojo: un modelo mas grande no cabe en la grafica pequeña y tirara de CPU
# —mas lento pero mas capaz—; eso es cosa de Ollama, no de aqui.
return f"Cambiado a {self._nombre_corto(nuevo)}, señor."
def _saber(self, texto: str) -> str: def _saber(self, texto: str) -> str:
"""Los apuntes de COFRE que vengan a cuento, para meterlos al prompt. """Los apuntes de COFRE que vengan a cuento, para meterlos al prompt.
@ -283,6 +376,8 @@ class Cerebro:
# toca la maquina, asi que funciona aunque el candado este echado. # toca la maquina, asi que funciona aunque el candado este echado.
if nombre == "buscar_en_apuntes": if nombre == "buscar_en_apuntes":
return self._busca_apuntes(args.get("consulta", "")) return self._busca_apuntes(args.get("consulta", ""))
if nombre == "elegir_modelo":
return self.cambia_modelo(args.get("cual", ""))
if self.manos is None: if self.manos is None:
return "no hay manos conectadas" return "no hay manos conectadas"
if nombre == "orden_del_catalogo": if nombre == "orden_del_catalogo":