ia: el modelo de chat, pineado — Qwen2.5-1.5B-Instruct Q4_K_M (Apache-2.0)
Elegido por tres cosas, y las tres medidas antes de pinearlo: licencia Apache-2.0 (lo que una distro puede shipear sin letra chica, al revés que Llama-3.2 o Gemma), habla español —se le preguntó qué es una distribución de GNU/Linux y contestó dos frases correctas— y corre en CPU: 20,1 tokens/s en el hub sin GPU, 1,04 GiB. El objeto pineado es un tar que envuelve el .gguf, publicado en el mirror y servido por sha256: takana extrae toda fuente con `tar` y un GGUF pelado no lo es. Es el camino de firefox-pgo-profile. La receta anota el sha256 del GGUF DE UPSTREAM (el lfs.oid de HuggingFace, verificado al bajarlo) y no sólo el del tar nuestro, para que nadie tenga que confiar en nuestro tar. Round-trip verificado: apartados el caché y el artefacto, el build lo bajó del mirror y selló el mismo ArtifactHash. Guardián en la receta, porque el fallo es callado: un fichero truncado o un HTML de error renombrado a .gguf se instala igual y sella en verde. Se comprueban el mágico GGUF y el tamaño. ⚠ Y el modelo de verdad destapó una CARRERA en el guardián del §6.7: el censo de motores contaba en el instante del cierre — con el de juguete daba 0 y con el de la imagen daba 1, que se lee como fuga cuando en realidad matar un proceso con un giga mapeado tarda ~1 s. Ahora espera hasta 15 s y anota cuánto tardó. La rotura a propósito sigue fallando, ahora con el tiempo a la vista. Falta decidir en qué imágenes se declara (con el motor son ~1,25 GiB por perfil) y pinear el de embeddings (multilingual-e5-small) para la mitad semántica del §6.3.
This commit is contained in:
@@ -1004,11 +1004,40 @@ intento de romperlo **tampoco rompía nada**: el motor de mentira moría al hace
|
|||||||
socket estaba en `/salida`, que se comparte entre las dos corridas; con el socket en `/tmp` —tmpfs
|
socket estaba en `/salida`, que se comparte entre las dos corridas; con el socket en `/tmp` —tmpfs
|
||||||
nuevo por corrida— la rotura rompe.
|
nuevo por corrida— la rotura rompe.
|
||||||
|
|
||||||
**Lo que queda es UNA decisión: qué modelo se pinea.** No es trabajo pendiente: la cadena funciona y
|
**El modelo de chat, PINEADO (2026-09-11).** `recipes/ia-modelo-chat.toml` ⇒ **Qwen2.5-1.5B-Instruct
|
||||||
el modelo entra por sha256 como el perfil de PGO de firefox. Son dos —uno de chat para esto y uno de
|
Q4_K_M**, 1.117.320.736 bytes. Elegido por tres cosas y las tres discutibles cambiando un sha256:
|
||||||
embeddings para el §6.3— y las dos preguntas que las gobiernan son el tamaño de la imagen y la
|
**Apache-2.0** (lo que una distro puede shipear sin letra chica, al revés que Llama-3.2 o Gemma),
|
||||||
licencia. Hasta que se decidan, la imagen no trae modelo y la barra lateral **lo dice**:
|
**habla español** —medido antes de pinearlo: se le preguntó qué es una distribución de GNU/Linux y
|
||||||
`no hay modelo en /usr/share/takana/ia/modelo.gguf: esta imagen no trae ninguno pineado`.
|
contestó dos frases correctas— y **corre en CPU**: 20,1 tokens/s en el hub sin GPU.
|
||||||
|
|
||||||
|
El objeto pineado es un **tar que envuelve el `.gguf`**, publicado en el mirror de fuentes y servido
|
||||||
|
por sha256, porque takana extrae toda fuente con `tar` y un GGUF pelado no es un tar. Es el camino de
|
||||||
|
`firefox-pgo-profile`. La procedencia queda escrita en la receta con **el sha256 del GGUF de
|
||||||
|
upstream** (el `lfs.oid` que publica HuggingFace, verificado al bajarlo), no sólo el del tar nuestro.
|
||||||
|
Round-trip verificado como manda el ADR 0013: apartados el caché local y el artefacto, `takana build`
|
||||||
|
lo **bajó del mirror** y selló el mismo `ArtifactHash`.
|
||||||
|
|
||||||
|
La receta trae guardián propio, porque el modo de fallo es callado: un fichero truncado o un HTML de
|
||||||
|
error renombrado a `.gguf` se instala igual y sella en verde. Se comprueban el mágico `GGUF` y el
|
||||||
|
tamaño.
|
||||||
|
|
||||||
|
⚠ **Y el modelo de verdad destapó una carrera en el guardián del §6.7.bis.** El censo de motores
|
||||||
|
contaba **en el instante** del cierre: con el modelo de juguete daba 0 y con el de la imagen daba 1 —
|
||||||
|
y eso se lee como fuga cuando lo que pasa es que matar un proceso con un giga mapeado tarda ~1 s.
|
||||||
|
Ahora el censo **espera hasta 15 s** y anota cuánto tardó: `0 llama-server vivos (1s después)`. La
|
||||||
|
rotura a propósito sigue fallando, ahora con el tiempo a la vista.
|
||||||
|
|
||||||
|
`scripts/test-atuq-ia.py --image-model` corre la cadena entera con el modelo de la imagen (llega como
|
||||||
|
**capa**, no copiado: mover 1 GiB a un tmpfs llenó `/tmp` y mató al guardián con un ENOSPC que no
|
||||||
|
tenía nada que ver con lo que mide). El default sigue siendo el modelo de juguete, que certifica la
|
||||||
|
misma cadena en un segundo.
|
||||||
|
|
||||||
|
**Lo que queda decidir: qué imágenes lo llevan.** `llama-cpp` (199 M) y el modelo (1,04 GiB) suman
|
||||||
|
~1,25 GiB a cada perfil donde se declaren, y `atuq` está en cuatro escritorios. Sigue sin declararse
|
||||||
|
en ninguno: una imagen no crece 1,25 G por decisión de un commit.
|
||||||
|
|
||||||
|
**Y falta el de embeddings** para la mitad semántica del §6.3 (multilingual-e5-small, decidido pero
|
||||||
|
no pineado todavía).
|
||||||
|
|
||||||
### 6.8 Proxy por contenedor — HECHO (v0.5, 2026-09-06)
|
### 6.8 Proxy por contenedor — HECHO (v0.5, 2026-09-06)
|
||||||
|
|
||||||
|
|||||||
@@ -262,8 +262,10 @@ no puede distinguir «no hay modelo» de «el modelo dijo eso».
|
|||||||
comprueba además que al cerrarse el navegador queden **cero** `llama-server`. Control negativo: sin
|
comprueba además que al cerrarse el navegador queden **cero** `llama-server`. Control negativo: sin
|
||||||
modelo, la causa aparece y **no hay respuesta inventada**.
|
modelo, la causa aparece y **no hay respuesta inventada**.
|
||||||
|
|
||||||
⚠ **La imagen todavía no trae modelo**, y es una decisión pendiente (tamaño y licencia), no trabajo:
|
El modelo ya está elegido y pineado: **Qwen2.5-1.5B-Instruct Q4_K_M** (`recipes/ia-modelo-chat.toml`,
|
||||||
ver el §6.7 del SDD 26.
|
Apache-2.0, 1,04 GiB, 20,1 tok/s en CPU, habla español — las tres cosas medidas antes de pinearlo).
|
||||||
|
Lo que sigue sin decidirse es **en qué imágenes se declara**: con el motor son ~1,25 GiB por perfil.
|
||||||
|
Mientras no se declare, la imagen no lo trae y el panel lo dice.
|
||||||
|
|
||||||
## `foco` — el navegador MUESTRA el foco, y no tiene con qué apagarlo (§6.5)
|
## `foco` — el navegador MUESTRA el foco, y no tiene con qué apagarlo (§6.5)
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,70 @@
|
|||||||
|
# ia-modelo-chat — el modelo que contesta en la barra lateral de `atuq` (SDD 26 §6.7).
|
||||||
|
#
|
||||||
|
# ══ QUÉ ES ═════════════════════════════════════════════════════════════════════════════════════
|
||||||
|
# **Qwen2.5-1.5B-Instruct, cuantizado Q4_K_M**: 1.117.320.736 bytes de GGUF. Es el modelo de chat de
|
||||||
|
# la distro, y entra como los pesos entran en cualquier lado — **fuente pineada por sha256**, no un
|
||||||
|
# build que lo produzca. Nadie entrena nada acá.
|
||||||
|
#
|
||||||
|
# ══ POR QUÉ ÉSTE ═══════════════════════════════════════════════════════════════════════════════
|
||||||
|
# Tres cosas, y las tres se pueden discutir cambiando un sha256:
|
||||||
|
#
|
||||||
|
# · **licencia Apache-2.0**, que es lo que una distro puede shipear sin letra chica. Las
|
||||||
|
# alternativas obvias (Llama-3.2, Gemma) traen licencias propias con restricciones de uso, y eso
|
||||||
|
# en una imagen que se distribuye no es un detalle;
|
||||||
|
# · **habla español**, que es el idioma de quien va a usar esto. Medido antes de pinearlo, no
|
||||||
|
# supuesto: se le preguntó qué es una distribución de GNU/Linux y contestó dos frases correctas
|
||||||
|
# en español;
|
||||||
|
# · **entra en la imagen y corre en CPU**: 1,04 GiB y **20,1 tokens/s** medidos en el hub sin GPU
|
||||||
|
# (89 tokens en 4,4 s). Un modelo de 7B con la misma licencia daría mejores respuestas y
|
||||||
|
# quintuplicaría el peso, con la mitad de la velocidad.
|
||||||
|
#
|
||||||
|
# ══ POR QUÉ EL TARBALL ES NUESTRO Y LA URL NO RESUELVE ═════════════════════════════════════════
|
||||||
|
# takana extrae toda fuente con `tar`, y un `.gguf` pelado no es un tar. Así que el objeto pineado es
|
||||||
|
# un tar que envuelve el fichero, publicado en el mirror de fuentes y servido **por sha256** — el
|
||||||
|
# mismo camino que `firefox-pgo-profile`. La URL `.invalid` no resuelve a propósito (ADR 0013: la URL
|
||||||
|
# no entra en `hash_inputs`, sólo el sha256).
|
||||||
|
#
|
||||||
|
# La PROCEDENCIA, para que nadie tenga que confiar en nuestro tar:
|
||||||
|
# upstream https://huggingface.co/Qwen/Qwen2.5-1.5B-Instruct-GGUF
|
||||||
|
# fichero qwen2.5-1.5b-instruct-q4_k_m.gguf
|
||||||
|
# sha256 6a1a2eb6d15622bf3c96857206351ba97e1af16c30d7a74ee38970e434e9407e ← del GGUF, no del tar
|
||||||
|
# Ese sha256 es el que HuggingFace publica como `lfs.oid`, y es el que se verificó al bajarlo.
|
||||||
|
#
|
||||||
|
# ⚠ `strip_components = 0`: el tar lleva el fichero en la raíz. Con el default (1) se recorta lo
|
||||||
|
# único que hay y el install muere con un `cp: cannot stat` que no menciona el recorte.
|
||||||
|
name = "ia-modelo-chat"
|
||||||
|
version = "qwen2.5-1.5b-instruct-q4_k_m"
|
||||||
|
license = "Apache-2.0"
|
||||||
|
|
||||||
|
[source]
|
||||||
|
tarball = "https://no-hay-upstream.invalid/takana/ia-modelo-chat.tar"
|
||||||
|
sha256 = "2560ee9452a979116ae24278615c51ecced38d2e62288ffe29f4a4bf7ddfc1c6"
|
||||||
|
strip_components = 0
|
||||||
|
|
||||||
|
[build]
|
||||||
|
compiler = "zig-cc"
|
||||||
|
|
||||||
|
[build.phases]
|
||||||
|
configure = "true"
|
||||||
|
compile = "true"
|
||||||
|
install = '''
|
||||||
|
set -e
|
||||||
|
mkdir -p /out/usr/share/takana/ia
|
||||||
|
cp qwen2.5-1.5b-instruct-q4_k_m.gguf /out/usr/share/takana/ia/modelo.gguf
|
||||||
|
|
||||||
|
M=/out/usr/share/takana/ia/modelo.gguf
|
||||||
|
|
||||||
|
# ── GUARDIÁN: QUE SEA UN GGUF, NO UN FICHERO GRANDE ─────────────────────────────────────────
|
||||||
|
# El modo de fallo callado acá es el de siempre: un fichero truncado o un HTML de error de 200 K
|
||||||
|
# renombrado a `.gguf` se instala igual, sella en verde, y lo único que falla es la respuesta —
|
||||||
|
# cuando ya no hay nadie mirando el build. Se comprueban las dos cosas que un GGUF de verdad tiene:
|
||||||
|
# el número mágico `GGUF` en los primeros cuatro bytes, y el tamaño.
|
||||||
|
head -c 4 "$M" | grep -q GGUF || {
|
||||||
|
echo "!! el fichero instalado no empieza con el mágico GGUF" >&2
|
||||||
|
head -c 64 "$M" | od -c | head -3 >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
n=$(stat -c%s "$M")
|
||||||
|
test "$n" -gt 900000000 || { echo "!! el GGUF pesa $n bytes: está truncado" >&2; exit 1; }
|
||||||
|
echo "guardián: GGUF válido — $n bytes"
|
||||||
|
'''
|
||||||
+48
-16
@@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
python3 scripts/test-atuq-ia.py # sale una respuesta generada acá
|
python3 scripts/test-atuq-ia.py # sale una respuesta generada acá
|
||||||
python3 scripts/test-atuq-ia.py --negative-control # sin modelo: lo DICE, no inventa
|
python3 scripts/test-atuq-ia.py --negative-control # sin modelo: lo DICE, no inventa
|
||||||
|
python3 scripts/test-atuq-ia.py --image-model # con el modelo DE LA IMAGEN (1 GiB, lento)
|
||||||
|
|
||||||
── QUÉ SE EJERCITA, DE PUNTA A PUNTA ─────────────────────────────────────────────────────────
|
── QUÉ SE EJERCITA, DE PUNTA A PUNTA ─────────────────────────────────────────────────────────
|
||||||
El navegador abre el panel de la extensión con una pregunta (`panel.html?q=…`), el panel se la pasa
|
El navegador abre el panel de la extensión con una pregunta (`panel.html?q=…`), el panel se la pasa
|
||||||
@@ -75,15 +76,18 @@ def modelo_de_prueba():
|
|||||||
return str(mod.bajar_modelo())
|
return str(mod.bajar_modelo())
|
||||||
|
|
||||||
|
|
||||||
def correr(atuq, costura, llama, modelo, salida, url, negativo):
|
def correr(atuq, costura, llama, capa_modelo, salida, url, negativo, tope=90):
|
||||||
borrar = f"rm -f {MODELO_EN_IMAGEN}" if negativo else "true"
|
borrar = f"rm -f {MODELO_EN_IMAGEN}" if negativo else "true"
|
||||||
|
# El modelo de la imagen llega como CAPA (su artefacto ya lo pone en el path de producción); el
|
||||||
|
# de juguete se copia, que son 1,1 MB.
|
||||||
|
poner_modelo = "true" if capa_modelo else f"cp /salida/modelo.gguf {MODELO_EN_IMAGEN}"
|
||||||
guion = f"""
|
guion = f"""
|
||||||
mkdir -p /root /salida/hogar /salida/run {os.path.dirname(MODELO_EN_IMAGEN)}
|
mkdir -p /root /salida/hogar /salida/run {os.path.dirname(MODELO_EN_IMAGEN)}
|
||||||
[ -s /etc/machine-id ] || tr -d - < /proc/sys/kernel/random/uuid > /etc/machine-id
|
[ -s /etc/machine-id ] || tr -d - < /proc/sys/kernel/random/uuid > /etc/machine-id
|
||||||
export HOME=/salida/hogar XDG_RUNTIME_DIR=/salida/run
|
export HOME=/salida/hogar XDG_RUNTIME_DIR=/salida/run
|
||||||
cp /salida/modelo.gguf {MODELO_EN_IMAGEN}
|
{poner_modelo}
|
||||||
{borrar}
|
{borrar}
|
||||||
timeout 90 /usr/bin/atuq --profile /salida/perfil --no-remote --headless '{url}' \
|
timeout {tope} /usr/bin/atuq --profile /salida/perfil --no-remote --headless '{url}' \
|
||||||
> /salida/consola.log 2>&1
|
> /salida/consola.log 2>&1
|
||||||
# ¿Se fue el motor con el navegador? Se cuenta ACÁ ADENTRO, con el namespace de PIDs todavía
|
# ¿Se fue el motor con el navegador? Se cuenta ACÁ ADENTRO, con el namespace de PIDs todavía
|
||||||
# vivo: desde el hub no se vería nada —`--unshare-pid` se lleva todo al cerrar la jaula— y esa
|
# vivo: desde el hub no se vería nada —`--unshare-pid` se lleva todo al cerrar la jaula— y esa
|
||||||
@@ -92,17 +96,27 @@ def correr(atuq, costura, llama, modelo, salida, url, negativo):
|
|||||||
# Se cuenta leyendo /proc a mano, y no con `pgrep -c`: busybox NO tiene esa opción, y el
|
# Se cuenta leyendo /proc a mano, y no con `pgrep -c`: busybox NO tiene esa opción, y el
|
||||||
# `|| echo 0` que parecía prudente convertía el error en un CERO — o sea en un veredicto verde.
|
# `|| echo 0` que parecía prudente convertía el error en un CERO — o sea en un veredicto verde.
|
||||||
# Medido: con un motor vivo a propósito, el guardián pasaba igual.
|
# Medido: con un motor vivo a propósito, el guardián pasaba igual.
|
||||||
n=0
|
# Se espera a que SE MUERA, hasta 15 s, y se anota cuánto tardó. Matar a un proceso con un
|
||||||
for p in /proc/[0-9]*; do
|
# giga de modelo mapeado no es instantáneo, y contar en el instante exacto del cierre confundía
|
||||||
[ "$(cat $p/comm 2>/dev/null)" = "llama-server" ] && n=$((n+1))
|
# «tardando en morir» con «fuga»: con el modelo de juguete daba 0 y con el de la imagen daba 1.
|
||||||
|
esperado=0
|
||||||
|
while [ $esperado -lt 15 ]; do
|
||||||
|
n=0
|
||||||
|
for p in /proc/[0-9]*; do
|
||||||
|
[ "$(cat $p/comm 2>/dev/null)" = "llama-server" ] && n=$((n+1))
|
||||||
|
done
|
||||||
|
[ "$n" = "0" ] && break
|
||||||
|
esperado=$((esperado+1))
|
||||||
|
sleep 1
|
||||||
done
|
done
|
||||||
echo $n > /salida/motores-vivos
|
echo "$n $esperado" > /salida/motores-vivos
|
||||||
exit 0
|
exit 0
|
||||||
"""
|
"""
|
||||||
subprocess.run([
|
subprocess.run([
|
||||||
"bwrap",
|
"bwrap",
|
||||||
"--overlay-src", ALPINE, "--overlay-src", RFS,
|
"--overlay-src", ALPINE, "--overlay-src", RFS,
|
||||||
"--overlay-src", atuq, "--overlay-src", costura, "--overlay-src", llama,
|
"--overlay-src", atuq, "--overlay-src", costura, "--overlay-src", llama,
|
||||||
|
*(["--overlay-src", capa_modelo] if capa_modelo else []),
|
||||||
"--tmp-overlay", "/",
|
"--tmp-overlay", "/",
|
||||||
"--dev-bind", "/dev", "/dev", "--proc", "/proc", "--bind", "/sys", "/sys",
|
"--dev-bind", "/dev", "/dev", "--proc", "/proc", "--bind", "/sys", "/sys",
|
||||||
"--tmpfs", "/tmp", "--unshare-pid", "--unshare-net",
|
"--tmpfs", "/tmp", "--unshare-pid", "--unshare-net",
|
||||||
@@ -128,6 +142,10 @@ def correr(atuq, costura, llama, modelo, salida, url, negativo):
|
|||||||
|
|
||||||
def main():
|
def main():
|
||||||
negativo = "--negative-control" in sys.argv[1:]
|
negativo = "--negative-control" in sys.argv[1:]
|
||||||
|
# Por defecto se mide con el modelo de juguete: 1,1 MB, instantáneo, y alcanza para certificar la
|
||||||
|
# CADENA. Con `--image-model` se mide el de la imagen — la misma cadena, pero contestando de
|
||||||
|
# verdad; cuesta 1 GiB de overlay y unos segundos de generación, y por eso no es el default.
|
||||||
|
de_la_imagen = "--image-model" in sys.argv[1:]
|
||||||
if negativo:
|
if negativo:
|
||||||
print("== MODO CONTROL NEGATIVO: sin modelo en la imagen; el panel tiene que DECIR la causa")
|
print("== MODO CONTROL NEGATIVO: sin modelo en la imagen; el panel tiene que DECIR la causa")
|
||||||
if not os.path.isdir(RFS):
|
if not os.path.isdir(RFS):
|
||||||
@@ -137,20 +155,30 @@ def main():
|
|||||||
llama = artefacto("llama-cpp", "llama-cpp", "LLAMA_DIR")
|
llama = artefacto("llama-cpp", "llama-cpp", "LLAMA_DIR")
|
||||||
print(f"artefactos: {os.path.basename(atuq)}\n {os.path.basename(costura)}"
|
print(f"artefactos: {os.path.basename(atuq)}\n {os.path.basename(costura)}"
|
||||||
f"\n {os.path.basename(llama)}")
|
f"\n {os.path.basename(llama)}")
|
||||||
modelo = modelo_de_prueba()
|
capa_modelo = None
|
||||||
print(f"modelo de prueba: {os.path.basename(modelo)}")
|
modelo = None
|
||||||
|
if de_la_imagen:
|
||||||
|
# Llega como CAPA: su artefacto ya instala el modelo en el path de producción. Copiarlo
|
||||||
|
# sería mover 1 GiB a un tmpfs — lento, y la primera vez llenó /tmp y el guardián murió con
|
||||||
|
# un ENOSPC que no tenía nada que ver con lo que mide.
|
||||||
|
capa_modelo = artefacto("ia-modelo-chat", "ia-modelo-chat")
|
||||||
|
print(f"modelo: el de la imagen — {os.path.basename(capa_modelo)}")
|
||||||
|
else:
|
||||||
|
modelo = modelo_de_prueba()
|
||||||
|
print(f"modelo de prueba: {os.path.basename(modelo)}")
|
||||||
|
|
||||||
tmp = tempfile.mkdtemp(prefix="atuq-ia-")
|
tmp = tempfile.mkdtemp(prefix="atuq-ia-")
|
||||||
salida = os.path.join(tmp, "salida")
|
salida = os.path.join(tmp, "salida")
|
||||||
os.makedirs(os.path.join(salida, "perfil"))
|
os.makedirs(os.path.join(salida, "perfil"))
|
||||||
shutil.copy(modelo, os.path.join(salida, "modelo.gguf"))
|
if modelo:
|
||||||
|
shutil.copy(modelo, os.path.join(salida, "modelo.gguf"))
|
||||||
try:
|
try:
|
||||||
with open(os.path.join(salida, "perfil", "user.js"), "w") as fh:
|
with open(os.path.join(salida, "perfil", "user.js"), "w") as fh:
|
||||||
fh.write('user_pref("browser.dom.window.dump.enabled", true);\n')
|
fh.write('user_pref("browser.dom.window.dump.enabled", true);\n')
|
||||||
fh.write('user_pref("browser.shell.checkDefaultBrowser", false);\n')
|
fh.write('user_pref("browser.shell.checkDefaultBrowser", false);\n')
|
||||||
|
|
||||||
# Primera corrida: descubrir la URL base que Gecko le asignó a la extensión (es por perfil).
|
# Primera corrida: descubrir la URL base que Gecko le asignó a la extensión (es por perfil).
|
||||||
lineas, log, _ = correr(atuq, costura, llama, modelo, salida, "about:blank", negativo)
|
lineas, log, _ = correr(atuq, costura, llama, capa_modelo, salida, "about:blank", negativo)
|
||||||
base = next((l[len("BASE "):].strip() for l in lineas if l.startswith("BASE ")), None)
|
base = next((l[len("BASE "):].strip() for l in lineas if l.startswith("BASE ")), None)
|
||||||
if not base:
|
if not base:
|
||||||
destino = os.path.join(ROOT, "work", "atuq-ia.log")
|
destino = os.path.join(ROOT, "work", "atuq-ia.log")
|
||||||
@@ -160,7 +188,9 @@ def main():
|
|||||||
print(f"\nextensión en {base}")
|
print(f"\nextensión en {base}")
|
||||||
|
|
||||||
panel = f"{base}panel.html?q={PREGUNTA.replace(' ', '%20')}"
|
panel = f"{base}panel.html?q={PREGUNTA.replace(' ', '%20')}"
|
||||||
lineas, log, motores = correr(atuq, costura, llama, modelo, salida, panel, negativo)
|
# 512 tokens a ~20 tok/s son ~25 s, más la carga del modelo: el tope se acompaña solo.
|
||||||
|
lineas, log, motores = correr(atuq, costura, llama, capa_modelo, salida, panel, negativo,
|
||||||
|
tope=180 if de_la_imagen else 90)
|
||||||
for l in lineas:
|
for l in lineas:
|
||||||
print(" " + l)
|
print(" " + l)
|
||||||
|
|
||||||
@@ -188,13 +218,15 @@ def main():
|
|||||||
texto = r.split("texto=", 1)[1] if "texto=" in r else ""
|
texto = r.split("texto=", 1)[1] if "texto=" in r else ""
|
||||||
if len(texto.strip(' "')) < 2:
|
if len(texto.strip(' "')) < 2:
|
||||||
fatal(f"la respuesta vino VACÍA: {r}")
|
fatal(f"la respuesta vino VACÍA: {r}")
|
||||||
if motores != "0":
|
quedaron, esperado = (motores.split() + ["?"])[:2]
|
||||||
fatal(f"quedaron {motores} `llama-server` vivos al cerrarse el navegador —"
|
if quedaron != "0":
|
||||||
" el motor tiene que irse con él (§6.7): medio giga de modelo huérfano no se ve")
|
fatal(f"quedaron {quedaron} `llama-server` vivos {esperado}s después de cerrarse el"
|
||||||
|
" navegador — el motor tiene que irse con él (§6.7): medio giga de modelo huérfano"
|
||||||
|
" no se ve hasta que la máquina se queda sin RAM")
|
||||||
print("\n✓ el navegador preguntó y un modelo de ESTA máquina contestó")
|
print("\n✓ el navegador preguntó y un modelo de ESTA máquina contestó")
|
||||||
print("✓ y nadie levantó el servidor: no hay servicio de IA en la imagen — lo levantó el host")
|
print("✓ y nadie levantó el servidor: no hay servicio de IA en la imagen — lo levantó el host")
|
||||||
print("✓ la jaula corre con `--unshare-net`: no hubo red para consultar a nadie más")
|
print("✓ la jaula corre con `--unshare-net`: no hubo red para consultar a nadie más")
|
||||||
print("✓ y el motor se fue con el navegador: 0 `llama-server` vivos al cerrarse")
|
print(f"✓ y el motor se fue con el navegador: 0 `llama-server` vivos ({esperado}s después)")
|
||||||
return 0
|
return 0
|
||||||
finally:
|
finally:
|
||||||
shutil.rmtree(tmp, ignore_errors=True)
|
shutil.rmtree(tmp, ignore_errors=True)
|
||||||
|
|||||||
Reference in New Issue
Block a user