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.
71 lines
4.1 KiB
TOML
71 lines
4.1 KiB
TOML
# 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"
|
|
'''
|