Load GGUF inside the BEAM.

Load GGUF models inside the BEAM.

ex_llama is a Rustler NIF around llama.cpp so Elixir can infer local GGUF models in-process — no Python sidecar.

hex 0.2.3 MIT 15 GitHub stars 820 Hex downloads Alpha
{:ok, llama} = ExLLama.load_model("model.gguf")

thread = [
  %{role: :user, content: "Say hello from the BEAM."}
]

{:ok, response} = ExLLama.chat_completion(llama, thread, %{seed: 2})

Why in-process

|> In-process GGUF

A Rustler NIF wrapping the llama_cpp crate. Load a .gguf in the same VM as your Elixir app — not an HTTP sidecar, not a Python process.

|> Sessions + chat completion

Load a model, open a session, run chat completion or token completion, and stream tokens back to the calling process.

|> Embeddings + special tokens

ExLLama.Model.embeddings/3 for vectors. Special tokens (bos, eos, eot, infill markers) are first-class on the model.

In practice

Chat completion — load a GGUF, pass a thread
{:ok, llama} = ExLLama.load_model("./models/tinyllama-1.1b-chat-v1.0.Q4_K_M.gguf")

thread = [
  %{role: :user, content: "Say Hello. And only hello."},
  %{role: :assistant, content: "Hello"},
  %{role: :user, content: "What did you just say?"}
]

{:ok, response} = ExLLama.chat_completion(llama, thread, %{seed: 2})
Sessions — advance context, complete, stream
{:ok, options} = ExLLama.Session.default_options()
{:ok, session} = ExLLama.create_session(llama, %{options | seed: 2})

ExLLama.advance_context(session, "<|user|>\nSay Hello.</s>\n<|assistant|>\n")
{:ok, text} = ExLLama.completion(session, 512, "</s>\n*")

# Stream tokens to the calling process
ExLLama.Session.start_completing_with(session, %{max_tokens: 512})
Embeddings + special tokens
{:ok, vectors} = ExLLama.Model.embeddings(llama, ["hello BEAM"], options)
{:ok, bos} = ExLLama.Model.bos(llama)
{:ok, eos} = ExLLama.Model.eos(llama)
Know before you ship
  • Alpha. Public, useful, not a finished product.
  • NIF crash can take the VM. A fault in the native llama.cpp path is not isolated the way a sidecar process is.
  • Chat templates. Auto-detect still defaults to Zephyr unless you pass meta[:template].

Get started

mix.exs
defp deps do
  [
    {:ex_llama, "~> 0.2.3"}
  ]
end
Then
mix deps.get
# Rustler builds the llama.cpp NIF on first compile