Skip to documentation

Self-hosting

Providers & keys

eli.ai needs at least one model provider to do anything intelligent. You can wire providers globally (env keys) or per workspace (Settings), point at any OpenAI-compatible endpoint, or run entirely local through Ollama. The conceptual model — slots, precedence, allowlists — is in Multi-provider AI.

Frontier providers via env keys

Set any of these before first boot and eli.ai seeds a provider plus default model slots automatically:

.envbash
OPENAI_API_KEY=sk-…
ANTHROPIC_API_KEY=sk-ant-…
GOOGLE_GENERATIVE_AI_API_KEY=

Seeding is idempotent: one ai_providers row per key present, with slots filled from pinned dated defaults (agent/chat on a top-tier model, extraction/judge on a fast tier). No env keys → no rows, and the UI shows its setup state instead. You can also (re)seed from the app via POST /api/w/:workspaceId/providers/seed.

Per-workspace keys (recommended for engagements)

Workspace keys beat env keys

A workspace-stored credential always winsover the env var. This is deliberate: it stops a client's workload from being billed to the consultant's personal key, and stops a scheduled job from shipping a regulated client's data to a provider that workspace forbids.
  1. Open workspace Settings

    Add a credential for the provider under the workspace's AI settings.
  2. Store the key

    It's encrypted AES-256-GCM under a per-workspace data key. The store is write-only — you'll see a last-4 for identification, never the full key again. Endpoint: POST /api/w/:workspaceId/providers/:providerId/credential.
  3. Enforce residency (optional)

    The per-workspace provider allowlist only resolves enabled providers. Disable the ones a regulated workspace must never use — enforcement is in the routing layer, not convention.

OpenAI-compatible endpoints

Any endpoint that speaks the OpenAI API — vLLM, Together, LM Studio, a corporate gateway — is added by base URL. Two manual capability toggles declare whether it supports tool-calling and JSON-schema output (there is no auto-probe; it misclassifies and rots when the endpoint restarts with different flags).

add a custom providerbash
curl -X POST https://your-host/api/w/$WS/providers \
  -H "Content-Type: application/json" \
  -d '{"name":"vllm-gateway","baseUrl":"https://gw.internal/v1","supportsTools":true,"supportsJsonSchema":true}'
# then bind a slot:
curl -X PUT https://your-host/api/w/$WS/providers/slots \
  -H "Content-Type: application/json" \
  -d '{"slot":"agent","providerId":"…","modelId":"my-model"}'

Agent slots need real tool-calling

Tool calling is never emulated. The UI blocks a non-tool-capable model from the agent slot rather than faking ReAct. Set the agent slot to a model that reliably emits structured tool calls.

The Ollama local-dev bridge

For fully-offline development, the one-click Ollama bridge wires an openai_compatible provider at localhost:11434/v1 (tools + JSON-schema toggles on) and assigns local models to the slots. It is a development convenience, not a production tier.

local models, no API costbash
ollama pull llama3.3          # agent slot — reliable tool-calling
ollama pull qwen3-coder:30b   # chat / extraction
ollama pull nomic-embed-text  # embeddings (vector retrieval)

# then in the app, enable the Ollama bridge preset (Settings), or:
curl -X POST https://your-host/api/w/$WS/providers/ollama-bridge

Model note: agents need reliable structured tool-calling through the OpenAI-compatible endpoint. llama3.3 and gpt-oss parse cleanly via Ollama; some models occasionally leak text-format tool calls under multi-tool prompts — set the agent slot to a known-good tool-caller. Numeric tool inputs are coerced, so models that stringify numbers still work.

Verifying

If a slot has no enabled provider, calls throw NotConfiguredError → the UI shows a setup screen and the API returns 409 no-model-configured. Bind the slot and retry. Watch spend under Cost & schedules.