Compare commits
18 Commits
docs/dedup
...
test/v3851
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ad3e293f9f | ||
|
|
c9f11d86b5 | ||
|
|
21ed68d8ac | ||
|
|
d35b3f9779 | ||
|
|
9587e07b69 | ||
|
|
12cc6ca834 | ||
|
|
4f11b2ae3d | ||
|
|
b4e76a7ed9 | ||
|
|
b85d0abb0b | ||
|
|
1325047d56 | ||
|
|
2e8326d531 | ||
|
|
7133585d1d | ||
|
|
bcda889f84 | ||
|
|
91ecb6be9c | ||
|
|
e02f11984f | ||
|
|
ca4df9bef8 | ||
|
|
0ce2b83005 | ||
|
|
124f4cf761 |
@@ -46,7 +46,7 @@ Repository map and Reference Documentation sections below.
|
||||
|
||||
## Project at a Glance
|
||||
|
||||
**OmniRoute** — unified AI proxy/router. One endpoint, 356 LLM providers, auto-fallback.
|
||||
**OmniRoute** — unified AI proxy/router. One endpoint, 357 LLM providers, auto-fallback.
|
||||
|
||||
| Layer | Location | Purpose |
|
||||
| ------------- | ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
|
||||
36
Dockerfile
@@ -184,19 +184,29 @@ ENV NODE_OPTIONS="--max-old-space-size=${OMNIROUTE_BUILD_MEMORY_MB}"
|
||||
# silently leaving no standalone bundle. Next derives the worker count from
|
||||
# CIRCLE_NODE_TOTAL (workers = N-1). (#10060)
|
||||
#
|
||||
# Lowered 8 → 3 (7 workers → 2). Every page-data worker inherits NODE_OPTIONS
|
||||
# above, so the ceiling is per PROCESS, not per build: 7 workers on a 16 GB
|
||||
# GitHub runner (ubuntu-24.04 / ubuntu-24.04-arm, 4 vCPU) exhausted the host and
|
||||
# buildkit failed the whole step with `ResourceExhausted: ... cannot allocate
|
||||
# memory`. The compile phase always finished ("✓ Compiled successfully in
|
||||
# 4.2min"); the kernel killed the build right after "Collecting page data using
|
||||
# 7 workers". It was intermittent for a while and went 100% on 2026-08-22, which
|
||||
# is what a threshold being crossed by ordinary codebase growth looks like.
|
||||
# tests/unit/docker-build-memory-budget.test.ts does the arithmetic and fails if
|
||||
# either knob is raised past what a 16 GB runner holds. 2 workers also stops
|
||||
# oversubscribing the runner's 4 vCPU, which 7 did. Override for a big builder:
|
||||
# `--build-arg OMNIROUTE_BUILD_WORKERS=8`.
|
||||
ARG OMNIROUTE_BUILD_WORKERS=3
|
||||
# Lowered 8 → 3 (7 workers → 2) in #11419, then 3 → 2 (2 workers → 1) in #7518.
|
||||
# Every page-data worker inherits NODE_OPTIONS above, so the ceiling is per
|
||||
# PROCESS, not per build: 7 workers on a 16 GB GitHub runner (ubuntu-24.04 /
|
||||
# ubuntu-24.04-arm, 4 vCPU) exhausted the host and buildkit failed the whole
|
||||
# step with `ResourceExhausted: ... cannot allocate memory`. The compile phase
|
||||
# always finished ("✓ Compiled successfully in 4.2min"); the kernel killed the
|
||||
# build right after "Collecting page data using N workers".
|
||||
#
|
||||
# #11419's first fix (8 → 3) modeled the per-worker peak as an INFERENCE
|
||||
# (2560 MB, guessed from "7 workers didn't fit") and assumed the parent
|
||||
# process's RSS tracked the V8 heap ceiling. Both assumptions were wrong: a
|
||||
# live VPS reproduction (issue #7518, dmesg OOM-killer report) measured the
|
||||
# real per-process RSS directly at ~4.5 GB, independent of the NODE_OPTIONS
|
||||
# heap flag (Turbopack itself is native/Rust, outside the V8 heap) — and it
|
||||
# applies to the parent process too, not just workers. 2 workers (3 processes
|
||||
# × 4.5 GB = 13.5 GB) still didn't fit the 12.288 GB (75%) budget on a 16 GB
|
||||
# runner, matching the still-live publish failures after #11419 merged. 1
|
||||
# worker (2 processes × 4.5 GB = 9 GB) fits with headroom to spare.
|
||||
# tests/unit/docker-build-memory-budget.test.ts does the arithmetic against
|
||||
# the measured figure and fails if either knob is raised past what a 16 GB
|
||||
# runner holds. Override for a big builder: `--build-arg
|
||||
# OMNIROUTE_BUILD_WORKERS=8`.
|
||||
ARG OMNIROUTE_BUILD_WORKERS=2
|
||||
ENV CIRCLE_NODE_TOTAL=${OMNIROUTE_BUILD_WORKERS}
|
||||
|
||||
COPY . ./
|
||||
|
||||
10
README.md
@@ -7,7 +7,7 @@
|
||||
|
||||
# 🚀 OmniRoute — The Free AI Gateway
|
||||
|
||||
<img src="./docs/diagrams/readme-hero.svg" width="100%" alt="OmniRoute — Never stop coding. Every AI tool → 356 providers — 90+ free — through one endpoint. Claude Code, Codex, Cursor, Cline, Copilot & Antigravity into FREE Claude / GPT / Gemini with auto-fallback. RTK + Caveman stacked compression saves 15–95% tokens (~89% avg) — never hit limits. 356 AI providers · 90+ free tiers · ~1.51B free tokens/mo · 19 routing strategies · $0 to start."/>
|
||||
<img src="./docs/diagrams/readme-hero.svg" width="100%" alt="OmniRoute — Never stop coding. Every AI tool → 357 providers — 90+ free — through one endpoint. Claude Code, Codex, Cursor, Cline, Copilot & Antigravity into FREE Claude / GPT / Gemini with auto-fallback. RTK + Caveman stacked compression saves 15–95% tokens (~89% avg) — never hit limits. 357 AI providers · 90+ free tiers · ~1.51B free tokens/mo · 19 routing strategies · $0 to start."/>
|
||||
|
||||
</div>
|
||||
|
||||
@@ -210,7 +210,7 @@ curl http://localhost:20128/v1/chat/completions \
|
||||
|
||||
</div>
|
||||
|
||||
<img src="./docs/diagrams/promise-pillars.svg" width="100%" alt="The Promise — One endpoint and 356 providers. Automatic fallback keeps routing while another healthy target is available. Six pillars: resilient fallback across 356 providers · up to 95% token savings on eligible workloads · $0 to start with 90+ free tiers and 56 recurring/keyless free-forever providers · 35 CLI/agent integrations through one config · OpenAI, Claude, Gemini and Responses API compatibility at /v1 · production controls including circuit breakers, TLS stealth, MCP 110 tools, A2A, memory, guardrails, evals and 39,000+ static test declarations across 5,100+ tracked test files."/>
|
||||
<img src="./docs/diagrams/promise-pillars.svg" width="100%" alt="The Promise — One endpoint and 357 providers. Automatic fallback keeps routing while another healthy target is available. Six pillars: resilient fallback across 357 providers · up to 95% token savings on eligible workloads · $0 to start with 90+ free tiers and 56 recurring/keyless free-forever providers · 35 CLI/agent integrations through one config · OpenAI, Claude, Gemini and Responses API compatibility at /v1 · production controls including circuit breakers, TLS stealth, MCP 110 tools, A2A, memory, guardrails, evals and 39,000+ static test declarations across 5,100+ tracked test files."/>
|
||||
|
||||
<br/>
|
||||
<br/>
|
||||
@@ -461,7 +461,7 @@ All **19** strategies — mix & match per combo step:
|
||||
|
||||
</div>
|
||||
|
||||
<img src="./docs/diagrams/comparison-table.svg" width="100%" alt="What sets OmniRoute apart — a dated feature snapshot vs 9router, OpenRouter, CLIProxyAPI and LiteLLM across 13 capabilities. OmniRoute: 356 providers, 90+ free tiers built in, 19 routing strategies, 12-engine token compression, built-in MCP server with 110 tools, A2A agent protocol, persistent memory, guardrails, cloud agents, TLS fingerprint stealth, Desktop/Termux/PWA and 43 i18n UI locales. OmniRoute is MIT-licensed and self-hostable. Competitor capabilities and counts may change; see the linked methodology."/>
|
||||
<img src="./docs/diagrams/comparison-table.svg" width="100%" alt="What sets OmniRoute apart — a dated feature snapshot vs 9router, OpenRouter, CLIProxyAPI and LiteLLM across 13 capabilities. OmniRoute: 357 providers, 90+ free tiers built in, 19 routing strategies, 12-engine token compression, built-in MCP server with 110 tools, A2A agent protocol, persistent memory, guardrails, cloud agents, TLS fingerprint stealth, Desktop/Termux/PWA and 43 i18n UI locales. OmniRoute is MIT-licensed and self-hostable. Competitor capabilities and counts may change; see the linked methodology."/>
|
||||
|
||||
<sub>📊 Full methodology & per-feature detail vs 9router, OpenRouter, CLIProxyAPI & LiteLLM → [`docs/comparison/OMNIROUTE_VS_ALTERNATIVES.md`](docs/comparison/OMNIROUTE_VS_ALTERNATIVES.md)</sub>
|
||||
|
||||
@@ -559,7 +559,7 @@ the current catalog at **[radar.omniroute.online/planos](https://radar.omniroute
|
||||
- **🖼️ New endpoints** — `/v1/ocr` (Mistral OCR) and `/v1/audio/translations` (Whisper-style) round out the media surface. → [API Reference](docs/reference/API_REFERENCE.md)
|
||||
- **🎨 Image / video / audio generation** — one API for media: xAI Grok Imagine & Novita AI video, ComfyUI, Freepik, Adobe Firefly, Microsoft Designer, Segmind, EdgeTTS. → [API Reference](docs/reference/API_REFERENCE.md)
|
||||
- **🌍 Deployment & ops** — reverse-proxy `basePath`, browser-language auto-detect, per-key device tracking, root-less MITM trust, zh-TW localization. → [Environment](docs/reference/ENVIRONMENT.md)
|
||||
- **🤝 More providers & agents** — Cursor Cloud Agent, Grok Build (xAI) with browser + OAuth login, Ollama first-class card, Claude Opus 5 & Sonnet 5, Kimi official partnership (Code/Web/Moonshot), Zed, Requesty, SenseNova, Yuanbao, Agnes AI… and a refreshed **356-provider catalog**. → [Providers](docs/reference/PROVIDER_REFERENCE.md)
|
||||
- **🤝 More providers & agents** — Cursor Cloud Agent, Grok Build (xAI) with browser + OAuth login, Ollama first-class card, Claude Opus 5 & Sonnet 5, Kimi official partnership (Code/Web/Moonshot), Zed, Requesty, SenseNova, Yuanbao, Agnes AI… and a refreshed **357-provider catalog**. → [Providers](docs/reference/PROVIDER_REFERENCE.md)
|
||||
- **📡 Routing transparency** — every response carries an `X-OmniRoute-Decision` header naming the strategy/provider/latency that served it, a new `cache-optimized` combo strategy + Auto-Combo `cacheAffinity` factor route repeat requests back to the connection holding the cached prefix, and a read-only `/v1/auto-combo/{channel}/candidates` endpoint exposes an `auto/*` channel's live candidate pool. → [Auto-Combo](docs/routing/AUTO-COMBO.md)
|
||||
- **⚡ Local performance & infra** — one-click local Redis, Cloudflare Workers / Deno Deploy relay deployers, Bifrost & Mux as supervised embedded services. → [Embedded Services](docs/frameworks/EMBEDDED-SERVICES.md)
|
||||
|
||||
@@ -642,7 +642,7 @@ of your shell history. → [CLI Integrations](docs/guides/CLI-INTEGRATIONS.md)
|
||||
|
||||
<div align="center">
|
||||
|
||||
## 🌐 356 AI Providers — 154 Catalog-Marked Free
|
||||
## 🌐 357 AI Providers — 154 Catalog-Marked Free
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
1
changelog.d/fixes/11296-kie-market-model-id-sweep.md
Normal file
@@ -0,0 +1 @@
|
||||
- **fix(kie):** correct 12 more KIE Market catalog ids that were sent to `createTask` unchanged but diverge from KIE's documented upstream `model` values — GPT Image 2 T2I/I2I (drops the `gpt/` prefix), GPT Image 1.5 T2I/I2I (`gpt-image/` namespace), Seedream 5.0 Lite T2I/I2I (drops the `.0`), all 4 Flux 2 variants (`flux-2/` namespace, generic variant renamed `flex`), and Wan 2.7 Image / Image Pro (dash instead of dot) — each verified individually against the literal example request published on docs.kie.ai. `#11326`'s "everything else already matches" claim was wrong a second time (#11296); `z-image/4.0-*`/`z-image/4.5-*` and `flux/kontext` remain open, documented as unresolved in `KIE_MARKET_UPSTREAM_MODEL_IDS`'s comment pending further verification.
|
||||
@@ -0,0 +1 @@
|
||||
- **fix(dashboard):** `useApiKeySave.handleSaveApiKey` no longer forces a full upstream `/models` catalog sync on every non-curated provider connection save — callers can now pass `skipModelSync: true` to opt out, so a workflow that only wants to add one manual model no longer floods the provider's available-models list with hundreds/thousands of synced entries. The flag is a client-side intent signal only and is stripped before the connection payload is POSTed to `/api/providers`; default behavior (full sync on save) is unchanged when the flag is omitted (#11324)
|
||||
1
changelog.d/fixes/11433-opencode-alias-collision.md
Normal file
@@ -0,0 +1 @@
|
||||
- **fix(combos):** the combo builder's precision-select, global-model-search, and manual-entry flows now serialize a model step's `model` string using the provider's already-computed routing-alias prefix (e.g. `oc/`) instead of rebuilding it from the raw canonical `providerId`, fixing the no-auth "OpenCode Free" provider (`opencode`) being routed to the unrelated paid "OpenCode Zen" provider (`opencode-zen`) because `opencode` doubles as a manual routing-prefix override ([#11433](https://github.com/diegosouzapw/OmniRoute/issues/11433)).
|
||||
1
changelog.d/fixes/11449-anysearch-icon-fallback.md
Normal file
@@ -0,0 +1 @@
|
||||
- fix(ui): let AnySearch use the normal provider-icon fallback when LobeHub has no matching icon (#11449)
|
||||
1
changelog.d/fixes/11462-roundrobin-combo-diagnostics.md
Normal file
@@ -0,0 +1 @@
|
||||
- **fix(combo):** attach the same combo-diagnostics trace (`poolSize`/`attemptOrder`/`excluded`/`terminalReason`, plus `x-omniroute-combo-*` headers) to the round-robin strategy's and the nested pipeline/fusion runtime-unit loop's "Maximum combo retry limit reached" 503 that the priority-strategy path already attaches for the identical terminal condition — previously those two paths returned a bare, contextless 503 ([#11462](https://github.com/diegosouzapw/OmniRoute/issues/11462)).
|
||||
1
changelog.d/fixes/11500-decrypt-log-dedup.md
Normal file
@@ -0,0 +1 @@
|
||||
- **fix(db):** dedupe the raw `[Encryption] Decryption failed...` log line emitted by the lazy-decrypt views (`createLazyRowProxy`/`createLazyConnectionView`), which power `getProviderConnections()` and were re-triggering that line on every CredentialHealth/model-sync cycle for the same corrupt or stale-key credential — a fresh Proxy over a fresh row on every cycle meant the per-proxy memoization never suppressed it, unlike the dedup `decryptConnectionFields()` already had since [#9927](https://github.com/diegosouzapw/OmniRoute/issues/9927). Now shares that dedupe tracking so the line logs at most once per credential ([#11500](https://github.com/diegosouzapw/OmniRoute/issues/11500)).
|
||||
@@ -0,0 +1 @@
|
||||
- **fix(sse):** stop dropping resolved `thoughtSignature` values on parallel (multi tool-call) turns sent to Gemini 3.x — the claude→gemini and openai→gemini translators previously kept the signature only on the *first* function call of a message, causing Gemini to reject subsequent calls in the same turn with HTTP 400 "Function call is missing a thought_signature"; each function call now keeps its own resolved signature ([#11510](https://github.com/diegosouzapw/OmniRoute/issues/11510)).
|
||||
@@ -0,0 +1 @@
|
||||
- **fix(sse):** cap the upstream headers-wait phase for STREAMING requests to a client-realistic ceiling (110s, under Codex's own ~120s hard client-abort window) instead of the flat 10-minute `FETCH_TIMEOUT_MS` default — that default was 5x longer than the body-phase readiness watchdog's own adaptive bound, so a request whose upstream never returned any response at all (not even headers, e.g. a stalled NVIDIA target behind a tool-heavy Responses→Chat translation) kept the client connection alive on keepalives only, guaranteeing the client's own patience ran out first with an opaque 499 instead of OmniRoute detecting and failing the stall fast. Non-streaming requests are unaffected — they keep the existing flat default (`open-sse/utils/fetchStartTimeoutPolicy.ts`) (#11526)
|
||||
1
changelog.d/fixes/7518-docker-build-memory-budget.md
Normal file
@@ -0,0 +1 @@
|
||||
- **fix(docker):** re-derive the Docker build's worker-pool memory budget from the MEASURED ~4.5 GB per-process RSS (the issue owner's own VPS dmesg OOM-killer reproduction) instead of the stale 2560 MB/worker inference #11419 shipped, and lower `OMNIROUTE_BUILD_WORKERS` 3 → 2 so 1 parent + 1 page-data worker (2 processes × 4.5 GB = 9 GB) fits the 12.288 GB (75%) budget on a 16 GB GitHub Actions runner — the previous default (1 parent + 2 workers = 13.5 GB) still overcommitted the runner and kept "Publish to Docker Hub" failing with `cannot allocate memory` after #11419 merged (#7518).
|
||||
@@ -3433,7 +3433,7 @@
|
||||
"count": 15
|
||||
},
|
||||
"@typescript-eslint/no-unused-vars": {
|
||||
"count": 2
|
||||
"count": 1
|
||||
}
|
||||
},
|
||||
"tests/integration/traffic-inspector-error-sanitization.test.ts": {
|
||||
@@ -3680,11 +3680,6 @@
|
||||
"count": 15
|
||||
}
|
||||
},
|
||||
"tests/unit/authz/probe-9033-repro.test.ts": {
|
||||
"@typescript-eslint/no-unused-vars": {
|
||||
"count": 1
|
||||
}
|
||||
},
|
||||
"tests/unit/autoCombo/tieredRotation.test.ts": {
|
||||
"@typescript-eslint/no-unused-vars": {
|
||||
"count": 3
|
||||
|
||||
@@ -188,14 +188,15 @@
|
||||
"dedicatedGate": true
|
||||
},
|
||||
"bundleSize": {
|
||||
"value": 8461,
|
||||
"value": 8653,
|
||||
"direction": "down",
|
||||
"dedicatedGate": true,
|
||||
"_rebaseline_2026_07_07_v3846_release_close": "5601->6534 (+933). v3.8.46 release close: gzip of the 4 bin/*.mjs entrypoints (size-limit + @size-limit/file) grew from this cycle's feature/fix merges pulled transitively into the CLI entrypoints (new providers, combo pipeline strategy #6396, effort/thinking standardization #6241, catalog cache-invalidation #6408). Measured 6534 locally via `check:bundle-size --ratchet` (deterministic gzip, matches CI). Legitimate cycle growth; shrink is separate debt.",
|
||||
"_rebaseline_2026_07_19_7808_codeql_alias_resolver_hook": "6534->6762 (+228). PR #7808 (CodeQL js/incomplete-url-substring-sanitization fix): the ESM loader hook source moved out of the inline `HOOK_SOURCE` template literal in bin/aliasResolver.mjs into a real file bin/aliasResolverHook.mjs, loaded via pathToFileURL() instead of a dynamically-built `data:text/javascript,...` URL. The new file is now counted by size-limit as a 5th bin/*.mjs entrypoint. Net +228 = the hook's gzip size (previously hidden inside aliasResolver.mjs because the template literal was compressed away). Security-driven; no shrink opportunity.",
|
||||
"_rebaseline_2026_07_28_v3849_release_preflight": "6762 -> 7666 (+904). Fechamento do ciclo v3.8.49: gzip dos entrypoints bin/*.mjs (size-limit + @size-limit/file) cresceu com o que os merges do ciclo puxam transitivamente para o CLI (novos provedores — 271->290, seletor de protocolo por conexão #8861, catálogos de busca #8814, resiliência). Crescimento legítimo de ciclo, medido localmente com `npm run check:bundle-size` = 7666 (gzip determinístico, bate com o CI). Encolher é dívida separada.",
|
||||
"_rebaseline_2026_08_09_v3850_release_close": "7666 -> 8045 (+379 gzip bytes, +4.9%). Release v3.8.50 close reconciliation measured twice with the real size-limit + @size-limit/file path on tip e0ce95c592. Per-entry measurements remain below their absolute budgets: omniroute.mjs 4380/15000, mcp-server.mjs 1195/5000, nodeRuntimeSupport.mjs 887/8000, reset-password.mjs 1583/6000. The growth accumulated through legitimate CLI/runtime work in this cycle, including global-install ESM alias resolution, Termux cache preparation, and MCP stdio startup hardening; no entrypoint is near its absolute ceiling. The direction:down ratchet stays blocking from this exact measured tip.",
|
||||
"_rebaseline_2026_08_24_ci_green_gates_f95b03d7": "8045 -> 8461 (+416 gzip bytes, +5.2%). CI run 32786966560 (release PR #8875, tip f95b03d7) measured bundleSize=8461 via check:bundle-size --ratchet, above the 8045 baseline left at the v3.8.50 close. The growth comes from the post-freeze back-merge cycle landing in the CLI entrypoints (Synthetic + Kilo Gateway providers, kilo-gateway routing surface). Re-baseline per the gate's own instruction (legitimate cycle growth); shrinking the entrypoints remains separate debt; direction:down ratchet stays blocking from this measured tip."
|
||||
"_rebaseline_2026_08_24_ci_green_gates_f95b03d7": "8045 -> 8461 (+416 gzip bytes, +5.2%). CI run 32786966560 (release PR #8875, tip f95b03d7) measured bundleSize=8461 via check:bundle-size --ratchet, above the 8045 baseline left at the v3.8.50 close. The growth comes from the post-freeze back-merge cycle landing in the CLI entrypoints (Synthetic + Kilo Gateway providers, kilo-gateway routing surface). Re-baseline per the gate's own instruction (legitimate cycle growth); shrinking the entrypoints remains separate debt; direction:down ratchet stays blocking from this measured tip.",
|
||||
"_rebaseline_2026_08_27_v3851_volatile_env_warning_11437": "8461 -> 8653 (+192 gzip bytes, +2.3%). Exact paired size-limit measurements on the VPS compared f95b03d709 with release/v3.8.51: only bin/omniroute.mjs changed, 4700 -> 4892; the other three entries remained 1195/983/1583. The growth originates in 943b9aaa84 (#11437), which warns users before a package-local .env is lost on the next global install. The CLI entry remains 4892/15000 bytes (32.6% of its absolute budget). Legitimate bug-fix growth; shrinking stays separate debt and direction:down remains blocking from this measured tip."
|
||||
},
|
||||
"openapiBreaking": {
|
||||
"value": 4,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<svg viewBox="0 0 1200 350" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Animated terminal demoing the OmniRoute CLI: omniroute providers list (356 providers registered, anthropic, codex, glm, kimi shown active), omniroute combo list (always-on priority, cost-saver, fusion-panel, context-relay) and omniroute health (healthy, 18412 requests in 24h, p95 412ms, circuit breakers 24 closed, 1 half-open, 0 open), cycling over 85 top-level commands: providers, oauth, keys, combo, nodes, models, cache, compression, cost, usage, quota, health, resilience, telemetry, logs, audit, mcp, a2a, cloud, memory, skills, eval, doctor, repl, tunnel, backup, sync, webhooks, policy, pricing, translator, simulate and more.">
|
||||
<svg viewBox="0 0 1200 350" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Animated terminal demoing the OmniRoute CLI: omniroute providers list (357 providers registered, anthropic, codex, glm, kimi shown active), omniroute combo list (always-on priority, cost-saver, fusion-panel, context-relay) and omniroute health (healthy, 18412 requests in 24h, p95 412ms, circuit breakers 24 closed, 1 half-open, 0 open), cycling over 85 top-level commands: providers, oauth, keys, combo, nodes, models, cache, compression, cost, usage, quota, health, resilience, telemetry, logs, audit, mcp, a2a, cloud, memory, skills, eval, doctor, repl, tunnel, backup, sync, webhooks, policy, pricing, translator, simulate and more.">
|
||||
<desc>Compact animated terminal cycling three real OmniRoute CLI commands with a typewriter effect and a scrolling subcommand ticker; the first frame shows the completed providers-list screen.</desc>
|
||||
<defs><clipPath id="tickerClip"><rect x="12" y="304" width="1176" height="40"/></clipPath><clipPath id="tw0"><rect x="64" y="46" height="26" width="0"><animate attributeName="width" calcMode="discrete" values="0;31;61;92;122;153;184;214;245;245" keyTimes="0;0.012;0.018;0.024;0.030;0.036;0.042;0.048;0.054;1" dur="18s" repeatCount="indefinite"/></rect></clipPath><clipPath id="tw1"><rect x="64" y="46" height="26" width="0"><animate attributeName="width" calcMode="discrete" values="0;26;51;76;102;128;153;178;204;204" keyTimes="0;0.348;0.351;0.357;0.363;0.369;0.375;0.381;0.387;1" dur="18s" repeatCount="indefinite"/></rect></clipPath><clipPath id="tw2"><rect x="64" y="46" height="26" width="0"><animate attributeName="width" calcMode="discrete" values="0;20;41;61;82;102;122;143;163;163" keyTimes="0;0.678;0.684;0.690;0.696;0.702;0.708;0.714;0.720;1" dur="18s" repeatCount="indefinite"/></rect></clipPath></defs>
|
||||
<rect width="1200" height="350" fill="#0d1117"/>
|
||||
|
||||
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
@@ -1,4 +1,4 @@
|
||||
<svg viewBox="0 0 1200 780" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Comparison table: OmniRoute versus 9router, OpenRouter, CLIProxyAPI and LiteLLM across 13 capabilities. OmniRoute is the only one with the full set: 356 providers, 90+ free providers built-in, 19 routing strategies, 12-engine token compression, a built-in MCP server with 110 tools, A2A protocol, persistent memory, guardrails, cloud agents, TLS fingerprint stealth, desktop/Termux/PWA, 43 UI locales and 100% MIT self-hosted. 9router has free providers, RTK compression and translation but no MCP, A2A, memory, guardrails, cloud agents or stealth. OpenRouter is a hosted SaaS with 400+ models, guardrails and a hosted MCP but is not self-hosted and lacks A2A, memory, cloud agents and stealth. CLIProxyAPI is a light OAuth proxy with two routing strategies. LiteLLM has 100+ providers, A2A and extensive guardrails but no memory, compression, free tier, stealth or cloud agents.">
|
||||
<svg viewBox="0 0 1200 780" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Comparison table: OmniRoute versus 9router, OpenRouter, CLIProxyAPI and LiteLLM across 13 capabilities. OmniRoute is the only one with the full set: 357 providers, 90+ free providers built-in, 19 routing strategies, 12-engine token compression, a built-in MCP server with 110 tools, A2A protocol, persistent memory, guardrails, cloud agents, TLS fingerprint stealth, desktop/Termux/PWA, 43 UI locales and 100% MIT self-hosted. 9router has free providers, RTK compression and translation but no MCP, A2A, memory, guardrails, cloud agents or stealth. OpenRouter is a hosted SaaS with 400+ models, guardrails and a hosted MCP but is not self-hosted and lacks A2A, memory, cloud agents and stealth. CLIProxyAPI is a light OAuth proxy with two routing strategies. LiteLLM has 100+ providers, A2A and extensive guardrails but no memory, compression, free tier, stealth or cloud agents.">
|
||||
<desc>Static-header comparison table where each capability row fades in top to bottom; the OmniRoute column is highlighted and shows a check or a leading value in every row, while competitors show a mix of checks, partials and crosses.</desc>
|
||||
<defs>
|
||||
<pattern id="gC" width="32" height="32" patternUnits="userSpaceOnUse"><path d="M 32 0 L 0 0 0 32" fill="none" stroke="#ffffff" stroke-opacity="0.05" stroke-width="1"/></pattern>
|
||||
|
||||
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 13 KiB |
@@ -1,4 +1,4 @@
|
||||
<svg viewBox="0 0 1200 540" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="The OmniRoute promise: one endpoint and 356 providers. Six pillars. Resilient fallback: automatic routing continues while another healthy target is available. Save up to 95 percent of eligible tokens: RTK plus Caveman stacked compression averages about 89 percent on tool-heavy sessions. Zero dollars to start: 90+ providers with a free tier and 56 recurring or keyless free-forever providers. Every tool works: 35 CLI and agent integration records, including Claude Code, Codex, Cursor, Cline, Copilot and Antigravity, through one config. One endpoint: OpenAI, Claude, Gemini and Responses API translation at /v1. Production controls: circuit breakers, TLS stealth, MCP with 110 tools, A2A, memory, guardrails, evals, and 39,000+ static test declarations across 5,100+ tracked test files.">
|
||||
<svg viewBox="0 0 1200 540" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="The OmniRoute promise: one endpoint and 357 providers. Six pillars. Resilient fallback: automatic routing continues while another healthy target is available. Save up to 95 percent of eligible tokens: RTK plus Caveman stacked compression averages about 89 percent on tool-heavy sessions. Zero dollars to start: 90+ providers with a free tier and 56 recurring or keyless free-forever providers. Every tool works: 35 CLI and agent integration records, including Claude Code, Codex, Cursor, Cline, Copilot and Antigravity, through one config. One endpoint: OpenAI, Claude, Gemini and Responses API translation at /v1. Production controls: circuit breakers, TLS stealth, MCP with 110 tools, A2A, memory, guardrails, evals, and 39,000+ static test declarations across 5,100+ tracked test files.">
|
||||
<desc>Animated promise card: six pillar tiles fade in in reading order, then a soft colored border highlight sweeps from tile to tile in a continuous cycle.</desc>
|
||||
<defs>
|
||||
<pattern id="gridPaperP" width="32" height="32" patternUnits="userSpaceOnUse">
|
||||
@@ -21,7 +21,7 @@
|
||||
<line x1="150" y1="53" x2="1160" y2="53" stroke="#232b38" stroke-width="1.5"/>
|
||||
</g>
|
||||
<g>
|
||||
<text x="40" y="100" font-family="Inter, 'Segoe UI', Arial, Helvetica, system-ui, sans-serif" font-size="23" font-weight="600" fill="#c9d1d9">One endpoint. <tspan fill="#a78bfa" font-weight="800">356 providers.</tspan> Never stop building — OmniRoute picks <tspan fill="#7ee787" font-weight="700">the cheapest one that works</tspan>.</text>
|
||||
<text x="40" y="100" font-family="Inter, 'Segoe UI', Arial, Helvetica, system-ui, sans-serif" font-size="23" font-weight="600" fill="#c9d1d9">One endpoint. <tspan fill="#a78bfa" font-weight="800">357 providers.</tspan> Never stop building — OmniRoute picks <tspan fill="#7ee787" font-weight="700">the cheapest one that works</tspan>.</text>
|
||||
</g>
|
||||
|
||||
<g font-family="Inter, 'Segoe UI', Arial, Helvetica, system-ui, sans-serif">
|
||||
@@ -38,7 +38,7 @@
|
||||
<line x1="3.9" y1="3.9" x2="18.1" y2="18.1"/>
|
||||
</g>
|
||||
<text x="102" y="170" font-size="18" font-weight="800" fill="#74b9ff">Never hit limits</text>
|
||||
<text x="66" y="204" font-size="13.5" fill="#a1a1aa">Auto-fallback across 356 providers in</text>
|
||||
<text x="66" y="204" font-size="13.5" fill="#a1a1aa">Auto-fallback across 357 providers in</text>
|
||||
<text x="66" y="226" font-size="13.5" fill="#a1a1aa">milliseconds. Quota out? The next provider</text>
|
||||
<text x="66" y="248" font-size="13.5" fill="#a1a1aa">takes over while a healthy target remains.</text>
|
||||
</g>
|
||||
|
||||
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 10 KiB |
@@ -1,4 +1,4 @@
|
||||
<svg viewBox="0 0 1200 548" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="OmniRoute hero: Never stop coding. Every AI tool to 356 providers — 90+ free — through one endpoint. Claude Code, Codex, Cursor, Cline, Copilot and Antigravity into free Claude, GPT and Gemini with auto-fallback. RTK + Caveman stacked compression saves 15 to 95 percent of tokens — about 89 percent average on tool-heavy sessions — so you never hit limits. Stats: 356 AI providers, 90+ free tiers, about 1.51B free tokens per month, 15 to 95 percent token savings, 19 routing strategies, zero dollars to start.">
|
||||
<svg viewBox="0 0 1200 548" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="OmniRoute hero: Never stop coding. Every AI tool to 357 providers — 90+ free — through one endpoint. Claude Code, Codex, Cursor, Cline, Copilot and Antigravity into free Claude, GPT and Gemini with auto-fallback. RTK + Caveman stacked compression saves 15 to 95 percent of tokens — about 89 percent average on tool-heavy sessions — so you never hit limits. Stats: 357 AI providers, 90+ free tiers, about 1.51B free tokens per month, 15 to 95 percent token savings, 19 routing strategies, zero dollars to start.">
|
||||
<desc>Animated hero card: a pulse travels the divider line and a compression bar demo repeatedly shrinks a prompt by up to 95 percent; all headline content is static and readable on the first frame.</desc>
|
||||
<defs>
|
||||
<pattern id="gridPaperH" width="32" height="32" patternUnits="userSpaceOnUse">
|
||||
@@ -28,7 +28,7 @@
|
||||
<text x="48" y="138" font-family="Inter, 'Segoe UI', Arial, Helvetica, system-ui, sans-serif" font-size="60" font-weight="800" fill="#e9edf3">Never stop coding<tspan fill="#a855f7">.</tspan></text>
|
||||
|
||||
<!-- subheadline -->
|
||||
<text x="48" y="184" font-family="Inter, 'Segoe UI', Arial, Helvetica, system-ui, sans-serif" font-size="25" font-weight="600" fill="#c9d1d9">Every AI tool → <tspan fill="#a78bfa" font-weight="800">356 providers</tspan> — <tspan fill="#7ee787" font-weight="800">90+ free</tspan> — through one endpoint.</text>
|
||||
<text x="48" y="184" font-family="Inter, 'Segoe UI', Arial, Helvetica, system-ui, sans-serif" font-size="25" font-weight="600" fill="#c9d1d9">Every AI tool → <tspan fill="#a78bfa" font-weight="800">357 providers</tspan> — <tspan fill="#7ee787" font-weight="800">90+ free</tspan> — through one endpoint.</text>
|
||||
|
||||
<!-- plug line -->
|
||||
<text x="48" y="222" font-family="Inter, 'Segoe UI', Arial, Helvetica, system-ui, sans-serif" font-size="16.5" fill="#a1a1aa">Claude Code · Codex · Cursor · Cline · Copilot · Antigravity  →  <tspan fill="#7ee787" font-weight="700">FREE</tspan> Claude / GPT / Gemini · auto-fallback</text>
|
||||
|
||||
|
Before Width: | Height: | Size: 7.3 KiB After Width: | Height: | Size: 7.3 KiB |
@@ -226,16 +226,21 @@ Three build args control what the `builder` stage costs. They are build-time onl
|
||||
| --------------------------- | ------- | ----------------------------------------------------------------------------------- |
|
||||
| `OMNIROUTE_USE_TURBOPACK` | `1` | `0` builds with webpack instead. Lower peak memory, slower. |
|
||||
| `OMNIROUTE_BUILD_MEMORY_MB` | `6144` | V8 heap ceiling (`--max-old-space-size`) for the spawned `next build`. |
|
||||
| `OMNIROUTE_BUILD_WORKERS` | `3` | Feeds `CIRCLE_NODE_TOTAL`; Next derives `workers = N - 1` for page-data collection. |
|
||||
| `OMNIROUTE_BUILD_WORKERS` | `2` | Feeds `CIRCLE_NODE_TOTAL`; Next derives `workers = N - 1` for page-data collection. |
|
||||
|
||||
`OMNIROUTE_BUILD_WORKERS` is the one to raise on a big builder and the one to
|
||||
suspect when a constrained build dies **after** `✓ Compiled successfully`. Each
|
||||
page-data worker is its own process and inherits `NODE_OPTIONS`, so the heap
|
||||
ceiling is per process, not per build: the default of `3` (→ 2 workers) is sized
|
||||
for the 16 GB / 4 vCPU GitHub-hosted runners the publish pipeline uses. At `8`
|
||||
(→ 7 workers) that runner ran out of memory and buildkit failed the step with
|
||||
`ResourceExhausted: ... cannot allocate memory`. `tests/unit/docker-build-memory-budget.test.ts`
|
||||
does the arithmetic and fails if either knob outgrows the runner.
|
||||
page-data worker is its own process, and so is the parent `next build` itself;
|
||||
a live VPS reproduction (issue #7518) measured each process's peak RSS at
|
||||
~4.5 GB independent of the `NODE_OPTIONS` heap flag (Turbopack compiles in
|
||||
native/Rust memory outside the V8 heap). The default of `2` (→ 1 worker, 2
|
||||
processes total) is sized for the 16 GB / 4 vCPU GitHub-hosted runners the
|
||||
publish pipeline uses. At `8` (→ 7 workers) that runner ran out of memory and
|
||||
buildkit failed the step with `ResourceExhausted: ... cannot allocate memory`;
|
||||
`3` (→ 2 workers) still didn't fit once the per-process RSS was measured
|
||||
directly instead of inferred. `tests/unit/docker-build-memory-budget.test.ts`
|
||||
does the arithmetic against the measured figure and fails if either knob
|
||||
outgrows the runner.
|
||||
|
||||
Turbopack compiles in native Rust memory that lives **outside** the V8 heap, so
|
||||
`OMNIROUTE_BUILD_MEMORY_MB` does not bound it. On a host with a memory ceiling the
|
||||
|
||||
@@ -4,8 +4,7 @@
|
||||
|
||||
---
|
||||
|
||||
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 356 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 357 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
|
||||
## Overview
|
||||
|
||||
@@ -282,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
## Key Features (v3.8.50)
|
||||
|
||||
### Core Proxy
|
||||
- **356 AI providers** with automatic format translation
|
||||
- **357 AI providers** with automatic format translation
|
||||
- **Provider categories**: Free (90+ free tiers), OAuth, API Key, Self-Hosted, Custom (OpenAI/Anthropic-compatible)
|
||||
- **18 routing strategies**: priority, weighted, round-robin, fill-first, p2c, random, least-used, cost-optimized, reset-aware, reset-window, headroom, strict-random, auto, lkgp, context-optimized, context-relay, fusion, pipeline
|
||||
- **4-tier fallback**: Subscription → API Key → Cheap → Free
|
||||
@@ -480,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
## v3.8.x Highlights
|
||||
|
||||
- **356-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **357-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **19 routing strategies** — including `fusion` (parallel panel + judge synthesis), `pipeline`, `reset-aware`, `reset-window`, `headroom`, and `context-relay`
|
||||
- **14-factor Auto-Combo scoring** with bandit exploration and progressive cooldown
|
||||
- **MCP server expanded to 110 tools / 33 scopes** (canonical + memory/skill/agentSkill/githubSkill/pool/notion/obsidian/localCorpus/gamification/plugin modules)
|
||||
|
||||
@@ -4,8 +4,7 @@
|
||||
|
||||
---
|
||||
|
||||
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 356 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 357 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
|
||||
## Overview
|
||||
|
||||
@@ -282,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
## Key Features (v3.8.50)
|
||||
|
||||
### Core Proxy
|
||||
- **356 AI providers** with automatic format translation
|
||||
- **357 AI providers** with automatic format translation
|
||||
- **Provider categories**: Free (90+ free tiers), OAuth, API Key, Self-Hosted, Custom (OpenAI/Anthropic-compatible)
|
||||
- **18 routing strategies**: priority, weighted, round-robin, fill-first, p2c, random, least-used, cost-optimized, reset-aware, reset-window, headroom, strict-random, auto, lkgp, context-optimized, context-relay, fusion, pipeline
|
||||
- **4-tier fallback**: Subscription → API Key → Cheap → Free
|
||||
@@ -480,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
## v3.8.x Highlights
|
||||
|
||||
- **356-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **357-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **19 routing strategies** — including `fusion` (parallel panel + judge synthesis), `pipeline`, `reset-aware`, `reset-window`, `headroom`, and `context-relay`
|
||||
- **14-factor Auto-Combo scoring** with bandit exploration and progressive cooldown
|
||||
- **MCP server expanded to 110 tools / 33 scopes** (canonical + memory/skill/agentSkill/githubSkill/pool/notion/obsidian/localCorpus/gamification/plugin modules)
|
||||
|
||||
@@ -4,8 +4,7 @@
|
||||
|
||||
---
|
||||
|
||||
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 356 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 357 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
|
||||
## Overview
|
||||
|
||||
@@ -282,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
## Key Features (v3.8.50)
|
||||
|
||||
### Core Proxy
|
||||
- **356 AI providers** with automatic format translation
|
||||
- **357 AI providers** with automatic format translation
|
||||
- **Provider categories**: Free (90+ free tiers), OAuth, API Key, Self-Hosted, Custom (OpenAI/Anthropic-compatible)
|
||||
- **18 routing strategies**: priority, weighted, round-robin, fill-first, p2c, random, least-used, cost-optimized, reset-aware, reset-window, headroom, strict-random, auto, lkgp, context-optimized, context-relay, fusion, pipeline
|
||||
- **4-tier fallback**: Subscription → API Key → Cheap → Free
|
||||
@@ -480,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
## v3.8.x Highlights
|
||||
|
||||
- **356-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **357-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **19 routing strategies** — including `fusion` (parallel panel + judge synthesis), `pipeline`, `reset-aware`, `reset-window`, `headroom`, and `context-relay`
|
||||
- **14-factor Auto-Combo scoring** with bandit exploration and progressive cooldown
|
||||
- **MCP server expanded to 110 tools / 33 scopes** (canonical + memory/skill/agentSkill/githubSkill/pool/notion/obsidian/localCorpus/gamification/plugin modules)
|
||||
|
||||
@@ -4,8 +4,7 @@
|
||||
|
||||
---
|
||||
|
||||
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 356 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 357 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
|
||||
## Overview
|
||||
|
||||
@@ -282,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
## Key Features (v3.8.50)
|
||||
|
||||
### Core Proxy
|
||||
- **356 AI providers** with automatic format translation
|
||||
- **357 AI providers** with automatic format translation
|
||||
- **Provider categories**: Free (90+ free tiers), OAuth, API Key, Self-Hosted, Custom (OpenAI/Anthropic-compatible)
|
||||
- **18 routing strategies**: priority, weighted, round-robin, fill-first, p2c, random, least-used, cost-optimized, reset-aware, reset-window, headroom, strict-random, auto, lkgp, context-optimized, context-relay, fusion, pipeline
|
||||
- **4-tier fallback**: Subscription → API Key → Cheap → Free
|
||||
@@ -480,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
## v3.8.x Highlights
|
||||
|
||||
- **356-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **357-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **19 routing strategies** — including `fusion` (parallel panel + judge synthesis), `pipeline`, `reset-aware`, `reset-window`, `headroom`, and `context-relay`
|
||||
- **14-factor Auto-Combo scoring** with bandit exploration and progressive cooldown
|
||||
- **MCP server expanded to 110 tools / 33 scopes** (canonical + memory/skill/agentSkill/githubSkill/pool/notion/obsidian/localCorpus/gamification/plugin modules)
|
||||
|
||||
@@ -4,8 +4,7 @@
|
||||
|
||||
---
|
||||
|
||||
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 356 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 357 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
|
||||
## Overview
|
||||
|
||||
@@ -282,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
## Key Features (v3.8.50)
|
||||
|
||||
### Core Proxy
|
||||
- **356 AI providers** with automatic format translation
|
||||
- **357 AI providers** with automatic format translation
|
||||
- **Provider categories**: Free (90+ free tiers), OAuth, API Key, Self-Hosted, Custom (OpenAI/Anthropic-compatible)
|
||||
- **18 routing strategies**: priority, weighted, round-robin, fill-first, p2c, random, least-used, cost-optimized, reset-aware, reset-window, headroom, strict-random, auto, lkgp, context-optimized, context-relay, fusion, pipeline
|
||||
- **4-tier fallback**: Subscription → API Key → Cheap → Free
|
||||
@@ -480,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
## v3.8.x Highlights
|
||||
|
||||
- **356-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **357-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **19 routing strategies** — including `fusion` (parallel panel + judge synthesis), `pipeline`, `reset-aware`, `reset-window`, `headroom`, and `context-relay`
|
||||
- **14-factor Auto-Combo scoring** with bandit exploration and progressive cooldown
|
||||
- **MCP server expanded to 110 tools / 33 scopes** (canonical + memory/skill/agentSkill/githubSkill/pool/notion/obsidian/localCorpus/gamification/plugin modules)
|
||||
|
||||
@@ -4,8 +4,7 @@
|
||||
|
||||
---
|
||||
|
||||
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 356 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 357 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
|
||||
## Overview
|
||||
|
||||
@@ -282,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
## Key Features (v3.8.50)
|
||||
|
||||
### Core Proxy
|
||||
- **356 AI providers** with automatic format translation
|
||||
- **357 AI providers** with automatic format translation
|
||||
- **Provider categories**: Free (90+ free tiers), OAuth, API Key, Self-Hosted, Custom (OpenAI/Anthropic-compatible)
|
||||
- **18 routing strategies**: priority, weighted, round-robin, fill-first, p2c, random, least-used, cost-optimized, reset-aware, reset-window, headroom, strict-random, auto, lkgp, context-optimized, context-relay, fusion, pipeline
|
||||
- **4-tier fallback**: Subscription → API Key → Cheap → Free
|
||||
@@ -480,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
## v3.8.x Highlights
|
||||
|
||||
- **356-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **357-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **19 routing strategies** — including `fusion` (parallel panel + judge synthesis), `pipeline`, `reset-aware`, `reset-window`, `headroom`, and `context-relay`
|
||||
- **14-factor Auto-Combo scoring** with bandit exploration and progressive cooldown
|
||||
- **MCP server expanded to 110 tools / 33 scopes** (canonical + memory/skill/agentSkill/githubSkill/pool/notion/obsidian/localCorpus/gamification/plugin modules)
|
||||
|
||||
@@ -4,8 +4,7 @@
|
||||
|
||||
---
|
||||
|
||||
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 356 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 357 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
|
||||
## Overview
|
||||
|
||||
@@ -282,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
## Key Features (v3.8.50)
|
||||
|
||||
### Core Proxy
|
||||
- **356 AI providers** with automatic format translation
|
||||
- **357 AI providers** with automatic format translation
|
||||
- **Provider categories**: Free (90+ free tiers), OAuth, API Key, Self-Hosted, Custom (OpenAI/Anthropic-compatible)
|
||||
- **18 routing strategies**: priority, weighted, round-robin, fill-first, p2c, random, least-used, cost-optimized, reset-aware, reset-window, headroom, strict-random, auto, lkgp, context-optimized, context-relay, fusion, pipeline
|
||||
- **4-tier fallback**: Subscription → API Key → Cheap → Free
|
||||
@@ -480,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
## v3.8.x Highlights
|
||||
|
||||
- **356-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **357-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **19 routing strategies** — including `fusion` (parallel panel + judge synthesis), `pipeline`, `reset-aware`, `reset-window`, `headroom`, and `context-relay`
|
||||
- **14-factor Auto-Combo scoring** with bandit exploration and progressive cooldown
|
||||
- **MCP server expanded to 110 tools / 33 scopes** (canonical + memory/skill/agentSkill/githubSkill/pool/notion/obsidian/localCorpus/gamification/plugin modules)
|
||||
|
||||
@@ -4,8 +4,7 @@
|
||||
|
||||
---
|
||||
|
||||
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 356 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 357 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
|
||||
## Overview
|
||||
|
||||
@@ -282,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
## Key Features (v3.8.50)
|
||||
|
||||
### Core Proxy
|
||||
- **356 AI providers** with automatic format translation
|
||||
- **357 AI providers** with automatic format translation
|
||||
- **Provider categories**: Free (90+ free tiers), OAuth, API Key, Self-Hosted, Custom (OpenAI/Anthropic-compatible)
|
||||
- **18 routing strategies**: priority, weighted, round-robin, fill-first, p2c, random, least-used, cost-optimized, reset-aware, reset-window, headroom, strict-random, auto, lkgp, context-optimized, context-relay, fusion, pipeline
|
||||
- **4-tier fallback**: Subscription → API Key → Cheap → Free
|
||||
@@ -480,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
## v3.8.x Highlights
|
||||
|
||||
- **356-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **357-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **19 routing strategies** — including `fusion` (parallel panel + judge synthesis), `pipeline`, `reset-aware`, `reset-window`, `headroom`, and `context-relay`
|
||||
- **14-factor Auto-Combo scoring** with bandit exploration and progressive cooldown
|
||||
- **MCP server expanded to 110 tools / 33 scopes** (canonical + memory/skill/agentSkill/githubSkill/pool/notion/obsidian/localCorpus/gamification/plugin modules)
|
||||
|
||||
@@ -4,8 +4,7 @@
|
||||
|
||||
---
|
||||
|
||||
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 356 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 357 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
|
||||
## Overview
|
||||
|
||||
@@ -282,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
## Key Features (v3.8.50)
|
||||
|
||||
### Core Proxy
|
||||
- **356 AI providers** with automatic format translation
|
||||
- **357 AI providers** with automatic format translation
|
||||
- **Provider categories**: Free (90+ free tiers), OAuth, API Key, Self-Hosted, Custom (OpenAI/Anthropic-compatible)
|
||||
- **18 routing strategies**: priority, weighted, round-robin, fill-first, p2c, random, least-used, cost-optimized, reset-aware, reset-window, headroom, strict-random, auto, lkgp, context-optimized, context-relay, fusion, pipeline
|
||||
- **4-tier fallback**: Subscription → API Key → Cheap → Free
|
||||
@@ -480,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
## v3.8.x Highlights
|
||||
|
||||
- **356-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **357-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **19 routing strategies** — including `fusion` (parallel panel + judge synthesis), `pipeline`, `reset-aware`, `reset-window`, `headroom`, and `context-relay`
|
||||
- **14-factor Auto-Combo scoring** with bandit exploration and progressive cooldown
|
||||
- **MCP server expanded to 110 tools / 33 scopes** (canonical + memory/skill/agentSkill/githubSkill/pool/notion/obsidian/localCorpus/gamification/plugin modules)
|
||||
|
||||
@@ -4,8 +4,7 @@
|
||||
|
||||
---
|
||||
|
||||
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 356 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 357 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
|
||||
## Overview
|
||||
|
||||
@@ -282,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
## Key Features (v3.8.50)
|
||||
|
||||
### Core Proxy
|
||||
- **356 AI providers** with automatic format translation
|
||||
- **357 AI providers** with automatic format translation
|
||||
- **Provider categories**: Free (90+ free tiers), OAuth, API Key, Self-Hosted, Custom (OpenAI/Anthropic-compatible)
|
||||
- **18 routing strategies**: priority, weighted, round-robin, fill-first, p2c, random, least-used, cost-optimized, reset-aware, reset-window, headroom, strict-random, auto, lkgp, context-optimized, context-relay, fusion, pipeline
|
||||
- **4-tier fallback**: Subscription → API Key → Cheap → Free
|
||||
@@ -480,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
## v3.8.x Highlights
|
||||
|
||||
- **356-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **357-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **19 routing strategies** — including `fusion` (parallel panel + judge synthesis), `pipeline`, `reset-aware`, `reset-window`, `headroom`, and `context-relay`
|
||||
- **14-factor Auto-Combo scoring** with bandit exploration and progressive cooldown
|
||||
- **MCP server expanded to 110 tools / 33 scopes** (canonical + memory/skill/agentSkill/githubSkill/pool/notion/obsidian/localCorpus/gamification/plugin modules)
|
||||
|
||||
@@ -4,8 +4,7 @@
|
||||
|
||||
---
|
||||
|
||||
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 356 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 357 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
|
||||
## Overview
|
||||
|
||||
@@ -282,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
## Key Features (v3.8.50)
|
||||
|
||||
### Core Proxy
|
||||
- **356 AI providers** with automatic format translation
|
||||
- **357 AI providers** with automatic format translation
|
||||
- **Provider categories**: Free (90+ free tiers), OAuth, API Key, Self-Hosted, Custom (OpenAI/Anthropic-compatible)
|
||||
- **18 routing strategies**: priority, weighted, round-robin, fill-first, p2c, random, least-used, cost-optimized, reset-aware, reset-window, headroom, strict-random, auto, lkgp, context-optimized, context-relay, fusion, pipeline
|
||||
- **4-tier fallback**: Subscription → API Key → Cheap → Free
|
||||
@@ -480,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
## v3.8.x Highlights
|
||||
|
||||
- **356-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **357-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **19 routing strategies** — including `fusion` (parallel panel + judge synthesis), `pipeline`, `reset-aware`, `reset-window`, `headroom`, and `context-relay`
|
||||
- **14-factor Auto-Combo scoring** with bandit exploration and progressive cooldown
|
||||
- **MCP server expanded to 110 tools / 33 scopes** (canonical + memory/skill/agentSkill/githubSkill/pool/notion/obsidian/localCorpus/gamification/plugin modules)
|
||||
|
||||
@@ -4,8 +4,7 @@
|
||||
|
||||
---
|
||||
|
||||
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 356 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 357 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
|
||||
## Overview
|
||||
|
||||
@@ -282,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
## Key Features (v3.8.50)
|
||||
|
||||
### Core Proxy
|
||||
- **356 AI providers** with automatic format translation
|
||||
- **357 AI providers** with automatic format translation
|
||||
- **Provider categories**: Free (90+ free tiers), OAuth, API Key, Self-Hosted, Custom (OpenAI/Anthropic-compatible)
|
||||
- **18 routing strategies**: priority, weighted, round-robin, fill-first, p2c, random, least-used, cost-optimized, reset-aware, reset-window, headroom, strict-random, auto, lkgp, context-optimized, context-relay, fusion, pipeline
|
||||
- **4-tier fallback**: Subscription → API Key → Cheap → Free
|
||||
@@ -480,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
## v3.8.x Highlights
|
||||
|
||||
- **356-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **357-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **19 routing strategies** — including `fusion` (parallel panel + judge synthesis), `pipeline`, `reset-aware`, `reset-window`, `headroom`, and `context-relay`
|
||||
- **14-factor Auto-Combo scoring** with bandit exploration and progressive cooldown
|
||||
- **MCP server expanded to 110 tools / 33 scopes** (canonical + memory/skill/agentSkill/githubSkill/pool/notion/obsidian/localCorpus/gamification/plugin modules)
|
||||
|
||||
@@ -4,8 +4,7 @@
|
||||
|
||||
---
|
||||
|
||||
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 356 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 357 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
|
||||
## Overview
|
||||
|
||||
@@ -282,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
## Key Features (v3.8.50)
|
||||
|
||||
### Core Proxy
|
||||
- **356 AI providers** with automatic format translation
|
||||
- **357 AI providers** with automatic format translation
|
||||
- **Provider categories**: Free (90+ free tiers), OAuth, API Key, Self-Hosted, Custom (OpenAI/Anthropic-compatible)
|
||||
- **18 routing strategies**: priority, weighted, round-robin, fill-first, p2c, random, least-used, cost-optimized, reset-aware, reset-window, headroom, strict-random, auto, lkgp, context-optimized, context-relay, fusion, pipeline
|
||||
- **4-tier fallback**: Subscription → API Key → Cheap → Free
|
||||
@@ -480,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
## v3.8.x Highlights
|
||||
|
||||
- **356-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **357-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **19 routing strategies** — including `fusion` (parallel panel + judge synthesis), `pipeline`, `reset-aware`, `reset-window`, `headroom`, and `context-relay`
|
||||
- **14-factor Auto-Combo scoring** with bandit exploration and progressive cooldown
|
||||
- **MCP server expanded to 110 tools / 33 scopes** (canonical + memory/skill/agentSkill/githubSkill/pool/notion/obsidian/localCorpus/gamification/plugin modules)
|
||||
|
||||
@@ -4,8 +4,7 @@
|
||||
|
||||
---
|
||||
|
||||
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 356 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 357 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
|
||||
## Overview
|
||||
|
||||
@@ -282,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
## Key Features (v3.8.50)
|
||||
|
||||
### Core Proxy
|
||||
- **356 AI providers** with automatic format translation
|
||||
- **357 AI providers** with automatic format translation
|
||||
- **Provider categories**: Free (90+ free tiers), OAuth, API Key, Self-Hosted, Custom (OpenAI/Anthropic-compatible)
|
||||
- **18 routing strategies**: priority, weighted, round-robin, fill-first, p2c, random, least-used, cost-optimized, reset-aware, reset-window, headroom, strict-random, auto, lkgp, context-optimized, context-relay, fusion, pipeline
|
||||
- **4-tier fallback**: Subscription → API Key → Cheap → Free
|
||||
@@ -480,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
## v3.8.x Highlights
|
||||
|
||||
- **356-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **357-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **19 routing strategies** — including `fusion` (parallel panel + judge synthesis), `pipeline`, `reset-aware`, `reset-window`, `headroom`, and `context-relay`
|
||||
- **14-factor Auto-Combo scoring** with bandit exploration and progressive cooldown
|
||||
- **MCP server expanded to 110 tools / 33 scopes** (canonical + memory/skill/agentSkill/githubSkill/pool/notion/obsidian/localCorpus/gamification/plugin modules)
|
||||
|
||||
@@ -4,8 +4,7 @@
|
||||
|
||||
---
|
||||
|
||||
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 356 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 357 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
|
||||
## Overview
|
||||
|
||||
@@ -282,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
## Key Features (v3.8.50)
|
||||
|
||||
### Core Proxy
|
||||
- **356 AI providers** with automatic format translation
|
||||
- **357 AI providers** with automatic format translation
|
||||
- **Provider categories**: Free (90+ free tiers), OAuth, API Key, Self-Hosted, Custom (OpenAI/Anthropic-compatible)
|
||||
- **18 routing strategies**: priority, weighted, round-robin, fill-first, p2c, random, least-used, cost-optimized, reset-aware, reset-window, headroom, strict-random, auto, lkgp, context-optimized, context-relay, fusion, pipeline
|
||||
- **4-tier fallback**: Subscription → API Key → Cheap → Free
|
||||
@@ -480,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
## v3.8.x Highlights
|
||||
|
||||
- **356-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **357-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **19 routing strategies** — including `fusion` (parallel panel + judge synthesis), `pipeline`, `reset-aware`, `reset-window`, `headroom`, and `context-relay`
|
||||
- **14-factor Auto-Combo scoring** with bandit exploration and progressive cooldown
|
||||
- **MCP server expanded to 110 tools / 33 scopes** (canonical + memory/skill/agentSkill/githubSkill/pool/notion/obsidian/localCorpus/gamification/plugin modules)
|
||||
|
||||
@@ -4,8 +4,7 @@
|
||||
|
||||
---
|
||||
|
||||
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 356 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 357 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
|
||||
## Overview
|
||||
|
||||
@@ -282,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
## Key Features (v3.8.50)
|
||||
|
||||
### Core Proxy
|
||||
- **356 AI providers** with automatic format translation
|
||||
- **357 AI providers** with automatic format translation
|
||||
- **Provider categories**: Free (90+ free tiers), OAuth, API Key, Self-Hosted, Custom (OpenAI/Anthropic-compatible)
|
||||
- **18 routing strategies**: priority, weighted, round-robin, fill-first, p2c, random, least-used, cost-optimized, reset-aware, reset-window, headroom, strict-random, auto, lkgp, context-optimized, context-relay, fusion, pipeline
|
||||
- **4-tier fallback**: Subscription → API Key → Cheap → Free
|
||||
@@ -480,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
## v3.8.x Highlights
|
||||
|
||||
- **356-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **357-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **19 routing strategies** — including `fusion` (parallel panel + judge synthesis), `pipeline`, `reset-aware`, `reset-window`, `headroom`, and `context-relay`
|
||||
- **14-factor Auto-Combo scoring** with bandit exploration and progressive cooldown
|
||||
- **MCP server expanded to 110 tools / 33 scopes** (canonical + memory/skill/agentSkill/githubSkill/pool/notion/obsidian/localCorpus/gamification/plugin modules)
|
||||
|
||||
@@ -4,8 +4,7 @@
|
||||
|
||||
---
|
||||
|
||||
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 356 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 357 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
|
||||
## Overview
|
||||
|
||||
@@ -282,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
## Key Features (v3.8.50)
|
||||
|
||||
### Core Proxy
|
||||
- **356 AI providers** with automatic format translation
|
||||
- **357 AI providers** with automatic format translation
|
||||
- **Provider categories**: Free (90+ free tiers), OAuth, API Key, Self-Hosted, Custom (OpenAI/Anthropic-compatible)
|
||||
- **18 routing strategies**: priority, weighted, round-robin, fill-first, p2c, random, least-used, cost-optimized, reset-aware, reset-window, headroom, strict-random, auto, lkgp, context-optimized, context-relay, fusion, pipeline
|
||||
- **4-tier fallback**: Subscription → API Key → Cheap → Free
|
||||
@@ -480,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
## v3.8.x Highlights
|
||||
|
||||
- **356-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **357-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **19 routing strategies** — including `fusion` (parallel panel + judge synthesis), `pipeline`, `reset-aware`, `reset-window`, `headroom`, and `context-relay`
|
||||
- **14-factor Auto-Combo scoring** with bandit exploration and progressive cooldown
|
||||
- **MCP server expanded to 110 tools / 33 scopes** (canonical + memory/skill/agentSkill/githubSkill/pool/notion/obsidian/localCorpus/gamification/plugin modules)
|
||||
|
||||
@@ -4,8 +4,7 @@
|
||||
|
||||
---
|
||||
|
||||
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 356 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 357 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
|
||||
## Overview
|
||||
|
||||
@@ -282,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
## Key Features (v3.8.50)
|
||||
|
||||
### Core Proxy
|
||||
- **356 AI providers** with automatic format translation
|
||||
- **357 AI providers** with automatic format translation
|
||||
- **Provider categories**: Free (90+ free tiers), OAuth, API Key, Self-Hosted, Custom (OpenAI/Anthropic-compatible)
|
||||
- **18 routing strategies**: priority, weighted, round-robin, fill-first, p2c, random, least-used, cost-optimized, reset-aware, reset-window, headroom, strict-random, auto, lkgp, context-optimized, context-relay, fusion, pipeline
|
||||
- **4-tier fallback**: Subscription → API Key → Cheap → Free
|
||||
@@ -480,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
## v3.8.x Highlights
|
||||
|
||||
- **356-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **357-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **19 routing strategies** — including `fusion` (parallel panel + judge synthesis), `pipeline`, `reset-aware`, `reset-window`, `headroom`, and `context-relay`
|
||||
- **14-factor Auto-Combo scoring** with bandit exploration and progressive cooldown
|
||||
- **MCP server expanded to 110 tools / 33 scopes** (canonical + memory/skill/agentSkill/githubSkill/pool/notion/obsidian/localCorpus/gamification/plugin modules)
|
||||
|
||||
@@ -4,8 +4,7 @@
|
||||
|
||||
---
|
||||
|
||||
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 356 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 357 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
|
||||
## Overview
|
||||
|
||||
@@ -282,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
## Key Features (v3.8.50)
|
||||
|
||||
### Core Proxy
|
||||
- **356 AI providers** with automatic format translation
|
||||
- **357 AI providers** with automatic format translation
|
||||
- **Provider categories**: Free (90+ free tiers), OAuth, API Key, Self-Hosted, Custom (OpenAI/Anthropic-compatible)
|
||||
- **18 routing strategies**: priority, weighted, round-robin, fill-first, p2c, random, least-used, cost-optimized, reset-aware, reset-window, headroom, strict-random, auto, lkgp, context-optimized, context-relay, fusion, pipeline
|
||||
- **4-tier fallback**: Subscription → API Key → Cheap → Free
|
||||
@@ -480,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
## v3.8.x Highlights
|
||||
|
||||
- **356-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **357-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **19 routing strategies** — including `fusion` (parallel panel + judge synthesis), `pipeline`, `reset-aware`, `reset-window`, `headroom`, and `context-relay`
|
||||
- **14-factor Auto-Combo scoring** with bandit exploration and progressive cooldown
|
||||
- **MCP server expanded to 110 tools / 33 scopes** (canonical + memory/skill/agentSkill/githubSkill/pool/notion/obsidian/localCorpus/gamification/plugin modules)
|
||||
|
||||
@@ -4,8 +4,7 @@
|
||||
|
||||
---
|
||||
|
||||
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 356 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 357 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
|
||||
## Overview
|
||||
|
||||
@@ -282,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
## Key Features (v3.8.50)
|
||||
|
||||
### Core Proxy
|
||||
- **356 AI providers** with automatic format translation
|
||||
- **357 AI providers** with automatic format translation
|
||||
- **Provider categories**: Free (90+ free tiers), OAuth, API Key, Self-Hosted, Custom (OpenAI/Anthropic-compatible)
|
||||
- **18 routing strategies**: priority, weighted, round-robin, fill-first, p2c, random, least-used, cost-optimized, reset-aware, reset-window, headroom, strict-random, auto, lkgp, context-optimized, context-relay, fusion, pipeline
|
||||
- **4-tier fallback**: Subscription → API Key → Cheap → Free
|
||||
@@ -480,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
## v3.8.x Highlights
|
||||
|
||||
- **356-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **357-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **19 routing strategies** — including `fusion` (parallel panel + judge synthesis), `pipeline`, `reset-aware`, `reset-window`, `headroom`, and `context-relay`
|
||||
- **14-factor Auto-Combo scoring** with bandit exploration and progressive cooldown
|
||||
- **MCP server expanded to 110 tools / 33 scopes** (canonical + memory/skill/agentSkill/githubSkill/pool/notion/obsidian/localCorpus/gamification/plugin modules)
|
||||
|
||||
@@ -4,8 +4,7 @@
|
||||
|
||||
---
|
||||
|
||||
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 356 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 357 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
|
||||
## Overview
|
||||
|
||||
@@ -282,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
## Key Features (v3.8.50)
|
||||
|
||||
### Core Proxy
|
||||
- **356 AI providers** with automatic format translation
|
||||
- **357 AI providers** with automatic format translation
|
||||
- **Provider categories**: Free (90+ free tiers), OAuth, API Key, Self-Hosted, Custom (OpenAI/Anthropic-compatible)
|
||||
- **18 routing strategies**: priority, weighted, round-robin, fill-first, p2c, random, least-used, cost-optimized, reset-aware, reset-window, headroom, strict-random, auto, lkgp, context-optimized, context-relay, fusion, pipeline
|
||||
- **4-tier fallback**: Subscription → API Key → Cheap → Free
|
||||
@@ -480,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
## v3.8.x Highlights
|
||||
|
||||
- **356-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **357-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **19 routing strategies** — including `fusion` (parallel panel + judge synthesis), `pipeline`, `reset-aware`, `reset-window`, `headroom`, and `context-relay`
|
||||
- **14-factor Auto-Combo scoring** with bandit exploration and progressive cooldown
|
||||
- **MCP server expanded to 110 tools / 33 scopes** (canonical + memory/skill/agentSkill/githubSkill/pool/notion/obsidian/localCorpus/gamification/plugin modules)
|
||||
|
||||
@@ -4,8 +4,7 @@
|
||||
|
||||
---
|
||||
|
||||
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 356 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 357 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
|
||||
## Overview
|
||||
|
||||
@@ -282,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
## Key Features (v3.8.50)
|
||||
|
||||
### Core Proxy
|
||||
- **356 AI providers** with automatic format translation
|
||||
- **357 AI providers** with automatic format translation
|
||||
- **Provider categories**: Free (90+ free tiers), OAuth, API Key, Self-Hosted, Custom (OpenAI/Anthropic-compatible)
|
||||
- **18 routing strategies**: priority, weighted, round-robin, fill-first, p2c, random, least-used, cost-optimized, reset-aware, reset-window, headroom, strict-random, auto, lkgp, context-optimized, context-relay, fusion, pipeline
|
||||
- **4-tier fallback**: Subscription → API Key → Cheap → Free
|
||||
@@ -480,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
## v3.8.x Highlights
|
||||
|
||||
- **356-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **357-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **19 routing strategies** — including `fusion` (parallel panel + judge synthesis), `pipeline`, `reset-aware`, `reset-window`, `headroom`, and `context-relay`
|
||||
- **14-factor Auto-Combo scoring** with bandit exploration and progressive cooldown
|
||||
- **MCP server expanded to 110 tools / 33 scopes** (canonical + memory/skill/agentSkill/githubSkill/pool/notion/obsidian/localCorpus/gamification/plugin modules)
|
||||
|
||||
@@ -4,8 +4,7 @@
|
||||
|
||||
---
|
||||
|
||||
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 356 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 357 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
|
||||
## Overview
|
||||
|
||||
@@ -282,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
## Key Features (v3.8.50)
|
||||
|
||||
### Core Proxy
|
||||
- **356 AI providers** with automatic format translation
|
||||
- **357 AI providers** with automatic format translation
|
||||
- **Provider categories**: Free (90+ free tiers), OAuth, API Key, Self-Hosted, Custom (OpenAI/Anthropic-compatible)
|
||||
- **18 routing strategies**: priority, weighted, round-robin, fill-first, p2c, random, least-used, cost-optimized, reset-aware, reset-window, headroom, strict-random, auto, lkgp, context-optimized, context-relay, fusion, pipeline
|
||||
- **4-tier fallback**: Subscription → API Key → Cheap → Free
|
||||
@@ -480,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
## v3.8.x Highlights
|
||||
|
||||
- **356-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **357-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **19 routing strategies** — including `fusion` (parallel panel + judge synthesis), `pipeline`, `reset-aware`, `reset-window`, `headroom`, and `context-relay`
|
||||
- **14-factor Auto-Combo scoring** with bandit exploration and progressive cooldown
|
||||
- **MCP server expanded to 110 tools / 33 scopes** (canonical + memory/skill/agentSkill/githubSkill/pool/notion/obsidian/localCorpus/gamification/plugin modules)
|
||||
|
||||
@@ -4,8 +4,7 @@
|
||||
|
||||
---
|
||||
|
||||
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 356 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 357 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
|
||||
## Overview
|
||||
|
||||
@@ -282,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
## Key Features (v3.8.50)
|
||||
|
||||
### Core Proxy
|
||||
- **356 AI providers** with automatic format translation
|
||||
- **357 AI providers** with automatic format translation
|
||||
- **Provider categories**: Free (90+ free tiers), OAuth, API Key, Self-Hosted, Custom (OpenAI/Anthropic-compatible)
|
||||
- **18 routing strategies**: priority, weighted, round-robin, fill-first, p2c, random, least-used, cost-optimized, reset-aware, reset-window, headroom, strict-random, auto, lkgp, context-optimized, context-relay, fusion, pipeline
|
||||
- **4-tier fallback**: Subscription → API Key → Cheap → Free
|
||||
@@ -480,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
## v3.8.x Highlights
|
||||
|
||||
- **356-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **357-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **19 routing strategies** — including `fusion` (parallel panel + judge synthesis), `pipeline`, `reset-aware`, `reset-window`, `headroom`, and `context-relay`
|
||||
- **14-factor Auto-Combo scoring** with bandit exploration and progressive cooldown
|
||||
- **MCP server expanded to 110 tools / 33 scopes** (canonical + memory/skill/agentSkill/githubSkill/pool/notion/obsidian/localCorpus/gamification/plugin modules)
|
||||
|
||||
@@ -4,8 +4,7 @@
|
||||
|
||||
---
|
||||
|
||||
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 356 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 357 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
|
||||
## Overview
|
||||
|
||||
@@ -282,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
## Key Features (v3.8.50)
|
||||
|
||||
### Core Proxy
|
||||
- **356 AI providers** with automatic format translation
|
||||
- **357 AI providers** with automatic format translation
|
||||
- **Provider categories**: Free (90+ free tiers), OAuth, API Key, Self-Hosted, Custom (OpenAI/Anthropic-compatible)
|
||||
- **18 routing strategies**: priority, weighted, round-robin, fill-first, p2c, random, least-used, cost-optimized, reset-aware, reset-window, headroom, strict-random, auto, lkgp, context-optimized, context-relay, fusion, pipeline
|
||||
- **4-tier fallback**: Subscription → API Key → Cheap → Free
|
||||
@@ -480,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
## v3.8.x Highlights
|
||||
|
||||
- **356-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **357-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **19 routing strategies** — including `fusion` (parallel panel + judge synthesis), `pipeline`, `reset-aware`, `reset-window`, `headroom`, and `context-relay`
|
||||
- **14-factor Auto-Combo scoring** with bandit exploration and progressive cooldown
|
||||
- **MCP server expanded to 110 tools / 33 scopes** (canonical + memory/skill/agentSkill/githubSkill/pool/notion/obsidian/localCorpus/gamification/plugin modules)
|
||||
|
||||
@@ -4,8 +4,7 @@
|
||||
|
||||
---
|
||||
|
||||
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 356 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 357 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
|
||||
## Overview
|
||||
|
||||
@@ -282,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
## Key Features (v3.8.50)
|
||||
|
||||
### Core Proxy
|
||||
- **356 AI providers** with automatic format translation
|
||||
- **357 AI providers** with automatic format translation
|
||||
- **Provider categories**: Free (90+ free tiers), OAuth, API Key, Self-Hosted, Custom (OpenAI/Anthropic-compatible)
|
||||
- **18 routing strategies**: priority, weighted, round-robin, fill-first, p2c, random, least-used, cost-optimized, reset-aware, reset-window, headroom, strict-random, auto, lkgp, context-optimized, context-relay, fusion, pipeline
|
||||
- **4-tier fallback**: Subscription → API Key → Cheap → Free
|
||||
@@ -480,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
## v3.8.x Highlights
|
||||
|
||||
- **356-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **357-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **19 routing strategies** — including `fusion` (parallel panel + judge synthesis), `pipeline`, `reset-aware`, `reset-window`, `headroom`, and `context-relay`
|
||||
- **14-factor Auto-Combo scoring** with bandit exploration and progressive cooldown
|
||||
- **MCP server expanded to 110 tools / 33 scopes** (canonical + memory/skill/agentSkill/githubSkill/pool/notion/obsidian/localCorpus/gamification/plugin modules)
|
||||
|
||||
@@ -4,8 +4,7 @@
|
||||
|
||||
---
|
||||
|
||||
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 356 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 357 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
|
||||
## Overview
|
||||
|
||||
@@ -282,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
## Key Features (v3.8.50)
|
||||
|
||||
### Core Proxy
|
||||
- **356 AI providers** with automatic format translation
|
||||
- **357 AI providers** with automatic format translation
|
||||
- **Provider categories**: Free (90+ free tiers), OAuth, API Key, Self-Hosted, Custom (OpenAI/Anthropic-compatible)
|
||||
- **18 routing strategies**: priority, weighted, round-robin, fill-first, p2c, random, least-used, cost-optimized, reset-aware, reset-window, headroom, strict-random, auto, lkgp, context-optimized, context-relay, fusion, pipeline
|
||||
- **4-tier fallback**: Subscription → API Key → Cheap → Free
|
||||
@@ -480,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
## v3.8.x Highlights
|
||||
|
||||
- **356-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **357-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **19 routing strategies** — including `fusion` (parallel panel + judge synthesis), `pipeline`, `reset-aware`, `reset-window`, `headroom`, and `context-relay`
|
||||
- **14-factor Auto-Combo scoring** with bandit exploration and progressive cooldown
|
||||
- **MCP server expanded to 110 tools / 33 scopes** (canonical + memory/skill/agentSkill/githubSkill/pool/notion/obsidian/localCorpus/gamification/plugin modules)
|
||||
|
||||
@@ -4,8 +4,7 @@
|
||||
|
||||
---
|
||||
|
||||
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 356 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 357 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
|
||||
## Overview
|
||||
|
||||
@@ -282,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
## Key Features (v3.8.50)
|
||||
|
||||
### Core Proxy
|
||||
- **356 AI providers** with automatic format translation
|
||||
- **357 AI providers** with automatic format translation
|
||||
- **Provider categories**: Free (90+ free tiers), OAuth, API Key, Self-Hosted, Custom (OpenAI/Anthropic-compatible)
|
||||
- **18 routing strategies**: priority, weighted, round-robin, fill-first, p2c, random, least-used, cost-optimized, reset-aware, reset-window, headroom, strict-random, auto, lkgp, context-optimized, context-relay, fusion, pipeline
|
||||
- **4-tier fallback**: Subscription → API Key → Cheap → Free
|
||||
@@ -480,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
## v3.8.x Highlights
|
||||
|
||||
- **356-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **357-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **19 routing strategies** — including `fusion` (parallel panel + judge synthesis), `pipeline`, `reset-aware`, `reset-window`, `headroom`, and `context-relay`
|
||||
- **14-factor Auto-Combo scoring** with bandit exploration and progressive cooldown
|
||||
- **MCP server expanded to 110 tools / 33 scopes** (canonical + memory/skill/agentSkill/githubSkill/pool/notion/obsidian/localCorpus/gamification/plugin modules)
|
||||
|
||||
@@ -4,8 +4,7 @@
|
||||
|
||||
---
|
||||
|
||||
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 356 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 357 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
|
||||
## Overview
|
||||
|
||||
@@ -282,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
## Key Features (v3.8.50)
|
||||
|
||||
### Core Proxy
|
||||
- **356 AI providers** with automatic format translation
|
||||
- **357 AI providers** with automatic format translation
|
||||
- **Provider categories**: Free (90+ free tiers), OAuth, API Key, Self-Hosted, Custom (OpenAI/Anthropic-compatible)
|
||||
- **18 routing strategies**: priority, weighted, round-robin, fill-first, p2c, random, least-used, cost-optimized, reset-aware, reset-window, headroom, strict-random, auto, lkgp, context-optimized, context-relay, fusion, pipeline
|
||||
- **4-tier fallback**: Subscription → API Key → Cheap → Free
|
||||
@@ -480,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
## v3.8.x Highlights
|
||||
|
||||
- **356-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **357-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **19 routing strategies** — including `fusion` (parallel panel + judge synthesis), `pipeline`, `reset-aware`, `reset-window`, `headroom`, and `context-relay`
|
||||
- **14-factor Auto-Combo scoring** with bandit exploration and progressive cooldown
|
||||
- **MCP server expanded to 110 tools / 33 scopes** (canonical + memory/skill/agentSkill/githubSkill/pool/notion/obsidian/localCorpus/gamification/plugin modules)
|
||||
|
||||
@@ -4,8 +4,7 @@
|
||||
|
||||
---
|
||||
|
||||
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 356 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 357 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
|
||||
## Overview
|
||||
|
||||
@@ -282,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
## Key Features (v3.8.50)
|
||||
|
||||
### Core Proxy
|
||||
- **356 AI providers** with automatic format translation
|
||||
- **357 AI providers** with automatic format translation
|
||||
- **Provider categories**: Free (90+ free tiers), OAuth, API Key, Self-Hosted, Custom (OpenAI/Anthropic-compatible)
|
||||
- **18 routing strategies**: priority, weighted, round-robin, fill-first, p2c, random, least-used, cost-optimized, reset-aware, reset-window, headroom, strict-random, auto, lkgp, context-optimized, context-relay, fusion, pipeline
|
||||
- **4-tier fallback**: Subscription → API Key → Cheap → Free
|
||||
@@ -480,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
## v3.8.x Highlights
|
||||
|
||||
- **356-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **357-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **19 routing strategies** — including `fusion` (parallel panel + judge synthesis), `pipeline`, `reset-aware`, `reset-window`, `headroom`, and `context-relay`
|
||||
- **14-factor Auto-Combo scoring** with bandit exploration and progressive cooldown
|
||||
- **MCP server expanded to 110 tools / 33 scopes** (canonical + memory/skill/agentSkill/githubSkill/pool/notion/obsidian/localCorpus/gamification/plugin modules)
|
||||
|
||||
@@ -4,8 +4,7 @@
|
||||
|
||||
---
|
||||
|
||||
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 356 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 357 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
|
||||
## Overview
|
||||
|
||||
@@ -282,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
## Key Features (v3.8.50)
|
||||
|
||||
### Core Proxy
|
||||
- **356 AI providers** with automatic format translation
|
||||
- **357 AI providers** with automatic format translation
|
||||
- **Provider categories**: Free (90+ free tiers), OAuth, API Key, Self-Hosted, Custom (OpenAI/Anthropic-compatible)
|
||||
- **18 routing strategies**: priority, weighted, round-robin, fill-first, p2c, random, least-used, cost-optimized, reset-aware, reset-window, headroom, strict-random, auto, lkgp, context-optimized, context-relay, fusion, pipeline
|
||||
- **4-tier fallback**: Subscription → API Key → Cheap → Free
|
||||
@@ -480,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
## v3.8.x Highlights
|
||||
|
||||
- **356-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **357-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **19 routing strategies** — including `fusion` (parallel panel + judge synthesis), `pipeline`, `reset-aware`, `reset-window`, `headroom`, and `context-relay`
|
||||
- **14-factor Auto-Combo scoring** with bandit exploration and progressive cooldown
|
||||
- **MCP server expanded to 110 tools / 33 scopes** (canonical + memory/skill/agentSkill/githubSkill/pool/notion/obsidian/localCorpus/gamification/plugin modules)
|
||||
|
||||
@@ -4,8 +4,7 @@
|
||||
|
||||
---
|
||||
|
||||
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 356 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 357 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
|
||||
## Overview
|
||||
|
||||
@@ -282,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
## Key Features (v3.8.50)
|
||||
|
||||
### Core Proxy
|
||||
- **356 AI providers** with automatic format translation
|
||||
- **357 AI providers** with automatic format translation
|
||||
- **Provider categories**: Free (90+ free tiers), OAuth, API Key, Self-Hosted, Custom (OpenAI/Anthropic-compatible)
|
||||
- **18 routing strategies**: priority, weighted, round-robin, fill-first, p2c, random, least-used, cost-optimized, reset-aware, reset-window, headroom, strict-random, auto, lkgp, context-optimized, context-relay, fusion, pipeline
|
||||
- **4-tier fallback**: Subscription → API Key → Cheap → Free
|
||||
@@ -480,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
## v3.8.x Highlights
|
||||
|
||||
- **356-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **357-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **19 routing strategies** — including `fusion` (parallel panel + judge synthesis), `pipeline`, `reset-aware`, `reset-window`, `headroom`, and `context-relay`
|
||||
- **14-factor Auto-Combo scoring** with bandit exploration and progressive cooldown
|
||||
- **MCP server expanded to 110 tools / 33 scopes** (canonical + memory/skill/agentSkill/githubSkill/pool/notion/obsidian/localCorpus/gamification/plugin modules)
|
||||
|
||||
@@ -4,8 +4,7 @@
|
||||
|
||||
---
|
||||
|
||||
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 356 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 357 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
|
||||
## Overview
|
||||
|
||||
@@ -282,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
## Key Features (v3.8.50)
|
||||
|
||||
### Core Proxy
|
||||
- **356 AI providers** with automatic format translation
|
||||
- **357 AI providers** with automatic format translation
|
||||
- **Provider categories**: Free (90+ free tiers), OAuth, API Key, Self-Hosted, Custom (OpenAI/Anthropic-compatible)
|
||||
- **18 routing strategies**: priority, weighted, round-robin, fill-first, p2c, random, least-used, cost-optimized, reset-aware, reset-window, headroom, strict-random, auto, lkgp, context-optimized, context-relay, fusion, pipeline
|
||||
- **4-tier fallback**: Subscription → API Key → Cheap → Free
|
||||
@@ -480,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
## v3.8.x Highlights
|
||||
|
||||
- **356-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **357-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **19 routing strategies** — including `fusion` (parallel panel + judge synthesis), `pipeline`, `reset-aware`, `reset-window`, `headroom`, and `context-relay`
|
||||
- **14-factor Auto-Combo scoring** with bandit exploration and progressive cooldown
|
||||
- **MCP server expanded to 110 tools / 33 scopes** (canonical + memory/skill/agentSkill/githubSkill/pool/notion/obsidian/localCorpus/gamification/plugin modules)
|
||||
|
||||
@@ -4,8 +4,7 @@
|
||||
|
||||
---
|
||||
|
||||
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 356 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 357 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
|
||||
## Overview
|
||||
|
||||
@@ -282,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
## Key Features (v3.8.50)
|
||||
|
||||
### Core Proxy
|
||||
- **356 AI providers** with automatic format translation
|
||||
- **357 AI providers** with automatic format translation
|
||||
- **Provider categories**: Free (90+ free tiers), OAuth, API Key, Self-Hosted, Custom (OpenAI/Anthropic-compatible)
|
||||
- **18 routing strategies**: priority, weighted, round-robin, fill-first, p2c, random, least-used, cost-optimized, reset-aware, reset-window, headroom, strict-random, auto, lkgp, context-optimized, context-relay, fusion, pipeline
|
||||
- **4-tier fallback**: Subscription → API Key → Cheap → Free
|
||||
@@ -480,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
## v3.8.x Highlights
|
||||
|
||||
- **356-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **357-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **19 routing strategies** — including `fusion` (parallel panel + judge synthesis), `pipeline`, `reset-aware`, `reset-window`, `headroom`, and `context-relay`
|
||||
- **14-factor Auto-Combo scoring** with bandit exploration and progressive cooldown
|
||||
- **MCP server expanded to 110 tools / 33 scopes** (canonical + memory/skill/agentSkill/githubSkill/pool/notion/obsidian/localCorpus/gamification/plugin modules)
|
||||
|
||||
@@ -4,8 +4,7 @@
|
||||
|
||||
---
|
||||
|
||||
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 356 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 357 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
|
||||
## Overview
|
||||
|
||||
@@ -282,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
## Key Features (v3.8.50)
|
||||
|
||||
### Core Proxy
|
||||
- **356 AI providers** with automatic format translation
|
||||
- **357 AI providers** with automatic format translation
|
||||
- **Provider categories**: Free (90+ free tiers), OAuth, API Key, Self-Hosted, Custom (OpenAI/Anthropic-compatible)
|
||||
- **18 routing strategies**: priority, weighted, round-robin, fill-first, p2c, random, least-used, cost-optimized, reset-aware, reset-window, headroom, strict-random, auto, lkgp, context-optimized, context-relay, fusion, pipeline
|
||||
- **4-tier fallback**: Subscription → API Key → Cheap → Free
|
||||
@@ -480,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
## v3.8.x Highlights
|
||||
|
||||
- **356-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **357-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **19 routing strategies** — including `fusion` (parallel panel + judge synthesis), `pipeline`, `reset-aware`, `reset-window`, `headroom`, and `context-relay`
|
||||
- **14-factor Auto-Combo scoring** with bandit exploration and progressive cooldown
|
||||
- **MCP server expanded to 110 tools / 33 scopes** (canonical + memory/skill/agentSkill/githubSkill/pool/notion/obsidian/localCorpus/gamification/plugin modules)
|
||||
|
||||
@@ -4,8 +4,7 @@
|
||||
|
||||
---
|
||||
|
||||
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 356 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 357 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
|
||||
## Overview
|
||||
|
||||
@@ -282,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
## Key Features (v3.8.50)
|
||||
|
||||
### Core Proxy
|
||||
- **356 AI providers** with automatic format translation
|
||||
- **357 AI providers** with automatic format translation
|
||||
- **Provider categories**: Free (90+ free tiers), OAuth, API Key, Self-Hosted, Custom (OpenAI/Anthropic-compatible)
|
||||
- **18 routing strategies**: priority, weighted, round-robin, fill-first, p2c, random, least-used, cost-optimized, reset-aware, reset-window, headroom, strict-random, auto, lkgp, context-optimized, context-relay, fusion, pipeline
|
||||
- **4-tier fallback**: Subscription → API Key → Cheap → Free
|
||||
@@ -480,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
## v3.8.x Highlights
|
||||
|
||||
- **356-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **357-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **19 routing strategies** — including `fusion` (parallel panel + judge synthesis), `pipeline`, `reset-aware`, `reset-window`, `headroom`, and `context-relay`
|
||||
- **14-factor Auto-Combo scoring** with bandit exploration and progressive cooldown
|
||||
- **MCP server expanded to 110 tools / 33 scopes** (canonical + memory/skill/agentSkill/githubSkill/pool/notion/obsidian/localCorpus/gamification/plugin modules)
|
||||
|
||||
@@ -4,8 +4,7 @@
|
||||
|
||||
---
|
||||
|
||||
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 356 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 357 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
|
||||
## Overview
|
||||
|
||||
@@ -282,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
## Key Features (v3.8.50)
|
||||
|
||||
### Core Proxy
|
||||
- **356 AI providers** with automatic format translation
|
||||
- **357 AI providers** with automatic format translation
|
||||
- **Provider categories**: Free (90+ free tiers), OAuth, API Key, Self-Hosted, Custom (OpenAI/Anthropic-compatible)
|
||||
- **18 routing strategies**: priority, weighted, round-robin, fill-first, p2c, random, least-used, cost-optimized, reset-aware, reset-window, headroom, strict-random, auto, lkgp, context-optimized, context-relay, fusion, pipeline
|
||||
- **4-tier fallback**: Subscription → API Key → Cheap → Free
|
||||
@@ -480,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
## v3.8.x Highlights
|
||||
|
||||
- **356-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **357-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **19 routing strategies** — including `fusion` (parallel panel + judge synthesis), `pipeline`, `reset-aware`, `reset-window`, `headroom`, and `context-relay`
|
||||
- **14-factor Auto-Combo scoring** with bandit exploration and progressive cooldown
|
||||
- **MCP server expanded to 110 tools / 33 scopes** (canonical + memory/skill/agentSkill/githubSkill/pool/notion/obsidian/localCorpus/gamification/plugin modules)
|
||||
|
||||
@@ -4,8 +4,7 @@
|
||||
|
||||
---
|
||||
|
||||
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 356 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 357 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
|
||||
## Overview
|
||||
|
||||
@@ -282,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
## Key Features (v3.8.50)
|
||||
|
||||
### Core Proxy
|
||||
- **356 AI providers** with automatic format translation
|
||||
- **357 AI providers** with automatic format translation
|
||||
- **Provider categories**: Free (90+ free tiers), OAuth, API Key, Self-Hosted, Custom (OpenAI/Anthropic-compatible)
|
||||
- **18 routing strategies**: priority, weighted, round-robin, fill-first, p2c, random, least-used, cost-optimized, reset-aware, reset-window, headroom, strict-random, auto, lkgp, context-optimized, context-relay, fusion, pipeline
|
||||
- **4-tier fallback**: Subscription → API Key → Cheap → Free
|
||||
@@ -480,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
## v3.8.x Highlights
|
||||
|
||||
- **356-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **357-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **19 routing strategies** — including `fusion` (parallel panel + judge synthesis), `pipeline`, `reset-aware`, `reset-window`, `headroom`, and `context-relay`
|
||||
- **14-factor Auto-Combo scoring** with bandit exploration and progressive cooldown
|
||||
- **MCP server expanded to 110 tools / 33 scopes** (canonical + memory/skill/agentSkill/githubSkill/pool/notion/obsidian/localCorpus/gamification/plugin modules)
|
||||
|
||||
@@ -4,8 +4,7 @@
|
||||
|
||||
---
|
||||
|
||||
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 356 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 357 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
|
||||
## Overview
|
||||
|
||||
@@ -282,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
## Key Features (v3.8.50)
|
||||
|
||||
### Core Proxy
|
||||
- **356 AI providers** with automatic format translation
|
||||
- **357 AI providers** with automatic format translation
|
||||
- **Provider categories**: Free (90+ free tiers), OAuth, API Key, Self-Hosted, Custom (OpenAI/Anthropic-compatible)
|
||||
- **18 routing strategies**: priority, weighted, round-robin, fill-first, p2c, random, least-used, cost-optimized, reset-aware, reset-window, headroom, strict-random, auto, lkgp, context-optimized, context-relay, fusion, pipeline
|
||||
- **4-tier fallback**: Subscription → API Key → Cheap → Free
|
||||
@@ -480,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
## v3.8.x Highlights
|
||||
|
||||
- **356-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **357-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **19 routing strategies** — including `fusion` (parallel panel + judge synthesis), `pipeline`, `reset-aware`, `reset-window`, `headroom`, and `context-relay`
|
||||
- **14-factor Auto-Combo scoring** with bandit exploration and progressive cooldown
|
||||
- **MCP server expanded to 110 tools / 33 scopes** (canonical + memory/skill/agentSkill/githubSkill/pool/notion/obsidian/localCorpus/gamification/plugin modules)
|
||||
|
||||
@@ -4,8 +4,7 @@
|
||||
|
||||
---
|
||||
|
||||
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 356 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 357 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
|
||||
## Overview
|
||||
|
||||
@@ -282,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
## Key Features (v3.8.50)
|
||||
|
||||
### Core Proxy
|
||||
- **356 AI providers** with automatic format translation
|
||||
- **357 AI providers** with automatic format translation
|
||||
- **Provider categories**: Free (90+ free tiers), OAuth, API Key, Self-Hosted, Custom (OpenAI/Anthropic-compatible)
|
||||
- **18 routing strategies**: priority, weighted, round-robin, fill-first, p2c, random, least-used, cost-optimized, reset-aware, reset-window, headroom, strict-random, auto, lkgp, context-optimized, context-relay, fusion, pipeline
|
||||
- **4-tier fallback**: Subscription → API Key → Cheap → Free
|
||||
@@ -480,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
## v3.8.x Highlights
|
||||
|
||||
- **356-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **357-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **19 routing strategies** — including `fusion` (parallel panel + judge synthesis), `pipeline`, `reset-aware`, `reset-window`, `headroom`, and `context-relay`
|
||||
- **14-factor Auto-Combo scoring** with bandit exploration and progressive cooldown
|
||||
- **MCP server expanded to 110 tools / 33 scopes** (canonical + memory/skill/agentSkill/githubSkill/pool/notion/obsidian/localCorpus/gamification/plugin modules)
|
||||
|
||||
@@ -4,8 +4,7 @@
|
||||
|
||||
---
|
||||
|
||||
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 356 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 357 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
|
||||
## Overview
|
||||
|
||||
@@ -282,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
## Key Features (v3.8.50)
|
||||
|
||||
### Core Proxy
|
||||
- **356 AI providers** with automatic format translation
|
||||
- **357 AI providers** with automatic format translation
|
||||
- **Provider categories**: Free (90+ free tiers), OAuth, API Key, Self-Hosted, Custom (OpenAI/Anthropic-compatible)
|
||||
- **18 routing strategies**: priority, weighted, round-robin, fill-first, p2c, random, least-used, cost-optimized, reset-aware, reset-window, headroom, strict-random, auto, lkgp, context-optimized, context-relay, fusion, pipeline
|
||||
- **4-tier fallback**: Subscription → API Key → Cheap → Free
|
||||
@@ -480,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
## v3.8.x Highlights
|
||||
|
||||
- **356-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **357-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **19 routing strategies** — including `fusion` (parallel panel + judge synthesis), `pipeline`, `reset-aware`, `reset-window`, `headroom`, and `context-relay`
|
||||
- **14-factor Auto-Combo scoring** with bandit exploration and progressive cooldown
|
||||
- **MCP server expanded to 110 tools / 33 scopes** (canonical + memory/skill/agentSkill/githubSkill/pool/notion/obsidian/localCorpus/gamification/plugin modules)
|
||||
|
||||
@@ -4,8 +4,7 @@
|
||||
|
||||
---
|
||||
|
||||
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 356 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 357 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
|
||||
## Overview
|
||||
|
||||
@@ -282,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
## Key Features (v3.8.50)
|
||||
|
||||
### Core Proxy
|
||||
- **356 AI providers** with automatic format translation
|
||||
- **357 AI providers** with automatic format translation
|
||||
- **Provider categories**: Free (90+ free tiers), OAuth, API Key, Self-Hosted, Custom (OpenAI/Anthropic-compatible)
|
||||
- **18 routing strategies**: priority, weighted, round-robin, fill-first, p2c, random, least-used, cost-optimized, reset-aware, reset-window, headroom, strict-random, auto, lkgp, context-optimized, context-relay, fusion, pipeline
|
||||
- **4-tier fallback**: Subscription → API Key → Cheap → Free
|
||||
@@ -480,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
## v3.8.x Highlights
|
||||
|
||||
- **356-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **357-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **19 routing strategies** — including `fusion` (parallel panel + judge synthesis), `pipeline`, `reset-aware`, `reset-window`, `headroom`, and `context-relay`
|
||||
- **14-factor Auto-Combo scoring** with bandit exploration and progressive cooldown
|
||||
- **MCP server expanded to 110 tools / 33 scopes** (canonical + memory/skill/agentSkill/githubSkill/pool/notion/obsidian/localCorpus/gamification/plugin modules)
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
---
|
||||
title: "Provider Reference"
|
||||
version: 3.8.51
|
||||
lastUpdated: 2026-08-26
|
||||
lastUpdated: 2026-08-27
|
||||
---
|
||||
|
||||
# Provider Reference
|
||||
|
||||
> **Auto-generated** from `src/shared/constants/providers.ts` — do not edit by hand.
|
||||
> Regenerate with: `npm run gen:provider-reference`
|
||||
> **Last generated:** 2026-08-26
|
||||
> **Last generated:** 2026-08-27
|
||||
|
||||
Total providers: **356**. See category breakdown below.
|
||||
Total providers: **357**. See category breakdown below.
|
||||
|
||||
## Categories
|
||||
|
||||
@@ -381,10 +381,11 @@ Use the dashboard at `/dashboard/providers` to enable, configure, and test each
|
||||
| `vllm` | `vllm` | vLLM | Local, self-hosted | [link](https://github.com/vllm-project/vllm) | API key optional. Configure the local vLLM OpenAI-compatible base URL (default: http://localhost:8000/v1). |
|
||||
| `xinference` | `xinference` | XInference | Local, self-hosted | [link](https://inference.readthedocs.io) | API key optional. Configure the local XInference OpenAI-compatible base URL (default: http://localhost:9997/v1). |
|
||||
|
||||
## Search Providers (16)
|
||||
## Search Providers (17)
|
||||
|
||||
| ID | Alias | Name | Tags | Website | Notes |
|
||||
|----|-------|------|------|---------|-------|
|
||||
| `anysearch-search` | `anysearch` | AnySearch | Search | [link](https://anysearch.com) | Optional API key from anysearch.com (as_sk_...) - free 1000/day; keyless tier has lower limits |
|
||||
| `brave-search` | `brave-search` | Brave Search | Search | [link](https://brave.com/search/api) | Subscription token from Brave Search API dashboard |
|
||||
| `context7` | `context7` | Context7 (library docs) | Search | [link](https://context7.com) | API key optional (ctx7sk-...) — anonymous tier works without a key; a key raises the rate limit |
|
||||
| `exa-search` | `exa-search` | Exa Search | Search | [link](https://exa.ai) | API key from dashboard.exa.ai |
|
||||
@@ -398,7 +399,6 @@ Use the dashboard at `/dashboard/providers` to enable, configure, and test each
|
||||
| `searxng-search` | `searxng` | SearXNG Search | Search | [link](https://docs.searxng.org) | API key is optional. Set your SearXNG base URL. Some instances may require a bearer token for access. |
|
||||
| `serper-search` | `serper-search` | Serper Search | Search | [link](https://serper.dev) | API key from serper.dev dashboard |
|
||||
| `tavily-search` | `tavily-search` | Tavily Search | Search | [link](https://tavily.com) | API key from app.tavily.com (format: tvly-...) |
|
||||
| `anysearch-search` | `anysearch` | AnySearch | Search | [link](https://anysearch.com/docs) | Optional API key (as_sk_...). Free public web search for agents; 1000 req/day per key, shared with extract. Fallback-only. |
|
||||
| `x-search` | `x_search` | X Search (Grok) | Search | [link](https://docs.x.ai/developers/tools/x-search) | SuperGrok OAuth (xai-oauth) or xAI API key. This is Grok X Search, not the X Developer MCP. |
|
||||
| `xquik-search` | `xquik` | Xquik X Search | Search | [link](https://docs.xquik.com) | Xquik API key (xq_...). Search is metered per returned post; the catalog estimate uses 5 results. |
|
||||
| `youcom-search` | `youcom-search` | You.com Search | Search | [link](https://you.com/business/api/) | X-API-Key from the You.com platform dashboard |
|
||||
@@ -445,7 +445,7 @@ Use the dashboard at `/dashboard/providers` to enable, configure, and test each
|
||||
|
||||
- Catalog: [`src/shared/constants/providers.ts`](../../src/shared/constants/providers.ts)
|
||||
- Registry (per-model details): [`open-sse/config/providerRegistry.ts`](../../open-sse/config/providerRegistry.ts)
|
||||
- Executors: [`open-sse/executors/`](../../open-sse/executors/) (111 implementations)
|
||||
- Executors: [`open-sse/executors/`](../../open-sse/executors/) (112 implementations)
|
||||
- Translators: [`open-sse/translator/`](../../open-sse/translator/)
|
||||
|
||||
## See Also
|
||||
|
||||
6
llm.txt
@@ -1,6 +1,6 @@
|
||||
# OmniRoute
|
||||
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 356 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 357 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
|
||||
## Overview
|
||||
|
||||
@@ -277,7 +277,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
## Key Features (v3.8.50)
|
||||
|
||||
### Core Proxy
|
||||
- **356 AI providers** with automatic format translation
|
||||
- **357 AI providers** with automatic format translation
|
||||
- **Provider categories**: Free (90+ free tiers), OAuth, API Key, Self-Hosted, Custom (OpenAI/Anthropic-compatible)
|
||||
- **18 routing strategies**: priority, weighted, round-robin, fill-first, p2c, random, least-used, cost-optimized, reset-aware, reset-window, headroom, strict-random, auto, lkgp, context-optimized, context-relay, fusion, pipeline
|
||||
- **4-tier fallback**: Subscription → API Key → Cheap → Free
|
||||
@@ -475,7 +475,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
## v3.8.x Highlights
|
||||
|
||||
- **356-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **357-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **19 routing strategies** — including `fusion` (parallel panel + judge synthesis), `pipeline`, `reset-aware`, `reset-window`, `headroom`, and `context-relay`
|
||||
- **14-factor Auto-Combo scoring** with bandit exploration and progressive cooldown
|
||||
- **MCP server expanded to 110 tools / 33 scopes** (canonical + memory/skill/agentSkill/githubSkill/pool/notion/obsidian/localCorpus/gamification/plugin modules)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { HTTP_STATUS, FETCH_TIMEOUT_MS } from "../config/constants.ts";
|
||||
import { getRegistryEntry } from "../config/providerRegistry.ts";
|
||||
import { resolveFetchStartTimeout } from "../utils/fetchStartTimeoutPolicy.ts";
|
||||
import {
|
||||
resolveAlternateFormat,
|
||||
type AlternateFormat,
|
||||
@@ -902,9 +903,24 @@ export class BaseExecutor {
|
||||
clampNestedThinkingBudget(transformedBody, thinkingBudgetClampedMax);
|
||||
}
|
||||
|
||||
// Timeout only covers response start; stream stalls are handled downstream.
|
||||
// #11526: streaming requests cap the headers-wait phase to a client-realistic
|
||||
// ceiling (see fetchStartTimeoutPolicy.ts) — non-streaming keeps the flat default.
|
||||
// Declared outside the try/catch below so the catch's TIMEOUT log (on the
|
||||
// error path) reports the same effective value the fetch actually used.
|
||||
const fetchStartTimeoutPolicy = resolveFetchStartTimeout({
|
||||
baseTimeoutMs: this.getTimeoutMs(),
|
||||
stream,
|
||||
});
|
||||
const fetchStartTimeoutMs = fetchStartTimeoutPolicy.timeoutMs;
|
||||
if (fetchStartTimeoutPolicy.capped) {
|
||||
log?.debug?.(
|
||||
"TIMEOUT",
|
||||
`fetch-start timeout capped ${fetchStartTimeoutPolicy.baseTimeoutMs}ms -> ${fetchStartTimeoutMs}ms (streaming)`
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
// Timeout only covers response start; stream stalls are handled downstream.
|
||||
const fetchStartTimeoutMs = this.getTimeoutMs();
|
||||
const fetchWithStartTimeout = async (requestUrl: string, requestOptions: RequestInit) => {
|
||||
// GHSA-4f49: guard here (not only next to the first buildUrl) so retries
|
||||
// and fallback URLs are validated too, before any bytes leave the host.
|
||||
@@ -1713,7 +1729,7 @@ export class BaseExecutor {
|
||||
// Distinguish timeout errors from other abort errors
|
||||
const err = error instanceof Error ? error : new Error(String(error));
|
||||
if (err.name === "TimeoutError") {
|
||||
log?.warn?.("TIMEOUT", `Fetch timeout after ${this.getTimeoutMs()}ms on ${url}`);
|
||||
log?.warn?.("TIMEOUT", `Fetch timeout after ${fetchStartTimeoutMs}ms on ${url}`);
|
||||
}
|
||||
lastError = err;
|
||||
if (!skipUpstreamRetry && urlIndex + 1 < fallbackCount) {
|
||||
|
||||
@@ -92,19 +92,65 @@ interface KieImageOptions {
|
||||
}
|
||||
|
||||
// KIE Market catalog ids are namespaced for OmniRoute's catalog
|
||||
// (`google-imagen/<model>`), but the KIE Market createTask API expects
|
||||
// (`<vendor>/<model>`), but the KIE Market createTask API expects
|
||||
// vendor-specific upstream ids that do not follow a single consistent
|
||||
// pattern (confirmed against docs.kie.ai/market/google/* — see #11225,
|
||||
// #11296): nano-banana-2 and nano-banana-pro drop the vendor namespace
|
||||
// entirely, while nano-banana and nano-banana-edit use a `google/` prefix
|
||||
// instead of `google-imagen/`. Every other KIE Market namespace (seedream,
|
||||
// flux, ideogram, qwen, wan, grok-imagine, gpt) already matches its real
|
||||
// upstream id byte-for-byte, so this map stays scoped to google-imagen.
|
||||
// pattern. Every entry below was confirmed individually against the literal
|
||||
// example request JSON published on docs.kie.ai (never inferred by pattern —
|
||||
// see #11326's false "everything else already matches" claim and #11296's
|
||||
// follow-up correction):
|
||||
// - google-imagen: nano-banana-2 and nano-banana-pro drop the vendor
|
||||
// namespace entirely; nano-banana and nano-banana-edit use a `google/`
|
||||
// prefix instead of `google-imagen/` (docs.kie.ai/market/google/*).
|
||||
// - gpt: gpt-image-2-* drops the `gpt/` namespace entirely
|
||||
// (docs.kie.ai/market/gpt/gpt-image-2-*); gpt-image-1.5-* uses a
|
||||
// `gpt-image/` namespace instead of `gpt/gpt-image-1.5-`, and keeps the
|
||||
// dot in "1.5" (docs.kie.ai/market/gpt-image/1-5-*).
|
||||
// - seedream: 5.0-lite-* drops the ".0" — real id is `5-lite-*`
|
||||
// (docs.kie.ai/market/seedream/5-lite-text-to-image); seedream 4.5 (T2I
|
||||
// and edit) already matches byte-for-byte.
|
||||
// - flux: `flux/2-*` uses a `flux-2/` namespace (dash, not slash); the
|
||||
// generic (non-"pro") variant is named `flex` upstream, not `2`
|
||||
// (docs.kie.ai/market/flux2/pro-*, .../flex-*).
|
||||
// - wan: `wan/2.7-*` keeps the dot in our catalog, but KIE's documented
|
||||
// enum uses a dash — real id is `wan/2-7-*`
|
||||
// (docs.kie.ai/market/wan/2-7-image[-pro]).
|
||||
// - ideogram (v3-text-to-image, v3-edit, v3-remix), qwen, qwen2, and
|
||||
// grok-imagine already match byte-for-byte
|
||||
// (docs.kie.ai/market/{ideogram,qwen,qwen2,grok-imagine}/*).
|
||||
// ideogram/v3-reframe has no dedicated docs.kie.ai page as of this sweep
|
||||
// (its 3 siblings above are all direct id matches, so it is assumed
|
||||
// correct by pattern, not independently confirmed).
|
||||
// Two catalog entries remain UNRESOLVED after this sweep and are
|
||||
// deliberately left untouched pending a follow-up (see #11296 discussion):
|
||||
// - z-image/4.0-text-to-image and z-image/4.5-text-to-image: the only
|
||||
// documented Z-Image Market page (docs.kie.ai/market/z-image/z-image)
|
||||
// shows a single fixed `model` enum value `"z-image"` with no
|
||||
// version-specific id or "version" input field found — unclear whether
|
||||
// both catalog ids should collapse to the same upstream call.
|
||||
// - flux/kontext: no `docs.kie.ai/market/flux2/kontext` (or similar)
|
||||
// Market page exists; Flux Kontext is documented under the separate
|
||||
// `/flux-kontext-api/*` docs tree with its own endpoint
|
||||
// (`POST /api/v1/flux/kontext/generate`, models `flux-kontext-pro`/
|
||||
// `flux-kontext-max`), not the Market `createTask` flow this map feeds.
|
||||
// This entry may be miscatalogued as `isMarket: true` and need a
|
||||
// dedicated reroute rather than an id rewrite.
|
||||
export const KIE_MARKET_UPSTREAM_MODEL_IDS: ReadonlyMap<string, string> = new Map([
|
||||
["google-imagen/nano-banana", "google/nano-banana"],
|
||||
["google-imagen/nano-banana-2", "nano-banana-2"],
|
||||
["google-imagen/nano-banana-pro", "nano-banana-pro"],
|
||||
["google-imagen/nano-banana-edit", "google/nano-banana-edit"],
|
||||
["gpt/gpt-image-2-text-to-image", "gpt-image-2-text-to-image"],
|
||||
["gpt/gpt-image-2-image-to-image", "gpt-image-2-image-to-image"],
|
||||
["gpt/gpt-image-1.5-text-to-image", "gpt-image/1.5-text-to-image"],
|
||||
["gpt/gpt-image-1.5-image-to-image", "gpt-image/1.5-image-to-image"],
|
||||
["seedream/5.0-lite-text-to-image", "seedream/5-lite-text-to-image"],
|
||||
["seedream/5.0-lite-image-to-image", "seedream/5-lite-image-to-image"],
|
||||
["flux/2-pro-text-to-image", "flux-2/pro-text-to-image"],
|
||||
["flux/2-pro-image-to-image", "flux-2/pro-image-to-image"],
|
||||
["flux/2-text-to-image", "flux-2/flex-text-to-image"],
|
||||
["flux/2-image-to-image", "flux-2/flex-image-to-image"],
|
||||
["wan/2.7-image", "wan/2-7-image"],
|
||||
["wan/2.7-image-pro", "wan/2-7-image-pro"],
|
||||
]);
|
||||
|
||||
export function resolveKieMarketUpstreamModelId(publicModelId: string): string {
|
||||
|
||||
@@ -3286,7 +3286,24 @@ async function handleRoundRobinCombo({
|
||||
"COMBO-RR",
|
||||
`Maximum combo attempts (${maxGlobalAttempts}) exceeded. Terminating loop to prevent runaway requests.`
|
||||
);
|
||||
return errorResponse(503, "Maximum combo retry limit reached");
|
||||
return errorResponseWithComboDiagnostics(
|
||||
503,
|
||||
"Maximum combo retry limit reached",
|
||||
{
|
||||
poolSize: modelCount,
|
||||
attempted: globalAttempts,
|
||||
excluded: [
|
||||
...[...exhaustedProviders].map((p) => ({ provider: p, reason: "exhausted" })),
|
||||
...[...exhaustedConnections].map((c) => formatExhaustedConnectionKey(String(c))),
|
||||
],
|
||||
attemptOrder: rrOutcomes.map((o) => ({
|
||||
provider: o.model.split("/")[0] || "unknown",
|
||||
model: o.model,
|
||||
})),
|
||||
terminalReason: "max_attempts_exceeded",
|
||||
recovery: buildRecoveryHint("max_attempts_exceeded"),
|
||||
}
|
||||
);
|
||||
}
|
||||
if (retry > 0) {
|
||||
log.info(
|
||||
|
||||
@@ -5,7 +5,8 @@
|
||||
* @changes
|
||||
* - [2026-07-24] [Composer] - Skip execute-mode units at concurrency cap before dispatch
|
||||
*/
|
||||
import { errorResponse } from "../../utils/error.ts";
|
||||
import { errorResponse, errorResponseWithComboDiagnostics } from "../../utils/error.ts";
|
||||
import type { ComboDiagnostics } from "../../utils/error.ts";
|
||||
import { recordComboRequest } from "../comboMetrics.ts";
|
||||
import { resolveDelayMs } from "./comboPredicates.ts";
|
||||
import { isRuntimeUnitAtConcurrencyCap } from "./runtimeUnitCapacity.ts";
|
||||
@@ -216,6 +217,17 @@ export async function executeRuntimeUnitCombo(args: {
|
||||
};
|
||||
const finalFailure = (response: Response): Response =>
|
||||
withQuotaExhaustionClassification(response, observedFailure ? allObservedFailuresQuota : null);
|
||||
// #11462: attempts already made this loop, tracked for the attempt-budget-exceeded
|
||||
// diagnostics trace below (mirrors the poolSize/attemptOrder shape combo.ts already
|
||||
// attaches for the priority/round-robin strategies).
|
||||
const attemptedUnits: Array<{ provider: string; model: string }> = [];
|
||||
const buildAttemptBudgetDiag = (): ComboDiagnostics => ({
|
||||
poolSize: orderedUnits.length,
|
||||
attempted: args.nesting.attemptBudget.count,
|
||||
excluded: [],
|
||||
attemptOrder: attemptedUnits,
|
||||
terminalReason: "max_attempts_exceeded",
|
||||
});
|
||||
|
||||
for (const unit of orderedUnits) {
|
||||
const protectedPriorityUnit =
|
||||
@@ -247,13 +259,21 @@ export async function executeRuntimeUnitCombo(args: {
|
||||
}
|
||||
args.nesting.attemptBudget.count += 1;
|
||||
if (args.nesting.attemptBudget.count > args.nesting.attemptBudget.limit) {
|
||||
lastResponse = errorResponse(503, "Maximum combo retry limit reached");
|
||||
lastResponse = errorResponseWithComboDiagnostics(
|
||||
503,
|
||||
"Maximum combo retry limit reached",
|
||||
buildAttemptBudgetDiag()
|
||||
);
|
||||
await observeFailure(lastResponse, unit);
|
||||
return { response: finalFailure(lastResponse), unit };
|
||||
}
|
||||
if (retry > 0) {
|
||||
await new Promise((resolve) => setTimeout(resolve, retryDelayMs));
|
||||
}
|
||||
attemptedUnits.push({
|
||||
provider: unit.kind === "model" ? unit.provider : "combo-ref",
|
||||
model: unitDisplayName(unit),
|
||||
});
|
||||
args.log.info(
|
||||
"COMBO",
|
||||
`Trying ${unit.kind} ${unitDisplayName(unit)}${retry > 0 ? ` (retry ${retry})` : ""}`
|
||||
|
||||
@@ -136,7 +136,6 @@ export function claudeToGeminiRequest(model, body, stream, credentials = null) {
|
||||
const omittedToolCallIds = new Set<string>();
|
||||
for (const msg of body.messages) {
|
||||
const parts = [];
|
||||
let shouldUseEmbeddedSignature = true;
|
||||
|
||||
if (Array.isArray(msg.content)) {
|
||||
for (const block of msg.content) {
|
||||
@@ -160,15 +159,15 @@ export function claudeToGeminiRequest(model, body, stream, credentials = null) {
|
||||
break;
|
||||
}
|
||||
|
||||
const embeddedThoughtSignature = shouldUseEmbeddedSignature
|
||||
? signatureForToolCall
|
||||
: undefined;
|
||||
if (embeddedThoughtSignature) {
|
||||
shouldUseEmbeddedSignature = false;
|
||||
}
|
||||
|
||||
// #11510: each functionCall part carries its OWN resolved
|
||||
// thoughtSignature — a parallel (multi tool_use) turn can have a
|
||||
// real, individually-valid signature per tool call, and Gemini
|
||||
// 3.x rejects the request if any functionCall in the turn is
|
||||
// missing one. Previously only the first functionCall of the
|
||||
// message kept its signature; this dropped valid signatures for
|
||||
// every subsequent parallel tool call in the same turn.
|
||||
parts.push({
|
||||
...(embeddedThoughtSignature ? { thoughtSignature: embeddedThoughtSignature } : {}),
|
||||
...(signatureForToolCall ? { thoughtSignature: signatureForToolCall } : {}),
|
||||
functionCall: {
|
||||
...(stripFunctionCallId ? {} : { id: block.id }),
|
||||
name: sanitizeToolName(block.name),
|
||||
|
||||
@@ -383,7 +383,6 @@ function openaiToGeminiBase(
|
||||
if (toolCalls && Array.isArray(toolCalls)) {
|
||||
const toolCallIds: string[] = [];
|
||||
const resolvedSignatures = new Map<string, string>();
|
||||
let firstPersistedSignature: string | undefined;
|
||||
for (const tc of toolCalls) {
|
||||
const id = tc.id as string;
|
||||
const resolved = resolveGeminiThoughtSignature(
|
||||
@@ -392,11 +391,9 @@ function openaiToGeminiBase(
|
||||
);
|
||||
if (typeof resolved === "string" && resolved.length > 0) {
|
||||
resolvedSignatures.set(id, resolved);
|
||||
firstPersistedSignature ??= resolved;
|
||||
}
|
||||
}
|
||||
|
||||
let shouldUseEmbeddedSignature = !parts.some((p) => p.thoughtSignature);
|
||||
const signaturelessToolCallMode = toolNameOptions.signaturelessToolCallMode;
|
||||
const stringifySignaturelessToolCalls = signaturelessToolCallMode === "text";
|
||||
const contextualizeSignaturelessToolResponses =
|
||||
@@ -433,13 +430,14 @@ function openaiToGeminiBase(
|
||||
}
|
||||
|
||||
const args = tryParseJSON(fn.arguments || "{}");
|
||||
const embeddedThoughtSignature = shouldUseEmbeddedSignature
|
||||
? firstPersistedSignature || signatureForToolCall
|
||||
: undefined;
|
||||
|
||||
if (embeddedThoughtSignature) {
|
||||
shouldUseEmbeddedSignature = false;
|
||||
}
|
||||
// #11510: each functionCall part carries its OWN resolved
|
||||
// thoughtSignature — a parallel (multi tool_calls) turn can have a
|
||||
// real, individually-valid signature per tool call, and Gemini 3.x
|
||||
// rejects the request if any functionCall in the turn is missing
|
||||
// one. Previously only the first functionCall of the message kept
|
||||
// its signature; this dropped valid signatures for every
|
||||
// subsequent parallel tool call in the same turn.
|
||||
const embeddedThoughtSignature = signatureForToolCall;
|
||||
|
||||
// Gemini expects the signature on the functionCall part itself.
|
||||
// If we are in a mode where missing signatures cause 400s (and we couldn't find one),
|
||||
|
||||
52
open-sse/utils/fetchStartTimeoutPolicy.ts
Normal file
@@ -0,0 +1,52 @@
|
||||
// #11526: the fetch-start (headers-wait) phase had no ceiling comparable to a
|
||||
// real client's patience for STREAMING requests — it inherited the flat,
|
||||
// non-adaptive FETCH_TIMEOUT_MS (default 600_000ms / 10 minutes), five times
|
||||
// longer than Codex's own ~120s hard client-abort window. When an upstream
|
||||
// never returns a response at all (not even headers), OmniRoute kept the
|
||||
// connection open with nothing but keepalives, guaranteeing the client gave
|
||||
// up first with an opaque 499 instead of OmniRoute detecting the stall and
|
||||
// failing fast/over within a client-realistic window.
|
||||
//
|
||||
// This mirrors the adaptive philosophy of streamReadinessPolicy.ts's
|
||||
// resolveStreamReadinessTimeout (which already protects the BODY phase, after
|
||||
// headers arrive) but inverted: instead of bumping a small base timeout up for
|
||||
// heavy payloads, it caps an oversized base timeout down for the HEADERS
|
||||
// phase of streaming requests specifically. Non-streaming requests are left
|
||||
// on the existing flat default — providers that are legitimately slow to
|
||||
// accept a connection (but not streaming SSE) are unaffected.
|
||||
|
||||
export type FetchStartTimeoutPolicyInput = {
|
||||
baseTimeoutMs: number;
|
||||
/** Only streaming requests are capped — non-streaming keeps the flat default. */
|
||||
stream?: boolean | null;
|
||||
capMs?: number;
|
||||
};
|
||||
|
||||
export type FetchStartTimeoutPolicyResult = {
|
||||
timeoutMs: number;
|
||||
baseTimeoutMs: number;
|
||||
/** True when the base timeout was reduced by the streaming cap. */
|
||||
capped: boolean;
|
||||
};
|
||||
|
||||
// Codex's documented hard client-abort window for a stalled turn (nothing but
|
||||
// keepalives in flight) is ~120s. Keep the cap safely under that so OmniRoute's
|
||||
// own headers-phase watchdog always fires before the client gives up on its own.
|
||||
export const CODEX_CLIENT_ABORT_MS = 120_000;
|
||||
export const DEFAULT_FETCH_START_TIMEOUT_CAP_MS = 110_000;
|
||||
|
||||
export function resolveFetchStartTimeout(
|
||||
input: FetchStartTimeoutPolicyInput
|
||||
): FetchStartTimeoutPolicyResult {
|
||||
const baseTimeoutMs = Math.max(0, Math.floor(input.baseTimeoutMs || 0));
|
||||
if (baseTimeoutMs <= 0 || !input.stream) {
|
||||
return { timeoutMs: baseTimeoutMs, baseTimeoutMs, capped: false };
|
||||
}
|
||||
|
||||
const capMs = Math.max(0, Math.floor(input.capMs ?? DEFAULT_FETCH_START_TIMEOUT_CAP_MS));
|
||||
if (capMs <= 0 || baseTimeoutMs <= capMs) {
|
||||
return { timeoutMs: baseTimeoutMs, baseTimeoutMs, capped: false };
|
||||
}
|
||||
|
||||
return { timeoutMs: capMs, baseTimeoutMs, capped: true };
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "omniroute",
|
||||
"version": "3.8.51",
|
||||
"description": "Unified AI router with 356 providers, RTK+Caveman compression, auto fallback, MCP/A2A, desktop, PWA, and OpenAI-compatible APIs.",
|
||||
"description": "Unified AI router with 357 providers, RTK+Caveman compression, auto fallback, MCP/A2A, desktop, PWA, and OpenAI-compatible APIs.",
|
||||
"type": "module",
|
||||
"bin": {
|
||||
"omniroute": "bin/omniroute.mjs",
|
||||
|
||||
@@ -47,7 +47,7 @@ curl https://localhost:20128/api/v1/search \
|
||||
|
||||
Run a unified search
|
||||
|
||||
Searches the web, news, or X through a configured provider. Set `provider` to `xquik-search` to use Xquik for X search. The aliases `xquik` and `xquik_search` resolve to the same provider.
|
||||
Searches the web, news, or X through a configured provider. Set `provider` to `xquik-search` to use Xquik for X search. The aliases `xquik` and `xquik_search` resolve to the same provider. AnySearch (`anysearch-search`, aliases `anysearch` / `anysearch_search`) provides free fallback-only web search.
|
||||
|
||||
```bash
|
||||
curl -X POST https://localhost:20128/api/v1/search \
|
||||
|
||||
@@ -2177,6 +2177,9 @@ function ComboFormModal({ isOpen, combo, onClose, onSave, activeProviders, combo
|
||||
builderConnectionId !== COMBO_BUILDER_AUTO_CONNECTION ? builderConnectionId : null,
|
||||
connectionLabel: selectedBuilderConnection?.label || null,
|
||||
allowedConnectionIds: builderEffectiveAllowedConnectionIds,
|
||||
// #11433: use the already-corrected routing prefix (e.g. "oc" for
|
||||
// OpenCode Free) instead of letting it default to the raw providerId.
|
||||
modelPrefix: parseQualifiedModel(selectedBuilderModel.qualifiedModel)?.providerId,
|
||||
})
|
||||
: null;
|
||||
const builderHasDuplicate =
|
||||
@@ -2501,6 +2504,9 @@ function ComboFormModal({ isOpen, combo, onClose, onSave, activeProviders, combo
|
||||
builderConnectionId !== COMBO_BUILDER_AUTO_CONNECTION ? builderConnectionId : null,
|
||||
connectionLabel: selectedBuilderConnection?.label || null,
|
||||
allowedConnectionIds: builderEffectiveAllowedConnectionIds,
|
||||
// #11433: use the already-corrected routing prefix (e.g. "oc" for
|
||||
// OpenCode Free) instead of letting it default to the raw providerId.
|
||||
modelPrefix: parseQualifiedModel(selectedBuilderModel.qualifiedModel)?.providerId,
|
||||
});
|
||||
|
||||
if (hasExactModelStepDuplicate(models, nextStep)) {
|
||||
|
||||
@@ -0,0 +1,126 @@
|
||||
// @vitest-environment jsdom
|
||||
// Regression for issue #11324: adding a custom/manual model connection for a
|
||||
// non-curated provider must not force a full upstream /models catalog sync
|
||||
// when the caller explicitly opts out via `skipModelSync`.
|
||||
import React, { act } from "react";
|
||||
import { createRoot } from "react-dom/client";
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
import { useApiKeySave } from "../hooks/useApiKeySave";
|
||||
|
||||
const t = ((key: string) => key) as Parameters<typeof useApiKeySave>[0]["t"];
|
||||
|
||||
function response(ok: boolean, body: unknown): Response {
|
||||
return { ok, json: async () => body } as Response;
|
||||
}
|
||||
|
||||
function renderApiKeySaveHook(): {
|
||||
hookResult: () => ReturnType<typeof useApiKeySave>;
|
||||
root: ReturnType<typeof createRoot>;
|
||||
container: HTMLDivElement;
|
||||
} {
|
||||
const container = document.createElement("div");
|
||||
document.body.appendChild(container);
|
||||
let hookResult: ReturnType<typeof useApiKeySave> | null = null;
|
||||
function Wrapper() {
|
||||
const result = useApiKeySave({
|
||||
providerId: "huge-catalog-openai-compatible",
|
||||
fetchConnections: vi.fn().mockResolvedValue(undefined),
|
||||
fetchProviderModelMeta: vi.fn().mockResolvedValue(undefined),
|
||||
setImportProgress: vi.fn(),
|
||||
setShowImportModal: vi.fn(),
|
||||
setShowAddApiKeyModal: vi.fn(),
|
||||
setSiliconFlowInitialBaseUrl: vi.fn(),
|
||||
notify: { success: vi.fn(), error: vi.fn() },
|
||||
t,
|
||||
});
|
||||
React.useEffect(() => {
|
||||
hookResult = result;
|
||||
}, [result]);
|
||||
return null;
|
||||
}
|
||||
const root = createRoot(container);
|
||||
act(() => root.render(<Wrapper />));
|
||||
return { hookResult: () => hookResult as ReturnType<typeof useApiKeySave>, root, container };
|
||||
}
|
||||
|
||||
describe("useApiKeySave.handleSaveApiKey — full-sync opt-out (#11324)", () => {
|
||||
let roots: ReturnType<typeof createRoot>[] = [];
|
||||
let containers: HTMLDivElement[] = [];
|
||||
|
||||
beforeEach(() => {
|
||||
(globalThis as any).IS_REACT_ACT_ENVIRONMENT = true;
|
||||
roots = [];
|
||||
containers = [];
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
for (const root of roots) act(() => root.unmount());
|
||||
for (const container of containers) container.remove();
|
||||
roots = [];
|
||||
containers = [];
|
||||
vi.unstubAllGlobals();
|
||||
});
|
||||
|
||||
it("does not auto-trigger a full /sync-models catalog fetch when the caller asks to add just one manual model", async () => {
|
||||
const fetchMock = vi.fn(async (input: RequestInfo | URL, _init?: RequestInit) => {
|
||||
const url = String(input);
|
||||
if (url === "/api/providers") return response(true, { connection: { id: "conn-1" } });
|
||||
if (url.includes("/sync-models")) {
|
||||
return response(true, {
|
||||
syncedModels: 1200,
|
||||
availableModelsCount: 1200,
|
||||
models: Array.from({ length: 1200 }, (_, i) => ({ id: `model-${i}` })),
|
||||
});
|
||||
}
|
||||
throw new Error(`Unexpected fetch: ${url}`);
|
||||
});
|
||||
vi.stubGlobal("fetch", fetchMock);
|
||||
|
||||
const { hookResult, root, container } = renderApiKeySaveHook();
|
||||
roots.push(root);
|
||||
containers.push(container);
|
||||
|
||||
await act(async () => {
|
||||
await hookResult().handleSaveApiKey({ apiKey: "sk-test", skipModelSync: true });
|
||||
});
|
||||
|
||||
const syncCalls = fetchMock.mock.calls.filter(([input]) =>
|
||||
String(input).includes("/sync-models")
|
||||
);
|
||||
expect(syncCalls).toHaveLength(0);
|
||||
|
||||
// The opt-out is a client-side intent signal only — it must never leak into the
|
||||
// persisted connection payload sent to the server.
|
||||
const providersCall = fetchMock.mock.calls.find(
|
||||
([input]) => String(input) === "/api/providers"
|
||||
);
|
||||
const postedBody = JSON.parse((providersCall?.[1] as RequestInit).body as string);
|
||||
expect(postedBody).not.toHaveProperty("skipModelSync");
|
||||
});
|
||||
|
||||
it("still auto-triggers the full /sync-models catalog fetch by default (legacy behavior preserved)", async () => {
|
||||
const fetchMock = vi.fn(async (input: RequestInfo | URL) => {
|
||||
const url = String(input);
|
||||
if (url === "/api/providers") return response(true, { connection: { id: "conn-1" } });
|
||||
if (url.includes("/sync-models")) {
|
||||
return response(true, { syncedModels: 3, availableModelsCount: 3, models: [] });
|
||||
}
|
||||
throw new Error(`Unexpected fetch: ${url}`);
|
||||
});
|
||||
vi.stubGlobal("fetch", fetchMock);
|
||||
|
||||
const { hookResult, root, container } = renderApiKeySaveHook();
|
||||
roots.push(root);
|
||||
containers.push(container);
|
||||
|
||||
await act(async () => {
|
||||
await hookResult().handleSaveApiKey({ apiKey: "sk-test" });
|
||||
});
|
||||
|
||||
const syncCalls = fetchMock.mock.calls.filter(([input]) =>
|
||||
String(input).includes("/sync-models")
|
||||
);
|
||||
expect(syncCalls).toHaveLength(1);
|
||||
});
|
||||
});
|
||||
@@ -57,13 +57,19 @@ export function useApiKeySave({
|
||||
}: UseApiKeySaveParams) {
|
||||
const handleSaveApiKey = useCallback(
|
||||
async (formData: Record<string, unknown>) => {
|
||||
// Issue #11324: callers that only want to add one manual model (rather than
|
||||
// importing an upstream provider's entire catalog) can pass `skipModelSync: true`
|
||||
// to opt out of the automatic post-save full /sync-models call. This flag is a
|
||||
// client-side intent signal only — strip it before it reaches the connection
|
||||
// creation payload.
|
||||
const { skipModelSync, ...connectionFormData } = formData;
|
||||
try {
|
||||
const res = await fetch("/api/providers", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({
|
||||
provider: resolveApiKeySaveProviderId(providerId),
|
||||
...formData,
|
||||
...connectionFormData,
|
||||
}),
|
||||
});
|
||||
if (res.ok) {
|
||||
@@ -75,7 +81,8 @@ export function useApiKeySave({
|
||||
|
||||
// Most providers sync their live catalog after connection creation. Curated-only
|
||||
// providers intentionally use the registry list and must not show an import flow.
|
||||
if (newConnection?.id && !providerUsesCuratedModelsOnly(providerId)) {
|
||||
// Issue #11324: callers may also opt out explicitly via `skipModelSync`.
|
||||
if (newConnection?.id && !providerUsesCuratedModelsOnly(providerId) && !skipModelSync) {
|
||||
setShowImportModal(true);
|
||||
setImportProgress({
|
||||
current: 0,
|
||||
|
||||
@@ -83,6 +83,7 @@ export function buildPrecisionComboModelStep({
|
||||
connectionLabel,
|
||||
allowedConnectionIds = null,
|
||||
weight = 0,
|
||||
modelPrefix,
|
||||
}: {
|
||||
providerId: string;
|
||||
modelId: string;
|
||||
@@ -91,9 +92,22 @@ export function buildPrecisionComboModelStep({
|
||||
/** #3266: account allowlist scoping round-robin to a subset of connections. */
|
||||
allowedConnectionIds?: string[] | null;
|
||||
weight?: number;
|
||||
/**
|
||||
* #11433: the routing-prefix segment to serialize into `model` (e.g. "oc"
|
||||
* for the no-auth OpenCode Free provider), when it differs from the
|
||||
* canonical `providerId`. Some canonical provider ids collide with an
|
||||
* unrelated manual `ALIAS_TO_PROVIDER_ID` routing override (`opencode` →
|
||||
* `opencode-zen`), so reconstructing `model` from the raw `providerId`
|
||||
* alone can round-trip to the wrong provider on request routing. Falls
|
||||
* back to `providerId` when omitted/blank. `step.providerId` always stays
|
||||
* the canonical id regardless, so routing/duplicate-detection identity is
|
||||
* unaffected.
|
||||
*/
|
||||
modelPrefix?: string | null;
|
||||
}): ComboModelStep {
|
||||
const normalizedProviderId = toTrimmedString(providerId) || "provider";
|
||||
const normalizedModelId = toTrimmedString(modelId) || "model";
|
||||
const normalizedModelPrefix = toTrimmedString(modelPrefix) || normalizedProviderId;
|
||||
const normalizedConnectionId = toTrimmedString(connectionId);
|
||||
const normalizedConnectionLabel = toTrimmedString(connectionLabel);
|
||||
// A pinned single connection wins over an allowlist, so only carry the allowlist
|
||||
@@ -110,7 +124,7 @@ export function buildPrecisionComboModelStep({
|
||||
return {
|
||||
kind: "model",
|
||||
providerId: normalizedProviderId,
|
||||
model: `${normalizedProviderId}/${normalizedModelId}`,
|
||||
model: `${normalizedModelPrefix}/${normalizedModelId}`,
|
||||
...(normalizedConnectionId ? { connectionId: normalizedConnectionId } : {}),
|
||||
...(normalizedConnectionLabel ? { label: normalizedConnectionLabel } : {}),
|
||||
...(normalizedAllowed.length > 0 ? { allowedConnectionIds: normalizedAllowed } : {}),
|
||||
@@ -160,10 +174,15 @@ export function buildManualComboModelStep({
|
||||
const providerId = resolveComboBuilderProviderId(parsed.providerId, providers);
|
||||
if (!providerId) return null;
|
||||
|
||||
// #11433: preserve the user-typed prefix (e.g. "oc") as the routing prefix
|
||||
// instead of letting buildPrecisionComboModelStep rebuild `model` from the
|
||||
// resolved canonical providerId, which can collide with an unrelated
|
||||
// manual alias override (e.g. "opencode" -> "opencode-zen").
|
||||
return buildPrecisionComboModelStep({
|
||||
providerId,
|
||||
modelId: parsed.modelId,
|
||||
weight,
|
||||
modelPrefix: parsed.providerId,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -225,7 +244,7 @@ type ComboBuilderGlobalProvider = {
|
||||
displayName?: unknown;
|
||||
connectionCount?: unknown;
|
||||
connections?: unknown[];
|
||||
models?: Array<{ id?: unknown; name?: unknown }>;
|
||||
models?: Array<{ id?: unknown; name?: unknown; qualifiedModel?: unknown }>;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -252,12 +271,18 @@ export function buildGlobalModelList(
|
||||
const modelId = toTrimmedString(model?.id);
|
||||
if (!modelId) return;
|
||||
const modelName = toTrimmedString(model?.name) || modelId;
|
||||
// #11433: derive the routing prefix from the model's already-corrected
|
||||
// `qualifiedModel` (e.g. "oc/<model>" for the OpenCode Free provider)
|
||||
// instead of defaulting to the raw providerId, which can collide with
|
||||
// an unrelated manual alias override.
|
||||
const modelPrefix = parseQualifiedModel(model?.qualifiedModel)?.providerId || providerId;
|
||||
const step = buildPrecisionComboModelStep({
|
||||
providerId,
|
||||
modelId,
|
||||
connectionId: null,
|
||||
connectionLabel: null,
|
||||
allowedConnectionIds: [],
|
||||
modelPrefix,
|
||||
});
|
||||
list.push({
|
||||
providerId,
|
||||
|
||||
@@ -288,6 +288,36 @@ export function decrypt(
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* #11500 — decrypt() wrapper for callers outside decryptConnectionFields()
|
||||
* (the lazy-decrypt views in providers/lazyConnectionView.ts, which call
|
||||
* decrypt() directly on every fresh getProviderConnections() cycle). A
|
||||
* fresh Proxy wraps a fresh row object each cycle, so per-proxy memoization
|
||||
* never survives across cycles — without this wrapper the raw
|
||||
* "[Encryption] Decryption failed..." line re-fires every single cycle for
|
||||
* the same corrupt/stale-key credential. Shares the loggedDecryptFailures
|
||||
* Set with decryptConnectionFields() so a credential already flagged via one
|
||||
* path does not re-log via the other, and logs the SAME raw message
|
||||
* decrypt() would emit (unlike decryptConnectionFields()'s enriched
|
||||
* message) — just deduped to once per (provider + connection + field +
|
||||
* ciphertext) instead of once per cycle.
|
||||
*/
|
||||
export function decryptQuiet(
|
||||
ciphertext: string | null | undefined,
|
||||
meta: { connectionId: string; provider: string; field: string }
|
||||
): string | null | undefined {
|
||||
if (!looksEncrypted(ciphertext)) {
|
||||
return decrypt(ciphertext);
|
||||
}
|
||||
const signature = `${meta.provider}::${meta.connectionId}::${meta.field}:${ciphertext}`;
|
||||
const alreadyLogged = loggedDecryptFailures.has(signature);
|
||||
const result = decrypt(ciphertext, { quiet: alreadyLogged });
|
||||
if (result === null && !alreadyLogged) {
|
||||
loggedDecryptFailures.add(signature);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encrypt sensitive fields in a connection object (mutates in-place).
|
||||
* After decryption that required legacy key, re-encrypt with static key
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
* admin, and catalog callers during Phase 2/3 of the lazy-decrypt rollout.
|
||||
*/
|
||||
|
||||
import { decrypt } from "../encryption";
|
||||
import { decryptQuiet } from "../encryption";
|
||||
|
||||
type JsonRecord = Record<string, unknown>;
|
||||
|
||||
@@ -119,10 +119,18 @@ export function createLazyConnectionView(row: Record<string, unknown>): Provider
|
||||
|
||||
const ensureDecrypted = () => {
|
||||
if (!decrypted) {
|
||||
const connectionId = base.id;
|
||||
const provider = base.provider;
|
||||
decrypted = {
|
||||
apiKey: toStringOrNull(decrypt(base.apiKey)),
|
||||
accessToken: toStringOrNull(decrypt(base.accessToken)),
|
||||
refreshToken: toStringOrNull(decrypt(base.refreshToken)),
|
||||
apiKey: toStringOrNull(
|
||||
decryptQuiet(base.apiKey, { connectionId, provider, field: "apiKey" })
|
||||
),
|
||||
accessToken: toStringOrNull(
|
||||
decryptQuiet(base.accessToken, { connectionId, provider, field: "accessToken" })
|
||||
),
|
||||
refreshToken: toStringOrNull(
|
||||
decryptQuiet(base.refreshToken, { connectionId, provider, field: "refreshToken" })
|
||||
),
|
||||
};
|
||||
}
|
||||
return decrypted;
|
||||
@@ -154,11 +162,13 @@ export function createLazyRowProxy(row: Record<string, unknown>): Record<string,
|
||||
|
||||
const ensureDecrypted = () => {
|
||||
if (!decrypted) {
|
||||
const connectionId = typeof row.id === "string" ? row.id : "";
|
||||
const provider = typeof row.provider === "string" ? row.provider : "unknown";
|
||||
decrypted = {
|
||||
apiKey: lazyDecrypt(row.apiKey),
|
||||
accessToken: lazyDecrypt(row.accessToken),
|
||||
refreshToken: lazyDecrypt(row.refreshToken),
|
||||
idToken: lazyDecrypt(row.idToken),
|
||||
apiKey: lazyDecrypt(row.apiKey, { connectionId, provider, field: "apiKey" }),
|
||||
accessToken: lazyDecrypt(row.accessToken, { connectionId, provider, field: "accessToken" }),
|
||||
refreshToken: lazyDecrypt(row.refreshToken, { connectionId, provider, field: "refreshToken" }),
|
||||
idToken: lazyDecrypt(row.idToken, { connectionId, provider, field: "idToken" }),
|
||||
};
|
||||
}
|
||||
return decrypted;
|
||||
@@ -189,7 +199,10 @@ export function createLazyRowProxy(row: Record<string, unknown>): Record<string,
|
||||
});
|
||||
}
|
||||
|
||||
function lazyDecrypt(value: unknown): string | null | undefined {
|
||||
function lazyDecrypt(
|
||||
value: unknown,
|
||||
meta: { connectionId: string; provider: string; field: string }
|
||||
): string | null | undefined {
|
||||
if (typeof value !== "string") return undefined;
|
||||
return decrypt(value);
|
||||
return decryptQuiet(value, meta);
|
||||
}
|
||||
|
||||
@@ -447,8 +447,6 @@ const LOBE_PROVIDER_ALIASES = {
|
||||
suno: "Suno",
|
||||
tavily: "Tavily",
|
||||
"tavily-search": "Tavily",
|
||||
anysearch: "AnySearch",
|
||||
"anysearch-search": "AnySearch",
|
||||
tencent: "Tencent",
|
||||
"codebuddy-cn": "Tencent",
|
||||
together: "Together",
|
||||
|
||||
@@ -195,6 +195,7 @@
|
||||
"tests/unit/combo-round-robin-streaming-lock-3811.test.ts",
|
||||
"tests/unit/combo-roundrobin-compat-fallback-6238.test.ts",
|
||||
"tests/unit/combo-routing-engine.test.ts",
|
||||
"tests/unit/combo-rr-diagnostics-11462.test.ts",
|
||||
"tests/unit/combo-rr-fallback-advance-948.test.ts",
|
||||
"tests/unit/combo-rr-sticky-9router.test.ts",
|
||||
"tests/unit/combo-scoring-inspector.test.ts",
|
||||
|
||||
@@ -12,6 +12,11 @@
|
||||
"provider": "golden-test-unknown-provider"
|
||||
},
|
||||
"searchGuard": {
|
||||
"anysearch-search": {
|
||||
"message": "Provider \"anysearch-search\" is a search provider and does not support chat completions; use the /v1/search endpoint instead.",
|
||||
"status": 400,
|
||||
"throws": true
|
||||
},
|
||||
"brave-search": {
|
||||
"message": "Provider \"brave-search\" is a search provider and does not support chat completions; use the /v1/search endpoint instead.",
|
||||
"status": 400,
|
||||
@@ -97,11 +102,6 @@
|
||||
"status": 400,
|
||||
"throws": true
|
||||
},
|
||||
"anysearch-search": {
|
||||
"message": "Provider \"anysearch-search\" is a search provider and does not support chat completions; use the /v1/search endpoint instead.",
|
||||
"status": 400,
|
||||
"throws": true
|
||||
},
|
||||
"youcom-search": {
|
||||
"message": "Provider \"youcom-search\" is a search provider and does not support chat completions; use the /v1/search endpoint instead.",
|
||||
"status": 400,
|
||||
|
||||
@@ -1311,7 +1311,7 @@
|
||||
"Authorization": "Bearer <TOK>",
|
||||
"Content-Type": "application/json",
|
||||
"Openai-Beta": "responses=experimental",
|
||||
"User-Agent": "codex-cli/0.144.1 (<OS>; <ARCH>)",
|
||||
"User-Agent": "codex-cli/0.149.0 (<OS>; <ARCH>)",
|
||||
"Version": "0.149.0",
|
||||
"X-Codex-Beta-Features": "responses_websockets"
|
||||
},
|
||||
@@ -1319,7 +1319,7 @@
|
||||
"Authorization": "Bearer <TOK>",
|
||||
"Content-Type": "application/json",
|
||||
"Openai-Beta": "responses=experimental",
|
||||
"User-Agent": "codex-cli/0.144.1 (<OS>; <ARCH>)",
|
||||
"User-Agent": "codex-cli/0.149.0 (<OS>; <ARCH>)",
|
||||
"Version": "0.149.0",
|
||||
"X-Codex-Beta-Features": "responses_websockets"
|
||||
},
|
||||
@@ -1328,7 +1328,7 @@
|
||||
"Authorization": "Bearer <TOK>",
|
||||
"Content-Type": "application/json",
|
||||
"Openai-Beta": "responses=experimental",
|
||||
"User-Agent": "codex-cli/0.144.1 (<OS>; <ARCH>)",
|
||||
"User-Agent": "codex-cli/0.149.0 (<OS>; <ARCH>)",
|
||||
"Version": "0.149.0",
|
||||
"X-Codex-Beta-Features": "responses_websockets"
|
||||
}
|
||||
|
||||
@@ -7,6 +7,9 @@
|
||||
*/
|
||||
import { test } from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
import fs from "node:fs";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import {
|
||||
ALIBABA_FREE_TIER_TEXT_CAPABLE_MODELS,
|
||||
ALIBABA_NO_FREE_TIER_TEXT_MODELS,
|
||||
@@ -29,16 +32,29 @@ test("built-in allowlist includes operator free models and excludes paid blockli
|
||||
|
||||
test("allowlist JSON pack overrides embedded lists when valid", () => {
|
||||
const previousPath = process.env.ALIBABA_FREE_TIER_ALLOWLIST_PATH;
|
||||
const packPath = `${process.cwd()}/config/alibaba-free-tier-allowlist.json`;
|
||||
process.env.ALIBABA_FREE_TIER_ALLOWLIST_PATH = packPath;
|
||||
resetAlibabaFreeTierAllowlistCache();
|
||||
const fixtureDir = fs.mkdtempSync(path.join(os.tmpdir(), "omniroute-alibaba-allowlist-"));
|
||||
const packPath = path.join(fixtureDir, "allowlist.json");
|
||||
|
||||
const pack = loadAlibabaFreeTierAllowlistPack();
|
||||
assert.ok(pack);
|
||||
assert.ok(isAlibabaFreeTierAllowlistPackValid(pack!));
|
||||
assert.ok(pack!.capable.includes("qwen3.6-plus"));
|
||||
try {
|
||||
fs.writeFileSync(
|
||||
packPath,
|
||||
JSON.stringify({
|
||||
asOf: "2026-08-27",
|
||||
capable: ["qwen3.6-plus"],
|
||||
noFreeTier: ["qwen3.7-max"],
|
||||
})
|
||||
);
|
||||
process.env.ALIBABA_FREE_TIER_ALLOWLIST_PATH = packPath;
|
||||
resetAlibabaFreeTierAllowlistCache();
|
||||
|
||||
if (previousPath) process.env.ALIBABA_FREE_TIER_ALLOWLIST_PATH = previousPath;
|
||||
else delete process.env.ALIBABA_FREE_TIER_ALLOWLIST_PATH;
|
||||
resetAlibabaFreeTierAllowlistCache();
|
||||
const pack = loadAlibabaFreeTierAllowlistPack();
|
||||
assert.ok(pack);
|
||||
assert.ok(isAlibabaFreeTierAllowlistPackValid(pack));
|
||||
assert.ok(pack.capable.includes("qwen3.6-plus"));
|
||||
} finally {
|
||||
if (previousPath) process.env.ALIBABA_FREE_TIER_ALLOWLIST_PATH = previousPath;
|
||||
else delete process.env.ALIBABA_FREE_TIER_ALLOWLIST_PATH;
|
||||
resetAlibabaFreeTierAllowlistCache();
|
||||
fs.rmSync(fixtureDir, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
@@ -194,11 +194,11 @@ describe("Per-Connection Rotation", () => {
|
||||
}
|
||||
expect(seenConnections.size).toBeGreaterThanOrEqual(10);
|
||||
},
|
||||
// 200 synchronous selectProvider() calls over a 43-connection pool are CPU-bound and
|
||||
// vitest's 5000ms default is too tight under shared-devbox contention (load avg 40+
|
||||
// observed alongside parallel test/tsc/lint runs) — the assertion itself is unchanged,
|
||||
// only the execution-time budget is widened. Refs #9985.
|
||||
20000
|
||||
// 200 synchronous selectProvider() calls over a 43-connection pool are CPU-bound and can
|
||||
// exceed 20s under the full Vitest worker load on the validation VPS, while the isolated
|
||||
// file remains green. The assertion is unchanged; only the execution budget is widened.
|
||||
// Refs #9985.
|
||||
60000
|
||||
);
|
||||
|
||||
it("different combos maintain independent round-robin state", () => {
|
||||
|
||||
@@ -42,6 +42,12 @@ test("buildPrecisionComboModelStep preserves provider/model/account triple", ()
|
||||
});
|
||||
|
||||
test("buildManualComboModelStep resolves provider aliases and uses dynamic account", () => {
|
||||
// #11433: `providerId` resolves to the canonical id ("codex") for
|
||||
// duplicate-detection/routing identity, but the serialized `model` string
|
||||
// now preserves the user-typed prefix ("cx/") verbatim instead of
|
||||
// collapsing back to the canonical id — some canonical ids (e.g.
|
||||
// "opencode") collide with an unrelated manual routing-alias override, so
|
||||
// rebuilding `model` from the canonical id alone can silently misroute.
|
||||
assert.deepEqual(
|
||||
builderDraft.buildManualComboModelStep({
|
||||
value: "cx/gpt-5.5",
|
||||
@@ -50,7 +56,7 @@ test("buildManualComboModelStep resolves provider aliases and uses dynamic accou
|
||||
{
|
||||
kind: "model",
|
||||
providerId: "codex",
|
||||
model: "codex/gpt-5.5",
|
||||
model: "cx/gpt-5.5",
|
||||
weight: 0,
|
||||
}
|
||||
);
|
||||
|
||||
77
tests/unit/combo-rr-diagnostics-11462.test.ts
Normal file
@@ -0,0 +1,77 @@
|
||||
import test from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
import fs from "node:fs";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
|
||||
const TEST_DATA_DIR = fs.mkdtempSync(path.join(os.tmpdir(), "omniroute-combo-rr-diag-11462-"));
|
||||
const ORIGINAL_DATA_DIR = process.env.DATA_DIR;
|
||||
process.env.DATA_DIR = TEST_DATA_DIR;
|
||||
|
||||
const { handleComboChat } = await import("../../open-sse/services/combo.ts");
|
||||
const core = await import("../../src/lib/db/core.ts");
|
||||
const { resetAllComboMetrics } = await import("../../open-sse/services/comboMetrics.ts");
|
||||
const { resetAllCircuitBreakers } = await import("../../src/shared/utils/circuitBreaker.ts");
|
||||
const { resetAll: resetAllSemaphores } = await import(
|
||||
"../../open-sse/services/rateLimitSemaphore.ts"
|
||||
);
|
||||
|
||||
function createLog() {
|
||||
return { info: () => {}, warn: () => {}, error: () => {}, debug: () => {} };
|
||||
}
|
||||
function failResponse() {
|
||||
return new Response(JSON.stringify({ error: { message: "upstream 500" } }), {
|
||||
status: 500,
|
||||
headers: { "content-type": "application/json" },
|
||||
});
|
||||
}
|
||||
|
||||
test.beforeEach(() => {
|
||||
resetAllComboMetrics();
|
||||
resetAllCircuitBreakers();
|
||||
resetAllSemaphores();
|
||||
});
|
||||
test.after(() => {
|
||||
resetAllComboMetrics();
|
||||
resetAllCircuitBreakers();
|
||||
resetAllSemaphores();
|
||||
core.resetDbInstance();
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
if (ORIGINAL_DATA_DIR === undefined) delete process.env.DATA_DIR;
|
||||
else process.env.DATA_DIR = ORIGINAL_DATA_DIR;
|
||||
});
|
||||
|
||||
test(
|
||||
"#11462: round-robin combo's 'Maximum combo retry limit reached' 503 must carry " +
|
||||
"the combo diagnostics trace (poolSize/attemptOrder/excluded/terminalReason)",
|
||||
async () => {
|
||||
const result = await handleComboChat({
|
||||
body: { messages: [{ role: "user", content: "hi" }] },
|
||||
combo: {
|
||||
name: "rr-probe-11462",
|
||||
strategy: "round-robin",
|
||||
models: ["openai/rr-a", "anthropic/rr-b"],
|
||||
config: {
|
||||
maxRetries: 0,
|
||||
maxGlobalAttempts: 1,
|
||||
concurrencyPerModel: 1,
|
||||
queueTimeoutMs: 1000,
|
||||
},
|
||||
},
|
||||
handleSingleModel: async () => failResponse(),
|
||||
isModelAvailable: async () => true,
|
||||
log: createLog(),
|
||||
settings: null,
|
||||
relayOptions: null,
|
||||
allCombos: null,
|
||||
});
|
||||
|
||||
assert.equal(result.status, 503);
|
||||
const body = await result.json();
|
||||
assert.equal(body.error.message, "Maximum combo retry limit reached");
|
||||
assert.ok(body.diagnostics, "round-robin 503 should carry a diagnostics field");
|
||||
assert.ok(typeof body.diagnostics.poolSize === "number");
|
||||
assert.ok(Array.isArray(body.diagnostics.attemptOrder));
|
||||
assert.ok(typeof body.diagnostics.terminalReason === "string");
|
||||
}
|
||||
);
|
||||
88
tests/unit/combo-runtimeunits-diagnostics-11462.test.ts
Normal file
@@ -0,0 +1,88 @@
|
||||
/**
|
||||
* #11462: the nested runtime-unit loop (open-sse/services/combo/runtimeUnits.ts,
|
||||
* used by the pipeline/fusion combo strategies via dispatchPrelude.ts and
|
||||
* fusionPanel.ts) had the same bare-`errorResponse()` gap as the round-robin
|
||||
* strategy's "Maximum combo retry limit reached" 503 — no diagnostics trace.
|
||||
*/
|
||||
import test from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
|
||||
import { executeRuntimeUnitCombo } from "../../open-sse/services/combo/runtimeUnits.ts";
|
||||
import type { ResolvedComboUnit, ComboNestingContext } from "../../open-sse/services/combo/types.ts";
|
||||
|
||||
function noopLog() {
|
||||
return { info: () => {}, warn: () => {}, error: () => {}, debug: () => {} };
|
||||
}
|
||||
|
||||
function failResponse(): Response {
|
||||
return new Response(JSON.stringify({ error: { message: "upstream 500" } }), {
|
||||
status: 500,
|
||||
headers: { "content-type": "application/json" },
|
||||
});
|
||||
}
|
||||
|
||||
test(
|
||||
"#11462: nested runtime-unit combo's attempt-budget-exceeded 503 must carry " +
|
||||
"the combo diagnostics trace (poolSize/attemptOrder/terminalReason)",
|
||||
async () => {
|
||||
const units: ResolvedComboUnit[] = [
|
||||
{
|
||||
kind: "model",
|
||||
stepId: "step-a",
|
||||
executionKey: "a",
|
||||
modelStr: "openai/ru-a",
|
||||
provider: "openai",
|
||||
providerId: null,
|
||||
connectionId: null,
|
||||
weight: 1,
|
||||
label: null,
|
||||
},
|
||||
{
|
||||
kind: "model",
|
||||
stepId: "step-b",
|
||||
executionKey: "b",
|
||||
modelStr: "anthropic/ru-b",
|
||||
provider: "anthropic",
|
||||
providerId: null,
|
||||
connectionId: null,
|
||||
weight: 1,
|
||||
label: null,
|
||||
},
|
||||
];
|
||||
|
||||
const nesting: ComboNestingContext = {
|
||||
depth: 0,
|
||||
maxDepth: 5,
|
||||
visitedComboNames: [],
|
||||
rootComboName: "ru-probe-11462",
|
||||
// Budget of 1 trips on the very first attempt, deterministically hitting the
|
||||
// terminal branch under test without needing every unit to actually fail.
|
||||
attemptBudget: { count: 0, limit: 1 },
|
||||
};
|
||||
|
||||
const result = await executeRuntimeUnitCombo({
|
||||
body: { messages: [{ role: "user", content: "hi" }] },
|
||||
combo: { name: "ru-probe-11462", strategy: "pipeline" },
|
||||
strategy: "pipeline",
|
||||
units,
|
||||
handleSingleModel: async () => failResponse(),
|
||||
log: noopLog() as never,
|
||||
config: { maxRetries: 0 },
|
||||
allCombos: [],
|
||||
nesting,
|
||||
baseOptions: {} as never,
|
||||
runCombo: async () => failResponse(),
|
||||
});
|
||||
|
||||
assert.equal(result.response.status, 503);
|
||||
const body = (await result.response.json()) as {
|
||||
error: { message: string };
|
||||
diagnostics?: { poolSize: number; attemptOrder: unknown[]; terminalReason: string };
|
||||
};
|
||||
assert.equal(body.error.message, "Maximum combo retry limit reached");
|
||||
assert.ok(body.diagnostics, "runtime-unit 503 should carry a diagnostics field");
|
||||
assert.ok(typeof body.diagnostics?.poolSize === "number");
|
||||
assert.ok(Array.isArray(body.diagnostics?.attemptOrder));
|
||||
assert.equal(body.diagnostics?.terminalReason, "max_attempts_exceeded");
|
||||
}
|
||||
);
|
||||
@@ -4,28 +4,37 @@ import { readFileSync } from "node:fs";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
// The Docker publish workflow builds on GitHub-hosted runners (ubuntu-24.04 and
|
||||
// ubuntu-24.04-arm): 4 vCPU, 16 GB RAM. Every Next page-data worker is its own
|
||||
// process and inherits NODE_OPTIONS, so the V8 ceiling is per PROCESS: the
|
||||
// build's worst case is roughly `workers × OMNIROUTE_BUILD_MEMORY_MB`.
|
||||
// ubuntu-24.04-arm): 4 vCPU, 16 GB RAM. Every Next page-data worker AND the
|
||||
// parent `next build` process are separate OS processes, so the budget has to
|
||||
// cover all of them, not just the workers.
|
||||
//
|
||||
// With 7 workers × 6144 MB the runner ran out and buildkit failed the step with
|
||||
// `ResourceExhausted: ... cannot allocate memory`, right after "Collecting page
|
||||
// data using 7 workers" — every Docker publish since 2026-08-22 23:14 UTC.
|
||||
// Lowering to 2 workers (#10060 / PR #11419) was not enough: it modeled the
|
||||
// per-process peak as an INFERENCE (`WORKER_PEAK_MB = 2560`, derived only from
|
||||
// "7 workers didn't fit") and assumed the parent process tracked the V8 heap
|
||||
// ceiling (`OMNIROUTE_BUILD_MEMORY_MB`) rather than its own RSS. The owner's
|
||||
// live VPS reproduction (issue #7518, dmesg OOM-killer report, 2026-08-24)
|
||||
// measured the real number directly: `next-build (v16) ... anon-rss:4522744kB`
|
||||
// (~4.5 GB) per process, independent of the NODE_OPTIONS heap flag — Turbopack
|
||||
// itself is native/Rust and compiles outside the V8 heap. With 2 workers that
|
||||
// keeps the publish pipeline failing at "Collecting page data using 2 workers"
|
||||
// (run 32907937950, 2026-08-25).
|
||||
//
|
||||
// This pins the budget so raising either knob has to be a deliberate change
|
||||
// that re-does the arithmetic, not a one-line bump that silently reds the
|
||||
// publish pipeline again.
|
||||
// This pins the budget on the MEASURED figure, applied uniformly to every
|
||||
// process (parent + workers), so raising the worker count has to be a
|
||||
// deliberate change that re-does the arithmetic, not a one-line bump that
|
||||
// silently reds the publish pipeline again.
|
||||
|
||||
const RUNNER_MEMORY_MB = 16 * 1024;
|
||||
// Leave room for buildkit, the snapshotter and page cache.
|
||||
const HEADROOM_FRACTION = 0.75;
|
||||
// Planning figure for one page-data worker's peak RSS. It is an INFERENCE, not
|
||||
// a measurement: 7 workers did not fit in 16 GB alongside the parent, which
|
||||
// puts the per-worker peak somewhere north of ~1.8 GB. 2.5 GB is that bound
|
||||
// rounded up, so the budget below stays conservative. If a future build OOMs
|
||||
// again with a worker count this test accepts, raise this number — do not
|
||||
// weaken the budget.
|
||||
const WORKER_PEAK_MB = 2560;
|
||||
// Measured (not inferred) peak RSS for a single Next/Turbopack build process —
|
||||
// parent or page-data worker alike — from the dmesg OOM-killer report above.
|
||||
// If a future build OOMs again, re-measure via dmesg before raising this
|
||||
// number — do not weaken the budget with another guess.
|
||||
const MEASURED_PROCESS_RSS_MB = 4500;
|
||||
|
||||
const dockerfile = readFileSync(
|
||||
fileURLToPath(new URL("../../Dockerfile", import.meta.url)),
|
||||
@@ -51,25 +60,27 @@ test("the Docker build's worker pool is derived from OMNIROUTE_BUILD_WORKERS", (
|
||||
);
|
||||
});
|
||||
|
||||
test("worker count × per-process heap fits a 16 GB GitHub runner", () => {
|
||||
test("worker count × measured per-process RSS fits a 16 GB GitHub runner", () => {
|
||||
const workerPool = readArgDefault("OMNIROUTE_BUILD_WORKERS");
|
||||
const heapMb = readArgDefault("OMNIROUTE_BUILD_MEMORY_MB");
|
||||
|
||||
// Next derives `workers = CIRCLE_NODE_TOTAL - 1`.
|
||||
const workers = workerPool - 1;
|
||||
assert.ok(workers >= 1, `CIRCLE_NODE_TOTAL=${workerPool} leaves no build workers`);
|
||||
|
||||
// The parent `next build` process is the one that genuinely needs the raised
|
||||
// ceiling (the webpack/turbopack production pass, #4076); the workers are
|
||||
// budgeted at their inferred peak instead.
|
||||
const worstCaseMb = heapMb + workers * WORKER_PEAK_MB;
|
||||
// Every process — the parent `next build` process AND each page-data
|
||||
// worker — is budgeted at the measured per-process RSS floor (see the file
|
||||
// banner comment). The V8 heap ceiling (OMNIROUTE_BUILD_MEMORY_MB) bounds
|
||||
// JS allocations but not Turbopack's native/Rust memory, so it cannot stand
|
||||
// in for the parent process's real RSS.
|
||||
const processes = workers + 1;
|
||||
const worstCaseMb = processes * MEASURED_PROCESS_RSS_MB;
|
||||
const budgetMb = RUNNER_MEMORY_MB * HEADROOM_FRACTION;
|
||||
assert.ok(
|
||||
worstCaseMb <= budgetMb,
|
||||
`parent ${heapMb} MB + ${workers} workers × ${WORKER_PEAK_MB} MB = ${worstCaseMb} MB ` +
|
||||
`exceeds the ${budgetMb} MB budget on a ${RUNNER_MEMORY_MB} MB runner — the Docker ` +
|
||||
`publish step dies with "ResourceExhausted: cannot allocate memory" during page-data ` +
|
||||
`collection`
|
||||
`${processes} processes (1 parent + ${workers} workers) × ${MEASURED_PROCESS_RSS_MB} MB ` +
|
||||
`measured RSS = ${worstCaseMb} MB exceeds the ${budgetMb} MB budget on a ` +
|
||||
`${RUNNER_MEMORY_MB} MB runner — the Docker publish step dies with "ResourceExhausted: ` +
|
||||
`cannot allocate memory" during page-data collection`
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
83
tests/unit/issue-11433-opencode-alias-collision.test.ts
Normal file
@@ -0,0 +1,83 @@
|
||||
import { test } from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
|
||||
import {
|
||||
buildPrecisionComboModelStep,
|
||||
buildGlobalModelList,
|
||||
buildManualComboModelStep,
|
||||
} from "../../src/lib/combos/builderDraft.ts";
|
||||
import { resolveProviderAlias, parseModel } from "../../open-sse/services/model.ts";
|
||||
|
||||
// Issue #11433: the combo builder's precision-select path builds a step's
|
||||
// `model` string as `${providerId}/${modelId}` using the CANONICAL provider id.
|
||||
// For the no-auth "opencode" (OpenCode Free) provider this produces
|
||||
// `model: "opencode/<modelId>"`, but `opencode` is ALSO a manual routing-prefix
|
||||
// override (`ALIAS_TO_PROVIDER_ID["opencode"] = "opencode-zen"`) intended only
|
||||
// for user-typed `opencode/` prefixes referring to the OpenCode Zen (api-key)
|
||||
// tier. Parsing the step's own `model` string therefore resolves to a
|
||||
// DIFFERENT provider than the one recorded in `step.providerId`.
|
||||
|
||||
test('sanity: resolveProviderAlias("opencode") is the manual override causing the collision', () => {
|
||||
// Documents the root cause directly: the manual alias override in
|
||||
// open-sse/services/model.ts unconditionally rewrites "opencode" to
|
||||
// "opencode-zen", even though "opencode" is also a registered canonical
|
||||
// provider id (src/shared/constants/providers/noauth.ts).
|
||||
assert.equal(resolveProviderAlias("opencode"), "opencode-zen");
|
||||
});
|
||||
|
||||
test("issue #11433 fix: buildPrecisionComboModelStep honors an explicit modelPrefix override", () => {
|
||||
// The combo builder call sites now thread through the already-computed
|
||||
// routing-alias prefix (e.g. "oc") instead of letting the step default to
|
||||
// the raw providerId, so the serialized `model` field round-trips to the
|
||||
// correct provider.
|
||||
const step = buildPrecisionComboModelStep({
|
||||
providerId: "opencode",
|
||||
modelId: "big-pickle",
|
||||
modelPrefix: "oc",
|
||||
});
|
||||
|
||||
assert.equal(step.providerId, "opencode");
|
||||
assert.equal(step.model, "oc/big-pickle");
|
||||
|
||||
const parsed = parseModel(step.model);
|
||||
assert.equal(parsed.provider, step.providerId);
|
||||
});
|
||||
|
||||
test("issue #11433 fix: buildGlobalModelList derives modelPrefix from qualifiedModel for the no-auth OpenCode Free provider", () => {
|
||||
// Mirrors what src/lib/combos/builderOptions.ts::rewriteQualifiedModelPrefix
|
||||
// produces for the no-auth "opencode" provider entry: `qualifiedModel` is
|
||||
// already rewritten to the "oc/" alias prefix, but (pre-fix)
|
||||
// buildGlobalModelList ignored it and rebuilt `model` from the raw
|
||||
// providerId, producing "opencode/big-pickle" which parses back to the
|
||||
// wrong provider ("opencode-zen").
|
||||
const [entry] = buildGlobalModelList([
|
||||
{
|
||||
providerId: "opencode",
|
||||
displayName: "OpenCode Free",
|
||||
connectionCount: 0,
|
||||
connections: [],
|
||||
models: [{ id: "big-pickle", name: "Big Pickle", qualifiedModel: "oc/big-pickle" }],
|
||||
},
|
||||
]);
|
||||
|
||||
assert.equal(entry.step.providerId, "opencode");
|
||||
assert.equal(entry.step.model, "oc/big-pickle");
|
||||
assert.equal(parseModel(entry.step.model).provider, entry.step.providerId);
|
||||
});
|
||||
|
||||
test("issue #11433 fix: buildManualComboModelStep preserves a user-typed oc/<model> prefix", () => {
|
||||
// buildManualComboModelStep resolves the typed alias ("oc") back to the
|
||||
// canonical providerId ("opencode") before building the step. Pre-fix, it
|
||||
// then handed that canonical id straight to buildPrecisionComboModelStep,
|
||||
// which rebuilt `model` from it and collapsed "oc/<model>" back down to
|
||||
// "opencode/<model>" — reproducing the same collision for manual entry.
|
||||
const step = buildManualComboModelStep({
|
||||
value: "oc/big-pickle",
|
||||
providers: [{ providerId: "opencode", alias: "oc" }],
|
||||
});
|
||||
|
||||
assert.ok(step);
|
||||
assert.equal(step?.providerId, "opencode");
|
||||
assert.equal(step?.model, "oc/big-pickle");
|
||||
assert.equal(parseModel(step!.model).provider, step!.providerId);
|
||||
});
|
||||
@@ -0,0 +1,78 @@
|
||||
import { test } from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
import { openaiToGeminiRequest } from "../../open-sse/translator/request/openai-to-gemini.ts";
|
||||
import {
|
||||
storeGeminiThoughtSignature,
|
||||
buildGeminiThoughtSignatureKey,
|
||||
} from "../../open-sse/services/geminiThoughtSignatureStore.ts";
|
||||
|
||||
// Mirror of tests/unit/issue-11510-parallel-tool-thought-signature.test.ts for the
|
||||
// OpenAI-protocol → Gemini path (openai-to-gemini.ts). The same
|
||||
// shouldUseEmbeddedSignature/firstPersistedSignature gating dropped a resolved,
|
||||
// individually-valid thoughtSignature for every tool_calls[] entry after the
|
||||
// first one in a parallel (multi tool_calls) assistant turn, reproducing
|
||||
// Gemini 3.x's HTTP 400 "Function call is missing a thought_signature in
|
||||
// functionCall parts" (#11510).
|
||||
test("openai→gemini must attach EACH resolved thoughtSignature on a parallel (multi tool_calls) turn (#11510)", () => {
|
||||
const ns = "conn-11510-openai-parallel";
|
||||
const toolId1 = "call_11510_first";
|
||||
const toolId2 = "call_11510_second_webfetch";
|
||||
const sig1 = "SIG_11510_OPENAI_FIRST";
|
||||
const sig2 = "SIG_11510_OPENAI_SECOND";
|
||||
|
||||
storeGeminiThoughtSignature(buildGeminiThoughtSignatureKey(ns, toolId1), sig1);
|
||||
storeGeminiThoughtSignature(buildGeminiThoughtSignatureKey(ns, toolId2), sig2);
|
||||
|
||||
const result = openaiToGeminiRequest(
|
||||
"gemini-3.5-flash",
|
||||
{
|
||||
messages: [
|
||||
{
|
||||
role: "assistant",
|
||||
content: null,
|
||||
tool_calls: [
|
||||
{
|
||||
id: toolId1,
|
||||
type: "function",
|
||||
function: { name: "default_api:Read", arguments: JSON.stringify({ path: "/a" }) },
|
||||
},
|
||||
{
|
||||
id: toolId2,
|
||||
type: "function",
|
||||
function: {
|
||||
name: "default_api:WebFetch",
|
||||
arguments: JSON.stringify({ url: "https://example.com" }),
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{ role: "tool", tool_call_id: toolId1, content: "file a" },
|
||||
{ role: "tool", tool_call_id: toolId2, content: "fetched" },
|
||||
],
|
||||
},
|
||||
false,
|
||||
{ _signatureNamespace: ns }
|
||||
) as { contents: Array<{ role: string; parts: Array<Record<string, unknown>> }> };
|
||||
|
||||
const modelTurn = result.contents.find(
|
||||
(c) => c.role === "model" && c.parts?.some((p) => p.functionCall)
|
||||
);
|
||||
assert.ok(modelTurn, "expected a model turn with functionCall parts");
|
||||
|
||||
const fcParts = modelTurn!.parts.filter((p) => p.functionCall) as Array<{
|
||||
thoughtSignature?: string;
|
||||
functionCall: { name: string };
|
||||
}>;
|
||||
assert.equal(fcParts.length, 2, "both tool_calls entries must be emitted as native functionCall");
|
||||
|
||||
const webFetchPart = fcParts.find((p) => p.functionCall.name.includes("WebFetch"));
|
||||
assert.ok(webFetchPart, "WebFetch functionCall part must be present");
|
||||
|
||||
assert.equal(
|
||||
webFetchPart!.thoughtSignature,
|
||||
sig2,
|
||||
"second functionCall in a parallel tool_calls turn must keep its own resolved " +
|
||||
"thoughtSignature, or Gemini 3.x rejects the request with HTTP 400 " +
|
||||
"'Function call is missing a thought_signature in functionCall parts'"
|
||||
);
|
||||
});
|
||||
@@ -0,0 +1,85 @@
|
||||
import { test } from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
import { claudeToGeminiRequest } from "../../open-sse/translator/request/claude-to-gemini.ts";
|
||||
import {
|
||||
storeGeminiThoughtSignature,
|
||||
buildGeminiThoughtSignatureKey,
|
||||
} from "../../open-sse/services/geminiThoughtSignatureStore.ts";
|
||||
|
||||
// Repro for GitHub issue #11510: Claude Code (or any client) issuing a PARALLEL
|
||||
// tool-call turn (2+ tool_use blocks in one assistant message) against a Gemini
|
||||
// 3.x thinking model. Gemini attaches (and requires) an individual
|
||||
// thoughtSignature on EVERY functionCall part in a multi-call turn, and OmniRoute
|
||||
// stores one per tool_use id (gemini-to-claude.ts reads `part.thoughtSignature`
|
||||
// per-part, not just a single pending value). But claude-to-gemini.ts's
|
||||
// `shouldUseEmbeddedSignature` flag strips the signature from every functionCall
|
||||
// after the first one in the SAME assistant message, even when a real resolved
|
||||
// signature exists for it — reproducing Gemini's exact HTTP 400:
|
||||
// "Function call is missing a thought_signature in functionCall parts."
|
||||
test("claude→gemini must attach EACH resolved thoughtSignature on a parallel (multi tool_use) turn (#11510)", () => {
|
||||
const ns = "conn-11510-parallel";
|
||||
const toolId1 = "toolu_11510_first";
|
||||
const toolId2 = "toolu_11510_second_webfetch";
|
||||
const sig1 = "SIG_11510_FIRST";
|
||||
const sig2 = "SIG_11510_SECOND";
|
||||
|
||||
// Simulate what gemini-to-claude.ts really stores today: a distinct,
|
||||
// individually-valid signature per tool_use id, because Gemini attached one
|
||||
// to each functionCall part of the original response turn.
|
||||
storeGeminiThoughtSignature(buildGeminiThoughtSignatureKey(ns, toolId1), sig1);
|
||||
storeGeminiThoughtSignature(buildGeminiThoughtSignatureKey(ns, toolId2), sig2);
|
||||
|
||||
const result = claudeToGeminiRequest(
|
||||
"gemini-3.5-flash",
|
||||
{
|
||||
messages: [
|
||||
{
|
||||
role: "assistant",
|
||||
content: [
|
||||
{ type: "tool_use", id: toolId1, name: "default_api:Read", input: { path: "/a" } },
|
||||
{
|
||||
type: "tool_use",
|
||||
id: toolId2,
|
||||
name: "default_api:WebFetch",
|
||||
input: { url: "https://example.com" },
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
role: "user",
|
||||
content: [
|
||||
{ type: "tool_result", tool_use_id: toolId1, content: "file a" },
|
||||
{ type: "tool_result", tool_use_id: toolId2, content: "fetched" },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
false,
|
||||
{ _signatureNamespace: ns }
|
||||
) as { contents: Array<{ role: string; parts: Array<Record<string, unknown>> }> };
|
||||
|
||||
const modelTurn = result.contents.find(
|
||||
(c) => c.role === "model" && c.parts?.some((p) => p.functionCall)
|
||||
);
|
||||
assert.ok(modelTurn, "expected a model turn with functionCall parts");
|
||||
|
||||
const fcParts = modelTurn!.parts.filter((p) => p.functionCall) as Array<{
|
||||
thoughtSignature?: string;
|
||||
functionCall: { name: string };
|
||||
}>;
|
||||
assert.equal(fcParts.length, 2, "both tool_use blocks must be emitted as native functionCall");
|
||||
|
||||
const webFetchPart = fcParts.find((p) => p.functionCall.name.includes("WebFetch"));
|
||||
assert.ok(webFetchPart, "WebFetch functionCall part must be present");
|
||||
|
||||
// THIS is the reported bug: the second tool_use in the turn has a real,
|
||||
// resolved thoughtSignature (sig2) available, but the translator drops it
|
||||
// because it is not the first functionCall in the message.
|
||||
assert.equal(
|
||||
webFetchPart!.thoughtSignature,
|
||||
sig2,
|
||||
"second functionCall in a parallel tool-call turn must keep its own resolved " +
|
||||
"thoughtSignature, or Gemini 3.x rejects the request with HTTP 400 " +
|
||||
"'Function call is missing a thought_signature in functionCall parts'"
|
||||
);
|
||||
});
|
||||
63
tests/unit/issue-11526-repro.test.ts
Normal file
@@ -0,0 +1,63 @@
|
||||
import test from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
import { resolveStreamReadinessTimeout } from "../../open-sse/utils/streamReadinessPolicy.ts";
|
||||
import {
|
||||
resolveFetchStartTimeout,
|
||||
CODEX_CLIENT_ABORT_MS,
|
||||
} from "../../open-sse/utils/fetchStartTimeoutPolicy.ts";
|
||||
import { getUpstreamTimeoutConfig } from "../../src/shared/utils/runtimeTimeouts.ts";
|
||||
|
||||
function items(count: number): Array<{ role: string; content: string }> {
|
||||
return Array.from({ length: count }, (_, index) => ({
|
||||
role: "user",
|
||||
content: `message ${index}`,
|
||||
}));
|
||||
}
|
||||
|
||||
function tools(count: number): Array<{ type: string; name: string }> {
|
||||
return Array.from({ length: count }, (_, index) => ({ type: "function", name: `tool_${index}` }));
|
||||
}
|
||||
|
||||
test("issue #11526: body-phase readiness watchdog stays comfortably under Codex's ~120s patience for the reported tool-heavy payload shape", () => {
|
||||
const result = resolveStreamReadinessTimeout({
|
||||
baseTimeoutMs: 80_000,
|
||||
provider: "nvidia",
|
||||
model: "some-nvidia-model",
|
||||
body: { input: items(68), tools: tools(16) },
|
||||
});
|
||||
|
||||
assert.ok(
|
||||
result.timeoutMs < CODEX_CLIENT_ABORT_MS,
|
||||
`body-phase watchdog (${result.timeoutMs}ms) must stay under Codex's ~120s patience`
|
||||
);
|
||||
});
|
||||
|
||||
test("issue #11526 (fixed): headers-phase watchdog for STREAMING requests is bounded under Codex's ~120s patience", () => {
|
||||
const { fetchTimeoutMs } = getUpstreamTimeoutConfig({});
|
||||
// Default FETCH_TIMEOUT_MS (600000ms) is still the flat non-streaming baseline —
|
||||
// the fix does not touch that default, it caps how much of it a STREAMING
|
||||
// request's headers-wait phase is allowed to consume.
|
||||
assert.equal(fetchTimeoutMs, 600_000);
|
||||
|
||||
const streaming = resolveFetchStartTimeout({ baseTimeoutMs: fetchTimeoutMs, stream: true });
|
||||
assert.ok(
|
||||
streaming.timeoutMs <= CODEX_CLIENT_ABORT_MS,
|
||||
`headers-phase watchdog for streaming requests (${streaming.timeoutMs}ms) must not exceed a realistic client abort window (${CODEX_CLIENT_ABORT_MS}ms)`
|
||||
);
|
||||
assert.ok(streaming.capped, "expected the oversized default to be capped for streaming requests");
|
||||
});
|
||||
|
||||
test("issue #11526 scope guard: non-streaming requests keep the flat FETCH_TIMEOUT_MS default", () => {
|
||||
const { fetchTimeoutMs } = getUpstreamTimeoutConfig({});
|
||||
const nonStreaming = resolveFetchStartTimeout({ baseTimeoutMs: fetchTimeoutMs, stream: false });
|
||||
|
||||
assert.equal(nonStreaming.timeoutMs, fetchTimeoutMs);
|
||||
assert.equal(nonStreaming.capped, false);
|
||||
});
|
||||
|
||||
test("issue #11526 scope guard: a base timeout already under the cap is left untouched for streaming requests", () => {
|
||||
const result = resolveFetchStartTimeout({ baseTimeoutMs: 30_000, stream: true });
|
||||
|
||||
assert.equal(result.timeoutMs, 30_000);
|
||||
assert.equal(result.capped, false);
|
||||
});
|
||||
88
tests/unit/kie-market-upstream-id-11296-probe.test.ts
Normal file
@@ -0,0 +1,88 @@
|
||||
import test from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
import { mkdtempSync } from "node:fs";
|
||||
import { tmpdir } from "node:os";
|
||||
import { join } from "node:path";
|
||||
|
||||
process.env.DATA_DIR = mkdtempSync(join(tmpdir(), "omniroute-kie-11296-probe-"));
|
||||
|
||||
const { handleImageGeneration } = await import(
|
||||
"../../open-sse/handlers/imageGeneration.ts"
|
||||
);
|
||||
|
||||
interface CapturedCreate {
|
||||
url: string;
|
||||
body: Record<string, unknown>;
|
||||
}
|
||||
|
||||
async function captureCreateTaskModel(publicModel: string): Promise<string> {
|
||||
const originalFetch = globalThis.fetch;
|
||||
let captured: CapturedCreate | undefined;
|
||||
|
||||
globalThis.fetch = (async (url: unknown, options: { body?: unknown } = {}) => {
|
||||
const stringUrl = String(url);
|
||||
|
||||
if (stringUrl === "https://api.kie.ai/api/v1/jobs/createTask") {
|
||||
captured = {
|
||||
url: stringUrl,
|
||||
body: JSON.parse(String(options.body ?? "{}")) as Record<string, unknown>,
|
||||
};
|
||||
return new Response(JSON.stringify({ code: 200, data: { taskId: "kie-probe-task-1" } }), {
|
||||
status: 200,
|
||||
headers: { "content-type": "application/json" },
|
||||
});
|
||||
}
|
||||
|
||||
if (stringUrl.startsWith("https://api.kie.ai/api/v1/jobs/recordInfo")) {
|
||||
return new Response(
|
||||
JSON.stringify({
|
||||
code: 200,
|
||||
data: {
|
||||
state: "success",
|
||||
resultJson: JSON.stringify({ resultUrls: ["https://example.com/kie-probe.png"] }),
|
||||
},
|
||||
}),
|
||||
{ status: 200, headers: { "content-type": "application/json" } }
|
||||
);
|
||||
}
|
||||
|
||||
throw new Error(`Unexpected URL: ${stringUrl}`);
|
||||
}) as typeof globalThis.fetch;
|
||||
|
||||
try {
|
||||
await handleImageGeneration({
|
||||
body: { model: publicModel, prompt: "probe prompt", size: "1024x1024", n: 1 },
|
||||
credentials: { apiKey: "test-kie-key" },
|
||||
log: null,
|
||||
});
|
||||
assert.ok(captured, "expected a createTask request to be captured");
|
||||
return String(captured.body.model);
|
||||
} finally {
|
||||
globalThis.fetch = originalFetch;
|
||||
}
|
||||
}
|
||||
|
||||
test("#11296: GPT Image 2 T2I sends KIE's real upstream id (no namespace prefix)", async () => {
|
||||
const sentModel = await captureCreateTaskModel("kie/gpt/gpt-image-2-text-to-image");
|
||||
assert.equal(sentModel, "gpt-image-2-text-to-image");
|
||||
});
|
||||
|
||||
test("#11296: GPT Image 2 I2I sends KIE's real upstream id (no namespace prefix)", async () => {
|
||||
const sentModel = await captureCreateTaskModel("kie/gpt/gpt-image-2-image-to-image");
|
||||
assert.equal(sentModel, "gpt-image-2-image-to-image");
|
||||
});
|
||||
|
||||
test("#11296: GPT Image 1.5 T2I sends KIE's real 'gpt-image/' namespace", async () => {
|
||||
const sentModel = await captureCreateTaskModel("kie/gpt/gpt-image-1.5-text-to-image");
|
||||
assert.equal(sentModel, "gpt-image/1.5-text-to-image");
|
||||
});
|
||||
|
||||
test("#11296: Seedream 5.0 Lite T2I sends KIE's real id without the '.0'", async () => {
|
||||
const sentModel = await captureCreateTaskModel("kie/seedream/5.0-lite-text-to-image");
|
||||
assert.equal(sentModel, "seedream/5-lite-text-to-image");
|
||||
});
|
||||
|
||||
test("#11296: Flux 2 Pro T2I sends KIE's real 'flux-2/' namespace (dash, not slash)", async () => {
|
||||
const sentModel = await captureCreateTaskModel("kie/flux/2-pro-text-to-image");
|
||||
assert.equal(sentModel, "flux-2/pro-text-to-image");
|
||||
});
|
||||
@@ -103,13 +103,21 @@ function resolveLiveKieMarketCatalog() {
|
||||
}));
|
||||
}
|
||||
|
||||
test("KIE Market resolver changes exactly the 4 google-imagen ids in the live market catalog", () => {
|
||||
test("KIE Market resolver changes exactly the documented mismatched ids in the live market catalog", () => {
|
||||
const roundTrips = resolveLiveKieMarketCatalog();
|
||||
const changed = roundTrips.filter(({ publicModelId, upstreamModelId }) => {
|
||||
return upstreamModelId !== publicModelId;
|
||||
});
|
||||
|
||||
assert.deepEqual(changed, [
|
||||
{
|
||||
publicModelId: "seedream/5.0-lite-text-to-image",
|
||||
upstreamModelId: "seedream/5-lite-text-to-image",
|
||||
},
|
||||
{
|
||||
publicModelId: "seedream/5.0-lite-image-to-image",
|
||||
upstreamModelId: "seedream/5-lite-image-to-image",
|
||||
},
|
||||
{
|
||||
publicModelId: "google-imagen/nano-banana-2",
|
||||
upstreamModelId: "nano-banana-2",
|
||||
@@ -126,19 +134,71 @@ test("KIE Market resolver changes exactly the 4 google-imagen ids in the live ma
|
||||
publicModelId: "google-imagen/nano-banana-edit",
|
||||
upstreamModelId: "google/nano-banana-edit",
|
||||
},
|
||||
{
|
||||
publicModelId: "flux/2-pro-image-to-image",
|
||||
upstreamModelId: "flux-2/pro-image-to-image",
|
||||
},
|
||||
{
|
||||
publicModelId: "flux/2-pro-text-to-image",
|
||||
upstreamModelId: "flux-2/pro-text-to-image",
|
||||
},
|
||||
{
|
||||
publicModelId: "flux/2-image-to-image",
|
||||
upstreamModelId: "flux-2/flex-image-to-image",
|
||||
},
|
||||
{
|
||||
publicModelId: "flux/2-text-to-image",
|
||||
upstreamModelId: "flux-2/flex-text-to-image",
|
||||
},
|
||||
{
|
||||
publicModelId: "gpt/gpt-image-1.5-text-to-image",
|
||||
upstreamModelId: "gpt-image/1.5-text-to-image",
|
||||
},
|
||||
{
|
||||
publicModelId: "gpt/gpt-image-1.5-image-to-image",
|
||||
upstreamModelId: "gpt-image/1.5-image-to-image",
|
||||
},
|
||||
{
|
||||
publicModelId: "gpt/gpt-image-2-text-to-image",
|
||||
upstreamModelId: "gpt-image-2-text-to-image",
|
||||
},
|
||||
{
|
||||
publicModelId: "gpt/gpt-image-2-image-to-image",
|
||||
upstreamModelId: "gpt-image-2-image-to-image",
|
||||
},
|
||||
{
|
||||
publicModelId: "wan/2.7-image",
|
||||
upstreamModelId: "wan/2-7-image",
|
||||
},
|
||||
{
|
||||
publicModelId: "wan/2.7-image-pro",
|
||||
upstreamModelId: "wan/2-7-image-pro",
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
const REWRITTEN_GOOGLE_IMAGEN_MARKET_IDS = new Set([
|
||||
const REWRITTEN_MARKET_IDS = new Set([
|
||||
"google-imagen/nano-banana",
|
||||
"google-imagen/nano-banana-2",
|
||||
"google-imagen/nano-banana-pro",
|
||||
"google-imagen/nano-banana-edit",
|
||||
"gpt/gpt-image-2-text-to-image",
|
||||
"gpt/gpt-image-2-image-to-image",
|
||||
"gpt/gpt-image-1.5-text-to-image",
|
||||
"gpt/gpt-image-1.5-image-to-image",
|
||||
"seedream/5.0-lite-text-to-image",
|
||||
"seedream/5.0-lite-image-to-image",
|
||||
"flux/2-pro-text-to-image",
|
||||
"flux/2-pro-image-to-image",
|
||||
"flux/2-text-to-image",
|
||||
"flux/2-image-to-image",
|
||||
"wan/2.7-image",
|
||||
"wan/2.7-image-pro",
|
||||
]);
|
||||
|
||||
test("KIE Market resolver preserves every other live market catalog id byte-identically", () => {
|
||||
for (const { publicModelId, upstreamModelId } of resolveLiveKieMarketCatalog()) {
|
||||
if (!REWRITTEN_GOOGLE_IMAGEN_MARKET_IDS.has(publicModelId)) {
|
||||
if (!REWRITTEN_MARKET_IDS.has(publicModelId)) {
|
||||
assert.equal(
|
||||
upstreamModelId,
|
||||
publicModelId,
|
||||
@@ -148,8 +208,8 @@ test("KIE Market resolver preserves every other live market catalog id byte-iden
|
||||
}
|
||||
});
|
||||
|
||||
test("KIE Market resolver keeps exactly the explicit google-imagen upstream id mappings (#11296)", () => {
|
||||
assert.equal(KIE_MARKET_UPSTREAM_MODEL_IDS.size, 4);
|
||||
test("KIE Market resolver keeps exactly the explicit upstream id mappings (#11296)", () => {
|
||||
assert.equal(KIE_MARKET_UPSTREAM_MODEL_IDS.size, 16);
|
||||
});
|
||||
|
||||
test("KIE Market resolver passes an unknown namespaced id through byte-identically", () => {
|
||||
@@ -209,6 +269,78 @@ test("KIE Market createTask sends the KIE upstream id for Nano Banana Edit (#112
|
||||
);
|
||||
});
|
||||
|
||||
test("KIE Market createTask sends the unprefixed upstream id for GPT Image 2 T2I (#11296)", async () => {
|
||||
const captured = await runKieMarketGeneration("kie/gpt/gpt-image-2-text-to-image");
|
||||
|
||||
assert.equal(captured.create.body.model, "gpt-image-2-text-to-image");
|
||||
});
|
||||
|
||||
test("KIE Market createTask sends the unprefixed upstream id for GPT Image 2 I2I (#11296)", async () => {
|
||||
const captured = await runKieMarketGeneration("kie/gpt/gpt-image-2-image-to-image");
|
||||
|
||||
assert.equal(captured.create.body.model, "gpt-image-2-image-to-image");
|
||||
});
|
||||
|
||||
test("KIE Market createTask sends the 'gpt-image/' namespace for GPT Image 1.5 T2I (#11296)", async () => {
|
||||
const captured = await runKieMarketGeneration("kie/gpt/gpt-image-1.5-text-to-image");
|
||||
|
||||
assert.equal(captured.create.body.model, "gpt-image/1.5-text-to-image");
|
||||
});
|
||||
|
||||
test("KIE Market createTask sends the 'gpt-image/' namespace for GPT Image 1.5 I2I (#11296)", async () => {
|
||||
const captured = await runKieMarketGeneration("kie/gpt/gpt-image-1.5-image-to-image");
|
||||
|
||||
assert.equal(captured.create.body.model, "gpt-image/1.5-image-to-image");
|
||||
});
|
||||
|
||||
test("KIE Market createTask drops the '.0' for Seedream 5.0 Lite T2I (#11296)", async () => {
|
||||
const captured = await runKieMarketGeneration("kie/seedream/5.0-lite-text-to-image");
|
||||
|
||||
assert.equal(captured.create.body.model, "seedream/5-lite-text-to-image");
|
||||
});
|
||||
|
||||
test("KIE Market createTask drops the '.0' for Seedream 5.0 Lite I2I (#11296)", async () => {
|
||||
const captured = await runKieMarketGeneration("kie/seedream/5.0-lite-image-to-image");
|
||||
|
||||
assert.equal(captured.create.body.model, "seedream/5-lite-image-to-image");
|
||||
});
|
||||
|
||||
test("KIE Market createTask sends the 'flux-2/' namespace for Flux 2 Pro T2I (#11296)", async () => {
|
||||
const captured = await runKieMarketGeneration("kie/flux/2-pro-text-to-image");
|
||||
|
||||
assert.equal(captured.create.body.model, "flux-2/pro-text-to-image");
|
||||
});
|
||||
|
||||
test("KIE Market createTask sends the 'flux-2/' namespace for Flux 2 Pro I2I (#11296)", async () => {
|
||||
const captured = await runKieMarketGeneration("kie/flux/2-pro-image-to-image");
|
||||
|
||||
assert.equal(captured.create.body.model, "flux-2/pro-image-to-image");
|
||||
});
|
||||
|
||||
test("KIE Market createTask sends the 'flux-2/flex-' name for Flux 2 T2I (#11296)", async () => {
|
||||
const captured = await runKieMarketGeneration("kie/flux/2-text-to-image");
|
||||
|
||||
assert.equal(captured.create.body.model, "flux-2/flex-text-to-image");
|
||||
});
|
||||
|
||||
test("KIE Market createTask sends the 'flux-2/flex-' name for Flux 2 I2I (#11296)", async () => {
|
||||
const captured = await runKieMarketGeneration("kie/flux/2-image-to-image");
|
||||
|
||||
assert.equal(captured.create.body.model, "flux-2/flex-image-to-image");
|
||||
});
|
||||
|
||||
test("KIE Market createTask sends the dash-separated id for Wan 2.7 Image (#11296)", async () => {
|
||||
const captured = await runKieMarketGeneration("kie/wan/2.7-image");
|
||||
|
||||
assert.equal(captured.create.body.model, "wan/2-7-image");
|
||||
});
|
||||
|
||||
test("KIE Market createTask sends the dash-separated id for Wan 2.7 Image Pro (#11296)", async () => {
|
||||
const captured = await runKieMarketGeneration("kie/wan/2.7-image-pro");
|
||||
|
||||
assert.equal(captured.create.body.model, "wan/2-7-image-pro");
|
||||
});
|
||||
|
||||
test("KIE Market createTask leaves genuinely namespaced upstream ids untouched (#11225 control)", async () => {
|
||||
const captured = await runKieMarketGeneration("kie/seedream/4.5-text-to-image");
|
||||
|
||||
|
||||
140
tests/unit/lazy-connection-view-decrypt-dedupe.test.ts
Normal file
@@ -0,0 +1,140 @@
|
||||
import test from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
import { createCipheriv, randomBytes, scryptSync } from "node:crypto";
|
||||
import path from "node:path";
|
||||
import { pathToFileURL } from "node:url";
|
||||
|
||||
// #11500 — the #9927 fix deduped raw decrypt-failure logging only inside
|
||||
// decryptConnectionFields(). The lazy-decrypt rollout (createLazyRowProxy /
|
||||
// createLazyConnectionView in src/lib/db/providers/lazyConnectionView.ts,
|
||||
// used by getProviderConnections() on every CredentialHealth/model-sync
|
||||
// cycle) called decrypt() directly with no quiet option and no dedup
|
||||
// tracking, so the raw "[Encryption] Decryption failed..." line re-fired on
|
||||
// every cycle for the same corrupt/stale-key credential.
|
||||
|
||||
const ORIGINAL_STORAGE_KEY = process.env.STORAGE_ENCRYPTION_KEY;
|
||||
|
||||
async function importFresh(modulePath: string) {
|
||||
const url = pathToFileURL(path.resolve(modulePath)).href;
|
||||
return import(`${url}?test=${Date.now()}-${Math.random().toString(16).slice(2)}`);
|
||||
}
|
||||
|
||||
function encryptWithStaticSalt(secret: string, salt: string, plaintext: string): string {
|
||||
const key = scryptSync(secret, salt, 32);
|
||||
const iv = randomBytes(16);
|
||||
const cipher = createCipheriv("aes-256-gcm", key, iv);
|
||||
let encrypted = cipher.update(plaintext, "utf8", "hex");
|
||||
encrypted += cipher.final("hex");
|
||||
const authTag = cipher.getAuthTag().toString("hex");
|
||||
return `enc:v1:${iv.toString("hex")}:${encrypted}:${authTag}`;
|
||||
}
|
||||
|
||||
test.after(() => {
|
||||
if (ORIGINAL_STORAGE_KEY === undefined) {
|
||||
delete process.env.STORAGE_ENCRYPTION_KEY;
|
||||
} else {
|
||||
process.env.STORAGE_ENCRYPTION_KEY = ORIGINAL_STORAGE_KEY;
|
||||
}
|
||||
});
|
||||
|
||||
function captureConsoleError(fn: () => void): string[] {
|
||||
const original = console.error;
|
||||
const logs: string[] = [];
|
||||
console.error = (...args: unknown[]) => {
|
||||
logs.push(args.map(String).join(" "));
|
||||
};
|
||||
try {
|
||||
fn();
|
||||
} finally {
|
||||
console.error = original;
|
||||
}
|
||||
return logs;
|
||||
}
|
||||
|
||||
test("#11500 — createLazyRowProxy dedupes decrypt-failure logging across sync cycles", async () => {
|
||||
process.env.STORAGE_ENCRYPTION_KEY = "probe-11500-current-key";
|
||||
|
||||
const { createLazyRowProxy } = await importFresh("src/lib/db/providers/lazyConnectionView.ts");
|
||||
|
||||
// Credential encrypted with a DIFFERENT key than the one currently
|
||||
// configured (stale STORAGE_ENCRYPTION_KEY / corrupted row) — produces
|
||||
// exactly "Auth tag validation likely failed."
|
||||
const staleCiphertext = encryptWithStaticSalt(
|
||||
"some-other-key-that-was-rotated-away",
|
||||
"omniroute-field-encryption-v1",
|
||||
"sk-super-secret-api-key"
|
||||
);
|
||||
|
||||
const rawRow = {
|
||||
id: "conn-zai-1",
|
||||
provider: "zai",
|
||||
apiKey: staleCiphertext,
|
||||
accessToken: null,
|
||||
refreshToken: null,
|
||||
idToken: null,
|
||||
};
|
||||
|
||||
const capturedLines = captureConsoleError(() => {
|
||||
// Simulate 3 separate CredentialHealth / model-sync cycles, each of
|
||||
// which calls getProviderConnections() fresh and gets a brand-new
|
||||
// createLazyRowProxy() over a brand-new row object for the SAME
|
||||
// underlying corrupt DB row.
|
||||
for (let cycle = 0; cycle < 3; cycle++) {
|
||||
const view = createLazyRowProxy({ ...rawRow });
|
||||
void view.apiKey;
|
||||
}
|
||||
});
|
||||
|
||||
const rawDecryptFailureLines = capturedLines.filter((line) =>
|
||||
line.includes("[Encryption] Decryption failed. Ciphertext prefix:")
|
||||
);
|
||||
|
||||
assert.equal(
|
||||
rawDecryptFailureLines.length,
|
||||
1,
|
||||
`expected the raw decrypt-failure line to be logged at most once across 3 sync cycles for the ` +
|
||||
`same corrupt credential, but it was logged ${rawDecryptFailureLines.length} times: ` +
|
||||
JSON.stringify(rawDecryptFailureLines, null, 2)
|
||||
);
|
||||
});
|
||||
|
||||
test("#11500 — createLazyConnectionView dedupes decrypt-failure logging across sync cycles", async () => {
|
||||
process.env.STORAGE_ENCRYPTION_KEY = "probe-11500-current-key-view";
|
||||
|
||||
const { createLazyConnectionView } = await importFresh(
|
||||
"src/lib/db/providers/lazyConnectionView.ts"
|
||||
);
|
||||
|
||||
const staleCiphertext = encryptWithStaticSalt(
|
||||
"some-other-key-that-was-rotated-away-view",
|
||||
"omniroute-field-encryption-v1",
|
||||
"sk-super-secret-api-key-view"
|
||||
);
|
||||
|
||||
const rawRow = {
|
||||
id: "conn-glm-1",
|
||||
provider: "glm",
|
||||
apiKey: staleCiphertext,
|
||||
accessToken: null,
|
||||
refreshToken: null,
|
||||
};
|
||||
|
||||
const capturedLines = captureConsoleError(() => {
|
||||
for (let cycle = 0; cycle < 3; cycle++) {
|
||||
const view = createLazyConnectionView({ ...rawRow });
|
||||
void view.apiKey;
|
||||
}
|
||||
});
|
||||
|
||||
const rawDecryptFailureLines = capturedLines.filter((line) =>
|
||||
line.includes("[Encryption] Decryption failed. Ciphertext prefix:")
|
||||
);
|
||||
|
||||
assert.equal(
|
||||
rawDecryptFailureLines.length,
|
||||
1,
|
||||
`expected the raw decrypt-failure line to be logged at most once across 3 sync cycles for the ` +
|
||||
`same corrupt credential, but it was logged ${rawDecryptFailureLines.length} times: ` +
|
||||
JSON.stringify(rawDecryptFailureLines, null, 2)
|
||||
);
|
||||
});
|
||||
12
tests/unit/ui/lobe-provider-icons-anysearch.test.tsx
Normal file
@@ -0,0 +1,12 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
import { getLobeProviderIcon } from "@/shared/components/lobeProviderIcons";
|
||||
|
||||
describe("AnySearch provider icon fallback", () => {
|
||||
it.each(["anysearch", "anysearch-search"])(
|
||||
"falls through when LobeHub has no icon for %s",
|
||||
(providerId) => {
|
||||
expect(getLobeProviderIcon(providerId)).toBeNull();
|
||||
}
|
||||
);
|
||||
});
|
||||