Hemlock bindings for llama.cpp, enabling LLM inference in the Hemlock programming language.
- Hemlock programming language installed
- CMake 3.14+
- C/C++ compiler (gcc, clang)
- libffi (for Hemlock FFI)
- Clone this repository with submodules:
git clone --recursive https://github.com/emnakamura/hemllama.git
cd hemllama- Build llama.cpp as a shared library:
./build.shFor GPU support (CUDA):
./build.sh -DGGML_CUDA=ONFor Metal (macOS):
./build.sh -DGGML_METAL=ON- Set the library path:
export LD_LIBRARY_PATH="$(pwd)/lib:$LD_LIBRARY_PATH"import "llama" from "path/to/hemllama/llama.hml";
// Initialize
llama.backend_init();
// Load model
let model = llama.model_load("model.gguf", {
n_gpu_layers: 0 // Increase for GPU offloading
});
// Create context
let ctx = llama.context_create(model, {
n_ctx: 2048
});
// Create sampler
let sampler = llama.sampler_create({
temp: 0.8,
top_k: 40,
top_p: 0.9
});
// Tokenize and decode prompt
let tokens = llama.tokenize(model, "Hello, world!", { add_special: true });
llama.decode(ctx, tokens, 0);
// Generate tokens
for (let i = 0; i < 100; i = i + 1) {
let token = llama.sample(sampler, ctx, -1);
if (llama.is_eog(model, token)) {
break;
}
let piece = llama.token_to_piece(model, token, false);
print(piece);
llama.decode(ctx, [token], tokens.length + i);
}
// Cleanup
llama.sampler_free(sampler);
llama.context_free(ctx);
llama.model_free(model);
llama.backend_free();
For simple use cases, use the complete() function:
import "llama" from "hemllama/llama.hml";
let result = llama.complete("model.gguf", "What is 2+2?", {
n_predict: 128,
temp: 0.7
});
print(result);
Or use generate() for more control:
llama.backend_init();
let model = llama.model_load("model.gguf");
let ctx = llama.context_create(model);
let result = llama.generate(ctx, model, "Tell me a joke:", {
n_predict: 256,
temp: 0.9,
callback: fn(token, piece) {
print_inline(piece);
return true; // Continue generating
}
});
llama.context_free(ctx);
llama.model_free(model);
llama.backend_free();
backend_init()- Initialize the llama backend (call once)backend_free()- Free the backend (call once at end)system_info()- Get system info stringsupports_gpu()- Check if GPU offloading is supported
model_load(path, options)- Load a GGUF model file- Options:
n_gpu_layers,use_mmap,use_mlock,split_mode,main_gpu
- Options:
model_free(model)- Free a loaded modelmodel_info(model)- Get model informationmodel_vocab(model)- Get the model's vocabulary pointer
context_create(model, options)- Create an inference context- Options:
n_ctx,n_batch,n_ubatch,n_threads,embeddings
- Options:
context_free(ctx)- Free a contextcontext_info(ctx)- Get context informationset_threads(ctx, n, n_batch)- Set thread counts
tokenize(model, text, options)- Convert text to tokens- Options:
add_special,parse_special
- Options:
detokenize(model, tokens, options)- Convert tokens to text- Options:
remove_special,unparse_special
- Options:
token_to_piece(model, token, special)- Convert single token to textvocab_info(model)- Get vocabulary informationis_eog(model, token)- Check if token is end-of-generation
sampler_create(options)- Create a sampler chain- Options:
top_k,top_p,min_p,temp,repeat_penalty,repeat_last_n,seed
- Options:
sampler_greedy()- Create a greedy samplersampler_free(sampler)- Free a samplersampler_reset(sampler)- Reset sampler statesample(sampler, ctx, idx)- Sample a tokensampler_accept(sampler, token)- Accept a token (for penalties)
sampler_init_grammar(model, grammar_str, root)- Grammar-constrained sampling (GBNF)sampler_init_logit_bias(model, biases)- Logit bias ([{ token, bias }])sampler_init_infill(model)- Fill-in-the-middle samplingsampler_init_top_k(k)- Top-k samplersampler_init_top_p(p, min_keep)- Nucleus samplersampler_init_min_p(p, min_keep)- Min-p samplersampler_init_temp(t)- Temperature samplersampler_init_temp_ext(t, delta, exp)- Dynamic temperaturesampler_init_typical(p, min_keep)- Typical probabilitysampler_init_mirostat(n_vocab, seed, tau, eta, m)- Mirostat v1sampler_init_mirostat_v2(seed, tau, eta)- Mirostat v2sampler_init_penalties(last_n, repeat, freq, presence)- Repetition penaltiessampler_init_greedy()- Greedy selectionsampler_init_dist(seed)- Distribution-based selection
sampler_chain_init()- Create empty sampler chainsampler_chain_add(chain, sampler)- Add sampler to chainsampler_chain_n(chain)- Number of samplers in chainsampler_chain_get(chain, i)- Get sampler at indexsampler_chain_remove(chain, i)- Remove sampler at indexsampler_clone(sampler)- Clone a samplersampler_name(sampler)- Get sampler name
decode(ctx, tokens, pos)- Decode tokensdecode_batch(ctx, tokens)- Decode tokens (simpler interface)get_logits(ctx)- Get logits for last tokenget_logits_ith(ctx, i)- Get logits for token at indexsynchronize(ctx)- Wait for async operations
kv_cache_clear(ctx)- Clear the KV cachekv_cache_seq_rm(ctx, seq_id, p0, p1)- Remove tokens from sequencekv_cache_seq_cp(ctx, src, dst, p0, p1)- Copy a sequencekv_cache_seq_keep(ctx, seq_id)- Keep only one sequencekv_cache_seq_add(ctx, seq_id, p0, p1, delta)- Shift positionskv_cache_seq_pos_min(ctx, seq_id)- Get min positionkv_cache_seq_pos_max(ctx, seq_id)- Get max position
encode(ctx, tokens)- Encode tokens for embedding modelsget_embeddings(ctx)- Get all embeddingsget_embeddings_ith(ctx, i)- Get embedding at indexembed(ctx, model, text)- Convenience: tokenize, encode, extract embeddingpooling_type(ctx)- Get pooling type (none/mean/cls/last)
lora_adapter_init(model, path)- Load a LoRA adapterlora_adapter_free(adapter)- Free a LoRA adapterlora_adapter_set(ctx, adapter, scale)- Apply adapter (scale: 0.0-1.0)lora_adapter_remove(ctx, adapter)- Remove a specific adapterlora_adapter_clear(ctx)- Remove all adapters
state_save(ctx)- Save full context statestate_load(ctx, state)- Restore full context statestate_free(state)- Free saved statestate_seq_save(ctx, seq_id)- Save single sequencestate_seq_load(ctx, state, seq_id)- Restore single sequencestate_get_size(ctx)- Get state buffer size
vocab_get_text(model, token)- Get token textvocab_get_score(model, token)- Get token scorevocab_get_attr(model, token)- Get token attributesis_control(model, token)- Check if control token
model_quantize(input, output, options)- Quantize a model- Options:
ftype,n_threads,allow_requantize,quantize_output_tensor
- Options:
generate(ctx, model, prompt, options)- Generate text with streaming callback- Options:
n_predict,sampler,stop_tokens,callback
- Options:
complete(model_path, prompt, options)- One-shot completion
perf_print(ctx)- Print context performance statsperf_reset(ctx)- Reset performance statsperf_sampler_print(sampler)- Print sampler performance statsperf_sampler_reset(sampler)- Reset sampler performance statstime_us()- Get current time in microseconds
See the examples/ directory:
| Example | Description |
|---|---|
simple.hml |
Basic text generation |
chat.hml |
Interactive chat interface |
oneshot.hml |
One-shot completion using complete() |
streaming.hml |
Streaming generation with performance metrics |
chat_template.hml |
Chat template formatting (ChatML, Llama, etc.) |
grammar.hml |
Grammar-constrained JSON generation |
embeddings.hml |
Text embeddings and similarity comparison |
tokenizer.hml |
Tokenizer/vocabulary exploration |
infill.hml |
Code infill / fill-in-the-middle (FIM) |
model_info.hml |
Model metadata and system inspection |
Run examples:
export LD_LIBRARY_PATH="$(pwd)/lib:$LD_LIBRARY_PATH"
# Basic generation
hemlock examples/simple.hml ~/models/llama-7b.gguf "What is the meaning of life?"
# Interactive chat
hemlock examples/chat.hml ~/models/llama-7b-chat.gguf
# Grammar-constrained JSON output
hemlock examples/grammar.hml ~/models/llama-7b.gguf "Describe a fictional character"
# Text embeddings
hemlock examples/embeddings.hml ~/models/nomic-embed-text.gguf
# Model inspection
hemlock examples/model_info.hml ~/models/llama-7b.ggufMIT License - see llama.cpp for its license terms.