SPB Git

spb/zyquo-router Public MIT

One local endpoint, every AI provider — a private OpenAI-compatible LLM gateway for your Mac (170 models, 12 providers).

Swift 95.7% Python 2.3% Shell 1.2% Makefile 0.9%
14.2 KB
Zyquo Router icon

# Zyquo Router

# One local endpoint. Every AI provider. Spec-exact.

Turn your Mac into a private LLM gateway — 170 models from 12 providers behind a single OpenAI-compatible API.


Release Platform Swift License Downloads

Notarized Universal SwiftNIO No Electron


Anthropic · OpenAI · xAI · Mistral · Google Gemini · Alibaba Qwen · DeepSeek · Kimi · Perplexity · Together AI · DeepInfra · Cerebras


# 📖 Table of Contents


# 💡 Why Zyquo Router

Every AI provider speaks a slightly different dialect. Anthropic wants x-api-key and content blocks; Gemini wants contents/parts and camelCase; Mistral renames seed; Perplexity ends its streams with a non-spec event. Your tools — SDKs, CLIs, IDE plugins, agents — mostly speak one dialect: the OpenAI API.

Zyquo Router runs a tiny, native gateway on your Mac. You store your provider keys once in an encrypted vault, pick a port, press Start — and everything that can talk to OpenAI can now talk to twelve providers through http://localhost:8787/v1, with per-request model routing, live traffic inspection, and cost tracking.

Think OpenRouter / LiteLLM — but native, local, private, and gorgeous.

  • 🔒 Private by design — keys never leave your Mac; requests go straight from your machine to the provider. No middleman, no telemetry, no accounts.
  • 🎯 Spec-exact — byte-exact chat.completion.chunk SSE streams that the official OpenAI Python and JS SDKs parse unmodified (verified across all 170 models).
  • 🖥 A real Mac app — SwiftUI control room with a menu bar extra, not a Docker container with a YAML file.

# ✨ Features

# 🌐 The Gateway

Capability Details
OpenAI-compatible API POST /v1/chat/completions (streaming + non-streaming), GET /v1/models, GET /v1/models/{id}, GET /health
170 models, 12 providers Full catalog with context windows, capabilities, and per-Mtok pricing under the x_zyquo extension key
Namespaced routing provider/model-id (e.g. anthropic/claude-sonnet-4-5, deepinfra/meta-llama/Llama-4-Maverick); bare IDs accepted when unambiguous
Aliases Friendly names — fastcerebras/…, bestanthropic/…
Fallback chains Ordered model lists tried on upstream failure; the response honestly reports the model that answered
Full translation Anthropic Messages API and Gemini generateContent translated bidirectionally: system extraction, turn merging, tools ⇄ tool_use/functionCall, images, finish-reason and usage normalization
Tool calling Streamed tool_calls argument deltas in exact OpenAI shape, from all three wire formats
Vision image_url content parts (data-URI base64 and remote URLs where supported)
Reasoning models Thinking output normalized to reasoning_content (DeepSeek convention) — works with Claude thinking, Gemini thoughts, DeepSeek-R1, Qwen, Magistral, Perplexity <think>, and more
Quirk normalization Together finish_reason:"eos", Mistral thinking arrays, Perplexity citations + .done events, missing object fields, argument-repeat streams — all ironed into the spec
Resilience Exponential-backoff retries with jitter (never after the first streamed byte), honest OpenAI-format error mapping (401/429/502/504), client-disconnect cancels the upstream call in <1s

# 🔐 Control & Security

  • Encrypted key vault — AES-256-GCM with an HKDF-derived, machine-bound master key (hardware UUID + salted pepper). No Keychain, no plaintext, ever.
  • Localhost by default — LAN exposure (0.0.0.0) is an explicit opt-in that requires at least one local API key.
  • Local API keyszyquo-sk-… bearer tokens, SHA-256-hashed at rest, shown once at creation, per-key enable/revoke and model allow-lists.
  • Redacted logging — request/response bodies hidden by default; revealing is an explicit per-session switch. Provider keys never appear in any response, log, or error.

# 📊 Observability

  • Live dashboard — requests/min sparkline, tokens, estimated cost (from real per-model pricing), error rate, active streams, uptime, per-provider breakdown bar.
  • Request inspector — every routed call with provider, status, latency, tokens, cost, SSE badge, and a detail pane with an upstream-TTFB timing waterfall.
  • Filters & export — by provider/status/model (⌘F), pause/clear, JSON export.

# 🛠 Developer Experience

  • Playground — built-in tester that calls the router's own endpoint, with side-by-side request JSON / raw SSE panes and copy-as-code.
  • In-app API docs — the full reference rendered beautifully inside the app, from the same source of truth the server implements.
  • Copy-as snippets — endpoint URL, curl, OpenAI-Python, OpenAI-JS, pre-filled with your port.
  • Menu bar extra — status dot, Start/Stop, req/min, today's cost, copy endpoint.
  • ⌘K command palette — server control, section jumps, fuzzy model-ID copy.
  • Headless modesZyquoRouter --serve [port] (no UI) and --load-vault (seed keys from environment variables) for scripting and CI.
  • Shortcuts — ⌘R start/stop · ⌘1–6 sections · ⌘F filter · ⌘⇧C copy endpoint · ⌘K palette.

# 📸 Screenshots

Dashboard — the control room

Dashboard: server card with endpoint and copy-as snippets, live tiles, sparkline



In-app API reference

Docs: hero card, code blocks with copy, styled sections

# 📦 Installation

  1. ⬇️ Download ZyquoRouter.dmg from the latest release
  2. Open the DMG and drag Zyquo Router into Applications
  3. Launch it — the app is Developer ID signed, notarized, and stapled, so Gatekeeper opens it without warnings

# First run — three steps to one endpoint

  1. Keys → paste the API keys for the providers you use (each row has a Test button that verifies the key and shows latency)
  2. Dashboard → pick a port (default 8787) → press Start
  3. Point anything OpenAI-compatible at http://localhost:8787/v1 🎉

# 🖥 Requirements

Minimum
macOS 13.0 Ventura or later
Architecture Universal binary — Apple Silicon & Intel
Disk ~15 MB
Accounts None. Bring your own provider API keys

# 🚀 Usage

# Python (official OpenAI SDK — works unmodified)

python
from openai import OpenAI

client = OpenAI(base_url="http://localhost:8787/v1", api_key="zyquo")

stream = client.chat.completions.create(
    model="anthropic/claude-sonnet-4-5",      # any of the 170 models
    messages=[{"role": "user", "content": "Hello!"}],
    stream=True,
)
for chunk in stream:
    print(chunk.choices[0].delta.content or "", end="")

# JavaScript / TypeScript

javascript
import OpenAI from "openai";

const client = new OpenAI({ baseURL: "http://localhost:8787/v1", apiKey: "zyquo" });
const r = await client.chat.completions.create({
  model: "gemini/gemini-2.5-flash",
  messages: [{ role: "user", content: "Hello!" }],
});

# curl

bash
curl http://localhost:8787/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{"model": "deepseek/deepseek-chat", "messages": [{"role": "user", "content": "Hi"}], "stream": true}'

# Good to know

  • Model discovery: GET /v1/models lists everything with context windows, pricing, and capability flags. Disabled models 404; favorites float to the top in-app.
  • Reasoning: pass the standard reasoning_effort — the router translates it per provider (Anthropic thinking budgets, Gemini thinkingConfig, Mistral prompt_mode, …) and normalizes the output into reasoning_content.
  • Provider extras pass straight through: Perplexity search_domain_filter, Qwen enable_thinking, Together top_k, Anthropic thinking, …
  • Auth: with no local keys the router is open on localhost. Create zyquo-sk-… keys in Keys → Local API Keys to require Authorization: Bearer … (mandatory for LAN mode).
  • The full contract lives in docs/API.md — also rendered in-app under Docs.

# 🏗️ Architecture

100 % native Swift — no Electron, no Python sidecar, no Docker.

text
SwiftUI control room  ─┐
                       ├─►  SwiftNIO HTTP/1.1 server (structured concurrency,
Menu bar extra  ───────┘    NIOAsyncChannel, spec-exact SSE writer)

                              RequestRouter (namespaces, aliases,
                              fallback chains, capability gates)

                 ┌───────────────────┼────────────────────┐
                 ▼                   ▼                    ▼
        AnthropicTranslator   GeminiTranslator     CompatAdjuster
        (Messages API ⇄       (generateContent ⇄   (per-provider param
         OpenAI, SSE event     OpenAI, SSE          tables + quirk
         state machine)        chunks)              normalization ×10)
                 └───────────────────┼────────────────────┘

                    UpstreamCall → provider APIs (your keys,
                    straight from your Mac — AES-256-GCM vault)
  • Server: SwiftNIO 2 with one task per connection; client disconnects propagate as cancellation all the way into the upstream URLSession transfer.
  • Translation: fixture-tested state machines convert Anthropic events and Gemini chunks into byte-exact OpenAI chat.completion.chunks.
  • Persistence: JSON documents in ~/Library/Application Support/ZyquoRouter/; keys in vault.zq (AES-256-GCM, machine-bound HKDF key, no Keychain).
  • Zero heavyweight deps: SwiftNIO, swift-nio-extras, swift-markdown. That's it.

# ⚙️ Building from Source

Requirements: macOS 13+, Swift 5.9+ toolchain (Xcode or CLT), librsvg for the icon pipeline (brew install librsvg). Built entirely with SPM — no Xcode IDE required.

bash
git clone https://github.com/spboucher-ai/zyquo-router.git
cd zyquo-router

make dev        # release build + ad-hoc signed dist/Zyquo Router.app
make test       # unit, fixture, and gateway-behavior test suites
make icon       # regenerate AppIcon.icns from assets/icon/zyquo-router.svg
make release    # universal binary + Developer ID signing + notarization + DMG

Headless gateway for scripts/CI:

bash
.build/release/ZyquoRouter --load-vault    # seed the vault from env vars
.build/release/ZyquoRouter --serve 8787    # run the gateway without the UI

# ✅ Verification

Every release is verified by scripts/verify.py, which drives all 170 catalog models through the local endpoint with the official OpenAI Python SDK — non-streaming, streaming chunk discipline, tool calling, vision, and reasoning. The current matrix is 170/170 green: see docs/VERIFICATION.md.

Gateway behavior is integration-tested against mock upstreams: client-disconnect cancellation, fallback-chain honesty, 429 retries, and graceful shutdown with active streams.


# 🗺 Roadmap

  • POST /v1/embeddings routed to embedding-capable providers
  • POST /v1/responses compatibility surface
  • Prompt-level request/response caching
  • Per-key rate limiting (token bucket) and budgets
  • Usage history persistence with daily/weekly summaries
  • Custom OpenAI-compatible endpoints (self-hosted vLLM/Ollama upstreams)

# 🤝 Contributing

Issues and pull requests are welcome!

  1. Fork → branch → make your change
  2. make test must stay green (unit + fixture + gateway-behavior suites)
  3. swift build must produce zero warnings
  4. Every code file carries the project header (see any source file)

# 📄 License

Released under the MIT License.


# 👤 Author

Simon-Pierre Boucher

📫 Contact: contact@spboucher.ai

Part of the Zyquo family — Cloud · Local · Agent · Atlas · MLX · Router

© 2026 Simon-Pierre Boucher. All rights reserved.