CLI Reference¶
Diapason provides a command-line interface through the diapason command. Built on Click, it offers subcommands for querying models, managing memory, running benchmarks, and serving an OpenAI-compatible API.
Global Options¶
diapason --version # Print the Diapason version
diapason --help # Show top-level help with all subcommands
diapason init¶
Detect local hardware (CPU, GPU, RAM) and generate a configuration file at ~/.diapason/config.toml.
diapason init # Interactive — refuses to overwrite existing config
diapason init --force # Overwrite existing config without prompting
| Option | Description |
|---|---|
--force |
Overwrite existing configuration without prompting |
The init command auto-detects:
- Platform (Linux, macOS, Windows)
- CPU brand and core count
- RAM in GB
- GPU vendor, model, VRAM, and count (via
nvidia-smi,rocm-smi, orsystem_profiler)
Based on the detected hardware, it recommends an appropriate inference engine and writes a pre-configured TOML file.
Example output:
Detecting hardware...
Platform : linux
CPU : AMD Ryzen 9 7950X (32 cores)
RAM : 64 GB
GPU : NVIDIA RTX 4090 (24.0 GB VRAM, x1)
Config written successfully.
diapason ask¶
Send a query to the inference engine (directly or through an agent) and print the response.
Options¶
| Option | Type | Default | Description |
|---|---|---|---|
-m, --model MODEL |
string | auto | Model to use for inference |
-e, --engine ENGINE |
string | auto | Engine backend (ollama, vllm, llamacpp, etc.) |
-t, --temperature TEMP |
float | 0.7 |
Sampling temperature |
--max-tokens N |
int | 1024 |
Maximum tokens to generate |
--json |
flag | off | Output raw JSON result instead of plain text |
--no-stream |
flag | off | Disable streaming (synchronous mode) |
--no-context |
flag | off | Disable memory context injection |
-a, --agent AGENT |
string | none | Agent to use (simple, orchestrator) |
--tools TOOLS |
string | none | Comma-separated tool names to enable |
-i, --image PATH |
path | none | Image file for a vision model (e.g. gemma3:4b); repeatable |
-S, --screen |
flag | off | Capture the current screen and send it to the vision model |
Direct Mode vs Agent Mode¶
Direct mode (default) sends the query straight to the inference engine:
Agent mode routes the query through an agent that can use tools and manage multi-turn interactions:
diapason ask --agent orchestrator "What is 2+2?"
diapason ask --agent orchestrator --tools calculator,think "Calculate sqrt(144) + 3^2"
diapason ask --agent simple "Hello"
Usage Examples¶
# Basic query
diapason ask "What is machine learning?"
# Specify a model
diapason ask -m qwen3:8b "Summarize this concept"
# Use the orchestrator agent with tools
diapason ask --agent orchestrator --tools calculator "What is 15% of 340?"
# Get JSON output
diapason ask --json "Hello"
# Disable memory context injection
diapason ask --no-context "Tell me about Python"
# Set maximum token generation
diapason ask --max-tokens 2048 "Write a detailed essay about AI"
Vision Input¶
Vision-capable models (such as gemma3:4b) can read images alongside your
text prompt. Attach one or more image files with -i/--image, or capture
the current screen with -S/--screen:
# Ask about a local image
diapason ask -i screenshot.png "What is shown in this image?"
# Send multiple images (the flag is repeatable)
diapason ask -i chart-a.png -i chart-b.png "Compare these two charts"
# Capture the current screen and ask about it
diapason ask --screen "Summarize what's on my screen"
Vision runs in direct mode only. If you also pass --agent, the image is
ignored and a note is printed — re-run with --agent "" to force direct mode.
The Ollama context window can be tuned for large images or long prompts with
the DIAPASON_NUM_CTX environment variable (default 16384):
For the server, set it once in config.toml instead — [intelligence]
num_ctx = 32768 — so the desktop chat keeps room for its history after the
10 000-token tool prefix (see docs/development/diagnostic-performances-2026-09-19.md).
The environment variable still wins when both are set.
Weather questions read Environment Canada's 7-day forecast page for the
city named in the question; when no city is named, the chat uses [tools]
ville = "Ottawa" from config.toml (any city of
server/sources_officielles.py's table). With no city configured, the
question must name one — Diapason never guesses a place.
Keep vision on-device
Images are sensitive. Diapason prints a privacy warning before sending
an image to a non-local engine, so a screenshot never leaves your machine
unnoticed. Use a local engine (e.g. ollama with gemma3:4b) to keep
vision fully local.
JSON Output Format¶
When using --json in direct mode, the output includes:
{
"content": "The response text...",
"usage": {
"prompt_tokens": 12,
"completion_tokens": 85,
"total_tokens": 97
}
}
When using --json in agent mode, the output includes:
{
"content": "The response text...",
"turns": 3,
"tool_results": [
{
"tool_name": "calculator",
"content": "51.0",
"success": true
}
]
}
diapason model¶
Manage and inspect language models available on running engines.
diapason model list¶
List all models available from running inference engines, displayed as a Rich table with model parameters, context length, and VRAM requirements.
Example output:
Available Models
┌─────────┬────────────────┬────────┬─────────┬──────┐
│ Engine │ Model │ Params │ Context │ VRAM │
├─────────┼────────────────┼────────┼─────────┼──────┤
│ ollama │ qwen3:8b │ 8B │ 32,768 │ 6GB │
│ ollama │ llama3.2:3b │ 3B │ 8,192 │ 3GB │
└─────────┴────────────────┴────────┴─────────┴──────┘
diapason model info <model>¶
Show detailed information about a specific model.
Example output:
┌─ Qwen 3 8B ──────────────────────────────┐
│ Model ID: qwen3:8b │
│ Name: Qwen 3 8B │
│ Parameters: 8B │
│ Context: 32,768 │
│ Quantization: none │
│ Min VRAM: 6GB │
│ Engines: ollama, vllm │
│ Provider: Alibaba │
│ API Key: not required │
└───────────────────────────────────────────┘
diapason model pull <model>¶
Download a model via Ollama. Shows a progress bar during download.
Note
The pull command requires a running Ollama instance. It connects to the Ollama API at the host configured in your config.toml.
diapason pearl¶
Access Pearl's native node, wallet, and RPC tools from the Diapason CLI.
diapason pearl doctor
diapason pearl node -- <pearld args>
diapason pearl wallet -- <oyster args>
diapason pearl ctl -- <prlctl args>
diapason pearl address
All Pearl wrapper commands use the diapason pearl <command> shape. The
pass-through commands map to Pearl's native binaries:
| Diapason command | Pearl binary | Use |
|---|---|---|
diapason pearl doctor |
n/a | Check whether pearld, oyster, and prlctl are discoverable |
diapason pearl node |
pearld |
Run the Pearl full node |
diapason pearl wallet |
oyster |
Run the Oyster wallet daemon |
diapason pearl ctl |
prlctl |
Query Pearl node or wallet RPC |
diapason pearl address |
prlctl --wallet getnewaddress |
Generate a wallet address from Oyster |
Use PEARL_HOME=/path/to/pearl or --pearl-home /path/to/pearl if Pearl's
bin/ directory is not on PATH. See the Pearl CLI guide for
examples.
diapason memory¶
Manage the document memory store for retrieval-augmented generation.
diapason memory index <path>¶
Index documents from a file or directory into the memory store.
diapason memory index ./docs/
diapason memory index ./notes.md
diapason memory index ./data/ --chunk-size 256 --chunk-overlap 32
diapason memory index ./docs/ --backend sqlite
| Option | Type | Default | Description |
|---|---|---|---|
--backend, -b |
string | config | Override the default memory backend |
--chunk-size |
int | 512 |
Chunk size in tokens |
--chunk-overlap |
int | 64 |
Overlap between chunks in tokens |
The ingestion pipeline supports text, markdown, code files, and PDF (with pdfplumber installed). Binary files and hidden directories are automatically skipped.
diapason memory search <query>¶
Search the memory store for relevant document chunks.
diapason memory search "machine learning basics"
diapason memory search -k 10 "neural networks"
diapason memory search --backend faiss "embeddings"
| Option | Type | Default | Description |
|---|---|---|---|
--top-k, -k |
int | 5 |
Number of results to return |
--backend, -b |
string | config | Override the default memory backend |
Results are displayed in a table with rank, score, source file, and a content preview.
diapason memory stats¶
Show memory store statistics including document count and database size.
| Option | Type | Default | Description |
|---|---|---|---|
--backend, -b |
string | config | Override the default memory backend |
diapason telemetry¶
Query and manage inference telemetry data stored in SQLite.
diapason telemetry stats¶
Show aggregated telemetry statistics including total calls, tokens, cost, and latency, broken down by model and engine.
| Option | Type | Default | Description |
|---|---|---|---|
-n, --top |
int | 10 |
Number of top models to show |
diapason telemetry export¶
Export raw telemetry records in JSON or CSV format.
diapason telemetry export # JSON to stdout
diapason telemetry export --format csv # CSV to stdout
diapason telemetry export --format json -o data.json # JSON to file
diapason telemetry export -f csv -o metrics.csv # CSV to file
| Option | Type | Default | Description |
|---|---|---|---|
-f, --format |
choice | json |
Output format: json or csv |
-o, --output |
path | stdout | Output file path |
diapason telemetry clear¶
Delete all telemetry records from the database.
diapason telemetry clear # Interactive confirmation
diapason telemetry clear --yes # Skip confirmation
| Option | Type | Default | Description |
|---|---|---|---|
-y, --yes |
flag | off | Skip confirmation prompt |
Warning
This permanently deletes all stored telemetry data. Use --yes to skip the confirmation prompt in automated scripts.
diapason bench¶
Run inference benchmarks against a running engine.
diapason bench run¶
Execute benchmarks and report results.
diapason bench run # Run all benchmarks, 10 samples
diapason bench run -n 20 # 20 samples per benchmark
diapason bench run -b latency # Only the latency benchmark
diapason bench run -b throughput -n 50 --json # Throughput, 50 samples, JSON output
diapason bench run -o results.jsonl # Write JSONL results to file
diapason bench run -m qwen3:8b -e ollama # Specific model and engine
| Option | Type | Default | Description |
|---|---|---|---|
-m, --model MODEL |
string | auto | Model to benchmark |
-e, --engine ENGINE |
string | auto | Engine backend |
-n, --samples N |
int | 10 |
Number of samples per benchmark |
-b, --benchmark NAME |
string | all | Specific benchmark to run |
-o, --output PATH |
path | none | Write JSONL results to file |
--json |
flag | off | Output JSON summary to stdout |
Available benchmarks:
- latency -- Measures per-call inference latency (mean, p50, p95, min, max)
- throughput -- Measures tokens-per-second throughput
diapason channel¶
Manage messaging channels for multi-platform communication. Channels connect directly to platform APIs (Telegram, Discord, Slack, etc.) -- no gateway required.
diapason channel list¶
List registered channel backends and their connection status.
diapason channel send¶
Send a message to a specific channel.
| Argument | Type | Description |
|---|---|---|
TARGET |
string | Channel name to send to |
MESSAGE |
string | Message content |
diapason channel status¶
Show connection status for configured channels.
Channel Dependencies
Each channel requires its platform-specific credentials (bot tokens, API keys) configured in the [channel.<platform>] section of your config. See Configuration for details.
diapason serve¶
Start an OpenAI-compatible API server.
diapason serve # Default host/port from config
diapason serve --port 8000 # Custom port
diapason serve --model qwen3:8b # Specify default model
diapason serve --agent orchestrator # Route requests through an agent
# Let your other devices reach this machine — mesh routes only, on a
# second socket. The full application stays on 127.0.0.1.
diapason serve --lan-host 0.0.0.0 # Mesh on 0.0.0.0:8001
diapason serve --lan-host 0.0.0.0 --lan-port 8123 # …on another port
| Option | Type | Default | Description |
|---|---|---|---|
--host HOST |
string | config | Bind address for the full application |
--port PORT |
int | config | Port number |
--lan-host HOST |
string | none | Listen address of the mesh alone, on a second socket (e.g. 0.0.0.0). Omitted: no second socket at all |
--lan-port PORT |
int | 8001 |
Port of that second socket. Must differ from --port |
-e, --engine ENGINE |
string | auto | Engine backend |
-m, --model MODEL |
string | config | Default model for inference |
-a, --agent AGENT |
string | none | Agent for non-streaming requests |
Server Dependencies
The serve command requires the server extra:
This installs FastAPI, uvicorn, and related dependencies.
Two sockets, one process¶
--host carries the whole application and is meant to stay on 127.0.0.1.
--lan-host opens a second socket that mounts nine mesh routes and nothing
else (create_lan_app in src/diapason/server/app.py):
| Method | Path | Description |
|---|---|---|
| POST | /v1/mesh/pairings/redeem |
Redeem an invitation code |
| POST | /v1/mesh/commands/deliver |
Deliver a command to this device |
| POST | /v1/mesh/commands/poll |
Poll for commands addressed here |
| POST | /v1/mesh/commands/ack |
Acknowledge a command |
| POST | /v1/mesh/presence |
Presence beat from a peer |
| POST | /v1/mesh/files/offer |
Offer a file, open a session |
| POST | /v1/mesh/files/{session_id}/chunk |
Push one encrypted chunk |
| POST | /v1/mesh/files/{session_id}/finish |
Verify the digest, reveal the file |
| POST | /v1/mesh/files/{session_id}/status |
Which chunks are still missing (resume) |
A few consequences worth knowing before you open that port:
POST /v1/chat/completionson the mesh socket answers 404, not 401 — the route is not mounted there at all. Chat, speech and Succès stay on--host.- Those nine routes authenticate by Ed25519 device signature, invitation or session token — not by the local API key.
- The second socket serves no
/docs,/redocor OpenAPI schema. - Both sockets run in a single process: the command inbox and the transfer sessions live in memory, and two processes would lose them.
--lan-portequal to--portis refused before anything starts (exit code2): on macOS both would bind silently, on Linux the second would fail.- When a second socket exists, it is the address peers are told to use — the
mesh beacon announces
--lan-host:--lan-port, not the loopback pair.
Startup prints the mesh line on its own line, before the Starting Diapason API server block:
--host 0.0.0.0 still exists and still puts the entire API on the network,
protected only by the local API key. Prefer --lan-host unless you genuinely
want every route reachable.
API Endpoints¶
The server exposes the following OpenAI-compatible endpoints:
| Method | Path | Description |
|---|---|---|
| POST | /v1/chat/completions |
Chat completions (streaming & non-streaming) |
| GET | /v1/models |
List available models |
| GET | /health |
Health check |
| GET | /v1/channels |
List available messaging channels |
| POST | /v1/channels/send |
Send a message to a channel |
| GET | /v1/channels/status |
Channel bridge connection status |
Example with curl:
curl http://localhost:8000/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "qwen3:8b",
"messages": [{"role": "user", "content": "Hello!"}]
}'
When an agent is configured (e.g., --agent orchestrator), non-streaming requests are routed through the agent with access to all registered tools. For tool-capable agents (orchestrator, react, openhands), all registered tools are automatically loaded and made available.
diapason serve-service¶
Keep the API server running at login, as a macOS LaunchAgent. All subcommands exit with an error on any other platform.
diapason serve-service install # 127.0.0.1:8000, at every login
diapason serve-service install --maillage-reseau # …plus the mesh on 0.0.0.0:8001
diapason serve-service status
diapason serve-service restart
diapason serve-service logs --lines 100
diapason serve-service uninstall
diapason serve-service install¶
| Option | Type | Default | Description |
|---|---|---|---|
--host HOST |
string | 127.0.0.1 |
Bind address of the full application. A non-loopback value is refused |
--port PORT |
int | 8000 |
Port of the full application |
--maillage-reseau |
flag | off | Also open the mesh socket on 0.0.0.0 |
--lan-port PORT |
int | 8001 |
Port of that mesh socket |
--maillage-reseau adds --lan-host 0.0.0.0 --lan-port <port> to the serve
command line written into the plist — the nine mesh routes, device signature
required. The full application stays on the loopback either way. Installing
prints a warning first, because any machine on your network will be able to
reach that port.
--allow-network is gone
The old --allow-network put the entire API on the network. It no
longer installs anything: the command fails, names --maillage-reseau as
its replacement, and exits non-zero. It fails rather than aliasing quietly
— the same command must not start doing something else without saying so.
Other refusals, all before anything is installed:
--hostanywhere but127.0.0.1,localhostor::1.--lan-portequal to--portwhen--maillage-reseauis set.--portalready served by something that is not this agent. Installing means launching (the plist carriesRunAtLoad), so a second server on the same port would be a silent duplicate. Re-installing over Diapason's own service is allowed — launchd replaces a job of the same label.
status reports four separate facts: whether the LaunchAgent is loaded, where its plist lives,
whether http://127.0.0.1:8000/health actually answers, and where the logs
are. Loaded is not the same as answering. See the
launchd deployment guide for the plist itself.
diapason mesh¶
The fleet of paired devices: join one, see who is in it, send a file to it. Pairing is invitation-based — the host machine shows a code under Appareils → Ajouter un appareil (the Devices page), and the guest redeems it here.
diapason mesh join¶
Join another Diapason's fleet.
diapason mesh join 192.168.0.5:8000 ABCD-1234
diapason mesh join 192.168.0.5:8001 ABCD-1234 --address 192.168.0.9:8001
| Argument | Type | Description |
|---|---|---|
HOST |
string | The other machine's address, e.g. 192.168.0.5:8000 |
TOKEN |
string | The invitation code it shows in Appareils → Ajouter un appareil |
| Option | Type | Default | Description |
|---|---|---|---|
--address ADDR |
string | guessed | The address this device is reachable at |
On success it prints the host's fleet identity, its device id, its address,
and the capabilities granted so far — or rien pour l'instant when none were,
rather than letting you assume a permission you did not receive. A refused
pairing exits 1.
HOST may be the mesh socket as well as the main one: pairings/redeem is one
of the nine routes carried there. A device whose registry already holds at least one
non-revoked device — whatever its trust level — refuses to join a different
fleet; so does one whose registry cannot be read at all. Adopting another
fleet identity would lose every peer it has at once, so the command stops
and you decide.
diapason mesh devices¶
List the devices of the fleet, with their presence.
Columns: name, platform, trust level, presence, device id.
diapason mesh send¶
Send a file to a device of the fleet.
| Argument | Type | Description |
|---|---|---|
FICHIER |
path | The file to send. Must exist and not be a directory |
APPAREIL |
string | The target, designated the way the mesh resolver reads it: by name ("PC du bureau") or by kind ("mon téléphone"). A device id is not accepted — the resolver matches names, device types and platforms only |
The command takes no options. What it does, in order:
- Reads the registry and keeps only
TRUSTEDdevices. With none paired it says so and exits1. - Resolves
APPAREILthrough the mesh resolver. A phrase that designates two devices is refused, never settled at random; so is one that matches nothing, or that means this very machine. The resolver's own sentence is printed verbatim and the command exits1. - Prints the file name and its human-readable size, then a
n/total morceauxcounter as the chunks go out. - Prints the receiver's message: green when the transfer landed
(
COMPLETE, orALREADY_PRESENTwhen content-deduplication found the file already there, whole and verified), yellow for any other status. When the receiver reports a path, it is shown aschez <device> : <path>.
A refusal from the recipient or the transport is relayed as it stands, in red,
with exit code 1 — it knows why, the sender does not.
This is the first production caller of the file-transfer core: the peer must
be reachable, which on a real network means it runs diapason serve with
--lan-host (see Two sockets, one process).
diapason mesh whoami¶
Print this device's identity in the fleet: device id and name, platform, fleet (owner) id, and the public key. The private key appears nowhere.
LLM-guided spec search (no CLI yet)¶
LLM-guided spec search (the frontier-driven harness-learning subsystem)
is exposed as a Python library only — there is currently no top-level
diapason subcommand for it. Construct a SpecSearchOrchestrator
directly from diapason.learning.spec_search.orchestrator and call
.run(trigger) with a trigger from
diapason.learning.spec_search.triggers. See
docs/user-guide/llm-guided-spec-search.md
for the architecture and the building blocks
(splits.py, external corpora, external_adapter).