Browse documentation

Model Providers

The routing lane makes exactly one kind of outbound call: an OpenAI-compatible chat completion. Which endpoint receives it is configuration, not a compiled-in fact. A provider is selected by id in routing.yaml, and an endpoint the built-in registry has never heard of works too, as long as your config supplies its base URL and the name of the environment variable holding its key.

ebrain providers list          # every known provider, and whether its key is present
ebrain providers show openai   # one provider's endpoint, key names, and metering

providers list reports presence, never value: a key is shown as set or not set. eBrain does not print, log, or transmit credential values, and a provider key is referenced everywhere by its environment-variable name.

Selecting a provider

routing.yaml lives in your config directory ($XDG_CONFIG_HOME/ebrain/routing.yaml, defaulting to $HOME/.config/ebrain/routing.yaml). ebrain up materializes it from config/routing.default.yaml the first time and never touches it again — once it is yours, it is yours.

provider:
  id: openrouter                              # selects a registry descriptor
  base_url: https://openrouter.ai/api/v1      # override, or supply for an unknown id
  key_env: OPENROUTER_API_KEY                 # an env var NAME; the value is read at call time

Changing providers is changing id. A config written before provider ids existed is matched back to its descriptor through base_url, so upgrading requires no edit.

What a descriptor carries

FieldWhy it matters
base_url, chat_pathWhere the completion request goes.
key_envThe environment variable names that may hold the key, in order of preference.
meteringWhether usage is reported by the provider, estimated locally, or local (no cost).
cost_pathWhere in the response body the reported cost is found, when there is one.
server_side_failoverWhether the provider accepts a model list and fails over itself.
extra_body_keysProvider-specific request fields it is known to understand.

Two of those change behavior in ways worth knowing about.

Failover. Some providers accept an array of models and pick the first that is available. Most expect a single model and reject an array. eBrain uses the provider’s own failover where it exists and walks the capability chain locally where it does not, so a fallback chain means the same thing on every endpoint rather than being an empty promise on most of them.

Request extras. provider.provider_routing is merged into the request body, and those fields are provider-specific by nature. When the selected provider is known not to understand them, they are left out and the omission is reported on stderr — a config copied between providers neither fails with an opaque 400 nor loses a privacy preference in silence. For an id the registry does not know, your config is the only authority on that endpoint, so its extras are sent as written.

Spend attribution

Every routed call is recorded with the provider that served it, and the monthly cap is measured against that lane. Switching providers therefore does not silently measure new spend against an old provider’s total.

ebrain spend --json     # routed spend against the local cap
ebrain cost --json      # factual token and usage attribution, grouped by provider

The cap is enforced before the call when budget.hard_stop is true. The config is validated on read: a malformed monthly_usd is an error naming the line, not a silently disabled cap.

Boundaries

See routing for the task-signal flow, profiles and targets for launch-time selection, and token telemetry for what the cost view reports.

Documentation

Search eBrain docs

Esc close CtrlK open