Bring your own model
Run the agent on Anthropic, OpenAI, Google, any OpenAI-compatible endpoint, or no model at all.
The tools are deterministic TypeScript. The model only plans, calls tools and writes the answer, so you can plug in any engine.
Pick a provider
Set these variables in next/.env.local (gitignored) and restart:
| Variable | Values |
|---|---|
AI_PROVIDER | anthropic, openai, google, openai-compatible or none. With only AI_API_KEY set, Anthropic is assumed |
AI_MODEL | The model id. Defaults to claude-opus-5-5 for Anthropic; required for the others |
AI_API_KEY | Your key. Optional for a local openai-compatible server |
AI_BASE_URL | Required for openai-compatible; optional to route the others through a gateway |
AI_EFFORT | low (default), medium or high |
AI_MAX_STEPS | Tool-loop steps per turn (default 12) |
AI_FALLBACKS | off disables Anthropic server-side fallbacks |
Examples
# Anthropic (default model claude-opus-5-5, low effort)
AI_PROVIDER=anthropic
AI_API_KEY=...
# A local model through Ollama or LM Studio
AI_PROVIDER=openai-compatible
AI_BASE_URL=http://127.0.0.1:11434/v1
AI_MODEL=your-local-model
# Any OpenAI-compatible gateway (OpenRouter, vLLM, a company gateway)
AI_PROVIDER=openai-compatible
AI_BASE_URL=https://your-gateway.example/v1
AI_API_KEY=...
AI_MODEL=provider/model-idSmaller local models may call tools less reliably. The golden questions in evals/fixtures are meant for comparing engines on the same questions once the live suite is built.
Or set it in the admin panel
A superuser can set the provider, model id, API key, base URL, maximum steps and Anthropic fallbacks in Admin › Model. Each field set there overrides its environment variable; "Use environment default" goes back to the env value. Changes apply to new chats within a few seconds, without a restart.
- Storing a key needs
SETTINGS_ENCRYPTION_KEYon the server (base64 of 32 random bytes, or 64 hex characters). Keys are encrypted with AES-256-GCM and never shown again: the form says only whether a key is stored. - Keys stay with their address.
AI_API_KEYfrom the environment is used only while the provider and the base URL also come from the environment (or the defaults). If you change either of them in the admin panel, enter the key for the new endpoint in the same save; otherwise the save is refused. This keeps a key from being sent to a host it was not meant for. - Addresses. In production the base URL must use https, and it may not resolve to a private, loopback or link-local address unless the server sets
ALLOW_PRIVATE_UPSTREAMS=1(useful for a model server on your own network). - Recent login. Model changes need a login from the last 15 minutes; the panel asks for your password again when needed.
- Reasoning effort is not a setting: it comes from
AI_EFFORTonly and is shown read-only.
How the agent talks to the model
- One model per conversation. Every response carries the model id, and a saved chat remembers the model it started with. If the model changes, the next turn in an older chat is refused and the user starts a new chat.
- A byte-stable system prompt and the full tool list from the first turn. Nothing about the user, the locale or the date is interpolated into the prompt, and tools are always listed in the same order. Consent-gated tools are present from turn 1 and only run after approval.
- Operator settings are versioned. The display name, the operator instructions and the switched-off tools from the admin panel form a block appended after the locked prompt. Each combination is stored once as a prompt version, and a chat keeps the version it started with on every turn, so the prompt never changes mid-conversation. A switched-off tool stays in the tool list and refuses to run. See Accounts and admin.
- Reasoning is kept. Reasoning parts and their provider metadata stay in the conversation and are sent back unchanged.
toolChoice: autoonly. The prompt asks the model to callplanfirst; nothing forces a tool.- Anthropic options: low effort, thinking summaries between tool calls, server-side fallbacks (
AI_FALLBACKS=offor Admin › Model disables them) and prompt caching. A refusal shows a notice instead of an answer. - Errors. Provider error text is shown to superusers only; everyone else sees a generic message, so keys, internal addresses and gateway details never reach visitors.
No model at all
- Replay mode plays scripted demos: the tools run live on the current data and the narration is templated from their results. It is the default when no model is configured.
- REST and MCP serve every public tool without any model. See REST API and MCP.
Use the tools from your own AI client
Any MCP client (Claude, ChatGPT, Cursor, VS Code and others) can connect to /mcp and use the tools with its own model. Nothing on the server calls a model for MCP or REST requests.
Accounts and terms
Whoever runs a public instance must use API keys on an account they own, within the provider's terms and supported regions. The live chat has a geofence (AI_GEOFENCE_DENY or Admin › Access, ISO country codes; it reads the country header of the platform named in TRUSTED_IP_HEADER, Cloudflare or Vercel) and rate limits per signed-in user, per guest IP and for all guests together. Behind a proxy, set TRUSTED_IP_HEADER so guests are counted per IP rather than in one shared bucket.
Quickstart
Run the agent locally on synthetic data, with or without an AI model, and create the first superuser.
Accounts and admin
Guests and accounts, sign-in by email code, password or Google, mail, daily message limits, the first superuser, the admin panel, settings and stored keys, operator instructions, retention, proxies and every related variable.
Code: Apache-2.0. Original docs text and diagrams: CC BY 4.0. Excluded from both: data, figures and charts derived from DLD or Data.Dubai (governed by DATA_LICENSE.md and any DLD grant); EIBOR values (CBUAE terms); third-party names and logos; and the product and operator names and logos (no trademark licence).
The default data is SYNTHETIC – not Dubai data. Nothing here is financial, legal or investment advice.