lulum is a lightweight local LLM shell with one interface for multiple engines.
On macOS 26+ with Apple Silicon, it can work out of the box with the built-in
Apple Intelligence model via FoundationModels. It can also talk to engines like
Ollama and MLX behind the same interactive CLI.
The goal is simple: one terminal-first chat shell, one command style, and easy switching between local backends.
Requires uv.
Install globally:
uv tool install lulum
lulum # then run itUpgrade:
uv tool upgrade lulum # or: lulum --updateTry without installing:
uvx lulumUninstall:
uv tool uninstall lulumgit clone https://github.com/rdubar/lulum
cd lulum
uv sync --group dev
uv run lulum
uv run pytest -q
uv run ruff check .uv sync commands only work from inside a cloned lulum project directory that
contains pyproject.toml. If you run uv sync --extra mlx from ~ or another
unrelated folder, uv will fail because there is no project there.
- uv — Python package manager
- At least one inference engine installed:
lulumThen inside the shell:
/models
/use ollama:llama3.2
Hello!
If Ollama is installed and running but has no local models yet, lulum will
tell you and suggest a pull command like:
ollama pull llama3.2:1bWhen you load an Ollama model, lulum checks that the model is pulled locally
before marking it ready. If Ollama reports llama3.2:latest, you can still use
the shorter /use ollama:llama3.2 form.
$ lulum
Loading ollama:gemma4:e2b...
Ready.
Auto-selected ollama:gemma4:e2b
lulum v0.1.2
Engines: apple (ready), ollama (ready), mlx (not available)
Active: ollama:gemma4:e2b
> Hello!
Hi there! How can I help you today?
lulum remembers the last model you used and restores it automatically on the
next launch. If that model is unavailable, it falls back to auto-selection.
Ollama models are lightly warmed during /use or startup restore, so missing or
slow-starting models fail early with a specific hint instead of failing on the
first chat message.
lulum -m ollama:llama3.2:1b -c "Explain quicksort in one sentence"If you cloned the repo and want MLX support in the local development environment:
cd ~/dev/lulum # or wherever you installed/cloned lulum
uv sync --extra mlx
uv run lulumIf you are using the globally installed lulum tool, uv sync --extra mlx is
not the right command, because it only works inside the repo. In that case,
reinstall or upgrade the global tool with MLX support separately.
lulum # interactive shell
lulum --update # upgrade the installed tool via uv
lulum -m ollama:llama3.2 # start with a model loaded
lulum -c "prompt" # one-shot (requires -m)
lulum engines # list available engines
lulum models # list available models
lulum --credits # show credits and project infolulum --update # upgrade the globally installed lulum tool
lulum --credits # show version, project URL, author, and license| Command | Description |
|---|---|
/use engine:model |
Load a model (e.g. /use ollama:llama3.2) |
/reset |
Switch to the default auto-selected model |
/engine |
Show the active engine and model |
/engines |
List engines, status, and available models |
/models |
List all available models |
/update |
Upgrade the installed tool via uv |
/history |
Show saved conversation history |
/clear |
Clear saved chat + input history |
/clear chat |
Clear only saved chat history |
/clear input |
Clear only prompt history |
/version |
Show version |
/credits |
Show credits, repo URL, and license |
/help |
Show help |
/quit |
Exit |
Inside the interactive shell:
/update # upgrade lulum via uv
/credits # show version, project URL, author, and license
lulum stores history locally per user in ~/.local/state/lulum/.
last_model.txtrecords the last model you used so it is restored automatically on the next launch. Override with-m engine:modelor switch with/use.chat_history.jsonstores the conversation that/historyshows and that can be restored when you reopen the shell. Saved chat history is restored only when it matches the active model.input_history.txtstores prompt history so the up/down arrow keys can recall previous lines across launches.
Use /clear to wipe both, /clear chat to remove only conversation history, or
/clear input to remove only prompt history.
Via Ollama — install a model with ollama pull <name>, then load it in lulum with /use ollama:<name>:
| Model | Command | Notes |
|---|---|---|
| Gemma 3 (Google) | ollama pull gemma3 |
Free, open-source, strong general performance |
| Llama 3.2 (Meta) | ollama pull llama3.2 |
Good all-rounder; 1b/3b/11b sizes |
| Mistral | ollama pull mistral |
Fast, efficient 7B model |
| Phi-4 (Microsoft) | ollama pull phi4 |
Small but capable reasoning model |
| Qwen 2.5 (Alibaba) | ollama pull qwen2.5 |
Strong coding and multilingual |
| Apple Intelligence | built-in | macOS 26+ with Apple Silicon, no download |
Browse the full catalogue at ollama.com/library.
| Engine | Status | Platform | Notes |
|---|---|---|---|
| Apple | Working | macOS 26+ / AS | Built-in Apple Intelligence, no setup needed |
| Ollama | Working | macOS/Linux/Win | Requires ollama installed and running |
| MLX | Scaffolded | Apple Silicon | uv sync --extra mlx |
| BitNet | Planned | macOS/Linux | 1-bit LLM inference |
Engines degrade gracefully — lulum auto-detects what's available at startup,
retries Ollama briefly if it is still coming up, and distinguishes between
"engine unavailable" and "engine is ready but has no discovered models yet."
For Ollama, /use also validates that the requested model is pulled locally,
accepts the common :latest shorthand, and performs a short warmup check before
declaring the model ready.
src/lulum/
├── __main__.py # entry point
├── cli.py # argument parsing
├── shell.py # interactive REPL
├── config.py # settings (TOML)
└── engine/
├── base.py # abstract Engine interface
├── apple.py # Apple Intelligence (FoundationModels, macOS 26+)
├── ollama.py # Ollama backend
└── mlx.py # MLX backend
All engines implement a common async interface — generate() returns an AsyncIterator[str] for streaming tokens. Adding a new engine means implementing one class with five methods.
Optional config file at ~/.config/lulum/config.toml:
[default]
engine = "ollama"
model = "llama3.2:1b"
[engines.ollama]
host = "http://localhost:11434"
[engines.bitnet]
path = "~/dev/lulum/engine/bitnet"Use either /credits inside the shell or lulum --credits in your terminal to
show the project URL, maintainer, and license.
Use either /update inside the shell or lulum --update in your terminal to
upgrade the tool.
MIT