Plugins / Providers
elelem-llama
llama.cpp, in-process.
Runs GGUF models inside the elelem process through llama.cpp. No server, no network call.
gem install elelem-llamaelelem-llama runs a GGUF model inside the elelem process. The gem builds
llama.cpp into a shared library when you install it, and the llama
provider calls it through Ruby’s Fiddle. There is no server to start and
no network call: your code and prompts stay on your machine.
Install #
gem install elelem elelem-builtins elelem-llamaelelem-builtins is optional but gives the agent its default tools.
Requires Ruby 4.0, a C++17 compiler, and CMake. The gem ships the
llama.cpp source and compiles it during gem install.
The build picks a backend in this order:
| Condition | Backend |
|---|---|
| macOS | metal |
nvcc on PATH | cuda |
glslc on PATH and a Vulkan loader | vulkan |
hipcc on PATH and rocminfo reports a GPU | hip |
| otherwise | cpu |
To force one, set ELELEM_LLAMA_BACKEND at install time:
ELELEM_LLAMA_BACKEND=vulkan gem install elelem-llamaIf a GPU build fails, the build falls back to CPU and says so. The cpu
backend compiles for the host CPU’s instruction set.
Configure #
Put a model in ~/.agents/models:
mkdir -p ~/.agents/models
mv ~/Downloads/model.gguf ~/.agents/models/The provider finds the model in this order:
--model PATHELELEM_GGUF_MODEL~/.agents/models/default.gguf, if it exists (a symlink is fine)- the only
.gguffile in~/.agents/models
With several models in that directory and no default.gguf, it stops and
asks you to pick one:
ln -s ~/.agents/models/model.gguf ~/.agents/models/default.ggufGPU offload. When ELELEM_GGUF_N_GPU_LAYERS is unset, the provider
offloads all layers (999) if the build used cuda, vulkan, or metal
(Apple Silicon only) and the model file is smaller than 80% of the GPU’s
free memory. Otherwise it uses 0 and runs on the CPU. A hip build
always defaults to 0; set ELELEM_GGUF_N_GPU_LAYERS=999 to use the GPU.
Context. With ELELEM_GGUF_N_CTX=0, the context is the model’s trained
length, capped at 65,536 tokens. If that does not fit in memory, the
provider halves it until it does, down to 4,096.
Sampling. Top-k, top-p, temperature, and the seeded sampler apply when
ELELEM_GGUF_TEMP is above 0. At 0 or below, sampling is greedy and the
other sampling variables are ignored.
Use #
elelem -p llama
elelem -p llama --model ~/.agents/models/other.gguf
ELELEM_GGUF_N_CTX=32768 elelem ask -p llama "list the TODOs in lib/"--model takes a file path, not a model name.
The gem also installs elelem-llama, which is elelem with llama as the
default provider:
elelem-llama chatUnlike elelem, elelem-llama does not load elelem-builtins, so the
agent has no tools unless a plugin file in ~/.agents/plugins adds them.
How it works #
gem install runs ext/elelem/llama/extconf.rb. It configures the
vendored llama.cpp with CMake (shared libraries, no server, tools,
examples, or tests), builds the llama and llama-common targets, then
compiles elelem.cpp into a small shim, libelelem_llama.so (.dylib on
macOS). The shim and the llama.cpp libraries are copied into the gem’s
ext/elelem/llama/native directory, along with a file that records the
backend.
At runtime the provider opens the shim with Fiddle and calls 4 C
functions: el_open loads the model and creates a context,
el_generate runs one turn, el_close frees it at exit, and
el_gpu_free_memory reports free GPU memory for the offload default. The
model loads once, when the provider is built.
Each turn, the shim formats elelem’s messages and tools with the model’s
own chat template (Jinja), tokenizes the prompt, and reuses the KV cache
for the prefix shared with the previous turn, so only new tokens are
evaluated. It streams raw output back to Ruby every 50 ms, which elelem
prints as thinking. When generation ends, llama.cpp’s chat parser splits
the output into reasoning, reply, and tool calls. If the parser finds no
tool call but tools were offered, the shim tries two fallbacks: a bare JSON
object with a known tool name and arguments, then a gpt-oss style
to=functions.NAME tag.
Limitations #
- The live stream is the model’s raw output, shown as thinking. The parsed reply appears when generation ends.
- Tool calls depend on the model’s chat template and llama.cpp’s parser. The fallbacks cover common cases, not all.
- One model per process. Switching models means restarting elelem.
- Thread count is not configurable. llama.cpp picks it from the CPU.
- The GPU offload default compares only the model file size with free
memory. It does not count the KV cache, so a large context can still run
out of GPU memory. Set
ELELEM_GGUF_N_GPU_LAYERSif that happens. - To change the backend, reinstall the gem with
ELELEM_LLAMA_BACKENDset. - If the reply runs out of tokens before any text or tool call, you get
[no reply: ran out of output tokens before finishing].
Development #
git clone https://git.mokhan.ca/elelem/llama.git
cd llama
bin/setup
bin/compile
bin/testbin/setup initializes the vendor/llama.cpp submodule, runs
bundle install, and downloads a GGUF model from Hugging Face into
~/.agents/models if it is not already there. bin/compile runs the same
build as gem install. The spec covers model discovery.
Environment #
| Variable | Default | Description |
|---|---|---|
ELELEM_GGUF_MODEL | none | Path to a .gguf file. Unset means look in ~/.agents/models. |
ELELEM_GGUF_N_CTX | 0 | Context size in tokens. 0 means the model’s trained context, capped at 65536. |
ELELEM_GGUF_N_GPU_LAYERS | auto | Layers to offload to the GPU. Auto picks 999 (all) or 0; see Configure. |
ELELEM_GGUF_MAX_TOKENS | 0 | Output token limit per reply. 0 means the rest of the context window. |
ELELEM_GGUF_TEMP | 0.7 | Sampling temperature. 0 or below means greedy sampling. |
ELELEM_GGUF_SEED | -1 | Sampling seed. |
ELELEM_TOP_K | 40 | Top-k sampling. Ignored when temperature is 0 or below. |
ELELEM_TOP_P | 0.95 | Top-p sampling. Ignored when temperature is 0 or below. |
ELELEM_REPEAT_LAST_N | 0 | Tokens considered by the repeat penalty. 0 disables the penalty. |
ELELEM_REPEAT_PENALTY | 1.0 | Repeat penalty. 1.0 means no penalty. |
ELELEM_KV_CACHE_TYPE | q8_0 | KV cache type. One of f16, f32, q4_0, q8_0; anything else means q8_0. |
ELELEM_LLAMA_BACKEND | auto | Build time only: auto, cpu, vulkan, cuda, hip, or metal. |
ELELEM_HIP_ARCH | detected, else gfx1100 | Build time only. GPU target for the hip backend. |
ELELEM_LOG_LEVEL | warn | Also sets the llama.cpp log threshold (debug, info, warn, error). |