route_source reported "+task-rule" whenever a rule existed for the task — so a math prompt routed low by the ordinary tier chain still wore the badge next to the local model, reading as "the rule picked this" when it hadn't (the rule only defines math@high). matched now means the head of the chain came from the rule's tier-specific or any-tier list; verified live: the same low math request now reports route_source "router".
Cortiq Gateway
English · Русский
A universal LLM gateway with intelligent routing. One OpenAI-compatible endpoint → automatic model selection from your pool (cheap local + expensive hosted) based on the task type and complexity decided by allaigate / cortiq-router.
In your agent/SDK you only change
base_url— and you get "smart" routing across models. No model-selection logic on the client side.
┌─────────────┐ OpenAI / Anthropic / MCP ┌──────────────────┐
│ Agent / │ ─────────────────────────────▶│ Cortiq Gateway │
│ developer │ (standard protocol) │ (this project) │
└─────────────┘ ◀─────────────────────────────└──────────────────┘
response + metadata │ │
│ /v1/route│ call LLM
▼ ▼
┌──────────────┐ ┌─────────────────────┐
│ cortiq-router│ │ Model pool │
│ (task type, │ │ • local llama.cpp │
│ complexity) │ │ • ollama / vLLM │
└──────────────┘ │ • OpenAI / Claude │
└─────────────────────┘
✨ Highlights
- Zero-config start.
cortiq-gatewaywith no flags: the config is created for you, a busy port is swapped for a free one, the browser opens on a setup wizard that downloads a ready-made local model and wires it into routing — no TOML, no token, no separate model server. - Fast. Rust/Tokio: sub-millisecond p99 gateway overhead — ~10× the throughput of Portkey and ~47× of LiteLLM in a like-for-like proxy benchmark (details).
- Drop-in OpenAI API + streaming. Point any OpenAI client at the gateway and send
model: "cortiq-auto";stream: trueis fully supported (SSE). - Multi-protocol. OpenAI Chat & Embeddings, Anthropic Messages (in/out, streaming), and an MCP server (
POST /mcp) exposing routing as tools for agent orchestrators. - Intelligent routing.
complexity.tier→ ordered model pool (low → local,high → cloud), with fallback and graceful degradation when the router is down. Plus per-task rules ([routing.tasks]): pin translations to a local model and code to your strongest coder — the complexity chain stays as fallback (docs). - Image & video generation. The Playground drives local text-to-image (Lumina) and text-to-video (MiniMax-H3)
.cmfmodels viacortiq imagine/animate: live progress with ETA, a generation gallery, PNG export, in-browser AVI playback — no ffmpeg anywhere. - Semantic cache. Optional embedding-based cache returns a stored answer for prompts semantically near a previous one — skipping the model call (hit-rate & savings on the dashboard).
- Built-in multilingual admin console. Manage models, routing, protocols, keys and secrets from a web UI — no hand-editing TOML, no restart (changes are hot-reloaded). Available in 7 languages (en, ru, de, fr, es, zh, tr) with light/dark themes.
- Live analytics. Per-request stats (tokens, cost, latency, success rate, failovers), time-series charts, breakdowns by model/tier/task, and a Prometheus
GET /metricsendpoint. - Playground. Send a prompt through the live pipeline and inspect the routing decision.
- Single self-contained binary. The SPA is embedded into the Rust binary — nothing extra to deploy.
- Secrets stay in the gateway. Agents hold only a virtual gateway key; provider keys live in the gateway and never leak to clients.
📦 Install
# Docker (GitHub Container Registry)
docker run -p 9000:9000 -e CORTIQ_ADMIN_TOKEN=secret \
-v "$PWD/config:/app/config" -v cortiq-data:/app/data \
ghcr.io/infosave2007/cortiq-gateway:latest
# → admin console at http://localhost:9000/admin
# from crates.io
cargo install cortiq-gateway
cortiq-gateway # no flags, no config file, no token — see Quick start
# or build from source
git clone https://github.com/infosave2007/cortiq-gateway
cd cortiq-gateway
cargo build --release # ./target/release/cortiq-gateway
The image ships a default config and runs out of the box; mount /app/config (with a
gateway.toml) to use your own. Downloaded models and stats live in /app/data — mount
a volume there so they survive container recreates (a host folder works too, e.g.
-v "$PWD/models:/app/data/models"). Tags: :latest, :X.Y.Z, :edge
(linux/amd64 + arm64).
Docker Compose — gateway + a local Ollama model in one command:
docker compose up -d
docker compose exec ollama ollama pull qwen2.5:0.5b # pull a model once
# → OpenAI API at http://localhost:9000/v1 · admin at /admin (token: change-me)
See docker-compose.yml. For smart routing, set CORTIQ_ROUTER_KEY
and add a [router] (see docs/ROUTER.md).
🖥️ Admin console
The gateway ships with an embedded web console at /admin:
| Models | Routing |
|---|---|
![]() |
![]() |
| add/edit/probe models, manage provider keys | visual tier editor (ordered, reorderable) |
| Playground |
|---|
![]() |
| test the live pipeline and see the routing decision |
The console is open by default — no token needed. To protect it, set any one of
--admin-token <TOKEN>, [admin].token, or [admin].token_env:
cortiq-gateway --admin-token <YOUR_TOKEN>
# open http://localhost:9000/admin?token=<YOUR_TOKEN>
Once a token is set, every /admin/api/* endpoint requires it as a Bearer token. Secret
values are never returned by the API (only their presence: store / env / missing).
🚀 Quick start
cortiq-gateway
That is the whole command. On first run the gateway:
- creates its config —
config/gateway.tomlwhen started from a checkout, otherwise~/.cortiq-gateway/gateway.toml(override with$CORTIQ_GATEWAY_HOMEor--config); - picks a free port if 9000 is taken, and writes the choice back to the config;
- opens your browser on the setup wizard at
/admin#/onboarding.
The wizard takes you from nothing to a working local model:
| Step | What happens |
|---|---|
| Language | 7 languages; changeable later from the header |
| Ready-made models | the infosave .cmf models, read live from HuggingFace — pick a quantization and watch the real download progress |
| Any HF model | search HuggingFace and auto-convert to .cmf |
| Finish | every completed download is connected as a local model, its cortiq serve is started, routing.default points at it, and you land in the Playground |
No TOML editing, no separate "register" step, no manually started model server. Prefer to skip the wizard? Add models from the console (Models, Import model) or hand-write the config — both still work, and changes hot-reload without a restart.
# explicit config / port / no browser — all optional
cortiq-gateway --config config/gateway.toml --no-browser --no-port-search
For smart routing across a pool, point [router] at the hosted allaigate router or a local
cortiq-router (see docs/ROUTER.md). Without a router the gateway degrades
gracefully to the default model, so a local-only setup works out of the box.
Now any OpenAI client works through the gateway:
from openai import OpenAI
client = OpenAI(base_url="http://localhost:9000/v1", api_key="sk-gw-...")
resp = client.chat.completions.create(
model="cortiq-auto", # ← magic model = "choose for me"
messages=[{"role": "user", "content": "Solve x^2 - 5x + 6 = 0"}],
)
print(resp.choices[0].message.content)
# response headers show what the gateway picked:
# X-Cortiq-Task-Label: math
# X-Cortiq-Complexity-Tier: low
# X-Cortiq-Selected-Model: local-qwen
model: "cortiq-auto" enables routing. Any real model name from the config
("gpt-4o-mini", "local-qwen") is a direct passthrough, without routing.
Using the hosted allaigate router? Set
url = "https://138.226.222.209",verify_tls = false,taxonomy_id = "data-assistant", and acortiq_…key inCORTIQ_ROUTER_KEY. On hard prompts the router escalates to an oracle (~10 s), so settimeout_ms = 12000+— otherwise the gateway gracefully degrades to the default model.
🧭 The router (allaigate)
cortiq-auto routing is powered by a semantic router — the classifier that reads a
prompt and returns its task type + complexity. Use the hosted
allaigate router (~94% accuracy on close task types,
patent-pending, privacy-first) or self-host cortiq-router. Pinned models need no router.
🎉 Launch promo: grab a $1 Starter key with code
LAUNCH1at api.allaigate.com (limited activations).
Setup, pricing, API contract and how to verify accuracy: docs/ROUTER.md.
Request flow
- Client →
POST /v1/chat/completions(or Anthropic/MCP) withmodel: "cortiq-auto". - The gateway extracts the text to route on (strategy is configurable, see docs/ROUTING.md).
- Gateway →
cortiq-router /v1/route→ getstask_label+complexity.tier. - Gateway selects a model from the pool via the routing table (with a fallback order).
- Gateway → the selected model's provider (translating the protocol if needed), returns the response.
- Gateway attaches routing metadata in headers/
usageand records cost & statistics.
Details — docs/ARCHITECTURE.md.
Supported protocols (inbound)
Each adapter is toggled in the config — take only what you need.
| Protocol | Endpoint | Status |
|---|---|---|
| OpenAI Chat Completions | POST /v1/chat/completions |
✅ implemented (+ streaming) |
| OpenAI Embeddings | POST /v1/embeddings |
✅ implemented |
| Anthropic Messages | POST /v1/messages |
✅ implemented (+ streaming) |
| MCP (Model Context Protocol) | POST /mcp (JSON-RPC) |
✅ implemented |
| OpenAI Completions (legacy) | POST /v1/completions |
✅ implemented (+ streaming) |
| OpenAI Models | GET /v1/models |
✅ implemented |
| Native passthrough | POST /route |
✅ implemented |
Supported providers (outbound)
| Provider | Covers |
|---|---|
openai (OpenAI-compatible) |
OpenAI, OpenRouter, Together, Groq, vLLM, llama.cpp server, LM Studio, Ollama (/v1) |
anthropic |
Claude Messages API (+ streaming) |
ollama |
native Ollama API |
http |
arbitrary HTTP endpoint (custom adapter) |
Most local servers already expose an OpenAI-compatible API — so the openai
adapter makes the gateway universal with almost no effort.
Configuration
listen = "0.0.0.0:9000"
[router]
url = "http://localhost:8080"
api_key_env = "CORTIQ_ROUTER_KEY"
[[models]]
id = "local-qwen"
provider = "openai"
base_url = "http://localhost:8000/v1"
model = "qwen2.5-7b-instruct"
cost_tier = "cheap"
[[models]]
id = "claude-opus"
provider = "anthropic"
base_url = "https://api.anthropic.com"
model = "claude-opus-4"
api_key_env = "ANTHROPIC_API_KEY"
cost_tier = "expensive"
[routing]
low = ["local-qwen"]
high = ["claude-opus", "local-qwen"]
default = "local-qwen"
Full example — config/gateway.example.toml. Routing & cost-aware selection — docs/ROUTING.md. Everything in the config is also editable from the admin console at runtime.
Cross-standards (like grown-up APIs)
- Auth:
Authorization: Bearer sk-gw-...(virtual gateway keys). - Errors: OpenAI-compatible
{"error": {...}}envelope — parsed natively by SDKs. - Routing metadata:
X-Cortiq-*headers on every response. - Cost accounting: token usage + USD cost and the selected model.
- Observability:
GET /metrics(Prometheus),GET /healthz,GET /readyz.
Details — docs/PROTOCOLS.md.
⚡ Performance & accuracy
Latency — gateway overhead proxying the same instant backend, ab -k -r -c 20 -n 5000:
| Gateway | req/s | p50 | p99 |
|---|---|---|---|
| Cortiq Gateway (Rust) | ~57,300 | 0 ms | 1 ms |
| Portkey (Node) | ~5,800 | 3 ms | 9 ms |
| LiteLLM (Python, 4 workers) | ~1,200 | 9 ms | 59 ms |
Accuracy — task-type routing on natural-language prompts (7 task types). LiteLLM/Portkey have no semantic task router; a keyword heuristic is the DIY stand-in:
| Classifier | Accuracy |
|---|---|
| allaigate semantic router | 100% (37/37) |
| keyword heuristic (no classifier) | 32% (12/37) |
Methodology, caveats and a reproducible harness: BENCHMARKS.md
(bash bench/run.sh, python3 bench/accuracy.py). Numbers vary; the gaps are the point.
Build & test
cargo build --release # single self-contained binary (SPA embedded)
cargo test # config round-trip, routing validation
✅ Tested
| Area | How it's verified | Status |
|---|---|---|
| Config load/save round-trip, routing validation | cargo test (unit) |
✅ |
| Build · format · lint | CI: cargo build / fmt --check / clippy -D warnings on Linux · macOS · Windows |
✅ |
| OpenAI Chat + SSE streaming | integration (mock upstream) | ✅ |
| Anthropic Messages — in/out, streaming + non-streaming | integration (mock) | ✅ |
Embeddings (POST /v1/embeddings) |
integration (mock) | ✅ |
| Semantic cache — hit / miss / savings | integration (mock) | ✅ |
MCP — initialize / tools/list / tools/call |
integration (mock) | ✅ |
Models · Completions · native POST /route |
integration (mock) | ✅ |
| Routing — auto / pinned / failover / degrade | integration (mock and live allaigate router) | ✅ |
| Hot config reload (no restart) | integration | ✅ |
| Latency benchmark vs LiteLLM / Portkey | bench/run.sh (Apache Bench) — see BENCHMARKS.md |
✅ |
| Task-type routing accuracy | bench/accuracy.py — live router 100% vs keyword heuristic 32% |
✅ |
Status
Implemented: OpenAI Chat Completions with SSE streaming, Anthropic provider +
inbound /v1/messages (streaming), embeddings, a semantic cache, MCP server,
routing with fallback & graceful degradation, cost/token accounting, the embedded
multilingual admin console with hot config reload, statistics and Prometheus
GET /metrics. Planned: per-account routing tables, router feedback loop. See the roadmap in
docs/ARCHITECTURE.md.
Contributions welcome — see CONTRIBUTING.md.
License
Apache-2.0 — see LICENSE.



