Getting started¶
Installation¶
Wallah is built from source with Cargo. The toolchain version is pinned by
rust-toolchain.toml, so a recent stable Rust (installed via
rustup) is all you need to get started.
A debug build lands at target/debug/wa and a release build at
target/release/wa. Copy that binary somewhere on your PATH (or add the
directory to PATH) so you can invoke it as wa.
Optional dependencies¶
- ripgrep (
rgonPATH) is recommended: thegrepandfindtools are only offered to the model when it is installed. - rtk is recommended if you want to cut token usage on shell commands (see Reducing token usage with rtk).
- The
claudeCLI is required only for theclaude-codeprovider, which runs under your Claude subscription. The nativeanthropicprovider talks to the Anthropic API directly with an API key and does not need it.
Configuring providers¶
Wallah reads its configuration from ~/.config/wallah/config.lua. This is a
shingetsu script which allows you to flexibly define
your configuration in code.
The config defines a
discover_providers event handler
to register the providers you want to use, and optionally names a default
provider, default model, and a set of favourite models to float to the top of
the /model picker.
A minimal config that wires up Claude Code, an OpenAI-compatible endpoint, and a local Ollama server:
agent.on('discover_providers', function(inventory)
-- Claude Code, running under your Claude subscription via the `claude` CLI.
inventory:add_claude_code {
name = 'claude-code',
billing = 'subscription',
}
-- The native Anthropic API, talking to the endpoint directly with an API
-- key. Unlike claude-code, this needs no `claude` CLI. Source the key from
-- the environment rather than hard-coding it.
inventory:add_anthropic {
name = 'anthropic',
api_key = os.getenv('ANTHROPIC_API_KEY'),
}
-- Any OpenAI-compatible endpoint (OpenAI itself, OpenRouter, vLLM, ...).
-- Source the key from the environment rather than hard-coding it.
inventory:add_openai {
name = 'openai',
base_url = 'https://api.openai.com/v1',
api_key = os.getenv('OPENAI_API_KEY'),
-- Optional: how long to tolerate the endpoint going silent mid-stream
-- before ending the turn with an error. A number of seconds, 'none' to
-- wait indefinitely, or 'default' (the default, 300s). Slow local or
-- long-context endpoints may want a larger value.
timeout = 300,
-- Optional: per-turn diagnostics are recorded under the data dir
-- (a `diagnostics/` sibling of `sessions/`) to help investigate a stuck
-- or misbehaving endpoint. On by default; set false to opt out.
diagnostics = true,
-- Optional: reuse HTTP connections across requests (keep-alive). On by
-- default, which suits a remote endpoint where a fresh connection costs a
-- TCP and TLS handshake. Set false for a local server that drops idle
-- connections quickly (llama-server closes one after a few seconds): reuse
-- can then send on a connection the server has already closed, and the
-- per-request handshake it avoids is negligible over a LAN.
http_keep_alive = true,
-- Optional: per-model overrides, keyed by the model id the endpoint
-- advertises. Set `context_window` for a local server (such as
-- llama-server) whose usable window differs from the public catalog, or
-- for a model the catalog does not know. `temperature` and `max_tokens`
-- supply defaults for turns that do not set their own; `top_p`, `top_k`,
-- `min_p`, `presence_penalty`, `frequency_penalty`, and `repetition_penalty`
-- have no per-turn equivalent and apply to every turn. `top_k`, `min_p`, and
-- `repetition_penalty` are outside the OpenAI schema: they are sent for
-- compatible servers (vLLM, llama.cpp) that honor them and ignored by those
-- that do not. These suit a local deployment whose preferred sampling
-- differs from the server's own defaults.
model_options = {
['gpt-oss'] = {
context_window = 32768,
temperature = 0.7,
top_p = 0.8,
top_k = 20,
min_p = 0.0,
-- Presence and frequency penalties (OpenAI schema, -2 to 2) and the
-- repetition penalty (vLLM/llama.cpp) curb the repetition some local
-- models fall into. Each has a neutral value that does nothing (0 for
-- presence and frequency, 1.0 for repetition); move a knob away from it
-- to apply that penalty, as presence_penalty does here.
presence_penalty = 1.5,
frequency_penalty = 0.0,
repetition_penalty = 1.0,
max_tokens = 2048,
-- Optional: whether to ask the server for parallel tool calling.
-- `true` enables it, `false` forces sequential calls, `nil` (the
-- default) omits the field entirely. Set `true` for local servers
-- (vLLM, llama.cpp) that default to sequential tool calls. Leave as
-- `nil` for OpenAI reasoning models (o1, o3) that reject this parameter.
parallel_tool_calls = true,
},
},
}
-- OpenRouter is also just an OpenAI-compatible endpoint: point `base_url` at
-- its API and supply your OpenRouter key.
inventory:add_openai {
name = 'openrouter',
base_url = 'https://openrouter.ai/api/v1',
api_key = os.getenv('OPENROUTER_API_KEY'),
}
-- Google Gemini exposes an OpenAI-compatible endpoint, so it too is just an
-- `add_openai` provider: point `base_url` at Gemini's OpenAI-compatible API
-- and supply your Gemini key. Models are named as usual, e.g.
-- `gemini/gemini-2.5-pro` or `gemini/gemini-2.5-flash`.
inventory:add_openai {
name = 'gemini',
base_url = 'https://generativelanguage.googleapis.com/v1beta/openai',
api_key = os.getenv('GEMINI_API_KEY'),
}
-- A local Ollama server. A bare local instance needs no api_key.
inventory:add_ollama {
name = 'ollama',
base_url = 'http://localhost:11434',
-- Optional: reuse HTTP connections across requests (keep-alive). On by
-- default. Set false for a server that drops idle connections quickly, so a
-- reused connection is not sent on after the server has closed it. This is
-- the transport connection, distinct from a model's keep-alive residency.
http_keep_alive = true,
}
-- Optional: pick the default provider and model used when `--provider` and
-- `--model` are omitted. If unset, the first provider added is the default.
inventory:set_default_provider('claude-code')
inventory:set_default_model('claude-code/claude-sonnet-4-6')
-- Optional: `provider/model` references to float to the top of `/model`.
inventory:add_favourite_models {
'claude-code/claude-sonnet-4-6',
'openai/gpt-4o',
'ollama/llama3.1:8b',
}
end)
os.getenv above is the standard Lua os module.
Each add_* call is a method on the
Inventory handed to
the handler, and accepts an optional billing field, either 'subscription'
or 'metered', which only affects how cost is displayed. It defaults to
'metered' everywhere except Ollama, where a local server defaults to
'subscription'.
Wallah does not create the config for you, and it reports an error if the file
is missing, so on a fresh install create ~/.config/wallah/config.lua (and its
parent directory) yourself before the first run. Once it is in place, wa
list-providers is a quick way to confirm it loads.
With a config in place, launch the interactive TUI by running wa.