LLM adapter

July 4, 2026 · View on GitHub

The LLM adapter is the only networked part of the whole workspace. It lives in its own crate, crates/copilot-llm, and is reachable only from the CLI's ask subcommand — never over the C ABI, so the language bindings cannot touch the network or a key. The deterministic core (GROUNDING.md) has no dependency on it.

How it works

  1. The core builds a deterministic MarketContext (see FACTS.md).
  2. render_prompt(context) turns that context into a fixed prompt — the facts and their human sentences, plus the question.
  3. A Provider preset selects one OpenAI-compatible chat endpoint, and a single OpenAiCompatible HTTP client drives it.
  4. The model's answer is returned alongside the context and the tool calls that were routed for the question.

One HTTP implementation drives every provider; the preset only changes the base URL, the default model and whether an API key is required.

Providers

Choose with --provider on the CLI (default ollama). Every preset except custom carries a default base URL and model; custom takes both from the environment.

PresetDefault base URLDefault modelAPI key
ollamahttp://localhost:11434/v1llama3none (local)
openaihttps://api.openai.com/v1(OpenAI default)required
claudehttps://api.anthropic.com/v1claude-3-5-sonnet-latestrequired
geminihttps://generativelanguage.googleapis.com/v1beta/openaigemini-1.5-flashrequired
customWICKRA_COPILOT_BASE_URLWICKRA_COPILOT_MODELas needed

All four hosted presets are reached through their OpenAI-compatible endpoints, so one client covers them. ollama runs fully offline and needs no key.

Environment

The adapter reads exactly three environment variables; nothing is hard-coded and nothing is written to disk:

VariablePurpose
WICKRA_COPILOT_API_KEYthe bearer key. Empty is allowed (e.g. Ollama). Never logged, never in Debug output.
WICKRA_COPILOT_BASE_URLoverride the preset's base URL (required for custom).
WICKRA_COPILOT_MODELoverride the preset's default model.

The key is your own, read from the environment at call time, and used only to authenticate the single request to the endpoint you chose. There is no SaaS, no telemetry, no proxy — the request goes straight from your machine to your provider.

Usage

# Local, offline, no key:
cargo run -p wickra-copilot -- ask \
  --spec golden/specs/dump.json --feeds golden/feeds \
  --question "Why did BTC just dump?" --provider ollama

# A hosted provider with your own key:
export WICKRA_COPILOT_API_KEY="sk-…"
cargo run -p wickra-copilot -- ask \
  --spec golden/specs/dump.json --feeds golden/feeds \
  --question "Why did BTC just dump?" --provider openai

Prefer to keep everything deterministic and offline? Use the context subcommand instead of ask — it prints the facts and never calls a model.

Why it is separate

Keeping the adapter out of the core and off the C ABI means:

  • the golden-tested surface has no network and no key to leak;
  • a security review of the bindings never has to reason about outbound requests;
  • the provider can be swapped (or the whole adapter removed) without touching a single fact.

The adapter is read-only: it reads market context and asks a question. It never places an order. Its output is not financial advice and can be wrong — only the facts under it are pinned.

See also

Grounding · Tool calling · Architecture.