Compare commits
113 Commits
fix/codeql
...
chore/reco
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a582bef675 | ||
|
|
f77254da79 | ||
|
|
b1474b992f | ||
|
|
86514aa481 | ||
|
|
a150c6bc40 | ||
|
|
c59661cfd6 | ||
|
|
b76866fa12 | ||
|
|
8a14cefcb1 | ||
|
|
158fb1a806 | ||
|
|
77ea656b12 | ||
|
|
471052b904 | ||
|
|
b8553c8f0d | ||
|
|
1255b5869b | ||
|
|
ea2746e594 | ||
|
|
c264c24de9 | ||
|
|
15cbc1ede7 | ||
|
|
a65666519d | ||
|
|
829634b741 | ||
|
|
09666c50e7 | ||
|
|
2b9f25accb | ||
|
|
9dc7711dc2 | ||
|
|
42c209797c | ||
|
|
8bbe92c692 | ||
|
|
bbc7bf4351 | ||
|
|
38527e4a21 | ||
|
|
56d64e29a4 | ||
|
|
7b36e45df8 | ||
|
|
ddee064f1b | ||
|
|
b1fdfd5ea4 | ||
|
|
71eeaf293c | ||
|
|
406f4524ff | ||
|
|
dfc5b5eec4 | ||
|
|
0a53c8a2ce | ||
|
|
93da24cd79 | ||
|
|
815c7c2864 | ||
|
|
8f15b79a84 | ||
|
|
07a378c86c | ||
|
|
34150506f2 | ||
|
|
76ac1c8b7e | ||
|
|
d732cf615d | ||
|
|
f58e8bef6f | ||
|
|
243445f210 | ||
|
|
13e29f2f39 | ||
|
|
2544ee9498 | ||
|
|
440113c8e8 | ||
|
|
3c2906a80e | ||
|
|
095f424658 | ||
|
|
20de0d9c79 | ||
|
|
9f30b76057 | ||
|
|
019ad33a61 | ||
|
|
dfc9257b07 | ||
|
|
37e71915db | ||
|
|
077bc1a8a2 | ||
|
|
378eff0f75 | ||
|
|
6de542b9b6 | ||
|
|
315b0a94e1 | ||
|
|
e1c2b347f9 | ||
|
|
f88aa48847 | ||
|
|
8301984734 | ||
|
|
028f1b91e4 | ||
|
|
2af1326adf | ||
|
|
644dd32d3f | ||
|
|
9df3f8923d | ||
|
|
fa7ccaf679 | ||
|
|
85b2a06fad | ||
|
|
0b7ac870ef | ||
|
|
9fedc1c411 | ||
|
|
e589831952 | ||
|
|
04d2a60331 | ||
|
|
d23bfefec0 | ||
|
|
c8ad44e018 | ||
|
|
c83116e634 | ||
|
|
7715825cb8 | ||
|
|
761d38f433 | ||
|
|
c6963ca5dd | ||
|
|
b010d8bf86 | ||
|
|
fdcd15e6a9 | ||
|
|
12b8df02dd | ||
|
|
38d21afc2d | ||
|
|
05e76d6e76 | ||
|
|
93135f8e18 | ||
|
|
22086a73fa | ||
|
|
d4ade9d1d3 | ||
|
|
f54c93c879 | ||
|
|
e2e48fdab8 | ||
|
|
d2cea0811a | ||
|
|
2f18a85310 | ||
|
|
38969ad16b | ||
|
|
dafb4ae808 | ||
|
|
c89bdb21a2 | ||
|
|
c5e694131a | ||
|
|
338c05dc6a | ||
|
|
6945bbaaba | ||
|
|
690f684bfc | ||
|
|
c3cd1f94c0 | ||
|
|
c21460f22a | ||
|
|
9b14896a6c | ||
|
|
29f26293c3 | ||
|
|
cb11592441 | ||
|
|
5ee646e68e | ||
|
|
6984676d95 | ||
|
|
79f8ae9d1e | ||
|
|
04b2c47940 | ||
|
|
24ac71465e | ||
|
|
8d6f91b558 | ||
|
|
c3698eedcb | ||
|
|
9e7096a048 | ||
|
|
a84f0b683e | ||
|
|
ae0b79ab7c | ||
|
|
6daa31de0b | ||
|
|
adca3b881c | ||
|
|
ac02c5b42f | ||
|
|
07d1816a45 |
@@ -46,7 +46,7 @@ Repository map and Reference Documentation sections below.
|
||||
|
||||
## Project at a Glance
|
||||
|
||||
**OmniRoute** — unified AI proxy/router. One endpoint, 350 LLM providers, auto-fallback.
|
||||
**OmniRoute** — unified AI proxy/router. One endpoint, 351 LLM providers, auto-fallback.
|
||||
|
||||
| Layer | Location | Purpose |
|
||||
| ------------- | ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# ── Multi-stage Dockerfile for Native Bun Runtime (web-latest-bun) ───────────
|
||||
FROM oven/bun:1.3.14-slim AS base
|
||||
FROM oven/bun:1.4.0-slim AS base
|
||||
WORKDIR /app
|
||||
|
||||
RUN apt-get update \
|
||||
@@ -50,7 +50,7 @@ ENV NODE_ENV=production
|
||||
RUN bun run --quiet build
|
||||
|
||||
# ── Runner Base stage (100% Bun Native Production Runtime) ──────────────────
|
||||
FROM oven/bun:1.3.14-slim AS runner-base
|
||||
FROM oven/bun:1.4.0-slim AS runner-base
|
||||
|
||||
LABEL org.opencontainers.image.title="omniroute" \
|
||||
org.opencontainers.image.description="Unified AI proxy — route any LLM through one endpoint (Bun Native)" \
|
||||
|
||||
12
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 → 350 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. 350 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 → 351 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. 351 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 350 providers. Automatic fallback keeps routing while another healthy target is available. Six pillars: resilient fallback across 350 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 351 providers. Automatic fallback keeps routing while another healthy target is available. Six pillars: resilient fallback across 351 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: 350 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: 351 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 **350-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 **351-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,11 +642,11 @@ of your shell history. → [CLI Integrations](docs/guides/CLI-INTEGRATIONS.md)
|
||||
|
||||
<div align="center">
|
||||
|
||||
## 🌐 350 AI Providers — 154 Catalog-Marked Free
|
||||
## 🌐 351 AI Providers — 154 Catalog-Marked Free
|
||||
|
||||
</div>
|
||||
|
||||
> **350 registered providers** across the canonical chat, media, search, local, cloud-agent and system collections, including **154 carrying `hasFree: true` discovery metadata**. The chat model registry covers **268 providers / 2,566 distinct provider-model pairs / 1,312 raw model IDs**; the separate free-budget catalog has **455 per-model rows**, **40 recurring pools** and **56 recurring/keyless free-forever providers**. These are different denominators by design; definitions and pool-deduped calculations live in the [Provider Reference](docs/reference/PROVIDER_REFERENCE.md) and [Free Tiers](docs/reference/FREE_TIERS.md).
|
||||
> **351 registered providers** across the canonical chat, media, search, local, cloud-agent and system collections, including **154 carrying `hasFree: true` discovery metadata**. The chat model registry covers **268 providers / 2,566 distinct provider-model pairs / 1,312 raw model IDs**; the separate free-budget catalog has **455 per-model rows**, **40 recurring pools** and **56 recurring/keyless free-forever providers**. These are different denominators by design; definitions and pool-deduped calculations live in the [Provider Reference](docs/reference/PROVIDER_REFERENCE.md) and [Free Tiers](docs/reference/FREE_TIERS.md).
|
||||
|
||||
<div align="center">
|
||||
|
||||
|
||||
1
changelog.d/features/11340-web-session-contract.md
Normal file
@@ -0,0 +1 @@
|
||||
- **feat(providers):** publish a management-authenticated versioned web-session credential contract from OmniRoute's canonical browser credential metadata ([#11340](https://github.com/diegosouzapw/OmniRoute/pull/11340)) — thanks @Zartharas
|
||||
1
changelog.d/features/11370-xquik-search-provider.md
Normal file
@@ -0,0 +1 @@
|
||||
- **feat(search):** Add Xquik X search with typed results, credential validation, REST routing, and MCP selection ([#11370](https://github.com/diegosouzapw/OmniRoute/pull/11370)) — thanks @kriptoburak
|
||||
@@ -0,0 +1 @@
|
||||
- **feat(dashboard):** surface durable exclusive managed leases in the existing Sessions view, keeping leased clients visible across idle gaps while marking connections with in-flight work as active ([#11389](https://github.com/diegosouzapw/OmniRoute/pull/11389)) — thanks @KaspaPulse
|
||||
1
changelog.d/fixes/11441-discontinued-free-models.md
Normal file
@@ -0,0 +1 @@
|
||||
- **fix(free-models):** the shared `isFreeModel()` predicate no longer reports catalog entries marked `freeType: "discontinued"` as free, so `hidePaidModels` can't route to Pollinations' seven premium models that now require a paid key ([#11441](https://github.com/diegosouzapw/OmniRoute/pull/11441))
|
||||
1
changelog.d/fixes/7592-windows-packaged-smoke.md
Normal file
@@ -0,0 +1 @@
|
||||
- **fix(electron):** the packaged Windows desktop build now passes the #7592 cold-restart smoke end to end. Five defects were found and fixed on the way: (1) optional-pack staging failed on any machine whose `tar` is GNU tar — it reads the drive letter in an absolute `-f C:\...` archive path as a remote rsh host (`Cannot connect to C:`), so staging now passes a bare filename with `cwd` at the tarball directory and surfaces tar stderr on failure; (2) the Electron `files` allowlist was missing `lib/loginHeaderCapture.js`, so the packaged main process crashed at startup with `Cannot find module './lib/loginHeaderCapture'`; (3) electron-builder ≥26 injects an `!**/node_modules/**` ignore into every extraResources pattern list that no positive filter can override, which silently dropped the staged runtime `node_modules` (including the better-sqlite3 N-API prebuild) from `resources/app` — a fresh v3.8.50 desktop build would have shipped with no native SQLite driver and reproduced the #7592 sql.js fallback on every machine; an `afterPack` hook now restores the staged `node_modules` after packing; (4) the packaged-app smoke harness redirected `USERPROFILE` into its temp DATA_DIR, but Electron resolves the Roaming profile from `%USERPROFILE%\AppData\Roaming\<name>` (USERPROFILE takes precedence over the APPDATA env var) and the path service throws instead of creating it, so `requestSingleInstanceLock()` returned false and the app exited(0) silently before `app.whenReady()` — the harness now pre-creates the derived tree, and `ensureSmokeEnvDirs` is exported and unit-tested; (5) the #7592 driver assertion parsed a `[DB] Driver: ...` line that the server's primary DB path never printed (only the unused `openDatabaseAsync()` did) — `getDbInstance()` now emits the same line on open so the guard can actually assert the native driver. Also: the smoke env-allowlist unit test hardcoded POSIX paths and could only pass on Linux/macOS; expectations are now host-agnostic, with new regression tests for the USERPROFILE-derived tree and `tarPack` under absolute Windows-style paths
|
||||
@@ -0,0 +1 @@
|
||||
- **fix(memory):** Embedding Model Quick select, Embedding Source remote dropdown, and Rerank selector now list every configured provider with embedding/rerank support instead of only chat-catalog text matches plus OpenRouter live discovery; a generic OpenAI-compatible `/embeddings` + Cohere-compatible `/rerank` runtime fallback resolves any configured chat provider's embedding/rerank endpoint, so unlisted providers no longer fail with "Unknown embedding provider"; both memory selectors gained a free-text model override
|
||||
@@ -12,6 +12,7 @@
|
||||
"@dnd-kit/core",
|
||||
"@dnd-kit/sortable",
|
||||
"@dnd-kit/utilities",
|
||||
"@eslint/compat",
|
||||
"@huggingface/transformers",
|
||||
"@lobehub/icons",
|
||||
"@modelcontextprotocol/sdk",
|
||||
@@ -60,6 +61,7 @@
|
||||
"eslint",
|
||||
"eslint-config-next",
|
||||
"eslint-plugin-sonarjs",
|
||||
"espree",
|
||||
"express",
|
||||
"fast-check",
|
||||
"fetch-socks",
|
||||
|
||||
@@ -2372,16 +2372,6 @@
|
||||
"count": 2
|
||||
}
|
||||
},
|
||||
"src/lib/guardrails/videoBridgeContactSheet.ts": {
|
||||
"@typescript-eslint/no-unused-vars": {
|
||||
"count": 1
|
||||
}
|
||||
},
|
||||
"src/lib/guardrails/videoBridgeRuntime.ts": {
|
||||
"@typescript-eslint/no-unused-vars": {
|
||||
"count": 1
|
||||
}
|
||||
},
|
||||
"src/lib/guardrails/visionBridgeHelpers.ts": {
|
||||
"@typescript-eslint/no-unused-vars": {
|
||||
"count": 1
|
||||
@@ -2422,11 +2412,6 @@
|
||||
"count": 1
|
||||
}
|
||||
},
|
||||
"src/lib/memory/embedding/index.ts": {
|
||||
"no-restricted-imports": {
|
||||
"count": 1
|
||||
}
|
||||
},
|
||||
"src/lib/memory/genericBackend.ts": {
|
||||
"@typescript-eslint/no-unused-vars": {
|
||||
"count": 1
|
||||
|
||||
1
docs/changelog/fragments/7778.md
Normal file
@@ -0,0 +1 @@
|
||||
feat(resilience): atomically enforce cumulative global, provider, and account concurrency limits
|
||||
98
docs/design/EXTERNAL-DB-BACKEND.md
Normal file
@@ -0,0 +1,98 @@
|
||||
# Design: Pluggable External Durable-State DB Backend (PostgreSQL / MySQL)
|
||||
|
||||
Status: PROPOSAL · Discussion issue: [#8075](https://github.com/diegosouzapw/OmniRoute/issues/8075) · Author: @oyi77
|
||||
|
||||
Grounded in the tree at `release/v3.8.51` (`3192eb88d`). Every number below was measured on that tree, not estimated.
|
||||
|
||||
---
|
||||
|
||||
## 0. Why this document exists
|
||||
|
||||
#8075 asks five questions (backends & order, abstraction contract, what stays SQLite-only, config surface, migration path). This document answers them with a census of how deeply the current data layer is coupled to SQLite, and proposes a repository-contract seam that lets external backends be adopted per domain instead of via a big-bang driver swap.
|
||||
|
||||
## 1. Measured coupling census (`release/v3.8.51`)
|
||||
|
||||
| Surface | Count | Implication for external backends |
|
||||
|---|---|---|
|
||||
| `src/lib/db/*.ts` modules calling `.prepare()` / `.transaction()` | **126 files** | The synchronous SQLite call shape is the de-facto data API; every module is a translation candidate |
|
||||
| `.pragma()` call sites | **36** | No equivalent in PG/MySQL — each needs an audit (many are performance tuning that becomes server config) |
|
||||
| SQLite migrations on disk | **159** | A second dialect needs its own migration lineage, not a translator |
|
||||
| `sqlite-vec` / `vec0` referencing files | **9** | Vector search must move to an external store (Qdrant proposed in #8075) |
|
||||
| FTS5 referencing files | **8** | Maps to Postgres `tsvector` / MySQL `FULLTEXT` — dialect work |
|
||||
| Existing driver adapters | **4** (`better-sqlite3`, `node:sqlite`, `bun:sqlite`, `sql.js`) behind `SqliteAdapter` (`src/lib/db/adapters/types.ts`) | Proves a seam exists at the DRIVER level — but all four are SQLite; the seam OmniRoute actually needs is one level up |
|
||||
|
||||
The decisive fact: **the adapter interface is synchronous** (`prepare().run/get/all`, sync closure transactions). PostgreSQL and MySQL drivers are inherently async. A faithful external backend behind `SqliteAdapter` is therefore impossible without faking sync over async — which is exactly the trap #8037/#8073 already flagged ("not a drop-in ORM/driver swap"). The contract must live ABOVE the adapter layer.
|
||||
|
||||
## 2. Proposed contract: durable-state repositories, not a driver swap
|
||||
|
||||
Introduce a `DurableStateStore` port with per-domain repositories, defined once and implemented per dialect:
|
||||
|
||||
```ts
|
||||
// src/lib/db/ports/ — dialect-agnostic, async, transactional by unit-of-work
|
||||
interface UnitOfWork {
|
||||
connections: ConnectionRepository; // provider connections + credentials
|
||||
apiKeys: ApiKeyRepository; // keys, groups, usage-limit fields
|
||||
combos: ComboRepository; // combo defs + routing policies
|
||||
quotaState: QuotaStateRepository; // quota/account state, cooldowns
|
||||
sessionAffinity: AffinityRepository; // sticky routing state
|
||||
audit: AuditRepository; // config audit log
|
||||
}
|
||||
interface DurableStateStore {
|
||||
readonly driver: "sqlite" | "postgres" | "mysql";
|
||||
withUnitOfWork<T>(fn: (uow: UnitOfWork) => Promise<T>): Promise<T>;
|
||||
healthCheck(): Promise<{ ok: boolean; latencyMs: number }>;
|
||||
}
|
||||
```
|
||||
|
||||
Deliberate exclusions from v1 (stay SQLite-local, matching #8075 §3): WAL/VACUUM/PRAGMA lifecycle, file backup/import/export, `sqlite-vec` vector storage (external backends delegate vectors to Qdrant), FTS (dialect-native fulltext later).
|
||||
|
||||
### What makes each repository shippable
|
||||
|
||||
The single-writer pattern OmniRoute already uses internally (targeted raw-SQL stat bumps like `touchConnectionLastUsed`, single-writer caches) maps cleanly onto `UnitOfWork`. Repositories are adopted **per domain**: a deployment can run `postgres` for connections+keys while hot-path ephemeral state stays local — because the port is injected at the repository boundary, not under 126 modules.
|
||||
|
||||
## 3. Sequencing: PostgreSQL first, MySQL second
|
||||
|
||||
1. **Postgres-first** (`pg` + `pg-pool`): richer native JSONB (several modules persist JSON blobs), `tsvector`, `INSERT ... ON CONFLICT` semantics closest to `INSERT OR REPLACE` usage.
|
||||
2. **MySQL 8+ after**, gated by a shared **dialect-conformance suite**: the same behavioral test pack runs against both implementations (schema bootstrap, UoW rollback, credential round-trip incl. encryption-at-rest fields, pagination/ordering contracts). A backend is "supported" only when the suite is green.
|
||||
|
||||
Rationale: one conformance suite prevents the MySQL implementation from drifting into a shim that passes smoke tests but corrupts ordering/collation edge cases.
|
||||
|
||||
## 4. Config surface
|
||||
|
||||
```
|
||||
DATABASE_DRIVER=sqlite # sqlite (default) | postgres | mysql
|
||||
DATABASE_URL=postgres://... # required when driver != sqlite
|
||||
DATABASE_POOL_MAX=10
|
||||
DATABASE_TLS_REJECT_UNAUTHORIZED=1
|
||||
DATABASE_MIGRATIONS_TABLE=_omniroute_migrations
|
||||
```
|
||||
|
||||
- Default stays `sqlite` — zero-config npm/Electron/Termux behavior is non-negotiable (#8075 §6).
|
||||
- Health check surfaces in the existing `/api/monitoring/health` payload next to `inflightRequests`.
|
||||
- Migrations: per-dialect lineage directories (`migrations-postgres/`, `migrations-mysql/`) with their own runner sharing the safety checks (mass-migration abort, pre-migration backup → pg_dump/mysqldump equivalent) already proven in `migrationRunner.ts`.
|
||||
|
||||
## 5. Migration path (one-way export tool)
|
||||
|
||||
`omniroute db export --to postgres --dsn ...`:
|
||||
1. Snapshot `storage.sqlite` (reuse `createPreMigrationBackup`).
|
||||
2. Replay schema from the target dialect's lineage to head.
|
||||
3. Copy tables row-by-row through the new repositories (not raw SQL translation), preserving IDs; re-encrypt nothing (credential ciphertexts are dialect-neutral strings today).
|
||||
4. Verify with row-count + checksum comparison per table; write a receipt file.
|
||||
5. Refuse to continue on any mismatch (fail-closed).
|
||||
|
||||
## 6. Phased delivery plan
|
||||
|
||||
| Phase | Deliverable | Risk gate |
|
||||
|---|---|---|
|
||||
| P1 | `ports/` contract + SQLite implementation of `ConnectionRepository` + conformance-suite skeleton running against SQLite itself | Suite green on the reference driver |
|
||||
| P2 | Postgres implementation of the same repo + suite green on PG | Suite green on PG |
|
||||
| P3 | Remaining domains (apiKeys, combos, quotaState, affinity, audit) on PG | Full-suite parity vs SQLite run |
|
||||
| P4 | MySQL via conformance suite | Suite green on MySQL |
|
||||
| P5 | Export tool + docs | Round-trip receipt on a 1GB-scale fixture |
|
||||
|
||||
P1–P2 alone deliver value: a clustered deployment can share connection/key state while everything else stays local.
|
||||
|
||||
## 7. Explicitly out of scope
|
||||
|
||||
- Translating the remaining ~120 SQLite-shaped modules verbatim ("just make prepare() hit Postgres") — rejected: it fakes sync over async and preserves dialect bugs forever.
|
||||
- Replacing `sqlite-vec` with pgvector — #8075 names Qdrant; keeping vector search out-of-process also keeps the lean proxy core lean (4.0 modular direction).
|
||||
@@ -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 (350 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 (351 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"/>
|
||||
@@ -6,7 +6,7 @@
|
||||
<path d="M 0 34 L 1200 34" stroke="#ffffff" stroke-opacity="0.08" stroke-width="1"/>
|
||||
<circle cx="24" cy="17" r="6" fill="#ff5f56"/><circle cx="46" cy="17" r="6" fill="#ffbd2e"/><circle cx="68" cy="17" r="6" fill="#27c93f"/>
|
||||
<text x="600" y="22" text-anchor="middle" font-family="Consolas, 'Courier New', monospace" font-size="13" fill="#71717a">omniroute — 85 top-level commands</text>
|
||||
<g font-family="Consolas, 'Courier New', monospace" font-size="17"><animate attributeName="opacity" values="1;0;0" keyTimes="0;0.006;1" dur="18s" repeatCount="indefinite"/><text x="64" y="66" fill="#F7F6FC">omniroute providers list</text><text x="40" y="100" font-weight="700" fill="#38bdf8">OmniRoute Providers</text><text x="40" y="128" fill="#a1a1aa">1f3a9c2e  anthropic   Claude Max 20x    <tspan fill='#22c55e'>active</tspan></text><text x="40" y="154" fill="#a1a1aa">8c2d5b1a  codex       Codex Pro (team)  <tspan fill='#22c55e'>active</tspan></text><text x="40" y="180" fill="#a1a1aa">f4e0a97b  glm         GLM Coding Plan   <tspan fill='#22c55e'>active</tspan></text><text x="40" y="206" fill="#a1a1aa">03bd6e5f  kimi        Kimi K2 free      <tspan fill='#22c55e'>active</tspan></text><text x="40" y="232" fill="#71717a">… 346 more providers</text></g><g opacity="1" font-family="Consolas, 'Courier New', monospace" font-size="17">
|
||||
<g font-family="Consolas, 'Courier New', monospace" font-size="17"><animate attributeName="opacity" values="1;0;0" keyTimes="0;0.006;1" dur="18s" repeatCount="indefinite"/><text x="64" y="66" fill="#F7F6FC">omniroute providers list</text><text x="40" y="100" font-weight="700" fill="#38bdf8">OmniRoute Providers</text><text x="40" y="128" fill="#a1a1aa">1f3a9c2e  anthropic   Claude Max 20x    <tspan fill='#22c55e'>active</tspan></text><text x="40" y="154" fill="#a1a1aa">8c2d5b1a  codex       Codex Pro (team)  <tspan fill='#22c55e'>active</tspan></text><text x="40" y="180" fill="#a1a1aa">f4e0a97b  glm         GLM Coding Plan   <tspan fill='#22c55e'>active</tspan></text><text x="40" y="206" fill="#a1a1aa">03bd6e5f  kimi        Kimi K2 free      <tspan fill='#22c55e'>active</tspan></text><text x="40" y="232" fill="#71717a">… 347 more providers</text></g><g opacity="1" font-family="Consolas, 'Courier New', monospace" font-size="17">
|
||||
<animate attributeName="opacity" values="1;1;0;0" keyTimes="0;0.315;0.33;1" dur="18s" repeatCount="indefinite"/>
|
||||
<text x="40" y="66" fill="#22c55e">$</text>
|
||||
<g clip-path="url(#tw0)"><text x="64" y="66" fill="#F7F6FC">omniroute providers list</text></g>
|
||||
@@ -14,7 +14,7 @@
|
||||
<animate attributeName="x" calcMode="discrete" values="64;95;125;156;186;217;248;278;309;309" keyTimes="0.000;0.012;0.018;0.024;0.030;0.036;0.042;0.048;0.054;1" dur="18s" repeatCount="indefinite"/>
|
||||
<animate attributeName="opacity" values="0;0;1;0.2;1;0.2;1;0;0" keyTimes="0;0.011;0.012;0.022;0.032;0.042;0.052;0.074;1" dur="18s" repeatCount="indefinite"/>
|
||||
</rect>
|
||||
<text x="40" y="100" font-weight="700" fill="#38bdf8" opacity="0"><animate attributeName="opacity" calcMode="discrete" values="0;0;1" keyTimes="0;0.045;0.047" dur="18s" repeatCount="indefinite"/>OmniRoute Providers</text><text x="40" y="128" fill="#a1a1aa" opacity="0"><animate attributeName="opacity" calcMode="discrete" values="0;0;1" keyTimes="0;0.053;0.055" dur="18s" repeatCount="indefinite"/>1f3a9c2e  anthropic   Claude Max 20x    <tspan fill='#22c55e'>active</tspan></text><text x="40" y="154" fill="#a1a1aa" opacity="0"><animate attributeName="opacity" calcMode="discrete" values="0;0;1" keyTimes="0;0.061;0.063" dur="18s" repeatCount="indefinite"/>8c2d5b1a  codex       Codex Pro (team)  <tspan fill='#22c55e'>active</tspan></text><text x="40" y="180" fill="#a1a1aa" opacity="0"><animate attributeName="opacity" calcMode="discrete" values="0;0;1" keyTimes="0;0.069;0.07100000000000001" dur="18s" repeatCount="indefinite"/>f4e0a97b  glm         GLM Coding Plan   <tspan fill='#22c55e'>active</tspan></text><text x="40" y="206" fill="#a1a1aa" opacity="0"><animate attributeName="opacity" calcMode="discrete" values="0;0;1" keyTimes="0;0.077;0.079" dur="18s" repeatCount="indefinite"/>03bd6e5f  kimi        Kimi K2 free      <tspan fill='#22c55e'>active</tspan></text><text x="40" y="232" fill="#71717a" opacity="0"><animate attributeName="opacity" calcMode="discrete" values="0;0;1" keyTimes="0;0.085;0.08700000000000001" dur="18s" repeatCount="indefinite"/>… 346 more providers</text>
|
||||
<text x="40" y="100" font-weight="700" fill="#38bdf8" opacity="0"><animate attributeName="opacity" calcMode="discrete" values="0;0;1" keyTimes="0;0.045;0.047" dur="18s" repeatCount="indefinite"/>OmniRoute Providers</text><text x="40" y="128" fill="#a1a1aa" opacity="0"><animate attributeName="opacity" calcMode="discrete" values="0;0;1" keyTimes="0;0.053;0.055" dur="18s" repeatCount="indefinite"/>1f3a9c2e  anthropic   Claude Max 20x    <tspan fill='#22c55e'>active</tspan></text><text x="40" y="154" fill="#a1a1aa" opacity="0"><animate attributeName="opacity" calcMode="discrete" values="0;0;1" keyTimes="0;0.061;0.063" dur="18s" repeatCount="indefinite"/>8c2d5b1a  codex       Codex Pro (team)  <tspan fill='#22c55e'>active</tspan></text><text x="40" y="180" fill="#a1a1aa" opacity="0"><animate attributeName="opacity" calcMode="discrete" values="0;0;1" keyTimes="0;0.069;0.07100000000000001" dur="18s" repeatCount="indefinite"/>f4e0a97b  glm         GLM Coding Plan   <tspan fill='#22c55e'>active</tspan></text><text x="40" y="206" fill="#a1a1aa" opacity="0"><animate attributeName="opacity" calcMode="discrete" values="0;0;1" keyTimes="0;0.077;0.079" dur="18s" repeatCount="indefinite"/>03bd6e5f  kimi        Kimi K2 free      <tspan fill='#22c55e'>active</tspan></text><text x="40" y="232" fill="#71717a" opacity="0"><animate attributeName="opacity" calcMode="discrete" values="0;0;1" keyTimes="0;0.085;0.08700000000000001" dur="18s" repeatCount="indefinite"/>… 347 more providers</text>
|
||||
</g><g opacity="0" font-family="Consolas, 'Courier New', monospace" font-size="17">
|
||||
<animate attributeName="opacity" values="0;0;1;1;0;0" keyTimes="0;0.333;0.34800000000000003;0.648;0.663;1" dur="18s" repeatCount="indefinite"/>
|
||||
<text x="40" y="66" fill="#22c55e">$</text>
|
||||
|
||||
|
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: 350 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: 351 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 350 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 351 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">350 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">351 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 350 providers in</text>
|
||||
<text x="66" y="204" font-size="13.5" fill="#a1a1aa">Auto-fallback across 351 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 350 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: 350 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 351 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: 351 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">350 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">351 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 |
@@ -66,22 +66,22 @@ Cursor, Cline, and compatible MCP client setup.
|
||||
|
||||
## Essential Tools (13) — Phase 1
|
||||
|
||||
| Tool | Scopes | Description |
|
||||
| :------------------------------ | :-------------------- | :------------------------------------------------------------ |
|
||||
| `omniroute_get_health` | `read:health` | Uptime, memory, circuit breakers, rate limits, cache stats |
|
||||
| `omniroute_list_combos` | `read:combos` | All configured combos with strategies (optional metrics) |
|
||||
| `omniroute_get_combo_metrics` | `read:combos` | Performance metrics for a specific combo |
|
||||
| `omniroute_switch_combo` | `write:combos` | Activate or deactivate a combo |
|
||||
| `omniroute_create_combo` | `write:combos` | Create a validated combo through the existing combo API |
|
||||
| `omniroute_check_quota` | `read:quota` | Quota used/total, percent remaining, reset time, token health |
|
||||
| `omniroute_route_request` | `execute:completions` | Send a chat completion through OmniRoute routing |
|
||||
| `omniroute_cost_report` | `read:usage` | Cost report by period (session/day/week/month) |
|
||||
| `omniroute_list_models_catalog` | `read:models` | Full model catalog with capabilities, status, pricing |
|
||||
| `omniroute_radar_catalog` | `read:radar` | Local signed Radar catalog; optional provider/family filters |
|
||||
| `omniroute_tool_search` | `read:tools` | Discover tools from the registered MCP catalog |
|
||||
| `omniroute_web_search` | `execute:search` | Web search through the configured search providers. Not X/Twitter. |
|
||||
| `omniroute_x_search` | `execute:search` | Search X (Twitter) through SuperGrok / xAI server-side `x_search`. Requires `xai-oauth` or an xAI API key. Not the X Developer Platform MCP. |
|
||||
| `omniroute_web_fetch` | `execute:search` | Fetch web content through the configured fetch providers |
|
||||
| Tool | Scopes | Description |
|
||||
| :------------------------------ | :-------------------- | :----------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `omniroute_get_health` | `read:health` | Uptime, memory, circuit breakers, rate limits, cache stats |
|
||||
| `omniroute_list_combos` | `read:combos` | All configured combos with strategies (optional metrics) |
|
||||
| `omniroute_get_combo_metrics` | `read:combos` | Performance metrics for a specific combo |
|
||||
| `omniroute_switch_combo` | `write:combos` | Activate or deactivate a combo |
|
||||
| `omniroute_create_combo` | `write:combos` | Create a validated combo through the existing combo API |
|
||||
| `omniroute_check_quota` | `read:quota` | Quota used/total, percent remaining, reset time, token health |
|
||||
| `omniroute_route_request` | `execute:completions` | Send a chat completion through OmniRoute routing |
|
||||
| `omniroute_cost_report` | `read:usage` | Cost report by period (session/day/week/month) |
|
||||
| `omniroute_list_models_catalog` | `read:models` | Full model catalog with capabilities, status, pricing |
|
||||
| `omniroute_radar_catalog` | `read:radar` | Local signed Radar catalog; optional provider/family filters |
|
||||
| `omniroute_tool_search` | `read:tools` | Discover tools from the registered MCP catalog |
|
||||
| `omniroute_web_search` | `execute:search` | Web search through the configured search providers. Not X/Twitter. |
|
||||
| `omniroute_x_search` | `execute:search` | Search X through xAI/SuperGrok, or choose `xquik-search` for Xquik API results. Requires credentials for the selected backend. |
|
||||
| `omniroute_web_fetch` | `execute:search` | Fetch web content through the configured fetch providers |
|
||||
|
||||
## Advanced Tools (11) — Phase 2
|
||||
|
||||
|
||||
@@ -4,7 +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 350 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 351 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
|
||||
|
||||
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
## Key Features (v3.8.50)
|
||||
|
||||
### Core Proxy
|
||||
- **350 AI providers** with automatic format translation
|
||||
- **351 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
|
||||
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
## v3.8.x Highlights
|
||||
|
||||
- **350-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **351-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,7 +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 350 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 351 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
|
||||
|
||||
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
## Key Features (v3.8.50)
|
||||
|
||||
### Core Proxy
|
||||
- **350 AI providers** with automatic format translation
|
||||
- **351 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
|
||||
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
## v3.8.x Highlights
|
||||
|
||||
- **350-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **351-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,7 +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 350 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 351 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
|
||||
|
||||
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
## Key Features (v3.8.50)
|
||||
|
||||
### Core Proxy
|
||||
- **350 AI providers** with automatic format translation
|
||||
- **351 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
|
||||
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
## v3.8.x Highlights
|
||||
|
||||
- **350-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **351-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,7 +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 350 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 351 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
|
||||
|
||||
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
## Key Features (v3.8.50)
|
||||
|
||||
### Core Proxy
|
||||
- **350 AI providers** with automatic format translation
|
||||
- **351 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
|
||||
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
## v3.8.x Highlights
|
||||
|
||||
- **350-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **351-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,7 +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 350 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 351 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
|
||||
|
||||
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
## Key Features (v3.8.50)
|
||||
|
||||
### Core Proxy
|
||||
- **350 AI providers** with automatic format translation
|
||||
- **351 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
|
||||
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
## v3.8.x Highlights
|
||||
|
||||
- **350-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **351-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,7 +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 350 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 351 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
|
||||
|
||||
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
## Key Features (v3.8.50)
|
||||
|
||||
### Core Proxy
|
||||
- **350 AI providers** with automatic format translation
|
||||
- **351 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
|
||||
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
## v3.8.x Highlights
|
||||
|
||||
- **350-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **351-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,7 +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 350 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 351 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
|
||||
|
||||
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
## Key Features (v3.8.50)
|
||||
|
||||
### Core Proxy
|
||||
- **350 AI providers** with automatic format translation
|
||||
- **351 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
|
||||
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
## v3.8.x Highlights
|
||||
|
||||
- **350-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **351-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,7 +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 350 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 351 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
|
||||
|
||||
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
## Key Features (v3.8.50)
|
||||
|
||||
### Core Proxy
|
||||
- **350 AI providers** with automatic format translation
|
||||
- **351 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
|
||||
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
## v3.8.x Highlights
|
||||
|
||||
- **350-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **351-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,7 +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 350 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 351 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
|
||||
|
||||
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
## Key Features (v3.8.50)
|
||||
|
||||
### Core Proxy
|
||||
- **350 AI providers** with automatic format translation
|
||||
- **351 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
|
||||
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
## v3.8.x Highlights
|
||||
|
||||
- **350-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **351-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,7 +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 350 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 351 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
|
||||
|
||||
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
## Key Features (v3.8.50)
|
||||
|
||||
### Core Proxy
|
||||
- **350 AI providers** with automatic format translation
|
||||
- **351 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
|
||||
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
## v3.8.x Highlights
|
||||
|
||||
- **350-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **351-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,7 +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 350 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 351 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
|
||||
|
||||
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
## Key Features (v3.8.50)
|
||||
|
||||
### Core Proxy
|
||||
- **350 AI providers** with automatic format translation
|
||||
- **351 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
|
||||
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
## v3.8.x Highlights
|
||||
|
||||
- **350-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **351-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,7 +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 350 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 351 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
|
||||
|
||||
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
## Key Features (v3.8.50)
|
||||
|
||||
### Core Proxy
|
||||
- **350 AI providers** with automatic format translation
|
||||
- **351 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
|
||||
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
## v3.8.x Highlights
|
||||
|
||||
- **350-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **351-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,7 +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 350 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 351 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
|
||||
|
||||
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
## Key Features (v3.8.50)
|
||||
|
||||
### Core Proxy
|
||||
- **350 AI providers** with automatic format translation
|
||||
- **351 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
|
||||
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
## v3.8.x Highlights
|
||||
|
||||
- **350-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **351-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,7 +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 350 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 351 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
|
||||
|
||||
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
## Key Features (v3.8.50)
|
||||
|
||||
### Core Proxy
|
||||
- **350 AI providers** with automatic format translation
|
||||
- **351 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
|
||||
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
## v3.8.x Highlights
|
||||
|
||||
- **350-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **351-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,7 +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 350 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 351 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
|
||||
|
||||
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
## Key Features (v3.8.50)
|
||||
|
||||
### Core Proxy
|
||||
- **350 AI providers** with automatic format translation
|
||||
- **351 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
|
||||
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
## v3.8.x Highlights
|
||||
|
||||
- **350-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **351-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,7 +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 350 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 351 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
|
||||
|
||||
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
## Key Features (v3.8.50)
|
||||
|
||||
### Core Proxy
|
||||
- **350 AI providers** with automatic format translation
|
||||
- **351 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
|
||||
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
## v3.8.x Highlights
|
||||
|
||||
- **350-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **351-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,7 +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 350 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 351 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
|
||||
|
||||
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
## Key Features (v3.8.50)
|
||||
|
||||
### Core Proxy
|
||||
- **350 AI providers** with automatic format translation
|
||||
- **351 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
|
||||
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
## v3.8.x Highlights
|
||||
|
||||
- **350-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **351-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,7 +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 350 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 351 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
|
||||
|
||||
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
## Key Features (v3.8.50)
|
||||
|
||||
### Core Proxy
|
||||
- **350 AI providers** with automatic format translation
|
||||
- **351 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
|
||||
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
## v3.8.x Highlights
|
||||
|
||||
- **350-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **351-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,7 +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 350 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 351 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
|
||||
|
||||
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
## Key Features (v3.8.50)
|
||||
|
||||
### Core Proxy
|
||||
- **350 AI providers** with automatic format translation
|
||||
- **351 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
|
||||
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
## v3.8.x Highlights
|
||||
|
||||
- **350-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **351-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,7 +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 350 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 351 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
|
||||
|
||||
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
## Key Features (v3.8.50)
|
||||
|
||||
### Core Proxy
|
||||
- **350 AI providers** with automatic format translation
|
||||
- **351 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
|
||||
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
## v3.8.x Highlights
|
||||
|
||||
- **350-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **351-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,7 +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 350 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 351 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
|
||||
|
||||
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
## Key Features (v3.8.50)
|
||||
|
||||
### Core Proxy
|
||||
- **350 AI providers** with automatic format translation
|
||||
- **351 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
|
||||
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
## v3.8.x Highlights
|
||||
|
||||
- **350-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **351-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,7 +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 350 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 351 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
|
||||
|
||||
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
## Key Features (v3.8.50)
|
||||
|
||||
### Core Proxy
|
||||
- **350 AI providers** with automatic format translation
|
||||
- **351 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
|
||||
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
## v3.8.x Highlights
|
||||
|
||||
- **350-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **351-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,7 +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 350 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 351 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
|
||||
|
||||
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
## Key Features (v3.8.50)
|
||||
|
||||
### Core Proxy
|
||||
- **350 AI providers** with automatic format translation
|
||||
- **351 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
|
||||
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
## v3.8.x Highlights
|
||||
|
||||
- **350-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **351-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,7 +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 350 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 351 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
|
||||
|
||||
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
## Key Features (v3.8.50)
|
||||
|
||||
### Core Proxy
|
||||
- **350 AI providers** with automatic format translation
|
||||
- **351 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
|
||||
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
## v3.8.x Highlights
|
||||
|
||||
- **350-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **351-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,7 +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 350 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 351 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
|
||||
|
||||
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
## Key Features (v3.8.50)
|
||||
|
||||
### Core Proxy
|
||||
- **350 AI providers** with automatic format translation
|
||||
- **351 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
|
||||
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
## v3.8.x Highlights
|
||||
|
||||
- **350-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **351-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,7 +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 350 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 351 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
|
||||
|
||||
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
## Key Features (v3.8.50)
|
||||
|
||||
### Core Proxy
|
||||
- **350 AI providers** with automatic format translation
|
||||
- **351 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
|
||||
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
## v3.8.x Highlights
|
||||
|
||||
- **350-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **351-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,7 +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 350 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 351 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
|
||||
|
||||
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
## Key Features (v3.8.50)
|
||||
|
||||
### Core Proxy
|
||||
- **350 AI providers** with automatic format translation
|
||||
- **351 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
|
||||
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
## v3.8.x Highlights
|
||||
|
||||
- **350-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **351-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,7 +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 350 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 351 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
|
||||
|
||||
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
## Key Features (v3.8.50)
|
||||
|
||||
### Core Proxy
|
||||
- **350 AI providers** with automatic format translation
|
||||
- **351 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
|
||||
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
## v3.8.x Highlights
|
||||
|
||||
- **350-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **351-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,7 +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 350 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 351 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
|
||||
|
||||
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
## Key Features (v3.8.50)
|
||||
|
||||
### Core Proxy
|
||||
- **350 AI providers** with automatic format translation
|
||||
- **351 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
|
||||
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
## v3.8.x Highlights
|
||||
|
||||
- **350-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **351-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,7 +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 350 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 351 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
|
||||
|
||||
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
## Key Features (v3.8.50)
|
||||
|
||||
### Core Proxy
|
||||
- **350 AI providers** with automatic format translation
|
||||
- **351 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
|
||||
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
## v3.8.x Highlights
|
||||
|
||||
- **350-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **351-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,7 +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 350 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 351 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
|
||||
|
||||
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
## Key Features (v3.8.50)
|
||||
|
||||
### Core Proxy
|
||||
- **350 AI providers** with automatic format translation
|
||||
- **351 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
|
||||
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
## v3.8.x Highlights
|
||||
|
||||
- **350-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **351-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,7 +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 350 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 351 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
|
||||
|
||||
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
## Key Features (v3.8.50)
|
||||
|
||||
### Core Proxy
|
||||
- **350 AI providers** with automatic format translation
|
||||
- **351 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
|
||||
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
## v3.8.x Highlights
|
||||
|
||||
- **350-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **351-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,7 +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 350 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 351 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
|
||||
|
||||
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
## Key Features (v3.8.50)
|
||||
|
||||
### Core Proxy
|
||||
- **350 AI providers** with automatic format translation
|
||||
- **351 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
|
||||
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
## v3.8.x Highlights
|
||||
|
||||
- **350-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **351-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,7 +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 350 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 351 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
|
||||
|
||||
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
## Key Features (v3.8.50)
|
||||
|
||||
### Core Proxy
|
||||
- **350 AI providers** with automatic format translation
|
||||
- **351 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
|
||||
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
## v3.8.x Highlights
|
||||
|
||||
- **350-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **351-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,7 +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 350 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 351 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
|
||||
|
||||
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
## Key Features (v3.8.50)
|
||||
|
||||
### Core Proxy
|
||||
- **350 AI providers** with automatic format translation
|
||||
- **351 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
|
||||
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
## v3.8.x Highlights
|
||||
|
||||
- **350-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **351-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,7 +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 350 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 351 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
|
||||
|
||||
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
## Key Features (v3.8.50)
|
||||
|
||||
### Core Proxy
|
||||
- **350 AI providers** with automatic format translation
|
||||
- **351 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
|
||||
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
## v3.8.x Highlights
|
||||
|
||||
- **350-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **351-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,7 +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 350 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 351 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
|
||||
|
||||
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
## Key Features (v3.8.50)
|
||||
|
||||
### Core Proxy
|
||||
- **350 AI providers** with automatic format translation
|
||||
- **351 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
|
||||
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
## v3.8.x Highlights
|
||||
|
||||
- **350-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **351-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,7 +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 350 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 351 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
|
||||
|
||||
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
## Key Features (v3.8.50)
|
||||
|
||||
### Core Proxy
|
||||
- **350 AI providers** with automatic format translation
|
||||
- **351 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
|
||||
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
## v3.8.x Highlights
|
||||
|
||||
- **350-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **351-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,7 +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 350 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 351 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
|
||||
|
||||
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
## Key Features (v3.8.50)
|
||||
|
||||
### Core Proxy
|
||||
- **350 AI providers** with automatic format translation
|
||||
- **351 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
|
||||
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
## v3.8.x Highlights
|
||||
|
||||
- **350-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **351-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,7 +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 350 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 351 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
|
||||
|
||||
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
## Key Features (v3.8.50)
|
||||
|
||||
### Core Proxy
|
||||
- **350 AI providers** with automatic format translation
|
||||
- **351 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
|
||||
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
## v3.8.x Highlights
|
||||
|
||||
- **350-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **351-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,7 +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 350 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 351 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
|
||||
|
||||
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
## Key Features (v3.8.50)
|
||||
|
||||
### Core Proxy
|
||||
- **350 AI providers** with automatic format translation
|
||||
- **351 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
|
||||
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
## v3.8.x Highlights
|
||||
|
||||
- **350-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **351-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,7 +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 350 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 351 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
|
||||
|
||||
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
## Key Features (v3.8.50)
|
||||
|
||||
### Core Proxy
|
||||
- **350 AI providers** with automatic format translation
|
||||
- **351 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
|
||||
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
## v3.8.x Highlights
|
||||
|
||||
- **350-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **351-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)
|
||||
|
||||
@@ -43,6 +43,8 @@ tags:
|
||||
description: Content moderation
|
||||
- name: Rerank
|
||||
description: Document reranking
|
||||
- name: Search
|
||||
description: Unified web, news, and X search
|
||||
- name: Models
|
||||
description: Available model listing
|
||||
- name: Providers
|
||||
@@ -1153,6 +1155,199 @@ paths:
|
||||
$ref: "#/components/responses/Unauthorized"
|
||||
# ─── Proxy Endpoints ──────────────────────────────────────────
|
||||
|
||||
/api/v1/search:
|
||||
get:
|
||||
tags: [Search]
|
||||
summary: List search providers
|
||||
description: Lists configured search providers and their supported search types.
|
||||
responses:
|
||||
"200":
|
||||
description: Search provider catalog
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
required: [object, data]
|
||||
properties:
|
||||
object:
|
||||
type: string
|
||||
const: list
|
||||
data:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
required: [id, object, created, name, search_types]
|
||||
properties:
|
||||
id:
|
||||
type: string
|
||||
object:
|
||||
type: string
|
||||
const: search_provider
|
||||
created:
|
||||
type: integer
|
||||
name:
|
||||
type: string
|
||||
search_types:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
enum: [web, news, x]
|
||||
post:
|
||||
tags: [Search]
|
||||
summary: Run a unified search
|
||||
description: >-
|
||||
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.
|
||||
security:
|
||||
- BearerAuth: []
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
required: [query]
|
||||
properties:
|
||||
query:
|
||||
type: string
|
||||
minLength: 1
|
||||
maxLength: 500
|
||||
provider:
|
||||
type: string
|
||||
minLength: 1
|
||||
description: A search provider id or registered alias.
|
||||
examples: [xquik-search]
|
||||
max_results:
|
||||
type: integer
|
||||
minimum: 1
|
||||
maximum: 100
|
||||
default: 5
|
||||
search_type:
|
||||
type: string
|
||||
enum: [web, news, x]
|
||||
default: web
|
||||
offset:
|
||||
type: integer
|
||||
minimum: 0
|
||||
default: 0
|
||||
country:
|
||||
type: string
|
||||
maxLength: 2
|
||||
language:
|
||||
type: string
|
||||
minLength: 2
|
||||
maxLength: 5
|
||||
time_range:
|
||||
type: string
|
||||
enum: [any, hour, day, week, month, year]
|
||||
content:
|
||||
type: object
|
||||
properties:
|
||||
snippet: { type: boolean, default: true }
|
||||
full_page: { type: boolean, default: false }
|
||||
format: { type: string, enum: [text, markdown], default: text }
|
||||
max_characters: { type: integer, minimum: 100, maximum: 100000 }
|
||||
filters:
|
||||
type: object
|
||||
properties:
|
||||
include_domains:
|
||||
type: array
|
||||
maxItems: 20
|
||||
items: { type: string, maxLength: 253 }
|
||||
exclude_domains:
|
||||
type: array
|
||||
maxItems: 20
|
||||
items: { type: string, maxLength: 253 }
|
||||
safe_search: { type: string, enum: [off, moderate, strict] }
|
||||
provider_options:
|
||||
type: object
|
||||
additionalProperties: true
|
||||
strict_filters:
|
||||
type: boolean
|
||||
default: false
|
||||
additionalProperties: true
|
||||
responses:
|
||||
"200":
|
||||
description: Normalized search results
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
required: [id, provider, query, results, answer, usage, metrics, errors, cached]
|
||||
properties:
|
||||
id:
|
||||
type: string
|
||||
pattern: ^search-
|
||||
provider:
|
||||
type: string
|
||||
query:
|
||||
type: string
|
||||
cached:
|
||||
type: boolean
|
||||
results:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
required: [title, url, snippet, position, citation]
|
||||
properties:
|
||||
title: { type: string }
|
||||
url: { type: string, format: uri }
|
||||
display_url: { type: string }
|
||||
snippet: { type: string }
|
||||
position: { type: integer, minimum: 1 }
|
||||
score:
|
||||
type: [number, "null"]
|
||||
minimum: 0
|
||||
maximum: 1
|
||||
published_at: { type: [string, "null"] }
|
||||
favicon_url: { type: [string, "null"], format: uri }
|
||||
citation:
|
||||
type: object
|
||||
required: [provider, retrieved_at, rank]
|
||||
properties:
|
||||
provider: { type: string }
|
||||
retrieved_at: { type: string, format: date-time }
|
||||
rank: { type: integer, minimum: 1 }
|
||||
answer:
|
||||
type: [object, "null"]
|
||||
usage:
|
||||
type: object
|
||||
required: [queries_used, search_cost_usd]
|
||||
properties:
|
||||
queries_used: { type: integer, minimum: 0 }
|
||||
search_cost_usd: { type: number, minimum: 0 }
|
||||
llm_tokens: { type: integer, minimum: 0 }
|
||||
metrics:
|
||||
type: object
|
||||
required: [response_time_ms, upstream_latency_ms, total_results_available]
|
||||
properties:
|
||||
response_time_ms: { type: number, minimum: 0 }
|
||||
upstream_latency_ms: { type: number, minimum: 0 }
|
||||
gateway_latency_ms: { type: number, minimum: 0 }
|
||||
total_results_available: { type: [integer, "null"], minimum: 0 }
|
||||
errors:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
required: [provider, code, message]
|
||||
properties:
|
||||
provider: { type: string }
|
||||
code: { type: string }
|
||||
message: { type: string }
|
||||
"400":
|
||||
description: Invalid request, provider, credentials, or search type
|
||||
"401":
|
||||
$ref: "#/components/responses/Unauthorized"
|
||||
"403":
|
||||
description: Search provider blocked by API key or security policy
|
||||
"429":
|
||||
description: Every eligible provider credential is rate limited
|
||||
"500":
|
||||
$ref: "#/components/responses/InternalError"
|
||||
"502":
|
||||
description: Search provider failed
|
||||
|
||||
/api/v1/chat/completions:
|
||||
post:
|
||||
tags: [Chat]
|
||||
|
||||
23
docs/plans/7778-hierarchical-admission.md
Normal file
@@ -0,0 +1,23 @@
|
||||
# #7778 hierarchical admission cleanup plan
|
||||
|
||||
1. Lock the existing single-key semaphore contract and the new atomic multi-key
|
||||
contract with focused tests: no partial reservations, FIFO queueing, abort,
|
||||
timeout, queue-full, idempotent release, stats, and cleanup.
|
||||
2. Generalize the existing account semaphore in place. Keep `acquire()` as a
|
||||
compatibility wrapper around `acquireMany()`; do not add a second scheduler
|
||||
or a dependency.
|
||||
3. Replace the account-only acquisition in `chatCore` with one cumulative
|
||||
global/provider/account acquisition immediately before `withRateLimit`.
|
||||
Reacquire the whole set whenever account rotation changes the connection,
|
||||
and retain the release through streaming completion.
|
||||
4. Extend the existing resilience settings pipeline (types, defaults,
|
||||
normalization, schema, API response, UI, and translations) with the global
|
||||
and provider caps. Relabel the old Bottleneck concurrency control as
|
||||
connection/quota-scope concurrency so its real scope is explicit.
|
||||
5. Run focused tests, lint, typecheck, static checks, and the full test suite;
|
||||
document the behavioral change in the changelog.
|
||||
|
||||
Behavior intentionally preserved: zero/null concurrency bypasses a gate,
|
||||
account-only callers keep using `acquire()`, blocked-account controls retain
|
||||
their key format and API, and provider rate-limit queue behavior remains
|
||||
unchanged.
|
||||
@@ -1,16 +1,16 @@
|
||||
---
|
||||
title: "Provider Reference"
|
||||
version: 3.8.50
|
||||
lastUpdated: 2026-08-23
|
||||
lastUpdated: 2026-08-24
|
||||
---
|
||||
|
||||
# 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-23
|
||||
> **Last generated:** 2026-08-24
|
||||
|
||||
Total providers: **350**. See category breakdown below.
|
||||
Total providers: **351**. See category breakdown below.
|
||||
|
||||
## Categories
|
||||
|
||||
@@ -377,7 +377,7 @@ 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 (14)
|
||||
## Search Providers (15)
|
||||
|
||||
| ID | Alias | Name | Tags | Website | Notes |
|
||||
|----|-------|------|------|---------|-------|
|
||||
@@ -394,6 +394,7 @@ Use the dashboard at `/dashboard/providers` to enable, configure, and test each
|
||||
| `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-...) |
|
||||
| `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 |
|
||||
|
||||
## Audio-only Providers (12)
|
||||
@@ -438,7 +439,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/) (108 implementations)
|
||||
- Executors: [`open-sse/executors/`](../../open-sse/executors/) (109 implementations)
|
||||
- Translators: [`open-sse/translator/`](../../open-sse/translator/)
|
||||
|
||||
## See Also
|
||||
|
||||
@@ -45,6 +45,7 @@
|
||||
"copyright": "Copyright © 2025 OmniRoute",
|
||||
"buildDependenciesFromSource": false,
|
||||
"npmRebuild": false,
|
||||
"afterPack": "../scripts/build/afterpack-copy-node-modules.mjs",
|
||||
"directories": {
|
||||
"output": "dist-electron",
|
||||
"buildResources": "assets"
|
||||
@@ -58,6 +59,7 @@
|
||||
"main.js",
|
||||
"preload.js",
|
||||
"loginManager.js",
|
||||
"lib/loginHeaderCapture.js",
|
||||
"processTree.js",
|
||||
"sqlite-inspection.js",
|
||||
"remoteServerPromptPreload.js",
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import { fixupConfigRules } from "@eslint/compat";
|
||||
import nextVitals from "eslint-config-next/core-web-vitals";
|
||||
import * as espree from "espree";
|
||||
import tseslint from "typescript-eslint";
|
||||
|
||||
// #7879: bar NEW local `toNumber` definitions outside the canonical helper.
|
||||
@@ -37,7 +39,22 @@ const IMPORT_BOUNDARY_RESTRICTIONS = {
|
||||
|
||||
/** @type {import("eslint").Linter.Config[]} */
|
||||
const eslintConfig = [
|
||||
...nextVitals,
|
||||
...fixupConfigRules(nextVitals),
|
||||
// eslint-config-next's Babel parser has not adopted ESLint 10's ScopeManager
|
||||
// finalize contract yet. Plain JS/JSX does not need that parser, so use the
|
||||
// ESLint-native parser while retaining Next's plugins and rules.
|
||||
{
|
||||
files: ["**/*.{js,jsx,mjs,cjs}"],
|
||||
languageOptions: {
|
||||
parser: espree,
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ["**/*.{mts,cts}"],
|
||||
languageOptions: {
|
||||
parser: tseslint.parser,
|
||||
},
|
||||
},
|
||||
// Pacote 4 (plano mestre testes+CI, 2026-07-04) — zero-warning policy: TODA regra roda
|
||||
// como "error" e a dívida pré-existente vive congelada por arquivo+regra em
|
||||
// config/quality/eslint-suppressions.json (ESLint bulk suppressions nativo). Violação
|
||||
@@ -133,28 +150,45 @@ const eslintConfig = [
|
||||
"no-restricted-syntax": "off",
|
||||
},
|
||||
},
|
||||
// Relaxed rules for open-sse and tests (incremental adoption)
|
||||
// Relaxed rules for TypeScript in open-sse and tests (incremental adoption).
|
||||
// eslint-config-next already registers @typescript-eslint for every TS file;
|
||||
// registering a second plugin object is rejected by ESLint 10.
|
||||
{
|
||||
files: ["open-sse/**/*.ts", "tests/**/*.mjs", "tests/**/*.ts"],
|
||||
plugins: {
|
||||
"@typescript-eslint": tseslint.plugin,
|
||||
},
|
||||
files: ["open-sse/**/*.ts", "tests/**/*.ts"],
|
||||
rules: {
|
||||
"@typescript-eslint/no-explicit-any": "error",
|
||||
"@next/next/no-assign-module-variable": "off",
|
||||
"react-hooks/rules-of-hooks": "off",
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ["tests/**/*.mjs"],
|
||||
plugins: {
|
||||
"@typescript-eslint": tseslint.plugin,
|
||||
},
|
||||
rules: {
|
||||
"@next/next/no-assign-module-variable": "off",
|
||||
"react-hooks/rules-of-hooks": "off",
|
||||
},
|
||||
},
|
||||
// JS/JSX files do not match eslint-config-next's TypeScript block. Register
|
||||
// the plugin only for that disjoint scope so the shared unused-vars ratchet
|
||||
// works without redefining the plugin for TS/TSX under ESLint 10.
|
||||
{
|
||||
files: ["src/**/*.{js,jsx}"],
|
||||
plugins: {
|
||||
"@typescript-eslint": tseslint.plugin,
|
||||
},
|
||||
},
|
||||
// Ratchet: bar NEW unused vars/args/catches outside the `_` escape hatch.
|
||||
// Pre-existing violations are frozen via config/quality/eslint-suppressions.json
|
||||
// (same pattern as #7879 toNumber); only genuinely NEW unused bindings fail
|
||||
// lint. `args: "all"` (not `after-used`) so a leading unused param is never
|
||||
// silently skipped, e.g. `function handle(req, _opts, next)` must flag `req`.
|
||||
// eslint-config-next already registers @typescript-eslint; registering it again
|
||||
// in this block is rejected by ESLint 10.
|
||||
{
|
||||
files: ["src/**/*.{ts,tsx,js,jsx}", "open-sse/**/*.ts", "tests/**/*.{ts,tsx,mjs}"],
|
||||
plugins: {
|
||||
"@typescript-eslint": tseslint.plugin,
|
||||
},
|
||||
rules: {
|
||||
"@typescript-eslint/no-unused-vars": [
|
||||
"error",
|
||||
|
||||
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 350 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 351 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
|
||||
- **350 AI providers** with automatic format translation
|
||||
- **351 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
|
||||
|
||||
- **350-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **351-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)
|
||||
|
||||
@@ -408,6 +408,7 @@ export const EMBEDDING_PROVIDERS: Record<string, EmbeddingProvider> = {
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
const EMBEDDING_PROVIDER_ALIASES: Record<string, string> = {
|
||||
@@ -470,6 +471,38 @@ export function getEmbeddingProvider(providerId: string): EmbeddingProvider | nu
|
||||
return EMBEDDING_PROVIDERS[resolveEmbeddingProviderId(providerId)] || null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Derive an OpenAI-compatible embeddings config for a chat provider that has NO
|
||||
* curated EMBEDDING_PROVIDERS entry. Works for any registry provider whose base
|
||||
* URL ends in /chat/completions by swapping that suffix for /embeddings (groq,
|
||||
* mistral, together, upstage, fireworks, nvidia, vercel-ai-gateway, ...).
|
||||
* Dynamic-URL providers (no usable static base) derive to
|
||||
* null — they need bespoke URL handling, not a bogus endpoint.
|
||||
*
|
||||
* This is a FALLBACK only: callers must check getEmbeddingProvider() first so
|
||||
* curated entries keep their specialized configuration.
|
||||
*/
|
||||
export function deriveEmbeddingProviderForChatProvider(
|
||||
providerId: string,
|
||||
chatEntry: { id?: string; baseUrl?: string | string[] } | null | undefined
|
||||
): EmbeddingProvider | null {
|
||||
if (!chatEntry) return null;
|
||||
const rawBase = Array.isArray(chatEntry.baseUrl)
|
||||
? chatEntry.baseUrl[0]
|
||||
: chatEntry.baseUrl;
|
||||
if (!rawBase || typeof rawBase !== "string") return null;
|
||||
// stripTrailingSlashes-equivalent without importing open-sse utils here:
|
||||
const base = rawBase.replace(/\/+$/, "");
|
||||
if (!base.endsWith("/chat/completions")) return null;
|
||||
return {
|
||||
id: providerId,
|
||||
baseUrl: `${base.slice(0, -"/chat/completions".length)}/embeddings`,
|
||||
authType: "apikey",
|
||||
authHeader: "bearer",
|
||||
models: [],
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse embedding model string (format: "provider/model" or just "model")
|
||||
* Returns { provider, model }
|
||||
@@ -485,6 +518,18 @@ export function parseEmbeddingModel(
|
||||
const slashIdx = modelStr.indexOf("/");
|
||||
if (slashIdx > 0) {
|
||||
const rawProvider = modelStr.slice(0, slashIdx);
|
||||
|
||||
// A configured provider_node whose prefix exactly equals the requested
|
||||
// provider segment always wins — even when that segment is also an alias
|
||||
// of a curated provider (a local node must not be hijacked by a registry
|
||||
// alias). Same exact-match precedence documented for
|
||||
// EMBEDDING_MODEL_ALIASES above.
|
||||
const dynamicExact =
|
||||
dynamicProviders && dynamicProviders.find((dp) => dp.id === rawProvider);
|
||||
if (dynamicExact) {
|
||||
return { provider: rawProvider, model: modelStr.slice(slashIdx + 1) };
|
||||
}
|
||||
|
||||
const resolvedProvider = resolveEmbeddingProviderId(rawProvider);
|
||||
|
||||
if (EMBEDDING_PROVIDERS[resolvedProvider]) {
|
||||
|
||||
@@ -69,6 +69,31 @@ export interface FreeModelTotals {
|
||||
|
||||
const RECURRING = new Set<FreeModelFreeType>(["recurring-daily", "recurring-monthly", "keyless"]);
|
||||
|
||||
/**
|
||||
* What each free-tier regime engages for "can I route here without paying?".
|
||||
* Exhaustive by construction: adding a member to `FreeModelFreeType` will not
|
||||
* compile until it is classified here. `discontinued` is the one regime a
|
||||
* provider uses to retire a free tier behind a paid key — it does NOT grant
|
||||
* free access, and the shared predicate (`isFreeModel`) must read this instead
|
||||
* of treating every catalogued id as free. `RECURRING` (above) answers a
|
||||
* different question (which regimes feed the headline token totals) and is left
|
||||
* independent on purpose — deriving it from this table would silently change
|
||||
* the homepage totals.
|
||||
*/
|
||||
const FREE_REGIME_TRAITS = {
|
||||
"recurring-daily": { grantsFreeAccess: true },
|
||||
"recurring-monthly": { grantsFreeAccess: true },
|
||||
"recurring-credit": { grantsFreeAccess: true },
|
||||
"recurring-uncapped": { grantsFreeAccess: true },
|
||||
"one-time-initial": { grantsFreeAccess: true },
|
||||
keyless: { grantsFreeAccess: true },
|
||||
discontinued: { grantsFreeAccess: false },
|
||||
} satisfies Record<FreeModelFreeType, { grantsFreeAccess: boolean }>;
|
||||
|
||||
export function grantsFreeAccess(freeType: FreeModelFreeType): boolean {
|
||||
return FREE_REGIME_TRAITS[freeType].grantsFreeAccess;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deposit-unlock boosts: a one-time small top-up that permanently raises a
|
||||
* provider's recurring free quota. Kept OUT of the steady headline and surfaced
|
||||
|
||||
@@ -12,5 +12,15 @@
|
||||
export const OPENCODE_ZEN_GO_SHARED_MODELS = Object.freeze([
|
||||
{ id: "kimi-k2.7-code", name: "Kimi K2.7 Code" },
|
||||
{ id: "qwen3.5-plus", name: "Qwen3.5 Plus", targetFormat: "claude", supportsVision: false },
|
||||
{ id: "qwen3.6-plus", name: "Qwen3.6 Plus", targetFormat: "claude", supportsVision: false },
|
||||
{
|
||||
id: "qwen3.6-plus",
|
||||
name: "Qwen3.6 Plus",
|
||||
targetFormat: "claude",
|
||||
supportsVision: false,
|
||||
// #10788: effort-tier aliases exist as explicit registry rows; declare the
|
||||
// vocabulary on the shared base row so variant synthesis and the sanitizer
|
||||
// agree on it for both opencode-go and opencode-zen.
|
||||
supportsReasoning: true,
|
||||
supportedThinkingEfforts: ["high", "max"],
|
||||
},
|
||||
]);
|
||||
|
||||
@@ -18,7 +18,17 @@ export const nvidiaProvider: RegistryEntry = {
|
||||
passthroughModels: true,
|
||||
models: [
|
||||
// #6108: z-ai/glm-5.1 EOL'd 2026-07-02 (direct probe returns 410) — dropped.
|
||||
{ id: "z-ai/glm-5.2", name: "GLM 5.2" },
|
||||
// #10788: NVIDIA's hosted GLM-5.2 exposes a BINARY thinking switch
|
||||
// (chat_template_kwargs.enable_thinking), not effort tiers — see
|
||||
// mapNvidiaGlm52ReasoningParams. Declaring an empty tier list keeps the
|
||||
// catalog from synthesizing unresolvable -low/-high/-max variant ids while
|
||||
// still marking the model reasoning-capable.
|
||||
{
|
||||
id: "z-ai/glm-5.2",
|
||||
name: "GLM 5.2",
|
||||
supportsReasoning: true,
|
||||
supportedThinkingEfforts: [],
|
||||
},
|
||||
// #3329/#6108: minimaxai/minimax-m3 stays excluded from the nvidia tier — it
|
||||
// still 404s here for most callers; the single 200 probe in #6108 was not
|
||||
// reproducible enough to override the #3329 guard. Re-add only once NVIDIA
|
||||
|
||||
@@ -19,9 +19,15 @@ export const opencode_goProvider: RegistryEntry = {
|
||||
// `kimi-k2.7-code` (the live API rejects the plain `kimi-k2.7` alias for
|
||||
// `/chat/completions`, even though the docs config example uses it).
|
||||
// GLM-5.2 — base model + effort-tier aliases (#6922).
|
||||
// OpencodeExecutor rewrites the alias to the canonical id and injects
|
||||
// reasoning_effort, mirroring the deepseek-v4-pro-* pattern.
|
||||
{ id: "glm-5.2", name: "GLM-5.2", supportsReasoning: true },
|
||||
// #10788: the tier vocabulary is declared on the base row so the catalog's
|
||||
// variant synthesis (#9485) and the effort sanitizer share one source of
|
||||
// truth with OpencodeExecutor's EFFORT_TIERS.
|
||||
{
|
||||
id: "glm-5.2",
|
||||
name: "GLM-5.2",
|
||||
supportsReasoning: true,
|
||||
supportedThinkingEfforts: ["high", "max"],
|
||||
},
|
||||
{ id: "glm-5.2-high", name: "GLM-5.2 (high effort)", supportsReasoning: true },
|
||||
{ id: "glm-5.2-max", name: "GLM-5.2 (max effort)", supportsReasoning: true },
|
||||
|
||||
@@ -34,11 +40,16 @@ export const opencode_goProvider: RegistryEntry = {
|
||||
{ id: "kimi-k2.6", name: "Kimi K2.6" },
|
||||
{ id: "kimi-k2.5", name: "Kimi K2.5" },
|
||||
// #8353: Kimi K3 base + max-effort alias from the OpenCode Go registry.
|
||||
{ id: "kimi-k3", name: "Kimi K3", supportsReasoning: true },
|
||||
{ id: "kimi-k3", name: "Kimi K3", supportsReasoning: true, supportedThinkingEfforts: ["max"] },
|
||||
{ id: "kimi-k3-max", name: "Kimi K3 (max effort)", supportsReasoning: true },
|
||||
// MiMo-V2.5 — base model + effort-tier aliases (#6922).
|
||||
{ id: "mimo-v2.5-pro", name: "MiMo-V2.5-Pro", supportsReasoning: true },
|
||||
{ id: "mimo-v2.5", name: "MiMo-V2.5", supportsReasoning: true },
|
||||
{
|
||||
id: "mimo-v2.5",
|
||||
name: "MiMo-V2.5",
|
||||
supportsReasoning: true,
|
||||
supportedThinkingEfforts: ["high", "max"],
|
||||
},
|
||||
{ id: "mimo-v2.5-high", name: "MiMo-V2.5 (high effort)", supportsReasoning: true },
|
||||
{ id: "mimo-v2.5-max", name: "MiMo-V2.5 (max effort)", supportsReasoning: true },
|
||||
// #3110: MiniMax M3 via OpenCode Go tier
|
||||
@@ -59,7 +70,14 @@ export const opencode_goProvider: RegistryEntry = {
|
||||
// so combo routing skips them when the request contains image blocks,
|
||||
// preventing image content from reaching a vision-incapable upstream.
|
||||
// #8353: effort-tier aliases from the OpenCode Go registry.
|
||||
{ id: "qwen3.7-max", name: "Qwen3.7 Max", targetFormat: "claude", supportsVision: false },
|
||||
{
|
||||
id: "qwen3.7-max",
|
||||
name: "Qwen3.7 Max",
|
||||
targetFormat: "claude",
|
||||
supportsVision: false,
|
||||
supportsReasoning: true,
|
||||
supportedThinkingEfforts: ["high", "max"],
|
||||
},
|
||||
{
|
||||
id: "qwen3.7-max-high",
|
||||
name: "Qwen3.7 Max (high effort)",
|
||||
@@ -79,6 +97,8 @@ export const opencode_goProvider: RegistryEntry = {
|
||||
name: "Qwen3.7 Plus",
|
||||
targetFormat: "claude",
|
||||
supportsVision: false,
|
||||
supportsReasoning: true,
|
||||
supportedThinkingEfforts: ["high", "max"],
|
||||
},
|
||||
{
|
||||
id: "qwen3.7-plus-high",
|
||||
@@ -111,7 +131,13 @@ export const opencode_goProvider: RegistryEntry = {
|
||||
supportsReasoning: true,
|
||||
},
|
||||
// #8353: hy3 is the Go-tier base id (distinct from hy3-preview / hy3-free).
|
||||
{ id: "hy3", name: "Hunyuan3", contextLength: 256000, supportsReasoning: true },
|
||||
{
|
||||
id: "hy3",
|
||||
name: "Hunyuan3",
|
||||
contextLength: 256000,
|
||||
supportsReasoning: true,
|
||||
supportedThinkingEfforts: ["none", "low", "high"],
|
||||
},
|
||||
{
|
||||
id: "hy3-none",
|
||||
name: "Hunyuan3 (none effort)",
|
||||
@@ -201,7 +227,12 @@ export const opencode_goProvider: RegistryEntry = {
|
||||
targetFormat: "openai-responses",
|
||||
},
|
||||
// #8353: Grok 4.5 + effort tiers from the OpenCode Go registry.
|
||||
{ id: "grok-4.5", name: "Grok 4.5", supportsReasoning: true },
|
||||
{
|
||||
id: "grok-4.5",
|
||||
name: "Grok 4.5",
|
||||
supportsReasoning: true,
|
||||
supportedThinkingEfforts: ["low", "medium", "high"],
|
||||
},
|
||||
{ id: "grok-4.5-low", name: "Grok 4.5 (low effort)", supportsReasoning: true },
|
||||
{ id: "grok-4.5-medium", name: "Grok 4.5 (medium effort)", supportsReasoning: true },
|
||||
{ id: "grok-4.5-high", name: "Grok 4.5 (high effort)", supportsReasoning: true },
|
||||
|
||||
@@ -79,18 +79,35 @@ export const opencode_zenProvider: RegistryEntry = {
|
||||
},
|
||||
|
||||
// ── DeepSeek ────────────────────────────────────────────────
|
||||
{ id: "deepseek-v4-pro", name: "DeepSeek V4 Pro" },
|
||||
{ id: "deepseek-v4-flash", name: "DeepSeek V4 Flash" },
|
||||
// #10788: same tier vocabulary as opencode-go's DeepSeek rows — the Zen
|
||||
// upstream accepts the identical effort set on these models.
|
||||
{
|
||||
id: "deepseek-v4-pro",
|
||||
name: "DeepSeek V4 Pro",
|
||||
supportsReasoning: true,
|
||||
supportedThinkingEfforts: ["none", "low", "high", "max"],
|
||||
},
|
||||
{
|
||||
id: "deepseek-v4-flash",
|
||||
name: "DeepSeek V4 Flash",
|
||||
supportsReasoning: true,
|
||||
supportedThinkingEfforts: ["none", "low", "high", "max"],
|
||||
},
|
||||
|
||||
// ── GLM / Z.AI ─────────────────────────────────────────────
|
||||
{ id: "glm-5.2", name: "GLM-5.2" },
|
||||
{
|
||||
id: "glm-5.2",
|
||||
name: "GLM-5.2",
|
||||
supportsReasoning: true,
|
||||
supportedThinkingEfforts: ["high", "max"],
|
||||
},
|
||||
|
||||
// ── MiniMax ────────────────────────────────────────────────
|
||||
// #3110: MiniMax M3 — frontier coding model with 1M context
|
||||
{ id: "minimax-m3", name: "MiniMax M3", contextLength: 1048576, supportsVision: true },
|
||||
|
||||
// ── Kimi / Moonshot ────────────────────────────────────────
|
||||
{ id: "kimi-k3", name: "Kimi K3" },
|
||||
{ id: "kimi-k3", name: "Kimi K3", supportsReasoning: true, supportedThinkingEfforts: ["max"] },
|
||||
// kimi-k2.7-code declared identically on opencode-go — see OPENCODE_ZEN_GO_SHARED_MODELS.
|
||||
|
||||
// ── Qwen ───────────────────────────────────────────────────
|
||||
|
||||
@@ -218,3 +218,29 @@ export function getAllRerankModels() {
|
||||
}
|
||||
return models;
|
||||
}
|
||||
|
||||
/**
|
||||
* Derive a Cohere-compatible rerank config for a chat provider that has NO
|
||||
* curated RERANK_PROVIDERS entry. Works for any registry provider whose base
|
||||
* URL ends in /chat/completions by swapping that suffix for /rerank (groq,
|
||||
* mistral, vercel-ai-gateway, ...). Dynamic-URL providers (no usable static
|
||||
* base, e.g. dynamic account-scoped hosts) derive to null — they need bespoke
|
||||
* URL handling.
|
||||
*
|
||||
* This is a FALLBACK only: callers must check getRerankProvider() first so
|
||||
* curated entries keep their specialized configuration and format adapters.
|
||||
*/
|
||||
export function deriveRerankProviderForChatProvider(providerId, chatEntry) {
|
||||
if (!chatEntry) return null;
|
||||
const rawBase = Array.isArray(chatEntry.baseUrl) ? chatEntry.baseUrl[0] : chatEntry.baseUrl;
|
||||
if (!rawBase || typeof rawBase !== "string") return null;
|
||||
const base = rawBase.replace(/\/+$/, "");
|
||||
if (!base.endsWith("/chat/completions")) return null;
|
||||
return {
|
||||
id: providerId,
|
||||
baseUrl: `${base.slice(0, -"/chat/completions".length)}/rerank`,
|
||||
authType: "apikey",
|
||||
authHeader: "bearer",
|
||||
models: [],
|
||||
};
|
||||
}
|
||||
|
||||
@@ -330,6 +330,25 @@ export const SEARCH_PROVIDERS: Record<string, SearchProviderConfig> = {
|
||||
timeoutMs: 60_000,
|
||||
cacheTTLMs: 5 * 60 * 1000,
|
||||
},
|
||||
|
||||
// Direct X API search through Xquik. Keep it fallback-only so the existing
|
||||
// SuperGrok provider remains the default for search_type "x".
|
||||
"xquik-search": {
|
||||
id: "xquik-search",
|
||||
name: "Xquik X Search",
|
||||
baseUrl: "https://xquik.com/api/v1/x/tweets/search",
|
||||
method: "GET",
|
||||
authType: "apikey",
|
||||
authHeader: "x-api-key",
|
||||
costPerQuery: 0.00075,
|
||||
freeMonthlyQuota: 0,
|
||||
searchTypes: ["x"],
|
||||
defaultMaxResults: 5,
|
||||
maxMaxResults: 20,
|
||||
timeoutMs: 15_000,
|
||||
cacheTTLMs: 5 * 60 * 1000,
|
||||
fallbackOnly: true,
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -377,6 +396,8 @@ export const SEARCH_PROVIDER_ALIASES: Record<string, string> = {
|
||||
c7: "context7",
|
||||
x_search: "x-search",
|
||||
x: "x-search",
|
||||
xquik: "xquik-search",
|
||||
xquik_search: "xquik-search",
|
||||
};
|
||||
|
||||
export function resolveSearchProviderId(providerId: string): string {
|
||||
|
||||
@@ -1,246 +1,218 @@
|
||||
import { SEARCH_PROVIDERS } from "../config/searchRegistry.ts";
|
||||
import { registerExecutor, getRegisteredExecutor, hasRegisteredExecutor } from "./registry.ts";
|
||||
import {
|
||||
registerLazyExecutor,
|
||||
loadRegisteredExecutor,
|
||||
hasRegisteredExecutor,
|
||||
} from "./registry.ts";
|
||||
// Type-only: pulls no runtime code, keeps DefaultExecutor the only eager class.
|
||||
import type { BaseExecutor } from "./base.ts";
|
||||
import { AntigravityExecutor } from "./antigravity.ts";
|
||||
import { GithubExecutor } from "./github.ts";
|
||||
import { GheCopilotExecutor } from "./ghe-copilot.ts";
|
||||
import { QoderExecutor } from "./qoder.ts";
|
||||
import { KiroExecutor } from "./kiro.ts";
|
||||
import { CodexExecutor } from "./codex.ts";
|
||||
import { CodexAppServerExecutor } from "./codex-app-server.ts";
|
||||
import { CursorExecutor } from "./cursor.ts";
|
||||
import { TraeExecutor } from "./trae.ts";
|
||||
import { DefaultExecutor } from "./default.ts";
|
||||
import { BedrockExecutor } from "./bedrock.ts";
|
||||
import { GlmExecutor } from "./glm.ts";
|
||||
import { PollinationsExecutor } from "./pollinations.ts";
|
||||
import { CloudflareAIExecutor } from "./cloudflare-ai.ts";
|
||||
import { FreebuffExecutor } from "./freebuff.ts";
|
||||
import { OpencodeExecutor } from "./opencode.ts";
|
||||
import { VertexExecutor } from "./vertex.ts";
|
||||
import { CliproxyapiExecutor } from "./cliproxyapi.ts";
|
||||
import { DarioExecutor } from "./dario.ts";
|
||||
import { NineRouterExecutor } from "./ninerouter.ts";
|
||||
import { PerplexityWebExecutor } from "./perplexity-web.ts";
|
||||
import { GrokWebExecutor } from "./grok-web.ts";
|
||||
import { GeminiWebExecutor } from "./gemini-web.ts";
|
||||
import { TencentAIStudioWebExecutor } from "./tencent-aistudio-web.ts";
|
||||
import { GeminiBusinessExecutor } from "./gemini-business.ts";
|
||||
import { ChatGptWebExecutor } from "./chatgpt-web.ts";
|
||||
import { ChatGptWebCodexExecutor } from "./chatgpt-web-codex.ts";
|
||||
import { BlackboxWebExecutor } from "./blackbox-web.ts";
|
||||
import { MuseSparkWebExecutor } from "./muse-spark-web.ts";
|
||||
import { AzureOpenAIExecutor } from "./azure-openai.ts";
|
||||
import { AzureAiExecutor } from "./azure-ai.ts";
|
||||
import { CommandCodeExecutor } from "./commandCode.ts";
|
||||
import { GitlabExecutor } from "./gitlab.ts";
|
||||
import { NlpCloudExecutor } from "./nlpcloud.ts";
|
||||
import { DevinDesktopExecutor } from "./devin-desktop.ts";
|
||||
import { ZedHostedExecutor } from "./zed-hosted.ts";
|
||||
import { DevinCliExecutor } from "./devin-cli.ts";
|
||||
import { ZcodeExecutor } from "./zcode.ts";
|
||||
import { DevinCliAgenticExecutor } from "./devin-cli-agentic.ts";
|
||||
import { AuggieExecutor } from "./auggie.ts";
|
||||
import { DeepSeekWebExecutor } from "./deepseek-web.ts";
|
||||
import { DeepSeekWebWithAutoRefreshExecutor } from "./deepseek-web-with-auto-refresh.ts";
|
||||
import { AdaptaWebExecutor } from "./adapta-web.ts";
|
||||
import { CopilotWebExecutor } from "./copilot-web.ts";
|
||||
import { CopilotM365WebExecutor } from "./copilot-m365-web.ts";
|
||||
import { MicrosoftDesignerWebExecutor } from "./microsoft-designer-web.ts";
|
||||
import { AdobeFireflyExecutor } from "./adobe-firefly.ts";
|
||||
import { VeoAIFreeWebExecutor } from "./veoaifree-web.ts";
|
||||
import { DuckDuckGoWebExecutor } from "./duckduckgo-web.ts";
|
||||
import { FeloWebExecutor } from "./felo-web.ts";
|
||||
import { T3ChatWebExecutor } from "./t3-chat-web.ts";
|
||||
import { ClaudeWebExecutor } from "./claude-web.ts";
|
||||
import { InnerAiExecutor } from "./inner-ai.ts";
|
||||
import { HuggingChatExecutor } from "./huggingchat.ts";
|
||||
import { YuanbaoWebExecutor } from "./yuanbao-web.ts";
|
||||
import { PoeWebExecutor } from "./poe-web.ts";
|
||||
import { VeniceWebExecutor } from "./venice-web.ts";
|
||||
import { NotionWebExecutor } from "./notion-web.ts";
|
||||
import { V0VercelWebExecutor } from "./v0-vercel-web.ts";
|
||||
import { CheaperInferenceExecutor } from "./cheaperinference.ts";
|
||||
import { KimiWebExecutor } from "./kimi-web.ts";
|
||||
import { DoubaoWebExecutor } from "./doubao-web.ts";
|
||||
import { QwenWebExecutor } from "./qwen-web.ts";
|
||||
import { RaycastExecutor } from "./raycast.ts";
|
||||
import { HailuoWebExecutor } from "./hailuo-web.ts";
|
||||
import { ZaiWebExecutor } from "./zai-web.ts";
|
||||
import { KimiExecutor } from "./kimi.ts";
|
||||
import { MoonshotExecutor } from "./moonshot.ts";
|
||||
import { TheOldLlmExecutor } from "./theoldllm.ts";
|
||||
import { ChipotleExecutor } from "./chipotle.ts";
|
||||
import { LMArenaExecutor } from "./lmarena.ts";
|
||||
import { GrokCliExecutor } from "./grok-cli.ts";
|
||||
import { CodeBuddyCnExecutor } from "./codebuddy-cn.ts";
|
||||
import { ZenmuxFreeExecutor } from "./zenmux-free.ts";
|
||||
import { CloudflarePlaygroundExecutor } from "./cloudflare-playground.ts";
|
||||
import { TinyCmsExecutor } from "./tinycms.ts";
|
||||
import { HyperAgentExecutor } from "./hyperagent.ts";
|
||||
import { XaiExecutor } from "./xai.ts";
|
||||
import { PromptQlExecutor } from "./promptql.ts";
|
||||
import { ConolWebExecutor } from "./conol-web.ts";
|
||||
|
||||
// R0.3 — declarative built-in table. The object literal stays as the single
|
||||
// place built-ins are declared (compile-time duplicate-key safety; the
|
||||
// check:known-symbols gate parses this literal from source), but lookup goes
|
||||
// through the ExecutorRegistry (./registry.ts): every entry is registered at
|
||||
// module load below, and getExecutor()/hasSpecializedExecutor() consult the
|
||||
// registry — the literal is never read at request time.
|
||||
const executors = {
|
||||
antigravity: new AntigravityExecutor(),
|
||||
agy: new AntigravityExecutor(),
|
||||
github: new GithubExecutor(),
|
||||
"ghe-copilot": new GheCopilotExecutor(),
|
||||
qoder: new QoderExecutor(),
|
||||
kiro: new KiroExecutor(),
|
||||
"amazon-q": new KiroExecutor("amazon-q"),
|
||||
bedrock: new BedrockExecutor(),
|
||||
codex: new CodexExecutor(),
|
||||
"codex-app-server": new CodexAppServerExecutor({}, "codex-app-server"),
|
||||
"chatgpt-web-codex": new ChatGptWebCodexExecutor(),
|
||||
"cgpt-codex": new ChatGptWebCodexExecutor(),
|
||||
cursor: new CursorExecutor(),
|
||||
trae: new TraeExecutor(),
|
||||
glm: new GlmExecutor("glm"),
|
||||
"glm-cn": new GlmExecutor("glm-cn"),
|
||||
glmt: new GlmExecutor("glmt"),
|
||||
cu: new CursorExecutor(), // Alias for cursor
|
||||
"cursor-api": new CursorExecutor("cursor-api"),
|
||||
cua: new CursorExecutor("cursor-api"),
|
||||
"azure-openai": new AzureOpenAIExecutor(),
|
||||
"azure-ai": new AzureAiExecutor(),
|
||||
"command-code": new CommandCodeExecutor(),
|
||||
cmd: new CommandCodeExecutor(), // Alias
|
||||
gitlab: new GitlabExecutor(),
|
||||
"gitlab-duo": new GitlabExecutor("gitlab-duo"),
|
||||
nlpcloud: new NlpCloudExecutor(),
|
||||
pollinations: new PollinationsExecutor(),
|
||||
pol: new PollinationsExecutor(), // Alias
|
||||
"cloudflare-ai": new CloudflareAIExecutor(),
|
||||
cf: new CloudflareAIExecutor(), // Alias
|
||||
freebuff: new FreebuffExecutor(),
|
||||
fb: new FreebuffExecutor(), // Alias
|
||||
"opencode-zen": new OpencodeExecutor("opencode-zen"),
|
||||
"opencode-go": new OpencodeExecutor("opencode-go"),
|
||||
opencode: new OpencodeExecutor("opencode-zen"), // Alias for opencode-zen
|
||||
vertex: new VertexExecutor(),
|
||||
"vertex-partner": new VertexExecutor(),
|
||||
cliproxyapi: new CliproxyapiExecutor(),
|
||||
cpa: new CliproxyapiExecutor(), // Alias
|
||||
dario: new DarioExecutor(),
|
||||
dr: new DarioExecutor(), // Alias
|
||||
"9router": new NineRouterExecutor(),
|
||||
nr: new NineRouterExecutor(), // Alias
|
||||
"perplexity-web": new PerplexityWebExecutor(),
|
||||
"pplx-web": new PerplexityWebExecutor(), // Alias
|
||||
"grok-web": new GrokWebExecutor(),
|
||||
"claude-web": new ClaudeWebExecutor(),
|
||||
"cw-web": new ClaudeWebExecutor(), // Alias
|
||||
"gemini-web": new GeminiWebExecutor(),
|
||||
gweb: new GeminiWebExecutor(), // Alias
|
||||
"gemini-business": new GeminiBusinessExecutor(),
|
||||
gembiz: new GeminiBusinessExecutor(), // Alias
|
||||
"chatgpt-web": new ChatGptWebExecutor(),
|
||||
"cgpt-web": new ChatGptWebExecutor(), // Alias
|
||||
"blackbox-web": new BlackboxWebExecutor(),
|
||||
"bb-web": new BlackboxWebExecutor(), // Alias
|
||||
"muse-spark-web": new MuseSparkWebExecutor(),
|
||||
"ms-web": new MuseSparkWebExecutor(), // Alias
|
||||
"devin-desktop": new DevinDesktopExecutor(),
|
||||
"zed-hosted": new ZedHostedExecutor(),
|
||||
"devin-cli": new DevinCliExecutor(),
|
||||
zcode: new ZcodeExecutor(),
|
||||
zc: new ZcodeExecutor(), // Alias
|
||||
"devin-cli-agentic": new DevinCliAgenticExecutor(),
|
||||
devin: new DevinCliExecutor(), // Alias
|
||||
"deepseek-web": new DeepSeekWebWithAutoRefreshExecutor(),
|
||||
"ds-web": new DeepSeekWebWithAutoRefreshExecutor(), // Alias
|
||||
"adapta-web": new AdaptaWebExecutor(),
|
||||
"adp-web": new AdaptaWebExecutor(), // Alias
|
||||
"copilot-web": new CopilotWebExecutor(),
|
||||
"copilot-m365-web": new CopilotM365WebExecutor(),
|
||||
copilot: new CopilotWebExecutor(), // Alias
|
||||
"microsoft-designer-web": new MicrosoftDesignerWebExecutor(),
|
||||
msdesigner: new MicrosoftDesignerWebExecutor(), // Alias
|
||||
"adobe-firefly": new AdobeFireflyExecutor(),
|
||||
firefly: new AdobeFireflyExecutor(), // Alias
|
||||
"veoaifree-web": new VeoAIFreeWebExecutor(),
|
||||
"veo-free": new VeoAIFreeWebExecutor(), // Alias
|
||||
"duckduckgo-web": new DuckDuckGoWebExecutor(),
|
||||
ddgw: new DuckDuckGoWebExecutor(), // Alias
|
||||
"felo-web": new FeloWebExecutor(),
|
||||
felo: new FeloWebExecutor(), // Alias
|
||||
"t3-web": new T3ChatWebExecutor(),
|
||||
t3chat: new T3ChatWebExecutor(), // Alias
|
||||
"inner-ai": new InnerAiExecutor(),
|
||||
"in-ai": new InnerAiExecutor(), // Alias
|
||||
huggingchat: new HuggingChatExecutor(),
|
||||
hc: new HuggingChatExecutor(), // Alias
|
||||
"yuanbao-web": new YuanbaoWebExecutor(),
|
||||
"tencent-aistudio-web": new TencentAIStudioWebExecutor(),
|
||||
tasw: new TencentAIStudioWebExecutor(),
|
||||
ybw: new YuanbaoWebExecutor(), // Alias
|
||||
"poe-web": new PoeWebExecutor(),
|
||||
// R0.3 — declarative built-in table, made LAZY by #11220.
|
||||
//
|
||||
// The object literal below stays as the single place built-ins are declared
|
||||
// (compile-time duplicate-key safety; the check:known-symbols gate parses this
|
||||
// literal from source), but its values are now deferred loaders instead of
|
||||
// eagerly-constructed instances. At module load every ALIAS is registered in
|
||||
// declaration order; the class import + construction happen on first use via
|
||||
// loadRegisteredExecutor() and are cached in the same registry a static
|
||||
// registration would have populated.
|
||||
//
|
||||
// Why: importing this barrel previously pulled all ~100 executor modules and
|
||||
// constructed every instance at boot — ~0.7–1.2s and ~35MB of heap measured on
|
||||
// top of the minimal set — even for deployments that use a handful of
|
||||
// providers. Bundlers split the dynamic imports into on-demand chunks.
|
||||
//
|
||||
// Contract preserved (pinned by tests/unit/executor-map-golden.test.ts):
|
||||
// - keys and their ORDER are byte-stable
|
||||
// - each alias still gets its OWN instance (aliases never share)
|
||||
// - ctor arguments are unchanged
|
||||
const lazyExecutors: Record<string, () => Promise<BaseExecutor>> = {
|
||||
antigravity: () => import("./antigravity.ts").then((m) => new m.AntigravityExecutor()),
|
||||
agy: () => import("./antigravity.ts").then((m) => new m.AntigravityExecutor()),
|
||||
github: () => import("./github.ts").then((m) => new m.GithubExecutor()),
|
||||
"ghe-copilot": () => import("./ghe-copilot.ts").then((m) => new m.GheCopilotExecutor()),
|
||||
qoder: () => import("./qoder.ts").then((m) => new m.QoderExecutor()),
|
||||
kiro: () => import("./kiro.ts").then((m) => new m.KiroExecutor()),
|
||||
"amazon-q": () => import("./kiro.ts").then((m) => new m.KiroExecutor("amazon-q")),
|
||||
bedrock: () => import("./bedrock.ts").then((m) => new m.BedrockExecutor()),
|
||||
codex: () => import("./codex.ts").then((m) => new m.CodexExecutor()),
|
||||
"codex-app-server": () =>
|
||||
import("./codex-app-server.ts").then(
|
||||
(m) => new m.CodexAppServerExecutor({}, "codex-app-server")
|
||||
),
|
||||
"chatgpt-web-codex": () =>
|
||||
import("./chatgpt-web-codex.ts").then((m) => new m.ChatGptWebCodexExecutor()),
|
||||
"cgpt-codex": () =>
|
||||
import("./chatgpt-web-codex.ts").then((m) => new m.ChatGptWebCodexExecutor()),
|
||||
cursor: () => import("./cursor.ts").then((m) => new m.CursorExecutor()),
|
||||
trae: () => import("./trae.ts").then((m) => new m.TraeExecutor()),
|
||||
glm: () => import("./glm.ts").then((m) => new m.GlmExecutor("glm")),
|
||||
"glm-cn": () => import("./glm.ts").then((m) => new m.GlmExecutor("glm-cn")),
|
||||
glmt: () => import("./glm.ts").then((m) => new m.GlmExecutor("glmt")),
|
||||
cu: () => import("./cursor.ts").then((m) => new m.CursorExecutor()), // Alias for cursor
|
||||
"cursor-api": () => import("./cursor.ts").then((m) => new m.CursorExecutor("cursor-api")),
|
||||
cua: () => import("./cursor.ts").then((m) => new m.CursorExecutor("cursor-api")),
|
||||
"azure-openai": () => import("./azure-openai.ts").then((m) => new m.AzureOpenAIExecutor()),
|
||||
"azure-ai": () => import("./azure-ai.ts").then((m) => new m.AzureAiExecutor()),
|
||||
"command-code": () => import("./commandCode.ts").then((m) => new m.CommandCodeExecutor()),
|
||||
cmd: () => import("./commandCode.ts").then((m) => new m.CommandCodeExecutor()), // Alias
|
||||
gitlab: () => import("./gitlab.ts").then((m) => new m.GitlabExecutor()),
|
||||
"gitlab-duo": () => import("./gitlab.ts").then((m) => new m.GitlabExecutor("gitlab-duo")),
|
||||
nlpcloud: () => import("./nlpcloud.ts").then((m) => new m.NlpCloudExecutor()),
|
||||
pollinations: () => import("./pollinations.ts").then((m) => new m.PollinationsExecutor()),
|
||||
pol: () => import("./pollinations.ts").then((m) => new m.PollinationsExecutor()), // Alias
|
||||
"cloudflare-ai": () => import("./cloudflare-ai.ts").then((m) => new m.CloudflareAIExecutor()),
|
||||
cf: () => import("./cloudflare-ai.ts").then((m) => new m.CloudflareAIExecutor()), // Alias
|
||||
freebuff: () => import("./freebuff.ts").then((m) => new m.FreebuffExecutor()),
|
||||
fb: () => import("./freebuff.ts").then((m) => new m.FreebuffExecutor()), // Alias
|
||||
"opencode-zen": () =>
|
||||
import("./opencode.ts").then((m) => new m.OpencodeExecutor("opencode-zen")),
|
||||
"opencode-go": () =>
|
||||
import("./opencode.ts").then((m) => new m.OpencodeExecutor("opencode-go")),
|
||||
opencode: () =>
|
||||
import("./opencode.ts").then((m) => new m.OpencodeExecutor("opencode-zen")), // Alias for opencode-zen
|
||||
vertex: () => import("./vertex.ts").then((m) => new m.VertexExecutor()),
|
||||
"vertex-partner": () => import("./vertex.ts").then((m) => new m.VertexExecutor()),
|
||||
cliproxyapi: () => import("./cliproxyapi.ts").then((m) => new m.CliproxyapiExecutor()),
|
||||
cpa: () => import("./cliproxyapi.ts").then((m) => new m.CliproxyapiExecutor()), // Alias
|
||||
dario: () => import("./dario.ts").then((m) => new m.DarioExecutor()),
|
||||
dr: () => import("./dario.ts").then((m) => new m.DarioExecutor()), // Alias
|
||||
"9router": () => import("./ninerouter.ts").then((m) => new m.NineRouterExecutor()),
|
||||
nr: () => import("./ninerouter.ts").then((m) => new m.NineRouterExecutor()), // Alias
|
||||
"perplexity-web": () =>
|
||||
import("./perplexity-web.ts").then((m) => new m.PerplexityWebExecutor()),
|
||||
"pplx-web": () =>
|
||||
import("./perplexity-web.ts").then((m) => new m.PerplexityWebExecutor()), // Alias
|
||||
"grok-web": () => import("./grok-web.ts").then((m) => new m.GrokWebExecutor()),
|
||||
"claude-web": () => import("./claude-web.ts").then((m) => new m.ClaudeWebExecutor()),
|
||||
"cw-web": () => import("./claude-web.ts").then((m) => new m.ClaudeWebExecutor()), // Alias
|
||||
"gemini-web": () => import("./gemini-web.ts").then((m) => new m.GeminiWebExecutor()),
|
||||
gweb: () => import("./gemini-web.ts").then((m) => new m.GeminiWebExecutor()), // Alias
|
||||
"gemini-business": () =>
|
||||
import("./gemini-business.ts").then((m) => new m.GeminiBusinessExecutor()),
|
||||
gembiz: () =>
|
||||
import("./gemini-business.ts").then((m) => new m.GeminiBusinessExecutor()), // Alias
|
||||
"chatgpt-web": () => import("./chatgpt-web.ts").then((m) => new m.ChatGptWebExecutor()),
|
||||
"cgpt-web": () => import("./chatgpt-web.ts").then((m) => new m.ChatGptWebExecutor()), // Alias
|
||||
"blackbox-web": () => import("./blackbox-web.ts").then((m) => new m.BlackboxWebExecutor()),
|
||||
"bb-web": () => import("./blackbox-web.ts").then((m) => new m.BlackboxWebExecutor()), // Alias
|
||||
"muse-spark-web": () =>
|
||||
import("./muse-spark-web.ts").then((m) => new m.MuseSparkWebExecutor()),
|
||||
"ms-web": () => import("./muse-spark-web.ts").then((m) => new m.MuseSparkWebExecutor()), // Alias
|
||||
"devin-desktop": () => import("./devin-desktop.ts").then((m) => new m.DevinDesktopExecutor()),
|
||||
"zed-hosted": () => import("./zed-hosted.ts").then((m) => new m.ZedHostedExecutor()),
|
||||
"devin-cli": () => import("./devin-cli.ts").then((m) => new m.DevinCliExecutor()),
|
||||
zcode: () => import("./zcode.ts").then((m) => new m.ZcodeExecutor()),
|
||||
zc: () => import("./zcode.ts").then((m) => new m.ZcodeExecutor()), // Alias
|
||||
"devin-cli-agentic": () =>
|
||||
import("./devin-cli-agentic.ts").then((m) => new m.DevinCliAgenticExecutor()),
|
||||
devin: () => import("./devin-cli.ts").then((m) => new m.DevinCliExecutor()), // Alias
|
||||
"deepseek-web": () =>
|
||||
import("./deepseek-web-with-auto-refresh.ts").then(
|
||||
(m) => new m.DeepSeekWebWithAutoRefreshExecutor()
|
||||
),
|
||||
"ds-web": () =>
|
||||
import("./deepseek-web-with-auto-refresh.ts").then(
|
||||
(m) => new m.DeepSeekWebWithAutoRefreshExecutor()
|
||||
), // Alias
|
||||
"adapta-web": () => import("./adapta-web.ts").then((m) => new m.AdaptaWebExecutor()),
|
||||
"adp-web": () => import("./adapta-web.ts").then((m) => new m.AdaptaWebExecutor()), // Alias
|
||||
"copilot-web": () => import("./copilot-web.ts").then((m) => new m.CopilotWebExecutor()),
|
||||
"copilot-m365-web": () =>
|
||||
import("./copilot-m365-web.ts").then((m) => new m.CopilotM365WebExecutor()),
|
||||
copilot: () => import("./copilot-web.ts").then((m) => new m.CopilotWebExecutor()), // Alias
|
||||
"microsoft-designer-web": () =>
|
||||
import("./microsoft-designer-web.ts").then((m) => new m.MicrosoftDesignerWebExecutor()),
|
||||
msdesigner: () =>
|
||||
import("./microsoft-designer-web.ts").then((m) => new m.MicrosoftDesignerWebExecutor()), // Alias
|
||||
"adobe-firefly": () => import("./adobe-firefly.ts").then((m) => new m.AdobeFireflyExecutor()),
|
||||
firefly: () => import("./adobe-firefly.ts").then((m) => new m.AdobeFireflyExecutor()), // Alias
|
||||
"veoaifree-web": () => import("./veoaifree-web.ts").then((m) => new m.VeoAIFreeWebExecutor()),
|
||||
"veo-free": () => import("./veoaifree-web.ts").then((m) => new m.VeoAIFreeWebExecutor()), // Alias
|
||||
"duckduckgo-web": () =>
|
||||
import("./duckduckgo-web.ts").then((m) => new m.DuckDuckGoWebExecutor()),
|
||||
ddgw: () => import("./duckduckgo-web.ts").then((m) => new m.DuckDuckGoWebExecutor()), // Alias
|
||||
"felo-web": () => import("./felo-web.ts").then((m) => new m.FeloWebExecutor()),
|
||||
felo: () => import("./felo-web.ts").then((m) => new m.FeloWebExecutor()), // Alias
|
||||
"t3-web": () => import("./t3-chat-web.ts").then((m) => new m.T3ChatWebExecutor()),
|
||||
t3chat: () => import("./t3-chat-web.ts").then((m) => new m.T3ChatWebExecutor()), // Alias
|
||||
"inner-ai": () => import("./inner-ai.ts").then((m) => new m.InnerAiExecutor()),
|
||||
"in-ai": () => import("./inner-ai.ts").then((m) => new m.InnerAiExecutor()), // Alias
|
||||
huggingchat: () => import("./huggingchat.ts").then((m) => new m.HuggingChatExecutor()),
|
||||
hc: () => import("./huggingchat.ts").then((m) => new m.HuggingChatExecutor()), // Alias
|
||||
"yuanbao-web": () => import("./yuanbao-web.ts").then((m) => new m.YuanbaoWebExecutor()),
|
||||
"tencent-aistudio-web": () =>
|
||||
import("./tencent-aistudio-web.ts").then((m) => new m.TencentAIStudioWebExecutor()),
|
||||
tasw: () =>
|
||||
import("./tencent-aistudio-web.ts").then((m) => new m.TencentAIStudioWebExecutor()), // Alias
|
||||
ybw: () => import("./yuanbao-web.ts").then((m) => new m.YuanbaoWebExecutor()), // Alias
|
||||
"poe-web": () => import("./poe-web.ts").then((m) => new m.PoeWebExecutor()),
|
||||
// #8969: do NOT alias canonical `poe` (API-key / api.poe.com) to PoeWebExecutor.
|
||||
// Registry declares executor:"default"; the hard-coded map previously won and
|
||||
// routed API-key traffic to GraphQL /api/gql_POST → HTTP 405.
|
||||
"venice-web": new VeniceWebExecutor(),
|
||||
ven: new VeniceWebExecutor(), // Alias
|
||||
"notion-web": new NotionWebExecutor(),
|
||||
nw: new NotionWebExecutor(), // Alias
|
||||
promptql: new PromptQlExecutor(),
|
||||
pql: new PromptQlExecutor(), // Alias
|
||||
"v0-vercel-web": new V0VercelWebExecutor(),
|
||||
v0: new V0VercelWebExecutor(), // Alias
|
||||
"kimi-web": new KimiWebExecutor(),
|
||||
"kimi-coding-apikey": new KimiExecutor("kimi-coding-apikey"), // Legacy alias
|
||||
"kimi-coding": new KimiExecutor(), // Alias
|
||||
moonshot: new MoonshotExecutor(),
|
||||
kimi: new MoonshotExecutor("kimi"), // Hidden legacy Moonshot provider id
|
||||
cheaperinference: new CheaperInferenceExecutor(),
|
||||
cinf: new CheaperInferenceExecutor("cheaperinference"), // Alias
|
||||
"doubao-web": new DoubaoWebExecutor(),
|
||||
db: new DoubaoWebExecutor(), // Alias
|
||||
"qwen-web": new QwenWebExecutor(),
|
||||
raycast: new RaycastExecutor(),
|
||||
rc: new RaycastExecutor(), // Alias
|
||||
"hailuo-web": new HailuoWebExecutor(),
|
||||
"zai-web": new ZaiWebExecutor(),
|
||||
zw: new ZaiWebExecutor(), // Alias
|
||||
theoldllm: new TheOldLlmExecutor(),
|
||||
tllm: new TheOldLlmExecutor(), // Alias
|
||||
chipotle: new ChipotleExecutor(),
|
||||
pepper: new ChipotleExecutor(), // Alias
|
||||
lmarena: new LMArenaExecutor(),
|
||||
lma: new LMArenaExecutor(), // Alias
|
||||
"grok-cli": new GrokCliExecutor(),
|
||||
gc: new GrokCliExecutor(), // Alias
|
||||
"codebuddy-cn": new CodeBuddyCnExecutor(),
|
||||
cbcn: new CodeBuddyCnExecutor(), // Alias for codebuddy-cn
|
||||
"zenmux-free": new ZenmuxFreeExecutor(),
|
||||
"cloudflare-playground": new CloudflarePlaygroundExecutor(),
|
||||
cfp: new CloudflarePlaygroundExecutor(), // Alias for cloudflare-playground
|
||||
"tinycms-web": new TinyCmsExecutor(),
|
||||
tcw: new TinyCmsExecutor(), // Alias
|
||||
hyperagent: new HyperAgentExecutor(),
|
||||
ha: new HyperAgentExecutor(), // Alias
|
||||
zmf: new ZenmuxFreeExecutor(), // Alias for zenmux-free
|
||||
auggie: new AuggieExecutor(),
|
||||
xai: new XaiExecutor(),
|
||||
"xai-oauth": new XaiExecutor("xai-oauth"),
|
||||
xao: new XaiExecutor("xai-oauth"),
|
||||
qw: new QwenWebExecutor(), // Alias
|
||||
"conol-web": new ConolWebExecutor(),
|
||||
cnl: new ConolWebExecutor(), // Alias
|
||||
"venice-web": () => import("./venice-web.ts").then((m) => new m.VeniceWebExecutor()),
|
||||
ven: () => import("./venice-web.ts").then((m) => new m.VeniceWebExecutor()), // Alias
|
||||
"notion-web": () => import("./notion-web.ts").then((m) => new m.NotionWebExecutor()),
|
||||
nw: () => import("./notion-web.ts").then((m) => new m.NotionWebExecutor()), // Alias
|
||||
promptql: () => import("./promptql.ts").then((m) => new m.PromptQlExecutor()),
|
||||
pql: () => import("./promptql.ts").then((m) => new m.PromptQlExecutor()), // Alias
|
||||
"v0-vercel-web": () => import("./v0-vercel-web.ts").then((m) => new m.V0VercelWebExecutor()),
|
||||
v0: () => import("./v0-vercel-web.ts").then((m) => new m.V0VercelWebExecutor()), // Alias
|
||||
"kimi-web": () => import("./kimi-web.ts").then((m) => new m.KimiWebExecutor()),
|
||||
"kimi-coding-apikey": () =>
|
||||
import("./kimi.ts").then((m) => new m.KimiExecutor("kimi-coding-apikey")), // Legacy alias
|
||||
"kimi-coding": () => import("./kimi.ts").then((m) => new m.KimiExecutor()), // Alias
|
||||
moonshot: () => import("./moonshot.ts").then((m) => new m.MoonshotExecutor()),
|
||||
kimi: () => import("./moonshot.ts").then((m) => new m.MoonshotExecutor("kimi")), // Hidden legacy Moonshot provider id
|
||||
cheaperinference: () =>
|
||||
import("./cheaperinference.ts").then((m) => new m.CheaperInferenceExecutor()),
|
||||
cinf: () =>
|
||||
import("./cheaperinference.ts").then(
|
||||
(m) => new m.CheaperInferenceExecutor("cheaperinference")
|
||||
), // Alias
|
||||
"doubao-web": () => import("./doubao-web.ts").then((m) => new m.DoubaoWebExecutor()),
|
||||
db: () => import("./doubao-web.ts").then((m) => new m.DoubaoWebExecutor()), // Alias
|
||||
"qwen-web": () => import("./qwen-web.ts").then((m) => new m.QwenWebExecutor()),
|
||||
raycast: () => import("./raycast.ts").then((m) => new m.RaycastExecutor()),
|
||||
rc: () => import("./raycast.ts").then((m) => new m.RaycastExecutor()), // Alias
|
||||
"hailuo-web": () => import("./hailuo-web.ts").then((m) => new m.HailuoWebExecutor()),
|
||||
"zai-web": () => import("./zai-web.ts").then((m) => new m.ZaiWebExecutor()),
|
||||
zw: () => import("./zai-web.ts").then((m) => new m.ZaiWebExecutor()), // Alias
|
||||
theoldllm: () => import("./theoldllm.ts").then((m) => new m.TheOldLlmExecutor()),
|
||||
tllm: () => import("./theoldllm.ts").then((m) => new m.TheOldLlmExecutor()), // Alias
|
||||
chipotle: () => import("./chipotle.ts").then((m) => new m.ChipotleExecutor()),
|
||||
pepper: () => import("./chipotle.ts").then((m) => new m.ChipotleExecutor()), // Alias
|
||||
lmarena: () => import("./lmarena.ts").then((m) => new m.LMArenaExecutor()),
|
||||
lma: () => import("./lmarena.ts").then((m) => new m.LMArenaExecutor()), // Alias
|
||||
"grok-cli": () => import("./grok-cli.ts").then((m) => new m.GrokCliExecutor()),
|
||||
gc: () => import("./grok-cli.ts").then((m) => new m.GrokCliExecutor()), // Alias
|
||||
"codebuddy-cn": () => import("./codebuddy-cn.ts").then((m) => new m.CodeBuddyCnExecutor()),
|
||||
cbcn: () => import("./codebuddy-cn.ts").then((m) => new m.CodeBuddyCnExecutor()), // Alias for codebuddy-cn
|
||||
"zenmux-free": () => import("./zenmux-free.ts").then((m) => new m.ZenmuxFreeExecutor()),
|
||||
"cloudflare-playground": () =>
|
||||
import("./cloudflare-playground.ts").then((m) => new m.CloudflarePlaygroundExecutor()),
|
||||
cfp: () =>
|
||||
import("./cloudflare-playground.ts").then((m) => new m.CloudflarePlaygroundExecutor()), // Alias for cloudflare-playground
|
||||
"tinycms-web": () => import("./tinycms.ts").then((m) => new m.TinyCmsExecutor()),
|
||||
tcw: () => import("./tinycms.ts").then((m) => new m.TinyCmsExecutor()), // Alias
|
||||
hyperagent: () => import("./hyperagent.ts").then((m) => new m.HyperAgentExecutor()),
|
||||
ha: () => import("./hyperagent.ts").then((m) => new m.HyperAgentExecutor()), // Alias
|
||||
zmf: () => import("./zenmux-free.ts").then((m) => new m.ZenmuxFreeExecutor()), // Alias for zenmux-free
|
||||
auggie: () => import("./auggie.ts").then((m) => new m.AuggieExecutor()),
|
||||
xai: () => import("./xai.ts").then((m) => new m.XaiExecutor()),
|
||||
"xai-oauth": () => import("./xai.ts").then((m) => new m.XaiExecutor("xai-oauth")),
|
||||
xao: () => import("./xai.ts").then((m) => new m.XaiExecutor("xai-oauth")),
|
||||
qw: () => import("./qwen-web.ts").then((m) => new m.QwenWebExecutor()), // Alias
|
||||
"conol-web": () => import("./conol-web.ts").then((m) => new m.ConolWebExecutor()),
|
||||
cnl: () => import("./conol-web.ts").then((m) => new m.ConolWebExecutor()), // Alias
|
||||
};
|
||||
|
||||
// Bootstrap: register every built-in in the ExecutorRegistry. registerExecutor
|
||||
// throws on duplicates, so an alias collision fails at module load, exactly as
|
||||
// loudly as a duplicate object key would have failed at lint time.
|
||||
for (const [alias, executor] of Object.entries(executors) as [string, BaseExecutor][]) {
|
||||
registerExecutor(alias, executor);
|
||||
// Bootstrap: declare every built-in alias in the ExecutorRegistry. Duplicate
|
||||
// aliases fail at module load, exactly as loudly as a duplicate object key
|
||||
// would have failed at lint time. Instances materialize on first use (#11220).
|
||||
for (const [alias, load] of Object.entries(lazyExecutors)) {
|
||||
registerLazyExecutor(alias, load);
|
||||
}
|
||||
|
||||
const defaultCache = new Map();
|
||||
@@ -267,9 +239,9 @@ const CHAT_UNSUPPORTED_CLOUD_AGENT_PROVIDERS = new Set(["jules"]);
|
||||
// providers must be executed through /v1/search, never the chat-completions path.
|
||||
const CHAT_UNSUPPORTED_SEARCH_PROVIDERS = new Set(Object.keys(SEARCH_PROVIDERS));
|
||||
|
||||
export function getExecutor(provider) {
|
||||
const registered = getRegisteredExecutor(provider);
|
||||
if (registered) return registered;
|
||||
export async function getExecutor(provider: string): Promise<BaseExecutor> {
|
||||
const loaded = await loadRegisteredExecutor(provider);
|
||||
if (loaded) return loaded;
|
||||
if (CHAT_UNSUPPORTED_CLOUD_AGENT_PROVIDERS.has(provider)) {
|
||||
const err = new Error(
|
||||
`Provider "${provider}" is a cloud-agent provider and does not support direct chat completions; use the Cloud Agents task API instead.`
|
||||
@@ -285,77 +257,19 @@ export function getExecutor(provider) {
|
||||
throw err;
|
||||
}
|
||||
if (!defaultCache.has(provider)) defaultCache.set(provider, new DefaultExecutor(provider));
|
||||
return defaultCache.get(provider);
|
||||
return defaultCache.get(provider)!;
|
||||
}
|
||||
|
||||
export function hasSpecializedExecutor(provider) {
|
||||
export function hasSpecializedExecutor(provider: string): boolean {
|
||||
return hasRegisteredExecutor(provider);
|
||||
}
|
||||
|
||||
export { registerExecutor, listExecutorAliases } from "./registry.ts";
|
||||
|
||||
export {
|
||||
registerExecutor,
|
||||
registerLazyExecutor,
|
||||
listExecutorAliases,
|
||||
} from "./registry.ts";
|
||||
// Value re-export: base.ts is already eager (DefaultExecutor extends it), and
|
||||
// scripts/check/check-known-symbols.ts reads this export from the module.
|
||||
export { BaseExecutor } from "./base.ts";
|
||||
export { AntigravityExecutor } from "./antigravity.ts";
|
||||
export { GithubExecutor } from "./github.ts";
|
||||
export { QoderExecutor } from "./qoder.ts";
|
||||
export { KiroExecutor } from "./kiro.ts";
|
||||
export { CodexExecutor } from "./codex.ts";
|
||||
export { CursorExecutor } from "./cursor.ts";
|
||||
export { TraeExecutor } from "./trae.ts";
|
||||
export { DefaultExecutor } from "./default.ts";
|
||||
export { BedrockExecutor } from "./bedrock.ts";
|
||||
export { GlmExecutor } from "./glm.ts";
|
||||
export { PollinationsExecutor } from "./pollinations.ts";
|
||||
export { CloudflareAIExecutor } from "./cloudflare-ai.ts";
|
||||
export { OpencodeExecutor } from "./opencode.ts";
|
||||
export { CliproxyapiExecutor } from "./cliproxyapi.ts";
|
||||
export { DarioExecutor } from "./dario.ts";
|
||||
export { NineRouterExecutor } from "./ninerouter.ts";
|
||||
export { VertexExecutor } from "./vertex.ts";
|
||||
export { PerplexityWebExecutor } from "./perplexity-web.ts";
|
||||
export { GrokWebExecutor } from "./grok-web.ts";
|
||||
export { GeminiWebExecutor } from "./gemini-web.ts";
|
||||
export { KieExecutor } from "./kie.ts";
|
||||
export { ChatGptWebExecutor } from "./chatgpt-web.ts";
|
||||
export { BlackboxWebExecutor } from "./blackbox-web.ts";
|
||||
export { MuseSparkWebExecutor } from "./muse-spark-web.ts";
|
||||
export { AzureOpenAIExecutor } from "./azure-openai.ts";
|
||||
export { AzureAiExecutor } from "./azure-ai.ts";
|
||||
export { CommandCodeExecutor } from "./commandCode.ts";
|
||||
export { GitlabExecutor } from "./gitlab.ts";
|
||||
export { NlpCloudExecutor } from "./nlpcloud.ts";
|
||||
export { DevinDesktopExecutor } from "./devin-desktop.ts";
|
||||
export { ZedHostedExecutor } from "./zed-hosted.ts";
|
||||
export { DevinCliExecutor } from "./devin-cli.ts";
|
||||
export { DevinCliAgenticExecutor } from "./devin-cli-agentic.ts";
|
||||
export { AuggieExecutor } from "./auggie.ts";
|
||||
export { CopilotWebExecutor } from "./copilot-web.ts";
|
||||
export { CopilotM365WebExecutor } from "./copilot-m365-web.ts";
|
||||
export { MicrosoftDesignerWebExecutor } from "./microsoft-designer-web.ts";
|
||||
export { AdobeFireflyExecutor } from "./adobe-firefly.ts";
|
||||
export { VeoAIFreeWebExecutor } from "./veoaifree-web.ts";
|
||||
export { DuckDuckGoWebExecutor } from "./duckduckgo-web.ts";
|
||||
export { FeloWebExecutor } from "./felo-web.ts";
|
||||
export { ClaudeWebExecutor } from "./claude-web.ts";
|
||||
export { DeepSeekWebExecutor } from "./deepseek-web.ts";
|
||||
export { DeepSeekWebWithAutoRefreshExecutor } from "./deepseek-web-with-auto-refresh.ts";
|
||||
export { AdaptaWebExecutor } from "./adapta-web.ts";
|
||||
export { YuanbaoWebExecutor } from "./yuanbao-web.ts";
|
||||
export { T3ChatWebExecutor } from "./t3-chat-web.ts";
|
||||
export { InnerAiExecutor } from "./inner-ai.ts";
|
||||
export { QwenWebExecutor } from "./qwen-web.ts";
|
||||
export { HailuoWebExecutor } from "./hailuo-web.ts";
|
||||
export { TheOldLlmExecutor } from "./theoldllm.ts";
|
||||
export { ChipotleExecutor } from "./chipotle.ts";
|
||||
export { LMArenaExecutor } from "./lmarena.ts";
|
||||
export { GrokCliExecutor } from "./grok-cli.ts";
|
||||
export { CodeBuddyCnExecutor } from "./codebuddy-cn.ts";
|
||||
export { ZenmuxFreeExecutor } from "./zenmux-free.ts";
|
||||
export { CloudflarePlaygroundExecutor } from "./cloudflare-playground.ts";
|
||||
export { TinyCmsExecutor } from "./tinycms.ts";
|
||||
export { HyperAgentExecutor } from "./hyperagent.ts";
|
||||
export { XaiExecutor } from "./xai.ts";
|
||||
export { MoonshotExecutor } from "./moonshot.ts";
|
||||
export { CheaperInferenceExecutor } from "./cheaperinference.ts";
|
||||
export { PromptQlExecutor } from "./promptql.ts";
|
||||
export { ConolWebExecutor } from "./conol-web.ts";
|
||||
|
||||
@@ -895,10 +895,21 @@ export class OpencodeExecutor extends BaseExecutor {
|
||||
const mb = modifiedBody as Record<string, unknown>;
|
||||
const parsed = parseEffortLevel(model);
|
||||
if (parsed) {
|
||||
mb.model = parsed.baseModel;
|
||||
if (mb.reasoning_effort === undefined) {
|
||||
mb.reasoning_effort = parsed.effort;
|
||||
const deepseekFamily =
|
||||
parsed.baseModel === "deepseek-v4-pro" || parsed.baseModel === "deepseek-v4-flash";
|
||||
if (deepseekFamily) {
|
||||
// DeepSeek via opencode-go proxies the native DeepSeek contract, which
|
||||
// accepts a flat reasoning_effort field (#4647).
|
||||
mb.model = parsed.baseModel;
|
||||
if (mb.reasoning_effort === undefined) {
|
||||
mb.reasoning_effort = parsed.effort;
|
||||
}
|
||||
}
|
||||
// #10788: every other family's ONLY native effort mechanism is the
|
||||
// -<tier> suffix in the model id itself (the ids `opencode models
|
||||
// opencode-go --verbose` lists). The opencode-go ChatCompletionRequest
|
||||
// carries no flat reasoning_effort field, so rewriting to the base id
|
||||
// silently dropped the tier — forward the aliased id verbatim instead.
|
||||
}
|
||||
}
|
||||
// #1543 / upstream PR #1099: thinking-mode upstreams routed through OpenCode
|
||||
|
||||
@@ -28,11 +28,46 @@ export function getRegisteredExecutor(alias: string): BaseExecutor | undefined {
|
||||
return registry.get(alias);
|
||||
}
|
||||
|
||||
export function hasRegisteredExecutor(alias: string): boolean {
|
||||
return registry.has(alias);
|
||||
// ── #11220: lazy registration ───────────────────────────────────────────────
|
||||
// Aliases may register a deferred loader instead of an instance. The alias and
|
||||
// its registration ORDER are declared eagerly — hasRegisteredExecutor() and
|
||||
// listExecutorAliases() stay synchronous and the golden snapshot keeps its
|
||||
// shape — while the class import + construction happen on first use. A
|
||||
// completed load caches into `registry`, so later resolution is identical to a
|
||||
// static registration.
|
||||
const lazyLoaders = new Map<string, () => Promise<BaseExecutor>>();
|
||||
const lazyInFlight = new Map<string, Promise<BaseExecutor>>();
|
||||
|
||||
export function registerLazyExecutor(alias: string, load: () => Promise<BaseExecutor>): void {
|
||||
if (registry.has(alias) || lazyLoaders.has(alias)) {
|
||||
throw new Error(`executor alias already registered: "${alias}"`);
|
||||
}
|
||||
lazyLoaders.set(alias, load);
|
||||
}
|
||||
|
||||
/** All registered aliases, in registration order. */
|
||||
export function loadRegisteredExecutor(alias: string): Promise<BaseExecutor> | undefined {
|
||||
const cached = registry.get(alias);
|
||||
if (cached) return Promise.resolve(cached);
|
||||
const load = lazyLoaders.get(alias);
|
||||
if (!load) return undefined;
|
||||
let inFlight = lazyInFlight.get(alias);
|
||||
if (!inFlight) {
|
||||
inFlight = load().then((executor) => {
|
||||
registerExecutor(alias, executor);
|
||||
lazyLoaders.delete(alias);
|
||||
lazyInFlight.delete(alias);
|
||||
return executor;
|
||||
});
|
||||
lazyInFlight.set(alias, inFlight);
|
||||
}
|
||||
return inFlight;
|
||||
}
|
||||
|
||||
export function hasRegisteredExecutor(alias: string): boolean {
|
||||
return registry.has(alias) || lazyLoaders.has(alias);
|
||||
}
|
||||
|
||||
/** All registered aliases — static and lazy — in registration order. */
|
||||
export function listExecutorAliases(): string[] {
|
||||
return [...registry.keys()];
|
||||
return [...registry.keys(), ...lazyLoaders.keys()];
|
||||
}
|
||||
|
||||
@@ -396,7 +396,7 @@ import {
|
||||
} from "../services/rateLimitManager.ts";
|
||||
import * as localLimiterErrors from "../services/rateLimitManager/errors.ts";
|
||||
import {
|
||||
acquire as acquireAccountSemaphore,
|
||||
acquireMany as acquireConcurrencyGates,
|
||||
markBlocked as markAccountSemaphoreBlocked,
|
||||
} from "../services/accountSemaphore.ts";
|
||||
import { lockModel, lockModelIfPerModelQuota } from "../services/accountFallback.ts";
|
||||
@@ -447,6 +447,7 @@ import { extractFacts } from "@/lib/memory/extraction";
|
||||
import { handleToolCallExecution } from "@/lib/skills/interception";
|
||||
import { MEMORY_BUILTIN_TOOL_NAMES } from "@/lib/skills/memoryBuiltins";
|
||||
import { OMNIROUTE_RESPONSE_HEADERS } from "@/shared/constants/headers";
|
||||
import { resolveProviderId } from "@/shared/constants/providers";
|
||||
import { getClaudeCodeCompatibleRequestDefaults } from "@/lib/providers/requestDefaults";
|
||||
import {
|
||||
buildClaudeCodeCompatibleRequest,
|
||||
@@ -525,6 +526,7 @@ export async function handleChatCore({
|
||||
managedLease = null,
|
||||
}) {
|
||||
let { provider, model, extendedContext } = modelInfo;
|
||||
const resilienceSettings = resolveResilienceSettings(cachedSettings);
|
||||
if (!skipResourcePressureGuard) {
|
||||
try {
|
||||
const pressureGuard = checkResourcePressureGuard();
|
||||
@@ -3051,6 +3053,10 @@ export async function handleChatCore({
|
||||
connectionId: attemptConnectionId,
|
||||
credentials: execCreds,
|
||||
});
|
||||
const canonicalProviderKey = resolveProviderId(String(provider).trim().toLowerCase());
|
||||
const providerConcurrency =
|
||||
resilienceSettings.providerQuotaOverrides[canonicalProviderKey]
|
||||
?.providerConcurrency ?? 0;
|
||||
|
||||
trace("pre_semaphore", {
|
||||
semaphoreKey: accountSemaphoreKey,
|
||||
@@ -3061,13 +3067,27 @@ export async function handleChatCore({
|
||||
stage: "waiting_account_slot",
|
||||
});
|
||||
}
|
||||
const releaseAccountSemaphore =
|
||||
accountSemaphoreKey && accountSemaphoreMaxConcurrency != null
|
||||
? await acquireAccountSemaphore(accountSemaphoreKey, {
|
||||
maxConcurrency: accountSemaphoreMaxConcurrency,
|
||||
signal: streamController.signal,
|
||||
})
|
||||
: () => {};
|
||||
const releaseAccountSemaphore = await acquireConcurrencyGates(
|
||||
[
|
||||
{
|
||||
key: "global",
|
||||
maxConcurrency: resilienceSettings.requestQueue.globalConcurrentRequests,
|
||||
},
|
||||
{
|
||||
key: `provider:${canonicalProviderKey}`,
|
||||
maxConcurrency: providerConcurrency,
|
||||
},
|
||||
{
|
||||
key: accountSemaphoreKey || "",
|
||||
maxConcurrency: accountSemaphoreKey ? accountSemaphoreMaxConcurrency : null,
|
||||
},
|
||||
],
|
||||
{
|
||||
timeoutMs: resilienceSettings.requestQueue.maxWaitMs,
|
||||
maxQueueSize: resilienceSettings.requestQueue.maxQueueDepth,
|
||||
signal: streamController.signal,
|
||||
}
|
||||
);
|
||||
trace("post_semaphore");
|
||||
updatePendingScope(pendingScope, {
|
||||
stage: "waiting_rate_limit",
|
||||
@@ -3328,6 +3348,7 @@ export async function handleChatCore({
|
||||
"ANTIGRAVITY_BYOP_ROTATION",
|
||||
`BYOP 422 on connection ${String(byopFailedId).slice(0, 8)} → rotating to ${String(byopNextCreds.connectionId).slice(0, 8)}`
|
||||
);
|
||||
releaseAccountSemaphore();
|
||||
Object.assign(credentials, byopNextCreds);
|
||||
antigravityByopRotationPending = true;
|
||||
continue;
|
||||
|
||||
@@ -14,12 +14,12 @@
|
||||
type ExecutorInput = {
|
||||
model: string;
|
||||
body: unknown;
|
||||
[key: string]: unknown;
|
||||
};
|
||||
|
||||
// No index signature: executors (BaseExecutor subclasses) must satisfy this
|
||||
// structurally, and class instances don't carry index signatures.
|
||||
type ExecutorLike = {
|
||||
execute: (input: ExecutorInput) => Promise<unknown>;
|
||||
[key: string]: unknown;
|
||||
};
|
||||
|
||||
export type CliproxyapiModelMapping = Record<string, unknown> | null | undefined;
|
||||
|
||||
@@ -19,12 +19,12 @@ import type { ProviderCredentials } from "../../executors/base.ts";
|
||||
|
||||
type ExecutorInput = {
|
||||
credentials: ProviderCredentials;
|
||||
[key: string]: unknown;
|
||||
};
|
||||
|
||||
// No index signature: executors (BaseExecutor subclasses) must satisfy this
|
||||
// structurally, and class instances don't carry index signatures.
|
||||
type ExecutorLike = {
|
||||
execute: (input: ExecutorInput) => Promise<unknown>;
|
||||
[key: string]: unknown;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -76,12 +76,15 @@ async function loadCliproxyapiSettings(): Promise<{
|
||||
* dedicated-credential wrappers applied. Used by the direct `cliproxyapi` leg
|
||||
* and the CLIProxyAPI branch of `fallback`.
|
||||
*/
|
||||
function resolveCliproxyapiExecutor(
|
||||
async function resolveCliproxyapiExecutor(
|
||||
cliproxyapiModelMapping: Record<string, unknown> | null,
|
||||
dedicatedApiKey: string | null
|
||||
) {
|
||||
return wrapExecutorWithCliproxyapiCredentials(
|
||||
wrapExecutorWithCliproxyapiModelMapping(getExecutor("cliproxyapi"), cliproxyapiModelMapping),
|
||||
wrapExecutorWithCliproxyapiModelMapping(
|
||||
await getExecutor("cliproxyapi"),
|
||||
cliproxyapiModelMapping
|
||||
),
|
||||
dedicatedApiKey
|
||||
);
|
||||
}
|
||||
@@ -138,7 +141,7 @@ export async function resolveExecutorWithProxy(
|
||||
// backend on specific failures. The backend defaults to CLIProxyAPI so every
|
||||
// pre-existing fallback config behaves exactly as before; fallbackBackend
|
||||
// === "dario" opts the retry leg over to Dario instead.
|
||||
const nativeExec = getExecutor(prov);
|
||||
const nativeExec = await getExecutor(prov);
|
||||
const fallbackBackend: FallbackBackend = cfg.fallbackBackend;
|
||||
const { fallbackCodes, dedicatedApiKey } = await loadCliproxyapiSettings();
|
||||
|
||||
@@ -146,8 +149,8 @@ export async function resolveExecutorWithProxy(
|
||||
// the native leg must keep seeing the original, unmapped model.
|
||||
const proxyExec =
|
||||
fallbackBackend === "dario"
|
||||
? getExecutor("dario")
|
||||
: resolveCliproxyapiExecutor(cfg.cliproxyapiModelMapping, dedicatedApiKey);
|
||||
? await getExecutor("dario")
|
||||
: await resolveCliproxyapiExecutor(cfg.cliproxyapiModelMapping, dedicatedApiKey);
|
||||
const backendLabel = fallbackBackend === "dario" ? "Dario" : "CLIProxyAPI";
|
||||
const isRetryableStatus = (s: number) => fallbackCodes.includes(s) || s === 0;
|
||||
|
||||
|
||||
@@ -201,6 +201,7 @@ export async function handleRerank({
|
||||
connectionId = null,
|
||||
apiKeyId = null,
|
||||
apiKeyName = null,
|
||||
resolvedProvider = null,
|
||||
}) {
|
||||
const startTime = Date.now();
|
||||
if (!model) return errorResponse(400, "model is required");
|
||||
@@ -210,7 +211,8 @@ export async function handleRerank({
|
||||
}
|
||||
|
||||
const { provider: providerId, model: modelId } = parseRerankModel(model);
|
||||
const providerConfig = providerId ? getRerankProvider(providerId) : null;
|
||||
const providerConfig =
|
||||
resolvedProvider || (providerId ? getRerankProvider(providerId) : null);
|
||||
|
||||
if (!providerConfig) {
|
||||
const availableProviders = Object.keys(RERANK_PROVIDERS).join(", ");
|
||||
@@ -219,10 +221,13 @@ export async function handleRerank({
|
||||
`No rerank provider found for model "${model}". Available: ${availableProviders}`
|
||||
);
|
||||
}
|
||||
// When a derived/generic provider is injected, its id is authoritative for
|
||||
// logging and cost attribution even though parseRerankModel returned null.
|
||||
const effectiveProviderId = providerConfig.id || providerId;
|
||||
|
||||
const token = credentials?.apiKey || credentials?.accessToken;
|
||||
if (!token) {
|
||||
return errorResponse(401, `No credentials for rerank provider: ${providerId}`);
|
||||
return errorResponse(401, `No credentials for rerank provider: ${effectiveProviderId}`);
|
||||
}
|
||||
|
||||
const requestBody = transformRequestForProvider(providerConfig, {
|
||||
@@ -275,8 +280,8 @@ export async function handleRerank({
|
||||
method: "POST",
|
||||
path: "/v1/rerank",
|
||||
status: res.status,
|
||||
model: `${providerId}/${modelId}`,
|
||||
provider: providerId,
|
||||
model: `${effectiveProviderId}/${modelId}`,
|
||||
provider: effectiveProviderId,
|
||||
connectionId: connectionId || undefined,
|
||||
duration: Date.now() - startTime,
|
||||
requestBody,
|
||||
@@ -296,14 +301,14 @@ export async function handleRerank({
|
||||
});
|
||||
|
||||
const searchUnits = Number(result?.meta?.billed_units?.search_units) || 0;
|
||||
const costUsd = await calculateModalCost("rerank", providerId, modelId, { searchUnits });
|
||||
const costUsd = await calculateModalCost("rerank", effectiveProviderId, modelId, { searchUnits });
|
||||
|
||||
saveCallLog({
|
||||
method: "POST",
|
||||
path: "/v1/rerank",
|
||||
status: 200,
|
||||
model: `${providerId}/${modelId}`,
|
||||
provider: providerId,
|
||||
model: `${effectiveProviderId}/${modelId}`,
|
||||
provider: effectiveProviderId,
|
||||
connectionId: connectionId || undefined,
|
||||
duration: Date.now() - startTime,
|
||||
tokens: { prompt_tokens: 0, completion_tokens: 0 },
|
||||
@@ -315,7 +320,7 @@ export async function handleRerank({
|
||||
|
||||
const headers = new Headers({ ...CORS_HEADERS, "Content-Type": "application/json" });
|
||||
attachOmniRouteMetaHeaders(headers, {
|
||||
provider: providerId,
|
||||
provider: effectiveProviderId,
|
||||
model: modelId,
|
||||
costUsd,
|
||||
latencyMs: Date.now() - startTime,
|
||||
|
||||
@@ -8,6 +8,7 @@ import { randomUUID } from "crypto";
|
||||
* firecrawl, google-pse-search, linkup-search, searchapi-search,
|
||||
* youcom-search, searxng-search, ollama-search, zai-search, jina-search,
|
||||
* duckduckgo-free, x-search (Grok / SuperGrok X Search — explicit or search_type "x")
|
||||
* and xquik-search (direct X API search — explicit or credentialed fallback)
|
||||
*
|
||||
* Request format:
|
||||
* {
|
||||
@@ -28,6 +29,7 @@ import * as fcSearch from "./search/firecrawlSearch.ts";
|
||||
import { type FirecrawlSearchEnvelope } from "./search/firecrawlSearch.ts";
|
||||
import { buildJinaSearchRequest, extractJinaSearchItems } from "./search/jinaSearch.ts";
|
||||
import * as xSearch from "./search/xSearch.ts";
|
||||
import * as xquikSearch from "./search/xquikSearch.ts";
|
||||
import { freeWebSearch } from "../services/freeWebSearch.ts";
|
||||
import { saveCallLog } from "@/lib/usageDb";
|
||||
import { safeOutboundFetch } from "@/shared/network/safeOutboundFetch";
|
||||
@@ -714,6 +716,7 @@ const requestBuilders: Record<string, SearchRequestBuilder> = {
|
||||
"ollama-search": buildOllamaRequest,
|
||||
"jina-search": buildJinaSearchRequest,
|
||||
"x-search": xSearch.buildXSearchRequest,
|
||||
"xquik-search": xquikSearch.buildXquikSearchRequest,
|
||||
};
|
||||
|
||||
function buildRequest(
|
||||
@@ -1290,6 +1293,7 @@ const responseNormalizers: Record<string, SearchResponseNormalizer> = {
|
||||
"ollama-search": normalizeOllamaResponse,
|
||||
"jina-search": normalizeJinaSearchResponse,
|
||||
"x-search": normalizeXSearchResponse,
|
||||
"xquik-search": (data) => xquikSearch.normalizeXquikSearchResponse(data, makeResult),
|
||||
};
|
||||
|
||||
function normalizeResponse(
|
||||
|
||||
158
open-sse/handlers/search/xquikSearch.ts
Normal file
@@ -0,0 +1,158 @@
|
||||
/** Xquik-backed X search for the unified search gateway. */
|
||||
|
||||
import { z } from "zod";
|
||||
import type { SearchProviderConfig } from "../../config/searchRegistry.ts";
|
||||
import type { SearchResult } from "../search.ts";
|
||||
|
||||
export const XQUIK_SEARCH_PROVIDER_ID = "xquik-search";
|
||||
|
||||
export interface XquikSearchParams {
|
||||
query: string;
|
||||
maxResults: number;
|
||||
token?: string;
|
||||
timeRange?: string;
|
||||
providerOptions?: Record<string, unknown>;
|
||||
providerSpecificData?: Record<string, unknown>;
|
||||
}
|
||||
|
||||
export interface XquikSearchHit {
|
||||
title: string;
|
||||
url: string;
|
||||
snippet: string;
|
||||
author?: string;
|
||||
publishedAt?: string;
|
||||
}
|
||||
|
||||
type MakeResult = (
|
||||
providerId: string,
|
||||
item: {
|
||||
title?: string;
|
||||
url?: string;
|
||||
snippet?: string;
|
||||
published_at?: string;
|
||||
author?: string;
|
||||
source_type?: string;
|
||||
},
|
||||
index: number,
|
||||
now: string
|
||||
) => SearchResult;
|
||||
|
||||
const X_HANDLE_RE = /^[A-Za-z0-9_]{1,15}$/;
|
||||
const TWEET_ID_RE = /^\d+$/;
|
||||
|
||||
const XquikTweetSchema = z
|
||||
.object({
|
||||
id: z.string().regex(TWEET_ID_RE),
|
||||
text: z.string(),
|
||||
createdAt: z.string().optional(),
|
||||
author: z
|
||||
.object({
|
||||
username: z.string().regex(X_HANDLE_RE),
|
||||
name: z.string().optional(),
|
||||
})
|
||||
.passthrough()
|
||||
.optional(),
|
||||
})
|
||||
.passthrough();
|
||||
|
||||
const XquikSearchEnvelopeSchema = z
|
||||
.object({
|
||||
tweets: z.array(z.unknown()).default([]),
|
||||
})
|
||||
.passthrough();
|
||||
|
||||
function getProviderSettingString(
|
||||
params: Pick<XquikSearchParams, "providerOptions" | "providerSpecificData">,
|
||||
key: string
|
||||
): string | undefined {
|
||||
const option = params.providerOptions?.[key];
|
||||
if (typeof option === "string" && option.trim()) return option.trim();
|
||||
const configured = params.providerSpecificData?.[key];
|
||||
if (typeof configured === "string" && configured.trim()) return configured.trim();
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function sinceTimeForRange(timeRange: string | undefined, now = Date.now()): string | undefined {
|
||||
const hour = 60 * 60 * 1000;
|
||||
const durations: Record<string, number> = {
|
||||
hour,
|
||||
day: 24 * hour,
|
||||
week: 7 * 24 * hour,
|
||||
month: 30 * 24 * hour,
|
||||
year: 365 * 24 * hour,
|
||||
};
|
||||
const duration = timeRange ? durations[timeRange] : undefined;
|
||||
return duration ? new Date(now - duration).toISOString() : undefined;
|
||||
}
|
||||
|
||||
export function buildXquikSearchRequest(
|
||||
config: SearchProviderConfig,
|
||||
params: XquikSearchParams
|
||||
): { url: string; init: RequestInit } {
|
||||
const queryType = getProviderSettingString(params, "queryType") === "Top" ? "Top" : "Latest";
|
||||
const query = new URLSearchParams({
|
||||
q: params.query,
|
||||
queryType,
|
||||
limit: String(params.maxResults),
|
||||
});
|
||||
const sinceTime = sinceTimeForRange(params.timeRange);
|
||||
if (sinceTime) query.set("sinceTime", sinceTime);
|
||||
|
||||
return {
|
||||
url: `${config.baseUrl.replace(/\/+$/, "")}?${query}`,
|
||||
init: {
|
||||
method: "GET",
|
||||
headers: {
|
||||
Accept: "application/json",
|
||||
...(params.token ? { "x-api-key": params.token } : {}),
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function extractXquikSearchHits(data: unknown, maxResults: number): XquikSearchHit[] {
|
||||
const envelope = XquikSearchEnvelopeSchema.safeParse(data);
|
||||
if (!envelope.success) return [];
|
||||
|
||||
const hits: XquikSearchHit[] = [];
|
||||
for (const value of envelope.data.tweets) {
|
||||
const parsed = XquikTweetSchema.safeParse(value);
|
||||
if (!parsed.success) continue;
|
||||
const tweet = parsed.data;
|
||||
const author = tweet.author?.username;
|
||||
hits.push({
|
||||
title: author ? `@${author}` : "X post",
|
||||
url: author
|
||||
? `https://x.com/${author}/status/${tweet.id}`
|
||||
: `https://x.com/i/status/${tweet.id}`,
|
||||
snippet: tweet.text,
|
||||
author,
|
||||
publishedAt: tweet.createdAt,
|
||||
});
|
||||
if (hits.length >= maxResults) break;
|
||||
}
|
||||
return hits;
|
||||
}
|
||||
|
||||
export function normalizeXquikSearchResponse(
|
||||
data: unknown,
|
||||
makeResult: MakeResult
|
||||
): { results: SearchResult[]; totalResults: number } {
|
||||
const now = new Date().toISOString();
|
||||
const results = extractXquikSearchHits(data, 20).map((hit, index) =>
|
||||
makeResult(
|
||||
XQUIK_SEARCH_PROVIDER_ID,
|
||||
{
|
||||
title: hit.title,
|
||||
url: hit.url,
|
||||
snippet: hit.snippet,
|
||||
published_at: hit.publishedAt,
|
||||
author: hit.author,
|
||||
source_type: "x",
|
||||
},
|
||||
index,
|
||||
now
|
||||
)
|
||||
);
|
||||
return { results, totalResults: results.length };
|
||||
}
|
||||
@@ -374,7 +374,7 @@ async function handleVertexVeoGeneration({ model, body, credentials, log }) {
|
||||
* Submits an AnimateDiff or SVD workflow, polls for completion, fetches output video
|
||||
*/
|
||||
async function handleVeoAiFreeVideoGeneration({ model, provider, body, credentials, log }) {
|
||||
const executor = getExecutor(provider);
|
||||
const executor = await getExecutor(provider);
|
||||
if (!executor) {
|
||||
return { success: false, status: 400, error: `Unknown video provider: ${provider}` };
|
||||
}
|
||||
|
||||
@@ -358,6 +358,38 @@ describe("omniroute_x_search handler (via MCP dispatch)", () => {
|
||||
expect(body.search_type).toBe("x");
|
||||
expect(body.provider).toBe("x-search");
|
||||
});
|
||||
|
||||
it("should route an explicit Xquik search through xquik-search", async () => {
|
||||
mockFetch.mockResolvedValueOnce({
|
||||
ok: true,
|
||||
json: async () => ({
|
||||
id: "xs2",
|
||||
provider: "xquik-search",
|
||||
query: "agents sdk",
|
||||
results: [
|
||||
{
|
||||
title: "@openai",
|
||||
url: "https://x.com/openai/status/1912345678901234567",
|
||||
snippet: "Agents SDK update",
|
||||
position: 1,
|
||||
},
|
||||
],
|
||||
cached: false,
|
||||
usage: { queries_used: 1, search_cost_usd: 0.00015 },
|
||||
}),
|
||||
});
|
||||
|
||||
const result = await client.callTool({
|
||||
name: "omniroute_x_search",
|
||||
arguments: { query: "agents sdk", max_results: 5, provider: "xquik-search" },
|
||||
});
|
||||
|
||||
expect(result.isError).toBeFalsy();
|
||||
const [, options] = mockFetch.mock.calls[0];
|
||||
const body = JSON.parse(options.body as string);
|
||||
expect(body.search_type).toBe("x");
|
||||
expect(body.provider).toBe("xquik-search");
|
||||
});
|
||||
});
|
||||
|
||||
// ── omniroute_get_health: handler dispatch tests ──────────────────────────────
|
||||
|
||||
@@ -531,12 +531,17 @@ export const xSearchInput = z.object({
|
||||
.max(20)
|
||||
.default(5)
|
||||
.describe("Maximum number of X results to return"),
|
||||
provider: z
|
||||
.enum(["x-search", "xquik-search"])
|
||||
.optional()
|
||||
.default("x-search")
|
||||
.describe("X search backend: x-search uses xAI/SuperGrok; xquik-search uses Xquik"),
|
||||
});
|
||||
|
||||
export const xSearchTool: McpToolDefinition<typeof xSearchInput, typeof webSearchOutput> = {
|
||||
name: "omniroute_x_search",
|
||||
description:
|
||||
"Search X (Twitter) through OmniRoute using SuperGrok / xAI server-side x_search. Requires a connected xai-oauth (SuperGrok) or xAI API key. This is Grok X Search, not web search and not the X Developer Platform MCP.",
|
||||
"Search X (Twitter) through OmniRoute. Uses SuperGrok / xAI server-side x_search by default, or Xquik when provider is xquik-search. Requires credentials for the selected backend. This is not web search.",
|
||||
inputSchema: xSearchInput,
|
||||
outputSchema: webSearchOutput,
|
||||
scopes: ["execute:search"],
|
||||
|
||||
@@ -666,7 +666,11 @@ async function handleWebSearch(args: {
|
||||
}
|
||||
}
|
||||
|
||||
async function handleXSearch(args: { query: string; max_results?: number }) {
|
||||
async function handleXSearch(args: {
|
||||
query: string;
|
||||
max_results?: number;
|
||||
provider?: "x-search" | "xquik-search";
|
||||
}) {
|
||||
const start = Date.now();
|
||||
try {
|
||||
const result = await omniRouteFetch("/v1/search", {
|
||||
@@ -675,7 +679,7 @@ async function handleXSearch(args: { query: string; max_results?: number }) {
|
||||
query: args.query,
|
||||
max_results: args.max_results ?? 5,
|
||||
search_type: "x",
|
||||
provider: "x-search",
|
||||
provider: args.provider ?? "x-search",
|
||||
}),
|
||||
signal: AbortSignal.timeout(120000),
|
||||
});
|
||||
|
||||
@@ -1,20 +1,40 @@
|
||||
/**
|
||||
* Account Semaphore
|
||||
* Hierarchical in-memory concurrency admission.
|
||||
*
|
||||
* In-memory provider/account concurrency limiter keyed by provider and account.
|
||||
* Requests beyond the configured concurrency cap wait in a FIFO queue until a slot opens,
|
||||
* the gate is unblocked, or the queue timeout expires.
|
||||
* `acquire()` preserves the account-semaphore API. `acquireMany()` admits one
|
||||
* request only when every applicable global/provider/account gate has room.
|
||||
*/
|
||||
|
||||
export interface AccountSemaphoreKeyParts {
|
||||
provider: string;
|
||||
accountKey: string;
|
||||
}
|
||||
|
||||
interface QueuedAcquire {
|
||||
export interface AcquireAccountSemaphoreOptions {
|
||||
maxConcurrency?: number | null;
|
||||
timeoutMs?: number;
|
||||
signal?: AbortSignal | null;
|
||||
maxQueueSize?: number;
|
||||
}
|
||||
|
||||
export interface SemaphoreRequirement {
|
||||
key: string;
|
||||
maxConcurrency?: number | null;
|
||||
}
|
||||
|
||||
export type AcquireManyOptions = Omit<AcquireAccountSemaphoreOptions, "maxConcurrency">;
|
||||
|
||||
interface AcquireRequest {
|
||||
keys: string[];
|
||||
resolve: (release: () => void) => void;
|
||||
reject: (error: Error) => void;
|
||||
timer: ReturnType<typeof setTimeout>;
|
||||
timer: ReturnType<typeof setTimeout> | null;
|
||||
signal: AbortSignal | null;
|
||||
abortListener: (() => void) | null;
|
||||
settled: boolean;
|
||||
}
|
||||
|
||||
interface QueuedAcquire {
|
||||
request: AcquireRequest;
|
||||
}
|
||||
|
||||
interface AccountGate {
|
||||
@@ -25,13 +45,6 @@ interface AccountGate {
|
||||
cleanupTimer: ReturnType<typeof setTimeout> | null;
|
||||
}
|
||||
|
||||
export interface AcquireAccountSemaphoreOptions {
|
||||
maxConcurrency?: number | null;
|
||||
timeoutMs?: number;
|
||||
signal?: AbortSignal | null;
|
||||
maxQueueSize?: number;
|
||||
}
|
||||
|
||||
export interface AccountSemaphoreStatsEntry {
|
||||
running: number;
|
||||
queued: number;
|
||||
@@ -41,12 +54,9 @@ export interface AccountSemaphoreStatsEntry {
|
||||
|
||||
const DEFAULT_TIMEOUT_MS = 30_000;
|
||||
const DEFAULT_MAX_QUEUE_SIZE = 20;
|
||||
|
||||
const gates = new Map<string, AccountGate>();
|
||||
const queuedRequests = new Set<AcquireRequest>();
|
||||
|
||||
/**
|
||||
* Build the canonical account semaphore key.
|
||||
*/
|
||||
export function buildAccountSemaphoreKey({
|
||||
provider,
|
||||
accountKey,
|
||||
@@ -55,25 +65,23 @@ export function buildAccountSemaphoreKey({
|
||||
}
|
||||
|
||||
function isBypassed(maxConcurrency?: number | null): boolean {
|
||||
return maxConcurrency == null || maxConcurrency <= 0;
|
||||
return maxConcurrency == null || !Number.isFinite(maxConcurrency) || maxConcurrency <= 0;
|
||||
}
|
||||
|
||||
function createNoopReleaseFn(): () => void {
|
||||
let released = false;
|
||||
|
||||
return () => {
|
||||
if (released) return;
|
||||
released = true;
|
||||
};
|
||||
}
|
||||
|
||||
function ensureGate(semaphoreKey: string, maxConcurrency: number): AccountGate {
|
||||
const existing = gates.get(semaphoreKey);
|
||||
function ensureGate(key: string, maxConcurrency: number): AccountGate {
|
||||
const existing = gates.get(key);
|
||||
if (existing) {
|
||||
existing.maxConcurrency = maxConcurrency;
|
||||
return existing;
|
||||
}
|
||||
|
||||
const created: AccountGate = {
|
||||
running: 0,
|
||||
maxConcurrency,
|
||||
@@ -81,7 +89,7 @@ function ensureGate(semaphoreKey: string, maxConcurrency: number): AccountGate {
|
||||
blockedUntil: null,
|
||||
cleanupTimer: null,
|
||||
};
|
||||
gates.set(semaphoreKey, created);
|
||||
gates.set(key, created);
|
||||
return created;
|
||||
}
|
||||
|
||||
@@ -100,91 +108,101 @@ function clearCleanupTimer(gate: AccountGate): void {
|
||||
gate.cleanupTimer = null;
|
||||
}
|
||||
|
||||
function cleanupGateIfIdle(semaphoreKey: string): void {
|
||||
const gate = gates.get(semaphoreKey);
|
||||
if (!gate) return;
|
||||
if (gate.running > 0 || gate.queue.length > 0 || isBlocked(gate)) return;
|
||||
function cleanupGateIfIdle(key: string): void {
|
||||
const gate = gates.get(key);
|
||||
if (!gate || gate.running > 0 || gate.queue.length > 0 || isBlocked(gate)) return;
|
||||
clearCleanupTimer(gate);
|
||||
gates.delete(semaphoreKey);
|
||||
gates.delete(key);
|
||||
}
|
||||
|
||||
function scheduleCleanup(semaphoreKey: string): void {
|
||||
const gate = gates.get(semaphoreKey);
|
||||
function scheduleCleanup(key: string): void {
|
||||
const gate = gates.get(key);
|
||||
if (!gate) return;
|
||||
clearCleanupTimer(gate);
|
||||
|
||||
gate.cleanupTimer = setTimeout(() => {
|
||||
gate.cleanupTimer = null;
|
||||
cleanupGateIfIdle(semaphoreKey);
|
||||
cleanupGateIfIdle(key);
|
||||
}, 0);
|
||||
|
||||
gate.cleanupTimer.unref?.();
|
||||
}
|
||||
|
||||
function drainQueue(semaphoreKey: string): void {
|
||||
const gate = gates.get(semaphoreKey);
|
||||
if (!gate) return;
|
||||
|
||||
while (gate.queue.length > 0 && gate.running < gate.maxConcurrency && !isBlocked(gate)) {
|
||||
const next = gate.queue.shift();
|
||||
if (!next) break;
|
||||
clearTimeout(next.timer);
|
||||
gate.running++;
|
||||
next.resolve(createReleaseFn(semaphoreKey));
|
||||
}
|
||||
|
||||
if (gate.running === 0 && gate.queue.length === 0) {
|
||||
scheduleCleanup(semaphoreKey);
|
||||
}
|
||||
}
|
||||
|
||||
function createReleaseFn(semaphoreKey: string): () => void {
|
||||
let released = false;
|
||||
|
||||
return () => {
|
||||
if (released) return;
|
||||
released = true;
|
||||
|
||||
const gate = gates.get(semaphoreKey);
|
||||
if (!gate) return;
|
||||
if (gate.running > 0) {
|
||||
gate.running--;
|
||||
}
|
||||
|
||||
if (gate.queue.length > 0) {
|
||||
drainQueue(semaphoreKey);
|
||||
return;
|
||||
}
|
||||
|
||||
scheduleCleanup(semaphoreKey);
|
||||
};
|
||||
}
|
||||
|
||||
function createSemaphoreTimeoutError(
|
||||
semaphoreKey: string,
|
||||
timeoutMs: number
|
||||
): Error & { code: string } {
|
||||
const error = new Error(`Semaphore timeout after ${timeoutMs}ms for ${semaphoreKey}`) as Error & {
|
||||
code: string;
|
||||
};
|
||||
error.code = "SEMAPHORE_TIMEOUT";
|
||||
function makeAbortError(signal: AbortSignal): Error {
|
||||
if (signal.reason instanceof Error) return signal.reason;
|
||||
const error = new Error(
|
||||
typeof signal.reason === "string" ? signal.reason : "The operation was aborted"
|
||||
);
|
||||
error.name = "AbortError";
|
||||
return error;
|
||||
}
|
||||
|
||||
function makeAbortError(signal: AbortSignal): Error {
|
||||
const reason = signal.reason;
|
||||
if (reason instanceof Error) return reason;
|
||||
const err = new Error(typeof reason === "string" ? reason : "The operation was aborted");
|
||||
err.name = "AbortError";
|
||||
return err;
|
||||
function createSemaphoreError(code: string, message: string): Error & { code: string } {
|
||||
const error = new Error(message) as Error & { code: string };
|
||||
error.code = code;
|
||||
return error;
|
||||
}
|
||||
|
||||
function removeRequest(request: AcquireRequest): void {
|
||||
queuedRequests.delete(request);
|
||||
if (request.timer) clearTimeout(request.timer);
|
||||
if (request.abortListener && request.signal) {
|
||||
request.signal.removeEventListener("abort", request.abortListener);
|
||||
}
|
||||
for (const key of request.keys) {
|
||||
const gate = gates.get(key);
|
||||
if (!gate) continue;
|
||||
const index = gate.queue.findIndex((queued) => queued.request === request);
|
||||
if (index >= 0) gate.queue.splice(index, 1);
|
||||
if (gate.running === 0 && gate.queue.length === 0) scheduleCleanup(key);
|
||||
}
|
||||
}
|
||||
|
||||
function canAcquire(request: AcquireRequest): boolean {
|
||||
return request.keys.every((key) => {
|
||||
const gate = gates.get(key);
|
||||
return (
|
||||
gate != null &&
|
||||
!isBlocked(gate) &&
|
||||
gate.running < gate.maxConcurrency &&
|
||||
gate.queue[0]?.request === request
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
function createCompositeReleaseFn(keys: string[]): () => void {
|
||||
let released = false;
|
||||
return () => {
|
||||
if (released) return;
|
||||
released = true;
|
||||
for (const key of keys) {
|
||||
const gate = gates.get(key);
|
||||
if (gate && gate.running > 0) gate.running--;
|
||||
}
|
||||
drainQueues();
|
||||
for (const key of keys) {
|
||||
const gate = gates.get(key);
|
||||
if (gate && gate.running === 0 && gate.queue.length === 0) scheduleCleanup(key);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function drainQueues(): void {
|
||||
let progressed = true;
|
||||
while (progressed) {
|
||||
progressed = false;
|
||||
for (const request of queuedRequests) {
|
||||
if (request.settled || !canAcquire(request)) continue;
|
||||
request.settled = true;
|
||||
removeRequest(request);
|
||||
for (const key of request.keys) gates.get(key)!.running++;
|
||||
request.resolve(createCompositeReleaseFn(request.keys));
|
||||
progressed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Acquire a slot for a provider/model/account tuple.
|
||||
* Returns an idempotent release function that is safe to call in finally blocks.
|
||||
*/
|
||||
export function acquire(
|
||||
semaphoreKey: string,
|
||||
key: string,
|
||||
{
|
||||
maxConcurrency = null,
|
||||
timeoutMs = DEFAULT_TIMEOUT_MS,
|
||||
@@ -192,146 +210,118 @@ export function acquire(
|
||||
maxQueueSize = DEFAULT_MAX_QUEUE_SIZE,
|
||||
}: AcquireAccountSemaphoreOptions = {}
|
||||
): Promise<() => void> {
|
||||
if (isBypassed(maxConcurrency)) {
|
||||
return Promise.resolve(createNoopReleaseFn());
|
||||
return acquireMany([{ key, maxConcurrency }], { timeoutMs, signal, maxQueueSize });
|
||||
}
|
||||
|
||||
/**
|
||||
* Acquire all enabled requirements as one FIFO reservation.
|
||||
*
|
||||
* Waiting never increments any gate, preventing a saturated child gate from
|
||||
* holding capacity in a parent gate.
|
||||
*/
|
||||
export function acquireMany(
|
||||
requirements: SemaphoreRequirement[],
|
||||
{
|
||||
timeoutMs = DEFAULT_TIMEOUT_MS,
|
||||
signal = null,
|
||||
maxQueueSize = DEFAULT_MAX_QUEUE_SIZE,
|
||||
}: AcquireManyOptions = {}
|
||||
): Promise<() => void> {
|
||||
const enabled = new Map<string, number>();
|
||||
for (const requirement of requirements) {
|
||||
if (isBypassed(requirement.maxConcurrency)) continue;
|
||||
const limit = Math.trunc(requirement.maxConcurrency as number);
|
||||
enabled.set(requirement.key, Math.min(enabled.get(requirement.key) ?? limit, limit));
|
||||
}
|
||||
if (enabled.size === 0) return Promise.resolve(createNoopReleaseFn());
|
||||
if (signal?.aborted) return Promise.reject(makeAbortError(signal));
|
||||
|
||||
const keys = [...enabled.keys()].sort();
|
||||
for (const key of keys) {
|
||||
const gate = ensureGate(key, enabled.get(key)!);
|
||||
clearCleanupTimer(gate);
|
||||
if (maxQueueSize > 0 && gate.queue.length >= maxQueueSize) {
|
||||
return Promise.reject(
|
||||
createSemaphoreError(
|
||||
"SEMAPHORE_QUEUE_FULL",
|
||||
`Semaphore queue full (${maxQueueSize}) for ${key}`
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (signal?.aborted) {
|
||||
return Promise.reject(makeAbortError(signal));
|
||||
}
|
||||
|
||||
// isBypassed() above already excluded null/<=0 — ensureGate requires a plain
|
||||
// number, but a boolean-returning helper isn't a type predicate TS can narrow on.
|
||||
const gate = ensureGate(semaphoreKey, maxConcurrency as number);
|
||||
clearCleanupTimer(gate);
|
||||
|
||||
if (gate.running < gate.maxConcurrency && !isBlocked(gate)) {
|
||||
gate.running++;
|
||||
return Promise.resolve(createReleaseFn(semaphoreKey));
|
||||
}
|
||||
|
||||
if (gate.queue.length >= maxQueueSize) {
|
||||
const err = new Error(`Semaphore queue full (${maxQueueSize}) for ${semaphoreKey}`) as Error & {
|
||||
code: string;
|
||||
};
|
||||
err.code = "SEMAPHORE_QUEUE_FULL";
|
||||
return Promise.reject(err);
|
||||
if (
|
||||
keys.every((key) => {
|
||||
const gate = gates.get(key)!;
|
||||
return gate.queue.length === 0 && gate.running < gate.maxConcurrency && !isBlocked(gate);
|
||||
})
|
||||
) {
|
||||
for (const key of keys) gates.get(key)!.running++;
|
||||
return Promise.resolve(createCompositeReleaseFn(keys));
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
let abortListener: (() => void) | null = null;
|
||||
|
||||
const cleanup = () => {
|
||||
if (abortListener && signal) {
|
||||
signal.removeEventListener("abort", abortListener);
|
||||
}
|
||||
const request: AcquireRequest = {
|
||||
keys,
|
||||
resolve,
|
||||
reject,
|
||||
timer: null,
|
||||
signal,
|
||||
abortListener: null,
|
||||
settled: false,
|
||||
};
|
||||
|
||||
const timer = setTimeout(() => {
|
||||
cleanup();
|
||||
const nextGate = gates.get(semaphoreKey);
|
||||
if (!nextGate) {
|
||||
reject(createSemaphoreTimeoutError(semaphoreKey, timeoutMs));
|
||||
return;
|
||||
}
|
||||
|
||||
const queueIndex = nextGate.queue.findIndex((item) => item.timer === timer);
|
||||
if (queueIndex !== -1) {
|
||||
nextGate.queue.splice(queueIndex, 1);
|
||||
}
|
||||
|
||||
if (nextGate.running === 0 && nextGate.queue.length === 0) {
|
||||
scheduleCleanup(semaphoreKey);
|
||||
}
|
||||
|
||||
reject(createSemaphoreTimeoutError(semaphoreKey, timeoutMs));
|
||||
request.timer = setTimeout(() => {
|
||||
if (request.settled) return;
|
||||
request.settled = true;
|
||||
removeRequest(request);
|
||||
reject(
|
||||
createSemaphoreError(
|
||||
"SEMAPHORE_TIMEOUT",
|
||||
`Semaphore timeout after ${timeoutMs}ms for ${keys.join(",")}`
|
||||
)
|
||||
);
|
||||
drainQueues();
|
||||
}, timeoutMs);
|
||||
|
||||
timer.unref?.();
|
||||
|
||||
const queueItem: QueuedAcquire = {
|
||||
resolve: (release) => {
|
||||
cleanup();
|
||||
resolve(release);
|
||||
},
|
||||
reject: (error) => {
|
||||
cleanup();
|
||||
reject(error);
|
||||
},
|
||||
timer,
|
||||
};
|
||||
|
||||
gate.queue.push(queueItem);
|
||||
|
||||
request.timer.unref?.();
|
||||
if (signal) {
|
||||
abortListener = () => {
|
||||
cleanup();
|
||||
clearTimeout(timer);
|
||||
|
||||
const nextGate = gates.get(semaphoreKey);
|
||||
if (!nextGate) {
|
||||
reject(makeAbortError(signal));
|
||||
return;
|
||||
}
|
||||
|
||||
const queueIndex = nextGate.queue.findIndex((item) => item.timer === timer);
|
||||
if (queueIndex !== -1) {
|
||||
nextGate.queue.splice(queueIndex, 1);
|
||||
}
|
||||
|
||||
if (nextGate.running === 0 && nextGate.queue.length === 0) {
|
||||
scheduleCleanup(semaphoreKey);
|
||||
}
|
||||
|
||||
request.abortListener = () => {
|
||||
if (request.settled) return;
|
||||
request.settled = true;
|
||||
removeRequest(request);
|
||||
reject(makeAbortError(signal));
|
||||
drainQueues();
|
||||
};
|
||||
if (signal.aborted) {
|
||||
abortListener();
|
||||
} else {
|
||||
signal.addEventListener("abort", abortListener);
|
||||
}
|
||||
signal.addEventListener("abort", request.abortListener, { once: true });
|
||||
}
|
||||
queuedRequests.add(request);
|
||||
for (const key of keys) gates.get(key)!.queue.push({ request });
|
||||
drainQueues();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Temporarily block new acquisitions for a key while allowing in-flight requests to finish.
|
||||
*/
|
||||
export function markBlocked(semaphoreKey: string, cooldownMs: number): void {
|
||||
const safeCooldownMs = Number.isFinite(cooldownMs) && cooldownMs > 0 ? cooldownMs : 0;
|
||||
if (safeCooldownMs <= 0) {
|
||||
const gate = gates.get(semaphoreKey);
|
||||
if (!gate) return;
|
||||
gate.blockedUntil = null;
|
||||
drainQueue(semaphoreKey);
|
||||
return;
|
||||
}
|
||||
|
||||
const gate = gates.get(semaphoreKey) ?? ensureGate(semaphoreKey, 1);
|
||||
export function markBlocked(key: string, until: Date | string | number): void {
|
||||
const untilMs =
|
||||
until instanceof Date
|
||||
? until.getTime()
|
||||
: typeof until === "number"
|
||||
? Date.now() + Math.max(0, until)
|
||||
: new Date(until).getTime();
|
||||
if (!Number.isFinite(untilMs) || untilMs <= Date.now()) return;
|
||||
const gate = ensureGate(key, gates.get(key)?.maxConcurrency ?? 1);
|
||||
clearCleanupTimer(gate);
|
||||
gate.blockedUntil = Date.now() + safeCooldownMs;
|
||||
gate.blockedUntil = untilMs;
|
||||
}
|
||||
|
||||
const timer = setTimeout(() => {
|
||||
const nextGate = gates.get(semaphoreKey);
|
||||
if (!nextGate) return;
|
||||
if (nextGate.blockedUntil && Date.now() >= nextGate.blockedUntil) {
|
||||
nextGate.blockedUntil = null;
|
||||
drainQueue(semaphoreKey);
|
||||
if (nextGate.running === 0 && nextGate.queue.length === 0) {
|
||||
scheduleCleanup(semaphoreKey);
|
||||
}
|
||||
}
|
||||
}, safeCooldownMs + 50);
|
||||
|
||||
timer.unref?.();
|
||||
export function unblock(key: string): void {
|
||||
const gate = gates.get(key);
|
||||
if (!gate) return;
|
||||
gate.blockedUntil = null;
|
||||
drainQueues();
|
||||
cleanupGateIfIdle(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the current in-memory semaphore snapshot.
|
||||
*/
|
||||
export function getStats(): Record<string, AccountSemaphoreStatsEntry> {
|
||||
const stats: Record<string, AccountSemaphoreStatsEntry> = {};
|
||||
|
||||
for (const [key, gate] of gates) {
|
||||
stats[key] = {
|
||||
running: gate.running,
|
||||
@@ -340,48 +330,50 @@ export function getStats(): Record<string, AccountSemaphoreStatsEntry> {
|
||||
blockedUntil: gate.blockedUntil ? new Date(gate.blockedUntil).toISOString() : null,
|
||||
};
|
||||
}
|
||||
|
||||
return stats;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if an account semaphore key is currently at or over its max concurrency limit.
|
||||
* Returns true if running >= maxConcurrency or blocked.
|
||||
*/
|
||||
export function isAccountSemaphoreFull(
|
||||
provider: string,
|
||||
accountKey: string,
|
||||
maxConcurrency?: number | null
|
||||
): boolean {
|
||||
if (isBypassed(maxConcurrency)) return false;
|
||||
const key = buildAccountSemaphoreKey({ provider, accountKey });
|
||||
const gate = gates.get(key);
|
||||
const gate = gates.get(buildAccountSemaphoreKey({ provider, accountKey }));
|
||||
if (!gate) return false;
|
||||
const effectiveCap = maxConcurrency ?? gate.maxConcurrency;
|
||||
if (isBypassed(effectiveCap)) return false;
|
||||
return gate.running >= effectiveCap || isBlocked(gate);
|
||||
return !isBypassed(effectiveCap) && (gate.running >= effectiveCap || isBlocked(gate));
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset a single key and reject queued waiters.
|
||||
*/
|
||||
export function reset(semaphoreKey: string): void {
|
||||
const gate = gates.get(semaphoreKey);
|
||||
export function reset(key: string): void {
|
||||
const gate = gates.get(key);
|
||||
if (!gate) return;
|
||||
|
||||
clearCleanupTimer(gate);
|
||||
for (const entry of gate.queue) {
|
||||
clearTimeout(entry.timer);
|
||||
entry.reject(new Error("Semaphore reset"));
|
||||
const error = createSemaphoreError("SEMAPHORE_RESET", `Semaphore reset for ${key}`);
|
||||
const rejections: AcquireRequest[] = [];
|
||||
for (const queued of [...gate.queue]) {
|
||||
const request = queued.request;
|
||||
if (request.settled) continue;
|
||||
request.settled = true;
|
||||
removeRequest(request);
|
||||
rejections.push(request);
|
||||
}
|
||||
gates.delete(semaphoreKey);
|
||||
gates.delete(key);
|
||||
for (const request of rejections) request.reject(error);
|
||||
drainQueues();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset all keys and reject queued waiters.
|
||||
*/
|
||||
export function resetAll(): void {
|
||||
for (const key of gates.keys()) {
|
||||
reset(key);
|
||||
const error = createSemaphoreError("SEMAPHORE_RESET", "Semaphore reset");
|
||||
const rejections: AcquireRequest[] = [];
|
||||
for (const request of [...queuedRequests]) {
|
||||
if (request.settled) continue;
|
||||
request.settled = true;
|
||||
removeRequest(request);
|
||||
rejections.push(request);
|
||||
}
|
||||
for (const gate of gates.values()) clearCleanupTimer(gate);
|
||||
gates.clear();
|
||||
queuedRequests.clear();
|
||||
for (const request of rejections) request.reject(error);
|
||||
}
|
||||
|
||||
@@ -50,6 +50,7 @@
|
||||
*/
|
||||
import {
|
||||
FREE_MODEL_BUDGETS,
|
||||
grantsFreeAccess,
|
||||
type FreeModelBudget,
|
||||
} from "@omniroute/open-sse/config/freeModelCatalog.ts";
|
||||
import { SYNTHETIC_NOAUTH_CONNECTION_ID } from "./resilienceCandidateFilter";
|
||||
@@ -184,7 +185,7 @@ export function evaluateCandidateConnections(
|
||||
// entries today, so it will correctly exclude).
|
||||
if (isGenuineNoAuthCandidate) return [SYNTHETIC_NOAUTH_CONNECTION_ID];
|
||||
}
|
||||
if (budgetEntry.freeType === "discontinued") return [];
|
||||
if (!grantsFreeAccess(budgetEntry.freeType)) return [];
|
||||
if (isGenuineNoAuthCandidate) return []; // no-auth path but a non-keyless catalog entry: contradictory metadata, fail closed
|
||||
|
||||
// Every remaining freeType (recurring-*, one-time-initial, a keyless entry
|
||||
|
||||
@@ -261,7 +261,7 @@ function getWindowsMapQuotaWindow(
|
||||
);
|
||||
}
|
||||
|
||||
function resolveQuotaWindowByName(
|
||||
export function resolveQuotaWindowByName(
|
||||
quota: unknown,
|
||||
windowName: ResetWindowName
|
||||
): QuotaWindowSnapshot | null {
|
||||
@@ -321,8 +321,8 @@ export function scoreResetAwareQuota(
|
||||
if (quota.limitReached === true) return { score: -Infinity };
|
||||
|
||||
const overallPercentUsed = clamp01(finiteNumberOrNull(quota.percentUsed) ?? 0.5);
|
||||
const sessionWindow = getQuotaWindow(quota, "window5h");
|
||||
const weeklyWindow = getQuotaWindow(quota, "window7d") || getQuotaWindow(quota, "windowWeekly");
|
||||
const sessionWindow = resolveQuotaWindowByName(quota, "session");
|
||||
const weeklyWindow = resolveQuotaWindowByName(quota, "weekly");
|
||||
const sessionRemaining = clamp01(1 - (sessionWindow?.percentUsed ?? overallPercentUsed));
|
||||
const weeklyRemaining = clamp01(1 - (weeklyWindow?.percentUsed ?? overallPercentUsed));
|
||||
const sessionScore = scoreQuotaWindow(
|
||||
|
||||
@@ -16,9 +16,10 @@ export function createExecutorModelClient(
|
||||
credentials: ProviderCredentials,
|
||||
costPerKTokenOut?: number
|
||||
): ModelClient {
|
||||
const executor = getExecutor(provider);
|
||||
return {
|
||||
async complete(model: string, messages: ChatTurn[]): Promise<ModelCallResult> {
|
||||
// #11220: getExecutor is async (lazy registry) — resolve per call.
|
||||
const executor = await getExecutor(provider);
|
||||
const body = { model, messages, stream: false };
|
||||
const input: ExecuteInput = {
|
||||
model,
|
||||
|
||||
@@ -150,16 +150,65 @@ export function convertUsageToQuotaInfo(usage: unknown): QuotaInfo | null {
|
||||
|
||||
if (Object.keys(windows).length === 0) return null;
|
||||
|
||||
const normalized = normalizeQuotaWindows(windows);
|
||||
Object.assign(windows, normalized);
|
||||
|
||||
return {
|
||||
used: 0,
|
||||
total: 0,
|
||||
percentUsed: worstPercent,
|
||||
resetAt: worstResetAt,
|
||||
windows,
|
||||
...normalized,
|
||||
limitReached: worstPercent >= 1 - 1e-9,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Map provider-native window keys to canonical structural windows so that
|
||||
* reset-aware / reset-window scoring works without knowing every provider's
|
||||
* naming convention.
|
||||
*
|
||||
* - Claude: "session (5h)" → window5h, "weekly (7d)" → window7d
|
||||
* - Antigravity: worst per-model quota → window5h; worst *_weekly quota → window7d
|
||||
*/
|
||||
function normalizeQuotaWindows(
|
||||
windows: Record<string, { percentUsed: number; resetAt: string | null }>
|
||||
): Record<string, { percentUsed: number; resetAt: string | null }> {
|
||||
const normalized: Record<string, { percentUsed: number; resetAt: string | null }> = {};
|
||||
|
||||
// Claude-style explicit time windows.
|
||||
if (windows["session (5h)"] && !normalized.window5h) {
|
||||
normalized.window5h = windows["session (5h)"];
|
||||
}
|
||||
if (windows["weekly (7d)"] && !normalized.window7d) {
|
||||
normalized.window7d = windows["weekly (7d)"];
|
||||
}
|
||||
|
||||
// Antigravity-style per-model 5h windows: pick the worst (most used) model quota.
|
||||
const modelWindows = Object.entries(windows).filter(
|
||||
([key]) =>
|
||||
key !== "credits" &&
|
||||
!key.endsWith("_weekly") &&
|
||||
!key.startsWith("window") &&
|
||||
!key.includes("(5h)") &&
|
||||
!key.includes("(7d)")
|
||||
);
|
||||
if (modelWindows.length > 0 && !normalized.window5h) {
|
||||
const worst = modelWindows.reduce((a, b) => (a[1].percentUsed > b[1].percentUsed ? a : b));
|
||||
normalized.window5h = worst[1];
|
||||
}
|
||||
|
||||
// Antigravity-style weekly family buckets: pick the worst *_weekly quota.
|
||||
const weeklyWindows = Object.entries(windows).filter(([key]) => key.endsWith("_weekly"));
|
||||
if (weeklyWindows.length > 0 && !normalized.window7d) {
|
||||
const worst = weeklyWindows.reduce((a, b) => (a[1].percentUsed > b[1].percentUsed ? a : b));
|
||||
normalized.window7d = worst[1];
|
||||
}
|
||||
|
||||
return normalized;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch quota for a connection by delegating to the appropriate
|
||||
* provider-specific usage fetcher and reshaping its output into the
|
||||
|
||||
@@ -48,6 +48,16 @@ export interface QuotaInfo {
|
||||
* (e.g. "session", "weekly", "monthly").
|
||||
*/
|
||||
windows?: Record<string, QuotaWindowInfo>;
|
||||
/**
|
||||
* Structural, canonical window snapshots used by reset-aware / reset-window
|
||||
* scoring. Providers that expose time-based windows (5h, weekly, monthly)
|
||||
* populate these in addition to the provider-native `windows` map so the
|
||||
* scorer does not need to know every provider's key naming convention.
|
||||
*/
|
||||
window5h?: QuotaWindowInfo;
|
||||
window7d?: QuotaWindowInfo;
|
||||
windowWeekly?: QuotaWindowInfo;
|
||||
windowMonthly?: QuotaWindowInfo;
|
||||
/** True when the upstream usage endpoint explicitly reports exhausted quota. */
|
||||
limitReached?: boolean;
|
||||
}
|
||||
|
||||
@@ -96,13 +96,6 @@ const ARK_CONSOLE_URL =
|
||||
/** Cookie names required for a valid console session (mirrors tokenExtractionConfig) */
|
||||
const REQUIRED_COOKIES = ["digest", "AccountID", "csrfToken", "userInfo"] as const;
|
||||
|
||||
/** Exact-domain match for session cookies — substring checks would also accept
|
||||
* look-alike hosts (e.g. `volcengine.com.evil.test`). Playwright may report the
|
||||
* domain with or without a leading dot. */
|
||||
function isVolcengineCookieDomain(domain: string): boolean {
|
||||
return domain === "volcengine.com" || domain.endsWith(".volcengine.com");
|
||||
}
|
||||
|
||||
const DEFAULT_SESSION_TIMEOUT = 300_000;
|
||||
const SUBMIT_COOKIE_TIMEOUT = 90_000;
|
||||
const CAPTURE_POLL_INTERVAL = 1_000;
|
||||
@@ -625,7 +618,7 @@ export class VolcengineConsoleAutoLoginService {
|
||||
for (const cookie of cookies as Array<{ name: string; domain: string; value: string }>) {
|
||||
if (
|
||||
REQUIRED_COOKIES.includes(cookie.name as (typeof REQUIRED_COOKIES)[number]) &&
|
||||
isVolcengineCookieDomain(cookie.domain)
|
||||
cookie.domain.includes("volcengine.com")
|
||||
) {
|
||||
credentials[cookie.name] = cookie.value;
|
||||
}
|
||||
@@ -770,7 +763,7 @@ export class VolcengineConsoleAutoLoginService {
|
||||
domain: string;
|
||||
}>;
|
||||
const present = REQUIRED_COOKIES.filter((name) =>
|
||||
cookies.some((c) => c.name === name && isVolcengineCookieDomain(c.domain))
|
||||
cookies.some((c) => c.name === name && c.domain.includes("volcengine.com"))
|
||||
);
|
||||
parts.push(
|
||||
`cookies=[${present.join(",") || "none of digest/AccountID/csrfToken/userInfo"}]`
|
||||
|
||||
2436
package-lock.json
generated
49
package.json
@@ -261,7 +261,7 @@
|
||||
"alibaba:sync-allowlist": "node --import tsx/esm scripts/ops/sync-alibaba-allowlist.mjs"
|
||||
},
|
||||
"dependencies": {
|
||||
"@aws-sdk/client-bedrock-runtime": "^3.1112.0",
|
||||
"@aws-sdk/client-bedrock-runtime": "^3.1116.0",
|
||||
"@dnd-kit/core": "^6.3.1",
|
||||
"@dnd-kit/sortable": "^10.0.0",
|
||||
"@dnd-kit/utilities": "^3.2.2",
|
||||
@@ -280,29 +280,29 @@
|
||||
"commander": "^15.0.0",
|
||||
"cron-parser": "^5.10.0",
|
||||
"csv-stringify": "^6.8.3",
|
||||
"dompurify": "^3.4.13",
|
||||
"dompurify": "^3.4.14",
|
||||
"express": "^5.2.1",
|
||||
"fetch-socks": "^1.3.3",
|
||||
"fflate": "^0.8.3",
|
||||
"fumadocs-core": "^16.14.4",
|
||||
"fumadocs-ui": "^16.14.4",
|
||||
"fumadocs-core": "^16.15.1",
|
||||
"fumadocs-ui": "^16.15.1",
|
||||
"http-proxy-middleware": "^4.0.0",
|
||||
"https-proxy-agent": "^9.0.0",
|
||||
"ink": "^7.0.3",
|
||||
"ink-spinner": "^5.0.0",
|
||||
"ink-text-input": "^6.0.0",
|
||||
"ioredis": "^5.10.1",
|
||||
"jose": "^6.2.9",
|
||||
"jose": "^6.2.10",
|
||||
"js-yaml": "^5.3.0",
|
||||
"jsonc-parser": "^3.3.1",
|
||||
"lowdb": "^7.0.1",
|
||||
"lucide-react": "^1.21.0",
|
||||
"marked": "^18.0.9",
|
||||
"lucide-react": "^1.33.0",
|
||||
"marked": "^18.0.10",
|
||||
"marked-terminal": "^7.3.0",
|
||||
"material-symbols": "^0.46.0",
|
||||
"mermaid": "^11.15.0",
|
||||
"mermaid": "^11.17.0",
|
||||
"monaco-editor": "^0.56.0",
|
||||
"next": "16.3.1",
|
||||
"next": "16.3.2",
|
||||
"next-intl": "^4.13.7",
|
||||
"next-themes": "^0.4.6",
|
||||
"node-machine-id": "^1.1.12",
|
||||
@@ -332,7 +332,7 @@
|
||||
"turndown-plugin-gfm": "1.0.2",
|
||||
"undici": "^8.10.0",
|
||||
"update-notifier": "^7.3.1",
|
||||
"uuid": "^14.0.0",
|
||||
"uuid": "^14.0.2",
|
||||
"ws": "^8.21.3",
|
||||
"xxhash-wasm": "^1.1.0",
|
||||
"yazl": "^3.3.1",
|
||||
@@ -348,11 +348,12 @@
|
||||
"onnxruntime-node": "1.24.3",
|
||||
"sqlite-vec": "^0.1.9",
|
||||
"tls-client-node": "^0.2.0",
|
||||
"wreq-js": "^3.0.0"
|
||||
"wreq-js": "^3.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@axe-core/playwright": "^4.13.0",
|
||||
"@cyclonedx/cyclonedx-npm": "6.0.1",
|
||||
"@eslint/compat": "^2.1.0",
|
||||
"@playwright/test": "^1.62.1",
|
||||
"@size-limit/file": "^13.0.3",
|
||||
"@stryker-mutator/core": "^10.0.0",
|
||||
@@ -363,24 +364,25 @@
|
||||
"@testing-library/react": "^16.3.2",
|
||||
"@testing-library/user-event": "^14.6.6",
|
||||
"@types/better-sqlite3": "^9.6.0",
|
||||
"@types/bun": "latest",
|
||||
"@types/bun": "1.4.0",
|
||||
"@types/node": "^26.2.0",
|
||||
"@types/react": "^19.2.18",
|
||||
"@types/react-dom": "^19.2.4",
|
||||
"@types/safe-regex": "^1.1.6",
|
||||
"@types/ws": "^8.18.0",
|
||||
"@vitejs/plugin-react": "^6.0.5",
|
||||
"bun": "1.3.14",
|
||||
"@vitejs/plugin-react": "^6.1.0",
|
||||
"bun": "1.4.0",
|
||||
"c8": "^12.0.0",
|
||||
"concurrently": "^10.0.5",
|
||||
"cross-env": "^10.1.0",
|
||||
"ctrf": "^0.3.0",
|
||||
"dpdm": "^4.3.0",
|
||||
"eslint": "^9.39.4",
|
||||
"eslint-config-next": "16.3.1",
|
||||
"eslint": "^10.9.0",
|
||||
"eslint-config-next": "16.3.2",
|
||||
"eslint-plugin-sonarjs": "^4.1.0",
|
||||
"espree": "^11.2.0",
|
||||
"fast-check": "^4.8.0",
|
||||
"fumadocs-mdx": "^15.2.3",
|
||||
"fumadocs-mdx": "^15.3.1",
|
||||
"glob": "^13.0.6",
|
||||
"httpyac": "^6.16.7",
|
||||
"husky": "^9.1.7",
|
||||
@@ -392,7 +394,7 @@
|
||||
"lint-staged": "^17.3.0",
|
||||
"lockfile-lint": "^5.0.1",
|
||||
"node-loader": "^2.1.0",
|
||||
"opencode-ai": "1.18.18",
|
||||
"opencode-ai": "1.18.21",
|
||||
"playwright-ctrf-json-reporter": "^0.0.29",
|
||||
"prettier": "^3.9.6",
|
||||
"promptfoo": "^0.122.0",
|
||||
@@ -401,7 +403,7 @@
|
||||
"type-coverage": "^2.30.1",
|
||||
"typescript": "^6.0.3",
|
||||
"typescript-eslint": "^8.67.0",
|
||||
"vitest": "^4.1.7",
|
||||
"vitest": "^4.1.11",
|
||||
"wait-on": "^9.1.0",
|
||||
"wtfnode": "^0.10.1"
|
||||
},
|
||||
@@ -435,12 +437,13 @@
|
||||
},
|
||||
"overrides": {
|
||||
"onnxruntime-node": "1.24.3",
|
||||
"eslint-plugin-react-hooks": "7.0.1",
|
||||
"fast-xml-parser": "^5.10.1",
|
||||
"sharp": "^0.35.3",
|
||||
"postcss": "^8.5.18",
|
||||
"ip-address": "^10.3.1",
|
||||
"qs": "^6.15.2",
|
||||
"uuid": "^14.0.0",
|
||||
"uuid": "^14.0.2",
|
||||
"form-data": "^4.0.6",
|
||||
"vite": "^8.0.16",
|
||||
"protobufjs": "^7.6.5",
|
||||
@@ -468,10 +471,6 @@
|
||||
},
|
||||
"socket.io-parser": "^4.2.7",
|
||||
"tar": "^7.5.21",
|
||||
"brace-expansion": "^5.0.9",
|
||||
"minimatch": {
|
||||
"brace-expansion": "^1.1.18"
|
||||
},
|
||||
"libxmljs2": {
|
||||
"minimatch": {
|
||||
"brace-expansion": "^2.1.4"
|
||||
@@ -496,7 +495,7 @@
|
||||
},
|
||||
"nanoid": "^3.3.17",
|
||||
"monaco-editor": {
|
||||
"dompurify": "^3.4.13"
|
||||
"dompurify": "^3.4.14"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
34
scripts/build/afterpack-copy-node-modules.mjs
Normal file
@@ -0,0 +1,34 @@
|
||||
import { cpSync, existsSync, readdirSync, rmSync } from "node:fs";
|
||||
import { join } from "node:path";
|
||||
|
||||
// electron-builder >=26 injects an "!**/node_modules/**" ignore into every
|
||||
// extraResources/extraFiles pattern list (app-builder-lib/out/fileMatcher.js),
|
||||
// and that ignore cannot be overridden by any later positive filter pattern —
|
||||
// verified empirically with a minimal fixture on 26.15.3. The standalone
|
||||
// server resolves better-sqlite3 (and other runtime deps) from the *primary*
|
||||
// node_modules at resources/app/node_modules (see
|
||||
// prepare-electron-standalone.mjs: "Verify better-sqlite3 Node-API prebuilds in
|
||||
// the primary node_modules"), so without this hook the packaged desktop app silently loses its native
|
||||
// SQLite driver and falls back to sql.js — the exact regression guarded by
|
||||
// issue #7592's cold-restart smoke check.
|
||||
export default async function afterPack(context) {
|
||||
const stagingNodeModules = join(
|
||||
context.packager.projectDir,
|
||||
"..",
|
||||
".build",
|
||||
"electron-standalone",
|
||||
"node_modules"
|
||||
);
|
||||
const destNodeModules = join(context.appOutDir, "resources", "app", "node_modules");
|
||||
|
||||
if (!existsSync(stagingNodeModules)) {
|
||||
console.warn(`[afterpack] no staged node_modules at ${stagingNodeModules} — skipping restore`);
|
||||
return;
|
||||
}
|
||||
|
||||
rmSync(destNodeModules, { recursive: true, force: true });
|
||||
cpSync(stagingNodeModules, destNodeModules, { recursive: true });
|
||||
console.log(
|
||||
`[afterpack] restored ${readdirSync(destNodeModules).length} runtime module(s) into resources/app/node_modules`
|
||||
);
|
||||
}
|
||||
@@ -89,16 +89,20 @@ function moveTree(src, dest) {
|
||||
return { removedFiles: files, removedBytes: bytes };
|
||||
}
|
||||
|
||||
function tarPack(packOutDir, tarballPath) {
|
||||
export function tarPack(packOutDir, tarballPath) {
|
||||
// bsdtar ships with macOS, Linux images, and Windows runners (System32\tar.exe).
|
||||
// GNU tar (common in Git-for-Windows environments) treats `C:\...` in `-f` as a
|
||||
// remote rsh target ("Cannot connect to C:"), so always pass a bare filename
|
||||
// and point cwd at the tarball directory instead.
|
||||
const result = spawnSync(
|
||||
process.platform === "win32" ? "tar.exe" : "tar",
|
||||
["-czf", tarballPath, "-C", packOutDir, "node_modules"],
|
||||
{ stdio: "pipe" }
|
||||
["-czf", path.basename(tarballPath), "-C", packOutDir, "node_modules"],
|
||||
{ stdio: "pipe", cwd: path.dirname(tarballPath) }
|
||||
);
|
||||
if (result.status !== 0) {
|
||||
throw new Error(
|
||||
`optional-pack tar failed for ${path.basename(tarballPath)} (exit ${result.status})`
|
||||
`optional-pack tar failed for ${path.basename(tarballPath)} (exit ${result.status})` +
|
||||
`: ${(result.stderr || result.stdout || "").toString().slice(-600)}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -360,7 +360,7 @@ function isInsideDir(parentDir, candidateDir) {
|
||||
return candidate === parent || candidate.startsWith(parent + sep);
|
||||
}
|
||||
|
||||
async function ensureSmokeEnvDirs(smokeEnv, dataDir) {
|
||||
export async function ensureSmokeEnvDirs(smokeEnv, dataDir) {
|
||||
const dirNames = [
|
||||
"DATA_DIR",
|
||||
"HOME",
|
||||
@@ -388,6 +388,16 @@ async function ensureSmokeEnvDirs(smokeEnv, dataDir) {
|
||||
dirs.push(join(smokeEnv.APPDATA, subdir));
|
||||
}
|
||||
}
|
||||
// Electron resolves the Roaming profile from %USERPROFILE%\AppData\Roaming
|
||||
// (USERPROFILE takes precedence over the APPDATA env var) and the path
|
||||
// service throws — rather than creates — when that directory is missing,
|
||||
// which makes requestSingleInstanceLock() return false and the app exit(0)
|
||||
// before app.whenReady(). Pre-create the derived tree as well.
|
||||
if (platform() === "win32" && smokeEnv.USERPROFILE) {
|
||||
for (const subdir of ["omniroute-desktop", "OmniRoute", "omniroute"]) {
|
||||
dirs.push(join(smokeEnv.USERPROFILE, "AppData", "Roaming", subdir));
|
||||
}
|
||||
}
|
||||
|
||||
await Promise.all(dirs.map((dir) => mkdir(dir, { recursive: true })));
|
||||
}
|
||||
@@ -506,7 +516,14 @@ async function waitForReady({ logs, smokeUrl, timeoutMs, settleMs, exitState })
|
||||
* by the single-launch path and the cold-restart (two-launch) path so both
|
||||
* exercise identical spawn/readiness/shutdown behavior.
|
||||
*/
|
||||
async function launchAndCollectLogs({ appExecutable, smokeUrl, dataDir, timeoutMs, settleMs, streamLogs }) {
|
||||
async function launchAndCollectLogs({
|
||||
appExecutable,
|
||||
smokeUrl,
|
||||
dataDir,
|
||||
timeoutMs,
|
||||
settleMs,
|
||||
streamLogs,
|
||||
}) {
|
||||
const smokeEnv = buildSmokeEnv({ dataDir });
|
||||
await assertPortIsFree(smokeUrl);
|
||||
await ensureSmokeEnvDirs(smokeEnv, dataDir);
|
||||
@@ -568,7 +585,14 @@ async function main() {
|
||||
!process.env.ELECTRON_SMOKE_DATA_DIR && process.env.ELECTRON_SMOKE_KEEP_DATA !== "1";
|
||||
|
||||
try {
|
||||
await launchAndCollectLogs({ appExecutable, smokeUrl, dataDir, timeoutMs, settleMs, streamLogs });
|
||||
await launchAndCollectLogs({
|
||||
appExecutable,
|
||||
smokeUrl,
|
||||
dataDir,
|
||||
timeoutMs,
|
||||
settleMs,
|
||||
streamLogs,
|
||||
});
|
||||
|
||||
if (!coldRestart) return;
|
||||
|
||||
|
||||