venagate
v1.9.0
Published
AI model router — CLI, headless, TUI. Multi-provider proxy with OpenAI-compatible API.
Readme
venagate
AI Model Router Gateway — OpenAI-compatible API proxy with multi-provider routing, account rotation, combo fallback chains, SSE streaming, and interactive management TUI.
Route requests to 60+ AI providers through a single unified endpoint.
Features
- OpenAI-compatible API — drop-in replacement for any OpenAI client
- Full request/response translation — automatic format conversion between OpenAI, Anthropic, Gemini, DeepSeek, and more
- SSE stream translation — Claude→OpenAI, Gemini→OpenAI streaming format conversion
- Thinking/reasoning support — transparent handling of
reasoning_effort,thinking,thinkingConfigacross all providers - 60+ providers — OpenAI, Anthropic, Gemini, DeepSeek, Groq, Mistral, GitHub Copilot, Claude, Codex, and more
- Multi-account rotation — fill-first or round-robin strategies per provider
- Combo fallback chains — named routing lists, tried top → bottom
- Proxy pool — SOCKS5/HTTP proxy rotation for free providers
- OAuth login — device-code and authorization-code (PKCE) flows
- Interactive TUI — Ink/React terminal UI with arrow-key navigation
- Headless CLI — every feature has a command-line equivalent
- Cross-platform service — systemd, launchd, Task Scheduler, Termux
- API key auth — gateway authentication with key management
- Usage tracking — request logs and statistics (buffered async writes)
- Token counting — CJK/code-aware token estimator
Quick Start
# Install globally
npm install -g venagate
# Start with interactive TUI
venagate
# Or start server only (headless)
venagate serverDefault endpoint: http://localhost:31079/v1
Usage
Interactive Mode (TUI)
venagateLaunches the Ink-based terminal UI with arrow-key navigation:
VENAGATE localhost:31079/v1
3 active · 2 accts · 1 combos
──────────────────────────────────────────────
❯ Providers enable/disable/configure
Accounts API keys per provider
Combos routing fallback chains
Proxy Pool outbound proxy rotation
API Keys gateway auth keys
Settings server info
Test Provider send test request
Test Endpoints test gateway endpoints
ExitNavigate with ↑↓ arrows, Enter to select, Esc to go back.
Headless Mode (Server Only)
venagate server [port] [host]
# Examples
venagate server # default: 0.0.0.0:31079
venagate server 8080 # custom port
venagate server 3000 127.0.0.1 # custom port + hostCLI Commands
Every interactive feature has a headless equivalent:
Providers
venagate providers # list all providers
venagate provider <id> # show provider details
venagate provider enable <id> # enable provider
venagate provider disable <id> # disable provider
venagate models [provider] # list modelsAccounts
venagate accounts # list all accounts
venagate account <provider> # show accounts for provider
venagate account add <provider> <api-key> # add API key
venagate account rm <account-id> # remove account
venagate account strategy <provider> fill-first # set strategy
venagate account strategy <provider> round-robin # set strategyCombos
venagate combos # list all combos
venagate combo <id> # show combo details
venagate combo add <name> # create new combo
venagate combo rm <id> # delete combo
venagate combo add-model <id> openai/gpt-4o # add model to combo
venagate combo toggle <id> # enable/disable comboProxy Pool
venagate proxy # list proxies
venagate proxy add <url> # add proxy (socks5/http)
venagate proxy rm <name> # remove proxy
venagate proxy on # enable pool
venagate proxy off # disable pool
venagate proxy strategy round-robin # set rotation strategy
venagate proxy strategy random # set rotation strategyAPI Keys
venagate keys # list all keys (full, copyable)
venagate keys add [name] # generate new key
venagate keys rm <full-key> # delete keyStatus & Config
venagate status # show gateway status
venagate config # show config JSON
venagate test # test all gateway endpointsHelp
venagate help
venagate --help
venagate -h
venagate --version
venagate -vAPI Endpoints
OpenAI-Compatible
| Method | Path | Description |
|--------|------|-------------|
| POST | /v1/chat/completions | Chat completions (OpenAI format, supports streaming) |
| POST | /v1/messages | Messages (Anthropic format) |
| GET | /v1/models | List all available models |
| GET | /v1/models/<id> | Get model info |
| POST | /v1/embeddings | Text embeddings |
| POST | /v1/images/generations | Image generation |
| POST | /v1/audio/speech | Text-to-speech |
| POST | /v1/audio/transcriptions | Speech-to-text |
| POST | /v1/videos/generations | Video generation |
| POST | /v1/search | Search |
| POST | /v1/web/fetch | Web fetch |
| POST | /v1/responses | Responses API |
| POST | /v1/messages/count_tokens | Token counting |
Admin API
| Method | Path | Description |
|--------|------|-------------|
| GET | /api/health | Health check (no auth) |
| GET | /api/version | Version info (no auth) |
| GET | /api/providers | List providers |
| GET | /api/providers/<id> | Provider details |
| POST | /api/providers/<id>/test | Test provider |
| GET | /api/keys | List API keys |
| POST | /api/keys | Generate API key |
| DELETE | /api/keys/<key> | Delete API key |
| GET | /api/settings | Get settings |
| POST | /api/settings | Update settings |
| GET | /api/combos | List combos |
| POST | /api/combos | Create combo |
| DELETE | /api/combos/<id> | Delete combo |
| GET | /api/proxy | List proxies |
| POST | /api/proxy | Add proxy |
| DELETE | /api/proxy/<name> | Remove proxy |
| GET | /api/usage/stats | Usage statistics |
| GET | /api/usage/logs | Request logs |
Authentication
When auth is enabled, include the API key in requests:
curl -H "x-api-key: YOUR_KEY" http://localhost:31079/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{"model":"openai/gpt-4o","messages":[{"role":"user","content":"Hello"}]}'Or via Authorization header:
curl -H "Authorization: Bearer YOUR_KEY" http://localhost:31079/v1/modelsChat Request Example
# Non-streaming
curl http://localhost:31079/v1/chat/completions \
-H "Content-Type: application/json" \
-H "x-api-key: YOUR_KEY" \
-d '{
"model": "openai/gpt-4o",
"messages": [{"role": "user", "content": "Hello!"}],
"max_tokens": 100
}'
# Streaming (SSE)
curl http://localhost:31079/v1/chat/completions \
-H "Content-Type: application/json" \
-H "x-api-key: YOUR_KEY" \
-d '{
"model": "deepseek/deepseek-chat",
"messages": [{"role": "user", "content": "Hello!"}],
"stream": true
}'Model format: <provider>/<model-id> (e.g. anthropic/claude-sonnet-4, deepseek/deepseek-chat)
Combo Routing
Use a combo as the model to try multiple providers in order:
curl http://localhost:31079/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{"model": "combo:my-fallback", "messages": [...]}'Streaming
When stream: true is set in the request body, venagate forwards the streaming request to the provider and pipes the SSE response directly to the client:
Client → venagate → Provider
│ │ │
│ stream:true │ stream:true (forwarded)
│ │ │
│ │ SSE ← │ data: {"choices":[{"delta":{"content":"Hello"}}]}
│ │ │ data: {"choices":[{"delta":{"content":" world"}}]}
│ │ │ data: [DONE]
│ │ │
│ SSE ← │ │ (raw SSE pipe)Supported by all providers that support streaming (OpenAI, Anthropic, DeepSeek, etc.).
Stream Translation
When the provider format differs from OpenAI (e.g. Anthropic, Gemini), venagate automatically translates the streaming response:
- Claude→OpenAI:
message_start/content_block_delta/message_stop→ OpenAI chunks,thinking_delta→reasoning_content - Gemini→OpenAI:
candidates[].content.parts[]→ OpenAI chunks,functionCall→tool_calls - OpenAI-compatible: pass-through (no translation needed)
Translator Layer
venagate includes a comprehensive translator layer (ported from9router) that handles cross-provider compatibility:
Request Translation
| Feature | Description |
|---------|-------------|
| filterToOpenAIFormat | Message sanitization, tool format normalization, role normalization (developer→system) |
| normalizeToolMessages | 3-pass tool call handling: ensure IDs, fix missing responses, format conversion |
| captureThinking + applyThinking | Thinking/reasoning translation across OpenAI, Claude, Gemini, DeepSeek formats |
| normalizeThinkingConfig | Strip thinking config on non-user turns (prevents provider rejection) |
| injectReasoningContent | Inject placeholder reasoning_content for providers that require it (DeepSeek, Kimi, MiniMax) |
| stripUnsupportedParams | Provider-specific parameter filtering (e.g. temperature for Claude) |
| adjustMaxTokens | Auto-increase for tools, ensure max_tokens > thinking budget |
| cleanJSONSchema + cleanGeminiTools | Gemini JSON Schema cleaning (remove unsupported keywords) |
| validateThinkingSignatures | Claude thinking block signature validation |
| applyClaudeCloaking | OAuth cloaking (billing header, fake user ID, tool renaming) |
| stripUnsupportedModalities | Remove image/audio/pdf for text-only models |
| prefetchRemoteImages | Image URL→base64 conversion with SSRF protection |
| dedupeTools | Remove duplicate built-in tools when MCP equivalents exist |
Response Translation
| Feature | Description |
|---------|-------------|
| toOpenAIFinish | Finish reason mapping (end_turn→stop, tool_use→tool_calls, STOP→stop) |
| extractUsage | Per-provider usage extraction with cache/reasoning token tracking |
| Stream translation | Claude/Gemini SSE → OpenAI chunk format |
Supported Format Conversions
| From | To | Request | Stream Response | |------|-----|---------|----------------| | OpenAI | Anthropic | ✅ | ✅ (via stream translator) | | OpenAI | Gemini | ✅ | ✅ (via stream translator) | | OpenAI | OpenAI-compat | ✅ | ✅ (pass-through) | | Anthropic | OpenAI | ✅ | ✅ | | Gemini | OpenAI | ✅ | ✅ |
Providers
Free (No Auth)
| Provider | Description |
|----------|-------------|
| opencode-free | DeepSeek V4 Flash, MiMo V2.5, HY3, Nemotron 3 Ultra |
| mimo-free | Xiaomi MiMo auto |
| kiro | Kiro AI (Claude, GPT, DeepSeek, Qwen, GLM) |
API Key
| Provider | Description |
|----------|-------------|
| openai | GPT-5.4, GPT-4o, o3, o4-mini |
| anthropic | Claude Sonnet 4, Claude Opus 4 |
| gemini | Gemini 3.1 Pro, Gemini 2.5 Flash |
| deepseek | DeepSeek V4 Pro/Flash |
| groq | Llama 3.3 70B, Qwen3 32B |
| mistral | Mistral Large 3, Codestral |
| openrouter | 100+ models via OpenRouter |
| cerebras | Ultra-fast inference |
| siliconflow | Chinese model gateway |
| glm | Zhipu GLM 5.2 (Anthropic compat) |
| minimax | MiniMax M3 (Anthropic compat) |
| dashscope | Alibaba Qwen |
| nvidia | NVIDIA NIM inference |
| blackbox | Blackbox AI multi-model |
| volcengine-ark | ByteDance Volcengine |
| xiaomi-mimo | Xiaomi MiMo API |
| xiaomi-tokenplan | Xiaomi MiMo Token Plan |
| opencode-go | OpenCode Go subscription |
| vercel-ai-gateway | Vercel AI Gateway |
| commandcode | Command Code gateway |
| ... | 40+ more providers |
OAuth (Login Required)
| Provider | Flow | Description |
|----------|------|-------------|
| github-copilot | Device Code | GitHub Copilot |
| codex | Auth Code PKCE | OpenAI Codex |
| claude | Auth Code PKCE | Claude Pro/Max |
| gemini-cli | Auth Code | Google Gemini CLI |
| antigravity | Auth Code | Google Cloud Code |
| xai | Device Code | xAI Grok |
| qwen | Device Code | Qwen Code |
| iflow | Auth Code | iFlow AI |
| kimi | Device Code | Kimi Code |
| grok-cli | Device Code | Grok CLI |
| cline | Auth Code | Cline |
| kilocode | Device Code | Kilo Code |
| kimchi | Device Code | Kimchi |
| qoder | Device Code | Qoder |
| cursor | Device Code | Cursor IDE |
| codebuddy-cn | Device Code | CodeBuddy CN |
| codebuddy-intl | Device Code | CodeBuddy International |
| clinepass | Auth Code | ClinePass |
| windsurf | Auth Code | Windsurf |
| zed | Auth Code | Zed |
| trae | Device Code | Trae (ByteDance) |
| gitlab | OAuth | GitLab Duo |
Configuration
Config and data directory: ~/.venagate/
~/.venagate/
├── config/
│ ├── config.json # Main config
│ ├── auth.json # API keys
│ ├── accounts.json # Provider accounts/credentials
│ ├── combos.json # Combo definitions
│ ├── proxies.json # Proxy pool
│ ├── providers.json # Custom providers
│ ├── models.json # Model aliases/disabled
│ └── usage.json # Usage logs
├── log/ # Log files
└── cache/ # CacheConfig File
{
"server": { "port": 31079, "host": "0.0.0.0" },
"proxy": {
"enabled": false,
"strategy": "round-robin",
"proxies": []
},
"combos": [
{
"id": "my-fallback",
"name": "My Fallback",
"enabled": true,
"items": [
{ "kind": "model", "provider": "openai", "model": "gpt-4o" },
{ "kind": "model", "provider": "deepseek", "model": "deepseek-chat" }
]
}
]
}Environment Variables
| Variable | Default | Description |
|----------|---------|-------------|
| VENAGATE_PORT | 31079 | Server port |
| VENAGATE_HOST | 0.0.0.0 | Server host |
| VENAGATE_HOME | ~/.venagate | Config and data directory |
Service Installation
Linux (systemd)
# Install service (auto-detects user and home directory)
sudo venagate install linux
# Enable and start
sudo systemctl daemon-reload
sudo systemctl enable venagate
sudo systemctl start venagate
# Check status
sudo systemctl status venagate
venagate status
# View logs
sudo journalctl -u venagate -fThe service file is auto-generated with the correct VENAGATE_HOME path. No manual configuration needed.
Important: If you manage venagate with sudo, always use sudo for all commands to keep config in the same location.
macOS (launchd)
venagate install darwin
launchctl load ~/Library/LaunchAgents/com.venagate.gateway.plist
launchctl start com.venagate.gateway
# Stop
launchctl unload ~/Library/LaunchAgents/com.venagate.gateway.plistWindows (Task Scheduler)
venagate install win32
# Create scheduled task
schtasks /create /tn "venagate" /tr "venagate-service.bat" /sc onstart
# Or add to startup folder:
# Win+R → shell:startup → create shortcut to venagate-service.batTermux (Android)
# Method 1: termux-services
pkg install termux-services
venagate install termux
sv-enable venagate
sv up venagate
# Method 2: Termux:Boot
venagate install termux
# Install Termux:Boot app from F-Droid
# It will run ~/.termux/boot/venagate.sh on device startUninstall Service
venagate uninstall # auto-detect platform
venagate uninstall linux # specify platform
venagate uninstall darwin
venagate uninstall win32
venagate uninstall termuxDevelopment
# Clone
git clone https://github.com/venenapro/venagate.git
cd venagate
# Install dependencies
npm install
# Development mode (TUI)
npm run dev
# Development mode (server only)
npm run dev:server
# Build
npm run build
# Type check
npm run typecheck
# Translator E2E tests
node scripts/test-e2e-translator.mjs
# E2E tests (requires running server)
npm run test:e2e
# Clean
npm run cleanArchitecture
src/
├── cli/
│ └── index.ts # CLI entry point (headless + TUI launch)
├── server/
│ ├── index.ts # HTTP server + routing
│ ├── middleware.ts # Auth, rate limiting
│ └── handlers/ # Request handlers
│ ├── chat.ts # Chat completions (streaming + non-streaming)
│ ├── messages.ts # Anthropic messages
│ ├── models.ts # Model listing
│ ├── responses.ts # Responses API
│ └── ...
├── provider/
│ ├── catalog/ # Provider definitions
│ │ ├── free.ts # Free providers
│ │ ├── apikey.ts # API key providers
│ │ └── oauth.ts # OAuth providers
│ ├── translator/ # Request/response translation layer
│ │ ├── index.ts # Barrel export
│ │ ├── openai.ts # filterToOpenAIFormat — message & tool normalization
│ │ ├── thinking.ts # Thinking/reasoning translation
│ │ ├── thinkingNormalize.ts # Strip thinking on non-user turns
│ │ ├── reasoningInject.ts # Inject reasoning_content placeholder
│ │ ├── params.ts # Provider-specific param filtering
│ │ ├── maxTokens.ts # Max token adjustment
│ │ ├── finishReason.ts # Finish reason mapping
│ │ ├── gemini.ts # Gemini schema cleaning + safety settings
│ │ ├── usage.ts # Usage extraction
│ │ ├── claudeSignature.ts # Claude thinking signature validation
│ │ ├── claudeCloaking.ts # OAuth cloaking (anti-ban)
│ │ ├── modality.ts # Modality stripping (image/audio/pdf)
│ │ ├── prefetch.ts # Image URL→base64 with SSRF protection
│ │ ├── stream.ts # Stream translation (Claude/Gemini→OpenAI)
│ │ ├── toolDedupe.ts # Tool deduplication
│ │ └── json.ts # Safe JSON parsing
│ ├── oauth/ # OAuth implementation
│ │ ├── manager.ts # Device code + auth code flows
│ │ ├── pkce.ts # PKCE helpers
│ │ └── server.ts # Loopback callback server
│ ├── proxy/ # Proxy pool
│ ├── registry.ts # Provider registry
│ └── service.ts # Chat routing (streaming + non-streaming)
├── account/
│ └── manager.ts # Multi-account management
├── core/
│ ├── config.ts # Config load/save (mtime-cached combos)
│ ├── types.ts # TypeScript types
│ ├── storage.ts # Usage tracking (buffered async writes)
│ ├── bus.ts # Event bus
│ └── global.ts # Paths and directory setup
├── util/
│ ├── log.ts # Logging
│ ├── tokenizer.ts # Token estimation
│ └── index.ts # Utilities
└── tui/
└── screens/ # TUI screensChangelog
v1.6.3
- Fix:
filterToOpenAIFormattools/tool_choice normalization works without messages - Fix:
adjustMaxTokensAnthropic ceiling increased to128k for thinking budgets - E2E test suite: 91 tests covering all translator modules
v1.6.2
- Fix: Eliminate all mutation bugs in translator pipeline (immutable transforms)
normalizeThinkingConfig,stripUnsupportedModalities,stripUnsupportedParamsnow return new objectsnormalizeToolMessagesdeep-clones nested tool_calls/toolCalls/content
v1.6.1
- Claude OAuth cloaking: billing header, fake user ID, tool name renaming with _cc suffix
- Response decloaking: strip _cc suffix from tool_use names
v1.6.0
- Stream translation: Claude→OpenAI, Gemini→OpenAI SSE format conversion
- Image prefetch: URL→base64 with SSRF protection
- Tool deduplication: remove duplicate built-in vs MCP tools
- Full paramSupport rules (Claude, Copilot, Cloudflare, VolcEngine, Ollama)
safeParseJSON+ensureJSONString
v1.5.5
- Modality stripping: remove image/audio/pdf for text-only models
- Claude thinking block signature validation
v1.5.4
- Claude thinking block signature validation (drop invalid, inject placeholders)
v1.5.3
- Claude cache control management (strip all, add to last assistant/system/tool)
fixToolUseOrderingrewrite: remove text after tool_use, merge same-role
v1.5.2
reasoning_contentinjection for DeepSeek/Kimi/MiniMax/GLM/Step/Hunyuan- Thinking config normalization (strip on non-user turns)
v1.5.1
- Preserve
reasoning_contentin all code paths filterToOpenAIFormatreturns assistant messages with reasoning as-is
v1.5.0
- Translator layer (ported from9router): 16 modules
filterToOpenAIFormat, thinking translation, param filtering, finish reason mapping- Gemini schema cleaning, safety settings
- Usage extraction with cache/reasoning token tracking
v1.4.2
- Fix: Service install uses effective user (no more
User=mismatch with sudo)
v1.4.1
- Fix:
venagate statusnow properly loads auth config
v1.4.0
- SSE streaming support —
stream: trueforwarded to provider, raw SSE piped to client - New
chatStream()method in AIService
v1.3.0
- Improved token counting (CJK/code-aware estimator)
- Combo cache (mtime-based, no disk read per request)
- Buffered async usage writes (5s flush interval)
- Proper Responses API format translation
X-Venagate-Streamheader for streaming status
v1.2.0
- Wire up
initLog()andinitStorage()(logging + usage tracking now functional) - 10MB request body size limit
- Clean all stale
ultimea/vrouterreferences - Service install auto-detects user via
SUDO_USER
v1.1.0
- Rename
VROUTER_*env vars toVENAGATE_* - Service install auto-detects user and home directory
User=andWorkingDirectory=in systemd service
License
MIT
