# llama.cpp b10901 — el MOTOR DE INFERENCIA LOCAL del corpus. Es la pieza que destraba el §6.7 del # SDD 26 (IA local en la barra lateral de `atuq`) y la mitad semántica del §6.3 (preguntarle al # archivo personal en lenguaje natural). # # ══ POR QUÉ ESTA RECETA, Y NO «ENCHUFAR LO QUE YA HAY» ═════════════════════════════════════════ # El §6.7 figuraba como costo «bajo» porque las piezas parecían estar escritas en tawasuyu. Se fue # a mirar y NO alcanzan, por dos razones independientes: # 1. `pluma-llm` tiene backends `anthropic`, `cohere`, `claude-cli` y `mock` — TODOS de nube o de # juguete. Ninguno corre un modelo en la máquina; # 2. `rimay-verbo-fastembed` —el daemon de embeddings que `rag-motor` exige— trae `fastembed` # con la feature `ort-download-binaries`: **baja onnxruntime precompilado (glibc) y el modelo # de HuggingFace en el primer arranque**. En un build hermético y en una distro musl eso no es # «lento», es imposible. # O sea que los dos pendientes comparten UN muro y el muro tiene forma de receta. llama.cpp lo # derriba entero: el mismo binario sirve `/v1/chat/completions` (el LLM del §6.7) y `/v1/embeddings` # (lo que `rag-motor` necesita para la mitad semántica del §6.3), sin ONNX, sin Python y sin red. # # ⚠ ESTA RECETA ES EL MOTOR, NO LA FUNCIÓN. Un motor sin modelo no responde nada. El modelo es una # FUENTE PINEADA aparte —igual que el perfil de PGO de firefox—, y es su propia unidad de trabajo. # Nadie debería leer «IA local» y esperar que esto conteste algo todavía. # # ══ LA VERSIÓN ES UN COMMIT, NO UNA SERIE ═════════════════════════════════════════════════════ # llama.cpp no publica versiones semánticas: sus releases son `bNNNN`, una por commit de `master` # (b10901 = 2026-09-10). El `LLAMA_VERSION` interno dice `0.4.0-dev` y no identifica nada. Se pinea # el tag, que es lo único que nombra un árbol concreto, y subirlo es su propia unidad de trabajo. # # ══ LAS CUATRO PERILLAS QUE NO SON GUSTO ══════════════════════════════════════════════════════ # # 1. **`-DLLAMA_USE_PREBUILT_UI=OFF` — viene ON DE FÁBRICA Y DESCARGA EN TIEMPO DE BUILD.** # `tools/ui/CMakeLists.txt` cuelga un `add_custom_target` que corre `scripts/ui-assets.cmake`, y # ése hace `file(DOWNLOAD …)` contra un bucket de HuggingFace para hornear la web UI dentro de # `llama-server`. Leído en la fuente del tarball pineado, no supuesto. Hoy el sandbox no tiene # red y el guión sólo avisa y sigue — pero apagarlo por AUSENCIA DE RED es exactamente la # protección que funciona por accidente: el día que el sandbox tenga red, entraría un blob que no # está en `hash_inputs` y el artefacto dejaría de reproducir sin que nadie tocara la receta. Se # apaga por declaración. La web UI no se pierde: no la queríamos — acá el cliente es el host de # native messaging del §7, que habla HTTP, no un navegador mirando el puerto. # # 2. **LAS SEIS PERILLAS DE ISA VAN UNA POR UNA, Y LA RAZÓN SE MIDIÓ ACÁ.** # El primer sello de esta receta llevaba sólo `-DGGML_NATIVE=OFF`, apoyado en leer # `ggml/CMakeLists.txt:141` — `if (GGML_NATIVE OR NOT GGML_NATIVE_DEFAULT) set(INS_ENB OFF)`—, # o sea: con NATIVE=OFF, ggml debería ENCENDER las perillas explícitas (SSE4.2/AVX/AVX2/BMI2/ # FMA/F16C) en vez de apoyarse en `-march=native`. **Y es falso en ESTE sandbox**, por una línea # 50 renglones más arriba (`ggml/CMakeLists.txt:105`): # # if (CMAKE_CROSSCOMPILING OR DEFINED ENV{SOURCE_DATE_EPOCH}) # set(GGML_NATIVE_DEFAULT OFF) # # El sandbox de takana exporta `SOURCE_DATE_EPOCH=1` (`sandbox.rs:453`) — que es lo que nos da # binarios reproducibles. Con eso `GGML_NATIVE_DEFAULT` queda OFF, `NOT GGML_NATIVE_DEFAULT` es # verdadero, e `INS_ENB` termina en **OFF**: las seis perillas apagadas. O sea que # **la variable que existe para que el build reproduzca apagó todas las instrucciones # vectoriales del motor de inferencia.** # # ⚠ Y no falló nada. El artefacto selló, `llama-cli --version` contesta, y el binario es x86-64 # pelado: **cero `%ymm`, cero `vfmadd`, cero `roundps`** en 1.634.770 líneas de `objdump -d`, # contadas sobre el artefacto sellado. Un motor de inferencia sin AVX2 no está roto: está varias # veces más lento, y ninguna métrica del repo lo dice. Es la figura de siempre — un ausente falla # ruidosamente; un desviado llega hasta el final diciendo que todo fue bien. # # Por eso las seis van declaradas y NO se confía en el default de nadie. Y por eso el `install` # las COMPRUEBA en el binario: es el único punto donde este modo de fallar se puede ver. # # ⚠ Eso fija un PISO declarado: x86-64-v3. Un CPU sin AVX2 (pre-2013) muere con SIGILL, no con un # mensaje. Medido antes de elegirlo, no deducido: `momento`/gioser y el worker `dev.gioser.net` # traen los seis flags, y el objetivo de metal (TigerLake, ver [[etapa-metal-usb]]) también. # Bajar el piso o publicar variantes es `docs/plan-variantes-cpu.md`, no esta receta. # `-DGGML_NATIVE=OFF` se queda igual, pero por lo que de verdad hace acá: prohibir `-march=native`, # que sería un artefacto distinto por máquina — la familia del `MAKE_STAMP` de nftables. # # 3. **`-DGGML_CCACHE=OFF` — viene ON de fábrica y se autodetecta.** Si algún día el lab trae # `ccache`, el build cambia de forma sin que el corpus haya cambiado en nada, y el lab NO entra # en `hash_inputs` (ver [[lab-fuera-de-hash-inputs]]): dos labs sellarían bytes distintos en la # misma dirección. Misma familia que el `CONFIG_SHELL=bash` que se rechazó en nftables. # # 4. **`-DGGML_OPENMP=OFF` — no es recorte, es evitar una dep invisible.** Con OpenMP encendido el # binario arrastra la runtime de OpenMP del compilador. ggml trae su PROPIO pool de hilos y lo usa # cuando OpenMP no está; el corpus prefiere no meterse la runtime de otro toolchain adentro # (ver el `-static-libstdc++` de cmake.toml, misma lección). # # `-DLLAMA_OPENSSL=OFF`: su único uso es HTTPS para BAJAR modelos de HuggingFace desde el propio # binario. Acá un modelo entra como entra cualquier fuente —pineado por sha256— así que esa función # no sólo no hace falta: no queremos que exista. `-DLLAMA_BUILD_APP=OFF` por lo mismo (`app/` es el # binario unificado con `download.cpp` adentro). # # ⚠ `LLAMA_BUILD_TOOLS=ON` NO es un extra: `llama-server` vive en `tools/server`, y `tools/CMakeLists.txt` # sólo entra a `server/` (y a `cli/`) si `LLAMA_BUILD_SERVER` está encendido DENTRO de TOOLS. Con # TOOLS=OFF no hay servidor. De yapa vienen `llama-quantize`, `llama-bench` y `llama-perplexity`, # que son las tres herramientas con las que se mide un modelo antes de pinearlo. # # El wrapper `.zwrap` es el mismo de `poppler-glib` y por lo mismo: cmake le pasa # `-Wl,--fatal-warnings` al linker y zig cc convierte avisos benignos en errores. # # El `install` comprueba que `llama-server` EXISTA. No es paranoia: si `LLAMA_BUILD_TOOLS` o # `LLAMA_BUILD_SERVER` se apagan solos —que es lo que hizo `ENABLE_GLIB` en poppler—, el build sale # 0 y sella un artefacto sin la única pieza por la que esta receta existe. name = "llama-cpp" version = "b10901" license = "MIT" [source] tarball = "https://github.com/ggml-org/llama.cpp/archive/refs/tags/b10901.tar.gz" sha256 = "2cf2d648c6a8ee94f67a94d8c70479620ba481e01a1d930a9075bc7780ff4f9f" [build] compiler = "zig-cc" target = "x86_64-linux-musl" link = "static" flags = [] # Sin esto el artefacto pesa 1,8 G: zig cc emite debug_info por defecto y acá son 16 binarios # ESTÁTICOS, o sea 16 copias de los símbolos de libllama+libggml+libc++. Medido: `llama-server` # pasa de 133 M a una fracción. Entra en el hash (`recipe.rs:574`), como tiene que ser. strip_debug = true [build.phases] configure = ''' ZW="$PWD/.zwrap"; mkdir -p "$ZW" cat > "$ZW/cc" <<'W' #!/bin/sh a=""; for x in "$@"; do [ "$x" = "-Wl,--fatal-warnings" ] && continue; a="$a $x"; done exec /opt/zig/zig cc -mcpu=baseline $a W cat > "$ZW/cxx" <<'W' #!/bin/sh a=""; for x in "$@"; do [ "$x" = "-Wl,--fatal-warnings" ] && continue; a="$a $x"; done exec /opt/zig/zig c++ -mcpu=baseline $a W chmod +x "$ZW/cc" "$ZW/cxx" cmake -B build -G Ninja -Wno-dev \ -DCMAKE_C_COMPILER="$ZW/cc" -DCMAKE_CXX_COMPILER="$ZW/cxx" \ -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr \ -DCMAKE_INTERPROCEDURAL_OPTIMIZATION=OFF -DBUILD_SHARED_LIBS=OFF \ -DGGML_NATIVE=OFF -DGGML_CCACHE=OFF -DGGML_OPENMP=OFF -DGGML_BLAS=OFF \ -DGGML_SSE42=ON -DGGML_AVX=ON -DGGML_AVX2=ON -DGGML_BMI2=ON -DGGML_FMA=ON -DGGML_F16C=ON \ -DGGML_RPC=OFF -DGGML_BACKEND_DL=OFF \ -DLLAMA_OPENSSL=OFF -DLLAMA_BUILD_UI=OFF -DLLAMA_USE_PREBUILT_UI=OFF \ -DLLAMA_BUILD_TESTS=OFF -DLLAMA_BUILD_EXAMPLES=OFF -DLLAMA_BUILD_APP=OFF \ -DLLAMA_BUILD_TOOLS=ON -DLLAMA_BUILD_SERVER=ON ''' compile = 'cmake --build build -j"$(nproc)"' install = ''' set -e DESTDIR=/out cmake --install build # Ver la cabecera: si TOOLS o SERVER se apagan solos, esto sella sin la pieza que justifica la receta. test -x /out/usr/bin/llama-server || { echo "ERROR: no se instaló llama-server — ¿LLAMA_BUILD_TOOLS/LLAMA_BUILD_SERVER apagados?" >&2 exit 1 } test -x /out/usr/bin/llama-cli || { echo "ERROR: no se instaló llama-cli" >&2 exit 1 } # Ver la perilla 2 de la cabecera: SOURCE_DATE_EPOCH apaga INS_ENB y el binario sale sin una sola # instrucción vectorial, sellando igual y corriendo igual. Esto es lo único que lo ve. objdump -d build/ggml/src/libggml-cpu.a 2>/dev/null | grep -q '%ymm' || { echo "ERROR: libggml-cpu.a NO tiene registros %ymm — AVX2 quedó apagado (¿INS_ENB=OFF?)" >&2 exit 1 } objdump -d build/ggml/src/libggml-cpu.a 2>/dev/null | grep -q 'vfmadd' || { echo "ERROR: libggml-cpu.a NO tiene vfmadd — FMA quedó apagado" >&2 exit 1 } ''' [deps] build = ["cmake", "samurai", "busybox", "binutils", "pkgconf"]