Compare commits
1 Commits
security/v
...
fix/codeql
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
528fba953d |
@@ -23,7 +23,7 @@
|
||||
"scripts": {
|
||||
"build": "tsup",
|
||||
"clean": "rm -rf dist",
|
||||
"test": "node --import tsx/esm --test tests/scaffold.test.ts tests/auth.test.ts tests/options-schema.test.ts tests/multi-instance.test.ts tests/fetch-interceptor.test.ts tests/provider.test.ts tests/gemini-sanitize.test.ts tests/combos.test.ts tests/config-shim.test.ts tests/features.test.ts tests/feature-defaults.test.ts tests/usable-combo.test.ts tests/disk-snapshot-perms.test.ts tests/fork-features.test.ts tests/auto-combo-context.test.ts tests/provider-id-routing.test.ts tests/management-read-token.test.ts tests/auto-sync.test.ts tests/model-allowlist.test.ts tests/log-level.test.ts tests/effort-tier-variants.test.ts tests/naming.test.ts tests/free-budget-magnitude.test.ts",
|
||||
"test": "node --import tsx/esm --test tests/scaffold.test.ts tests/auth.test.ts tests/options-schema.test.ts tests/multi-instance.test.ts tests/fetch-interceptor.test.ts tests/provider.test.ts tests/gemini-sanitize.test.ts tests/combos.test.ts tests/config-shim.test.ts tests/features.test.ts tests/feature-defaults.test.ts tests/usable-combo.test.ts tests/disk-snapshot-perms.test.ts tests/fork-features.test.ts tests/auto-combo-context.test.ts tests/provider-id-routing.test.ts tests/management-read-token.test.ts tests/auto-sync.test.ts tests/model-allowlist.test.ts tests/log-level.test.ts tests/effort-tier-variants.test.ts",
|
||||
"prepublishOnly": "npm run clean && npm run build && npm test"
|
||||
},
|
||||
"keywords": [
|
||||
|
||||
@@ -23,11 +23,27 @@ const ALIAS_UPPER_MAX_CHARS = 5;
|
||||
|
||||
// ── Auto Combo Types ─────────────────────────────────────────────────────
|
||||
|
||||
export type AutoVariant = "coding" | "fast" | "cheap" | "offline" | "smart" | "lkgp";
|
||||
export type AutoVariant =
|
||||
| "coding"
|
||||
| "fast"
|
||||
| "cheap"
|
||||
| "offline"
|
||||
| "smart"
|
||||
| "lkgp";
|
||||
|
||||
export const AUTO_VARIANTS: AutoVariant[] = ["coding", "fast", "cheap", "offline", "smart", "lkgp"];
|
||||
export const AUTO_VARIANTS: AutoVariant[] = [
|
||||
"coding",
|
||||
"fast",
|
||||
"cheap",
|
||||
"offline",
|
||||
"smart",
|
||||
"lkgp",
|
||||
];
|
||||
|
||||
export const AUTO_VARIANT_DESCRIPTIONS: Record<AutoVariant | "default", string> = {
|
||||
export const AUTO_VARIANT_DESCRIPTIONS: Record<
|
||||
AutoVariant | "default",
|
||||
string
|
||||
> = {
|
||||
default: "Best provider via scoring",
|
||||
coding: "Quality-first for code tasks",
|
||||
fast: "Latency-optimized routing",
|
||||
@@ -67,15 +83,24 @@ function titleCaseAlias(alias: string): string {
|
||||
* 3. Neither → undefined.
|
||||
*/
|
||||
export function shortProviderLabel(
|
||||
enrichment: { providerDisplayName?: string; providerAlias?: string } | undefined
|
||||
enrichment:
|
||||
| { providerDisplayName?: string; providerAlias?: string }
|
||||
| undefined,
|
||||
): string | undefined {
|
||||
if (!enrichment) return undefined;
|
||||
const raw =
|
||||
typeof enrichment.providerDisplayName === "string" ? enrichment.providerDisplayName.trim() : "";
|
||||
typeof enrichment.providerDisplayName === "string"
|
||||
? enrichment.providerDisplayName.trim()
|
||||
: "";
|
||||
if (raw.length > 0 && raw.length <= PROVIDER_LABEL_MAX_CHARS) return raw;
|
||||
const alias = typeof enrichment.providerAlias === "string" ? enrichment.providerAlias.trim() : "";
|
||||
const alias =
|
||||
typeof enrichment.providerAlias === "string"
|
||||
? enrichment.providerAlias.trim()
|
||||
: "";
|
||||
if (alias.length > 0) {
|
||||
return alias.length <= ALIAS_UPPER_MAX_CHARS ? alias.toUpperCase() : titleCaseAlias(alias);
|
||||
return alias.length <= ALIAS_UPPER_MAX_CHARS
|
||||
? alias.toUpperCase()
|
||||
: titleCaseAlias(alias);
|
||||
}
|
||||
// Long displayName with no alias to fall back on: keep the long label
|
||||
// rather than dropping the provider prefix entirely.
|
||||
@@ -106,33 +131,10 @@ export function normaliseFreeLabel(name: string): string {
|
||||
|
||||
// ── Free Budget Formatting ────────────────────────────────────────────────
|
||||
|
||||
/** Scales, largest first, so the unit is chosen by descending magnitude. */
|
||||
const TOKEN_UNITS = [
|
||||
[1e9, "B"],
|
||||
[1e6, "M"],
|
||||
[1e3, "K"],
|
||||
] as const;
|
||||
|
||||
/**
|
||||
* Format a token count as a short magnitude string: `25M`, `1.5K`, `999`.
|
||||
*
|
||||
* The unit has to be picked from the value that will actually be *printed*,
|
||||
* not from the raw input. `toFixed(1)` rounds to the nearest tenth, so at the
|
||||
* K scale 999_950 and above render as `1000.0` — and by then the M branch has
|
||||
* already been skipped, producing `1000K` for a number that is `1M`. The same
|
||||
* carry turns just under a billion into `1000M`. When the rounded value reaches
|
||||
* the next scale, re-render at that scale instead.
|
||||
*/
|
||||
function fmtTokens(n: number): string {
|
||||
for (let i = 0; i < TOKEN_UNITS.length; i++) {
|
||||
const [scale, suffix] = TOKEN_UNITS[i]!;
|
||||
if (n < scale) continue;
|
||||
const value = Number((n / scale).toFixed(1));
|
||||
// `Number()` also drops a trailing `.0`, which the previous regex did.
|
||||
if (value < 1000 || i === 0) return `${value}${suffix}`;
|
||||
const [nextScale, nextSuffix] = TOKEN_UNITS[i - 1]!;
|
||||
return `${Number((n / nextScale).toFixed(1))}${nextSuffix}`;
|
||||
}
|
||||
if (n >= 1e9) return (n / 1e9).toFixed(1).replace(/\.0$/, "") + "B";
|
||||
if (n >= 1e6) return (n / 1e6).toFixed(1).replace(/\.0$/, "") + "M";
|
||||
if (n >= 1e3) return (n / 1e3).toFixed(1).replace(/\.0$/, "") + "K";
|
||||
return String(n);
|
||||
}
|
||||
|
||||
@@ -182,11 +184,15 @@ export function formatFreeBudget(params: {
|
||||
*/
|
||||
export function formatAutoComboName(
|
||||
variant: AutoVariant | undefined,
|
||||
candidateCount?: number
|
||||
candidateCount?: number,
|
||||
): string {
|
||||
const label = variant ? variant.charAt(0).toUpperCase() + variant.slice(1) : "Default";
|
||||
const label = variant
|
||||
? variant.charAt(0).toUpperCase() + variant.slice(1)
|
||||
: "Default";
|
||||
const count =
|
||||
typeof candidateCount === "number" && candidateCount > 0 ? ` (${candidateCount}p)` : "";
|
||||
typeof candidateCount === "number" && candidateCount > 0
|
||||
? ` (${candidateCount}p)`
|
||||
: "";
|
||||
return `Auto: ${label}${count}`;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,78 +0,0 @@
|
||||
/**
|
||||
* Magnitude-crossover regression for the free-budget suffix
|
||||
* (`formatFreeBudget` -> `fmtTokens` in @omniroute/opencode-plugin/src/naming.ts).
|
||||
*
|
||||
* `fmtTokens` picked its unit from the raw input and then rounded with
|
||||
* `toFixed(1)`. Rounding can carry a value into the next magnitude *after* that
|
||||
* branch has been skipped, so 999_950..999_999 rendered as "1000K" rather than
|
||||
* "1M", and just under a billion rendered as "1000M" rather than "1B".
|
||||
*
|
||||
* These budgets are not always round numbers: `monthlyTokens` is derived from the
|
||||
* remote Radar feed (`tokensPerMonth`) and can be replaced wholesale by a
|
||||
* user-local override, so the crossover band is reachable with real data.
|
||||
*
|
||||
* Kept in its own file rather than added to naming.test.ts so this does not
|
||||
* collide with the coverage being added for `formatFreeBudget` in #11660.
|
||||
*/
|
||||
|
||||
import { test } from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
|
||||
import { formatFreeBudget } from "../src/naming.js";
|
||||
|
||||
/** `recurring-daily` is the shortest path from a token count to a rendered suffix. */
|
||||
const daily = (monthlyTokens: number) =>
|
||||
formatFreeBudget({ freeType: "recurring-daily", monthlyTokens }).replace(" tokens/day", "");
|
||||
|
||||
test("fmtTokens: a rounded K value that reaches 1000 is promoted to M", () => {
|
||||
// 999_950 is the true boundary, not 999_999: toFixed(1) rounds to the nearest
|
||||
// tenth, so 999.95K is the first value that carries to "1000.0".
|
||||
assert.equal(daily(999_950), "1M");
|
||||
assert.equal(daily(999_999), "1M");
|
||||
});
|
||||
|
||||
test("fmtTokens: a rounded M value that reaches 1000 is promoted to B", () => {
|
||||
assert.equal(daily(999_950_000), "1B");
|
||||
assert.equal(daily(999_999_999), "1B");
|
||||
});
|
||||
|
||||
test("fmtTokens: values just below the rounding boundary keep their own unit", () => {
|
||||
// The promotion must not fire early — 999.9K still rounds to 999.9, not 1000.
|
||||
assert.equal(daily(999_949), "999.9K");
|
||||
assert.equal(daily(999_499), "999.5K");
|
||||
assert.equal(daily(999_499_999), "999.5M");
|
||||
});
|
||||
|
||||
test("fmtTokens: ordinary magnitudes are unchanged", () => {
|
||||
assert.equal(daily(0), "0");
|
||||
assert.equal(daily(999), "999");
|
||||
assert.equal(daily(1_000), "1K");
|
||||
assert.equal(daily(1_500), "1.5K");
|
||||
assert.equal(daily(1_000_000), "1M");
|
||||
assert.equal(daily(1_500_000), "1.5M");
|
||||
assert.equal(daily(25_000_000), "25M");
|
||||
assert.equal(daily(1_234_567), "1.2M");
|
||||
assert.equal(daily(1_000_000_000), "1B");
|
||||
assert.equal(daily(2_500_000_000), "2.5B");
|
||||
});
|
||||
|
||||
test("fmtTokens: B is the top unit, so a carry there has nowhere to go", () => {
|
||||
// Deliberately pinned: promoting past B would need a unit that does not exist,
|
||||
// so "1000B" is the intended output rather than an oversight.
|
||||
assert.equal(daily(999_999_999_999), "1000B");
|
||||
});
|
||||
|
||||
test("formatFreeBudget: the promotion applies to every token-bearing branch", () => {
|
||||
assert.equal(
|
||||
formatFreeBudget({ freeType: "recurring-monthly", monthlyTokens: 999_999 }),
|
||||
"1M tokens/month"
|
||||
);
|
||||
assert.equal(
|
||||
formatFreeBudget({ freeType: "recurring-credit", creditTokens: 999_999 }),
|
||||
"1M credits"
|
||||
);
|
||||
assert.equal(
|
||||
formatFreeBudget({ freeType: "one-time-initial", creditTokens: 999_999 }),
|
||||
"1M credits (one-time)"
|
||||
);
|
||||
});
|
||||
@@ -1,81 +0,0 @@
|
||||
/**
|
||||
* Tests for `formatFreeBudget` (@omniroute/opencode-plugin/src/naming.ts):
|
||||
* formats a free-tier model's budget info into a short human-readable
|
||||
* suffix, branching on `freeType`.
|
||||
*/
|
||||
|
||||
import { test } from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
|
||||
import { formatFreeBudget, type FreeModelFreeType } from "../src/naming.js";
|
||||
|
||||
test("formatFreeBudget: recurring-daily formats tokens/day", () => {
|
||||
assert.equal(
|
||||
formatFreeBudget({ freeType: "recurring-daily", monthlyTokens: 25_000_000 }),
|
||||
"25M tokens/day"
|
||||
);
|
||||
});
|
||||
|
||||
test("formatFreeBudget: recurring-monthly formats tokens/month", () => {
|
||||
assert.equal(
|
||||
formatFreeBudget({ freeType: "recurring-monthly", monthlyTokens: 1_000_000 }),
|
||||
"1M tokens/month"
|
||||
);
|
||||
});
|
||||
|
||||
test("formatFreeBudget: recurring-credit formats credits", () => {
|
||||
assert.equal(
|
||||
formatFreeBudget({ freeType: "recurring-credit", creditTokens: 10_000_000 }),
|
||||
"10M credits"
|
||||
);
|
||||
});
|
||||
|
||||
test("formatFreeBudget: one-time-initial formats credits with (one-time) suffix", () => {
|
||||
assert.equal(
|
||||
formatFreeBudget({ freeType: "one-time-initial", creditTokens: 1_000_000 }),
|
||||
"1M credits (one-time)"
|
||||
);
|
||||
});
|
||||
|
||||
test("formatFreeBudget: keyless has no token/credit args", () => {
|
||||
assert.equal(formatFreeBudget({ freeType: "keyless" }), "(keyless)");
|
||||
});
|
||||
|
||||
test("formatFreeBudget: discontinued has no token/credit args", () => {
|
||||
assert.equal(formatFreeBudget({ freeType: "discontinued" }), "(discontinued)");
|
||||
});
|
||||
|
||||
test("formatFreeBudget: missing token/credit counts default to 0", () => {
|
||||
assert.equal(
|
||||
formatFreeBudget({ freeType: "recurring-daily" }),
|
||||
"0 tokens/day"
|
||||
);
|
||||
});
|
||||
|
||||
test("formatFreeBudget: unrecognised freeType falls through to the default branch", () => {
|
||||
// `freeType` is populated from catalog data at runtime, so a value the
|
||||
// build doesn't know about is reachable even though TypeScript treats the
|
||||
// `default:` arm as dead code for a well-typed caller.
|
||||
assert.equal(
|
||||
formatFreeBudget({ freeType: "some-future-type" as FreeModelFreeType }),
|
||||
""
|
||||
);
|
||||
});
|
||||
|
||||
test("formatFreeBudget: sub-1K token count is not abbreviated", () => {
|
||||
assert.equal(
|
||||
formatFreeBudget({ freeType: "recurring-daily", monthlyTokens: 500 }),
|
||||
"500 tokens/day"
|
||||
);
|
||||
});
|
||||
|
||||
test("formatFreeBudget: the 999_999 rounding wart is fixed — promotes to 1M", () => {
|
||||
// `toFixed(1)` rounds 999999/1e3 up to "1000.0" before the `>= 1e6` threshold
|
||||
// check has a chance to apply. fmtTokens now promotes a rounded-up "1000" in
|
||||
// any unit to the next unit up, so this correctly reads "1M" instead of the
|
||||
// old "1000K" wart.
|
||||
assert.equal(
|
||||
formatFreeBudget({ freeType: "recurring-daily", monthlyTokens: 999_999 }),
|
||||
"1M tokens/day"
|
||||
);
|
||||
});
|
||||
@@ -46,7 +46,7 @@ Repository map and Reference Documentation sections below.
|
||||
|
||||
## Project at a Glance
|
||||
|
||||
**OmniRoute** — unified AI proxy/router. One endpoint, 357 LLM providers, auto-fallback.
|
||||
**OmniRoute** — unified AI proxy/router. One endpoint, 354 LLM providers, auto-fallback.
|
||||
|
||||
| Layer | Location | Purpose |
|
||||
| ------------- | ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
|
||||
36
Dockerfile
@@ -184,29 +184,19 @@ ENV NODE_OPTIONS="--max-old-space-size=${OMNIROUTE_BUILD_MEMORY_MB}"
|
||||
# silently leaving no standalone bundle. Next derives the worker count from
|
||||
# CIRCLE_NODE_TOTAL (workers = N-1). (#10060)
|
||||
#
|
||||
# Lowered 8 → 3 (7 workers → 2) in #11419, then 3 → 2 (2 workers → 1) in #7518.
|
||||
# Every page-data worker inherits NODE_OPTIONS above, so the ceiling is per
|
||||
# PROCESS, not per build: 7 workers on a 16 GB GitHub runner (ubuntu-24.04 /
|
||||
# ubuntu-24.04-arm, 4 vCPU) exhausted the host and buildkit failed the whole
|
||||
# step with `ResourceExhausted: ... cannot allocate memory`. The compile phase
|
||||
# always finished ("✓ Compiled successfully in 4.2min"); the kernel killed the
|
||||
# build right after "Collecting page data using N workers".
|
||||
#
|
||||
# #11419's first fix (8 → 3) modeled the per-worker peak as an INFERENCE
|
||||
# (2560 MB, guessed from "7 workers didn't fit") and assumed the parent
|
||||
# process's RSS tracked the V8 heap ceiling. Both assumptions were wrong: a
|
||||
# live VPS reproduction (issue #7518, dmesg OOM-killer report) measured the
|
||||
# real per-process RSS directly at ~4.5 GB, independent of the NODE_OPTIONS
|
||||
# heap flag (Turbopack itself is native/Rust, outside the V8 heap) — and it
|
||||
# applies to the parent process too, not just workers. 2 workers (3 processes
|
||||
# × 4.5 GB = 13.5 GB) still didn't fit the 12.288 GB (75%) budget on a 16 GB
|
||||
# runner, matching the still-live publish failures after #11419 merged. 1
|
||||
# worker (2 processes × 4.5 GB = 9 GB) fits with headroom to spare.
|
||||
# tests/unit/docker-build-memory-budget.test.ts does the arithmetic against
|
||||
# the measured figure and fails if either knob is raised past what a 16 GB
|
||||
# runner holds. Override for a big builder: `--build-arg
|
||||
# OMNIROUTE_BUILD_WORKERS=8`.
|
||||
ARG OMNIROUTE_BUILD_WORKERS=2
|
||||
# Lowered 8 → 3 (7 workers → 2). Every page-data worker inherits NODE_OPTIONS
|
||||
# above, so the ceiling is per PROCESS, not per build: 7 workers on a 16 GB
|
||||
# GitHub runner (ubuntu-24.04 / ubuntu-24.04-arm, 4 vCPU) exhausted the host and
|
||||
# buildkit failed the whole step with `ResourceExhausted: ... cannot allocate
|
||||
# memory`. The compile phase always finished ("✓ Compiled successfully in
|
||||
# 4.2min"); the kernel killed the build right after "Collecting page data using
|
||||
# 7 workers". It was intermittent for a while and went 100% on 2026-08-22, which
|
||||
# is what a threshold being crossed by ordinary codebase growth looks like.
|
||||
# tests/unit/docker-build-memory-budget.test.ts does the arithmetic and fails if
|
||||
# either knob is raised past what a 16 GB runner holds. 2 workers also stops
|
||||
# oversubscribing the runner's 4 vCPU, which 7 did. Override for a big builder:
|
||||
# `--build-arg OMNIROUTE_BUILD_WORKERS=8`.
|
||||
ARG OMNIROUTE_BUILD_WORKERS=3
|
||||
ENV CIRCLE_NODE_TOTAL=${OMNIROUTE_BUILD_WORKERS}
|
||||
|
||||
COPY . ./
|
||||
|
||||
10
README.md
@@ -7,7 +7,7 @@
|
||||
|
||||
# 🚀 OmniRoute — The Free AI Gateway
|
||||
|
||||
<img src="./docs/diagrams/readme-hero.svg" width="100%" alt="OmniRoute — Never stop coding. Every AI tool → 357 providers — 90+ free — through one endpoint. Claude Code, Codex, Cursor, Cline, Copilot & Antigravity into FREE Claude / GPT / Gemini with auto-fallback. RTK + Caveman stacked compression saves 15–95% tokens (~89% avg) — never hit limits. 357 AI providers · 90+ free tiers · ~1.51B free tokens/mo · 19 routing strategies · $0 to start."/>
|
||||
<img src="./docs/diagrams/readme-hero.svg" width="100%" alt="OmniRoute — Never stop coding. Every AI tool → 353 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. 353 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 357 providers. Automatic fallback keeps routing while another healthy target is available. Six pillars: resilient fallback across 357 providers · up to 95% token savings on eligible workloads · $0 to start with 90+ free tiers and 56 recurring/keyless free-forever providers · 35 CLI/agent integrations through one config · OpenAI, Claude, Gemini and Responses API compatibility at /v1 · production controls including circuit breakers, TLS stealth, MCP 110 tools, A2A, memory, guardrails, evals and 39,000+ static test declarations across 5,100+ tracked test files."/>
|
||||
<img src="./docs/diagrams/promise-pillars.svg" width="100%" alt="The Promise — One endpoint and 353 providers. Automatic fallback keeps routing while another healthy target is available. Six pillars: resilient fallback across 353 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: 357 providers, 90+ free tiers built in, 19 routing strategies, 12-engine token compression, built-in MCP server with 110 tools, A2A agent protocol, persistent memory, guardrails, cloud agents, TLS fingerprint stealth, Desktop/Termux/PWA and 43 i18n UI locales. OmniRoute is MIT-licensed and self-hostable. Competitor capabilities and counts may change; see the linked methodology."/>
|
||||
<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: 353 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 **357-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 **353-provider catalog**. → [Providers](docs/reference/PROVIDER_REFERENCE.md)
|
||||
- **📡 Routing transparency** — every response carries an `X-OmniRoute-Decision` header naming the strategy/provider/latency that served it, a new `cache-optimized` combo strategy + Auto-Combo `cacheAffinity` factor route repeat requests back to the connection holding the cached prefix, and a read-only `/v1/auto-combo/{channel}/candidates` endpoint exposes an `auto/*` channel's live candidate pool. → [Auto-Combo](docs/routing/AUTO-COMBO.md)
|
||||
- **⚡ Local performance & infra** — one-click local Redis, Cloudflare Workers / Deno Deploy relay deployers, Bifrost & Mux as supervised embedded services. → [Embedded Services](docs/frameworks/EMBEDDED-SERVICES.md)
|
||||
|
||||
@@ -642,7 +642,7 @@ of your shell history. → [CLI Integrations](docs/guides/CLI-INTEGRATIONS.md)
|
||||
|
||||
<div align="center">
|
||||
|
||||
## 🌐 357 AI Providers — 154 Catalog-Marked Free
|
||||
## 🌐 353 AI Providers — 154 Catalog-Marked Free
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
- **feat(search):** Add AnySearch free web search + URL extract (webFetch) with typed results, credential validation, REST routing, and MCP selection - fallback-only
|
||||
@@ -1 +0,0 @@
|
||||
- **fix(kie):** correct 12 more KIE Market catalog ids that were sent to `createTask` unchanged but diverge from KIE's documented upstream `model` values — GPT Image 2 T2I/I2I (drops the `gpt/` prefix), GPT Image 1.5 T2I/I2I (`gpt-image/` namespace), Seedream 5.0 Lite T2I/I2I (drops the `.0`), all 4 Flux 2 variants (`flux-2/` namespace, generic variant renamed `flex`), and Wan 2.7 Image / Image Pro (dash instead of dot) — each verified individually against the literal example request published on docs.kie.ai. `#11326`'s "everything else already matches" claim was wrong a second time (#11296); `z-image/4.0-*`/`z-image/4.5-*` and `flux/kontext` remain open, documented as unresolved in `KIE_MARKET_UPSTREAM_MODEL_IDS`'s comment pending further verification.
|
||||
@@ -1 +0,0 @@
|
||||
- **fix(dashboard):** `useApiKeySave.handleSaveApiKey` no longer forces a full upstream `/models` catalog sync on every non-curated provider connection save — callers can now pass `skipModelSync: true` to opt out, so a workflow that only wants to add one manual model no longer floods the provider's available-models list with hundreds/thousands of synced entries. The flag is a client-side intent signal only and is stripped before the connection payload is POSTed to `/api/providers`; default behavior (full sync on save) is unchanged when the flag is omitted (#11324)
|
||||
@@ -1 +0,0 @@
|
||||
- **fix(combos):** the combo builder's precision-select, global-model-search, and manual-entry flows now serialize a model step's `model` string using the provider's already-computed routing-alias prefix (e.g. `oc/`) instead of rebuilding it from the raw canonical `providerId`, fixing the no-auth "OpenCode Free" provider (`opencode`) being routed to the unrelated paid "OpenCode Zen" provider (`opencode-zen`) because `opencode` doubles as a manual routing-prefix override ([#11433](https://github.com/diegosouzapw/OmniRoute/issues/11433)).
|
||||
@@ -1 +0,0 @@
|
||||
- fix(ui): let AnySearch use the normal provider-icon fallback when LobeHub has no matching icon (#11449)
|
||||
@@ -1 +0,0 @@
|
||||
- **fix(combo):** attach the same combo-diagnostics trace (`poolSize`/`attemptOrder`/`excluded`/`terminalReason`, plus `x-omniroute-combo-*` headers) to the round-robin strategy's and the nested pipeline/fusion runtime-unit loop's "Maximum combo retry limit reached" 503 that the priority-strategy path already attaches for the identical terminal condition — previously those two paths returned a bare, contextless 503 ([#11462](https://github.com/diegosouzapw/OmniRoute/issues/11462)).
|
||||
@@ -1 +0,0 @@
|
||||
- **fix(db):** dedupe the raw `[Encryption] Decryption failed...` log line emitted by the lazy-decrypt views (`createLazyRowProxy`/`createLazyConnectionView`), which power `getProviderConnections()` and were re-triggering that line on every CredentialHealth/model-sync cycle for the same corrupt or stale-key credential — a fresh Proxy over a fresh row on every cycle meant the per-proxy memoization never suppressed it, unlike the dedup `decryptConnectionFields()` already had since [#9927](https://github.com/diegosouzapw/OmniRoute/issues/9927). Now shares that dedupe tracking so the line logs at most once per credential ([#11500](https://github.com/diegosouzapw/OmniRoute/issues/11500)).
|
||||
@@ -1 +0,0 @@
|
||||
- **fix(sse):** stop dropping resolved `thoughtSignature` values on parallel (multi tool-call) turns sent to Gemini 3.x — the claude→gemini and openai→gemini translators previously kept the signature only on the *first* function call of a message, causing Gemini to reject subsequent calls in the same turn with HTTP 400 "Function call is missing a thought_signature"; each function call now keeps its own resolved signature ([#11510](https://github.com/diegosouzapw/OmniRoute/issues/11510)).
|
||||
@@ -1 +0,0 @@
|
||||
- **fix(sse):** cap the upstream headers-wait phase for STREAMING requests to a client-realistic ceiling (110s, under Codex's own ~120s hard client-abort window) instead of the flat 10-minute `FETCH_TIMEOUT_MS` default — that default was 5x longer than the body-phase readiness watchdog's own adaptive bound, so a request whose upstream never returned any response at all (not even headers, e.g. a stalled NVIDIA target behind a tool-heavy Responses→Chat translation) kept the client connection alive on keepalives only, guaranteeing the client's own patience ran out first with an opaque 499 instead of OmniRoute detecting and failing the stall fast. Non-streaming requests are unaffected — they keep the existing flat default (`open-sse/utils/fetchStartTimeoutPolicy.ts`) (#11526)
|
||||
@@ -1 +0,0 @@
|
||||
- **sse:** fix LiveWS/embed-WS servers crashing at startup under the Node/tsx runtime — `liveServer.ts`, `embedWsProxy.ts` and `apiBridgeServer.ts` imported `@/shared/utils/httpClientAbortGuard` without the `.mjs` extension, so the client-abort crash guard added by [#11556](https://github.com/diegosouzapw/OmniRoute/pull/11556) was unreachable and every dependent test failed with `ERR_MODULE_NOT_FOUND` ([#11556](https://github.com/diegosouzapw/OmniRoute/pull/11556)).
|
||||
@@ -1 +0,0 @@
|
||||
- **resilience:** restore the expired-connection retry-budget probe in the token-health sweep — the `!isGitHubAccessTokenOnlyConnection` carve-out reintroduced by #11608 contradicted the boundary pinned by #11592, so a GitHub connection parked at `expired` with retry budget remaining was never probed and could never self-heal ([#11592](https://github.com/diegosouzapw/OmniRoute/pull/11592)).
|
||||
@@ -1 +0,0 @@
|
||||
- **test(gamification):** pin the aggregate profile level to the XP-derived semantics of #11604 — `getAggregateXp()` now derives `currentLevel` from the summed XP (`calculateLevel(sum)`), not `MAX(stored current_level)`, and the #3484 fixture levels are aligned with the XP curve ([#11604](https://github.com/diegosouzapw/OmniRoute/pull/11604)).
|
||||
@@ -1 +0,0 @@
|
||||
- **test(chatcore):** move Codex/Claude combo fixtures off models the lifecycle guard now rejects — `gpt-5.1-codex`/`gpt-5-codex` are vendor-retired (snapshot, #11626) and `claude-3-5-sonnet-20241022` is shut down, so native-passthrough and combo-fallback tests switched to `gpt-5.6-sol` and `claude-sonnet-4.6` ([#11626](https://github.com/diegosouzapw/OmniRoute/pull/11626)).
|
||||
@@ -1 +0,0 @@
|
||||
- **fix(security):** Video Bridge transcript text is now omitted from OmniRoute-owned retained request, response, usage, error, stream, continuation, handoff, and Memory surfaces while the live bridge request remains intact. Server-generated descriptions are matched by bounded SHA-256/length identities emitted only by a successful Video Bridge rewrite; ordinary fields named `transcript` and caller-forged description prose remain untouched. Traversal is cycle-safe and bounded, and hostile getters, proxies, or budget overflow fail closed to a constant omission marker instead of leaking content or breaking the request. Custom plugins and guardrails remain privileged processors of the live payload and must secure any sinks they create themselves. ([#11658](https://github.com/diegosouzapw/OmniRoute/issues/11658))
|
||||
@@ -1 +0,0 @@
|
||||
- fix(opencode-plugin): stop a free-tier budget that rounds up across a magnitude from rendering as `1000K`/`1000M` in the model picker — `fmtTokens` chose its unit from the raw token count and then rounded with `toFixed(1)`, so 999,950–999,999 printed as `1000K` rather than `1M` and just under a billion printed as `1000M` rather than `1B` ([#11684](https://github.com/diegosouzapw/OmniRoute/pull/11684))
|
||||
@@ -1 +0,0 @@
|
||||
- **fix(docker):** re-derive the Docker build's worker-pool memory budget from the MEASURED ~4.5 GB per-process RSS (the issue owner's own VPS dmesg OOM-killer reproduction) instead of the stale 2560 MB/worker inference #11419 shipped, and lower `OMNIROUTE_BUILD_WORKERS` 3 → 2 so 1 parent + 1 page-data worker (2 processes × 4.5 GB = 9 GB) fits the 12.288 GB (75%) budget on a 16 GB GitHub Actions runner — the previous default (1 parent + 2 workers = 13.5 GB) still overcommitted the runner and kept "Publish to Docker Hub" failing with `cannot allocate memory` after #11419 merged (#7518).
|
||||
@@ -1 +0,0 @@
|
||||
- **docs:** sync the canonical provider count 354 → 356 across `README.md`, `AGENTS.md`, `llm.txt` (+ 42 i18n mirrors), the four README SVG diagrams, `docs/reference/PROVIDER_REFERENCE.md` (regenerated) and the `package.json` description after Opper (#11629) and 1min.ai (#11631) boarded the catalog — closes the `check:docs-counts-sync` strict drifts that kept `release/v3.8.51` red ([#11449](https://github.com/diegosouzapw/OmniRoute/issues/11449)).
|
||||
@@ -1 +0,0 @@
|
||||
- **test(providers):** update count-derived assertions after the v3.8.51 provider additions — `APIKEY_PROVIDERS` 233 → 235 (Opper #11629 + 1min.ai #11631), reserved-prefix REGISTRY walk 395 → 398, `WEB_FETCH_PROVIDERS` now includes `nimble-search` (#11620), and the provider translate-path golden snapshot regenerated ([#11449](https://github.com/diegosouzapw/OmniRoute/issues/11449)).
|
||||
@@ -1 +0,0 @@
|
||||
- **test(sse):** bump the hard-lease connection-query inventory for `src/lib/tokenHealthCheck.ts` to 2 — the verify-only web-cookie sweep added by #11495 split the single `getProviderConnections` call into oauth + cookie variants, which the frozen inventory had not tracked ([#11495](https://github.com/diegosouzapw/OmniRoute/pull/11495)).
|
||||
@@ -1 +0,0 @@
|
||||
- **test(opencode-plugin):** add unit test coverage for `formatFreeBudget()` naming helper ([#11660](https://github.com/diegosouzapw/OmniRoute/pull/11660)) — thanks @f9td56dbgh-hub
|
||||
@@ -2790,6 +2790,11 @@
|
||||
"count": 3
|
||||
}
|
||||
},
|
||||
"src/mitm/dns/dnsConfig.ts": {
|
||||
"@typescript-eslint/no-unused-vars": {
|
||||
"count": 1
|
||||
}
|
||||
},
|
||||
"src/mitm/dns/provision.ts": {
|
||||
"@typescript-eslint/no-unused-vars": {
|
||||
"count": 1
|
||||
@@ -2921,6 +2926,11 @@
|
||||
"count": 1
|
||||
}
|
||||
},
|
||||
"src/shared/middleware/chatBodyAdmission.ts": {
|
||||
"@typescript-eslint/no-unused-vars": {
|
||||
"count": 1
|
||||
}
|
||||
},
|
||||
"src/shared/services/apiKeyResolver.ts": {
|
||||
"no-restricted-imports": {
|
||||
"count": 1
|
||||
@@ -3430,10 +3440,10 @@
|
||||
},
|
||||
"tests/integration/skills-pipeline.test.ts": {
|
||||
"@typescript-eslint/no-explicit-any": {
|
||||
"count": 15
|
||||
"count": 14
|
||||
},
|
||||
"@typescript-eslint/no-unused-vars": {
|
||||
"count": 1
|
||||
"count": 2
|
||||
}
|
||||
},
|
||||
"tests/integration/traffic-inspector-error-sanitization.test.ts": {
|
||||
@@ -3680,6 +3690,11 @@
|
||||
"count": 15
|
||||
}
|
||||
},
|
||||
"tests/unit/authz/probe-9033-repro.test.ts": {
|
||||
"@typescript-eslint/no-unused-vars": {
|
||||
"count": 1
|
||||
}
|
||||
},
|
||||
"tests/unit/autoCombo/tieredRotation.test.ts": {
|
||||
"@typescript-eslint/no-unused-vars": {
|
||||
"count": 3
|
||||
|
||||
@@ -229,8 +229,7 @@
|
||||
"tests/unit/vscode-token-routes.test.ts": 1633,
|
||||
"tests/unit/executor-antigravity.test.ts": 1427,
|
||||
"tests/unit/guardrails/videoBridgeResultCache.test.ts": 1040,
|
||||
"_rebaseline_2026_08_24_video_bridge_fu01_fu03_fu04_result_cache_tests": "PRs #11362 (FU-01 cache hardening) + #11382 (FU-03 visual dedup policy identity) + #11383 (FU-04 focused analysis mode) own test growth: videoBridgeResultCache.test.ts <1000->1040, +40 (sum of three stacked PRs boarded together in the same merge-batch, each adding its own cache-identity assertions on the shared result-cache seam). Owner pre-authorized rebaseline for legitimate PR growth (2026-08-19 directive).",
|
||||
"tests/integration/skills-pipeline.test.ts": 1010
|
||||
"_rebaseline_2026_08_24_video_bridge_fu01_fu03_fu04_result_cache_tests": "PRs #11362 (FU-01 cache hardening) + #11382 (FU-03 visual dedup policy identity) + #11383 (FU-04 focused analysis mode) own test growth: videoBridgeResultCache.test.ts <1000->1040, +40 (sum of three stacked PRs boarded together in the same merge-batch, each adding its own cache-identity assertions on the shared result-cache seam). Owner pre-authorized rebaseline for legitimate PR growth (2026-08-19 directive)."
|
||||
},
|
||||
"_rebaseline_2026_06_09": "Re-baseline consciente pre-release v3.8.19: 9 arquivos cresceram durante o ciclo (features mergeadas: RequestLoggerV2 +281 request-logger rework, stream +101, combo +73, chatCore +45, catalog +32 fable-5/catalog-flag, callLogs +4, accountFallback +2, usageHistory novo 840) + core.ts +7 (fix resetAllDbModuleState, PR 3536). A catraca segue valendo destes valores — proximo crescimento falha. Decisao: encolher (esp. RequestLoggerV2/chatCore) e a issue #3501 ficam para o ciclo seguinte.",
|
||||
"_rebaseline_2026_06_11_phase1f": "Phase 1f (#3501): ProviderDetailPageClient.tsx 4948→4062 (-886 LOC); 3 novos hooks extraídos. useProviderConnections.ts=954 acima do cap=800 — justificado: extração direta do god-component (zero lógica nova), própria redução do cliente supera o custo. useProviderSettings.ts=263 e useProviderModels.ts=154 já abaixo do cap.",
|
||||
@@ -653,6 +652,5 @@
|
||||
"_rebaseline_2026_08_20_imageregistry_1034": "imageRegistry.ts 1033->1034: +1 line drift between #10842 (cursor image provider, froze at 1033) and its actual merged state on release (measured 1034) — trivial rebaseline, not a new feature.",
|
||||
"_rebaseline_2026_08_25_11146_subscription_first_auto": "PR #11146 (@yourspraveen, subscription-first auto groupings auto/subscription+auto/thrifty): open-sse/services/autoCombo/virtualFactory.ts is a NEW file in this PR landing at 1128 lines (+2 margin) — two opt-in flat auto ids built on the established auto/best-free pattern (connectionBillingCatalog + subscriptionLadder pure functions). Frozen at merge size per owner-authorized rebaseline directive (2026-08-19, merge-batch Step 4); no further growth without split rationale.",
|
||||
"_rebaseline_2026_08_26_mergebatch_v3851_batch1": "/merge-batch 2026-08-26 (v3.8.51): three legitimate growths from this batch. #11448 src/app/api/providers/[id]/test/route.ts 1237->1262 (auto-test-on-create wiring). #11495 src/sse/services/auth.ts 3346->3376 (web-cookie health-sweep verify-only path). #11561 src/lib/cloudflaredTunnel.ts new named-tunnel mode, lands at 1078 (+78 over the 1000 new-file cap) for the CLOUDFLARED_CONFIG named-tunnel flow (login->create->route dns config parsing + readiness detection). Owner-authorized rebaseline per merge-batch Step 4 (2026-08-19 directive); no further growth without split rationale.",
|
||||
"_rebaseline_2026_08_26_mergebatch_v3851_batch2": "/merge-batch 2026-08-26 (v3.8.51) batch 2: three legitimate growths. #11083 src/shared/components/RequestLoggerDetail.tsx new-file cap, lands at 1018 (+18 over 1000) — copy-all button for request detail modal. #11631 src/shared/constants/providers/apikey/gateways.ts 1321->1330 (1min.ai gateway entry). #11628 src/sse/services/auth.ts 3376->3432 (credential-health isolation from model failures). Owner-authorized rebaseline per merge-batch Step 4 (2026-08-19 directive); no further growth without split rationale.",
|
||||
"_rebaseline_2026_08_26_mergebatch_v3851_batch5": "/merge-batch 2026-08-26 (v3.8.51) batch 5: #11642 tests/integration/skills-pipeline.test.ts new regression test for the configured-provider-over-fallback search selection (#11524), lands at 1010 lines (+10 over the 1000 new-file testCap). Owner-authorized rebaseline per merge-batch Step 4 (2026-08-19 directive); no further growth without split rationale."
|
||||
"_rebaseline_2026_08_26_mergebatch_v3851_batch2": "/merge-batch 2026-08-26 (v3.8.51) batch 2: three legitimate growths. #11083 src/shared/components/RequestLoggerDetail.tsx new-file cap, lands at 1018 (+18 over 1000) — copy-all button for request detail modal. #11631 src/shared/constants/providers/apikey/gateways.ts 1321->1330 (1min.ai gateway entry). #11628 src/sse/services/auth.ts 3376->3432 (credential-health isolation from model failures). Owner-authorized rebaseline per merge-batch Step 4 (2026-08-19 directive); no further growth without split rationale."
|
||||
}
|
||||
|
||||
@@ -188,15 +188,14 @@
|
||||
"dedicatedGate": true
|
||||
},
|
||||
"bundleSize": {
|
||||
"value": 8653,
|
||||
"value": 8461,
|
||||
"direction": "down",
|
||||
"dedicatedGate": true,
|
||||
"_rebaseline_2026_07_07_v3846_release_close": "5601->6534 (+933). v3.8.46 release close: gzip of the 4 bin/*.mjs entrypoints (size-limit + @size-limit/file) grew from this cycle's feature/fix merges pulled transitively into the CLI entrypoints (new providers, combo pipeline strategy #6396, effort/thinking standardization #6241, catalog cache-invalidation #6408). Measured 6534 locally via `check:bundle-size --ratchet` (deterministic gzip, matches CI). Legitimate cycle growth; shrink is separate debt.",
|
||||
"_rebaseline_2026_07_19_7808_codeql_alias_resolver_hook": "6534->6762 (+228). PR #7808 (CodeQL js/incomplete-url-substring-sanitization fix): the ESM loader hook source moved out of the inline `HOOK_SOURCE` template literal in bin/aliasResolver.mjs into a real file bin/aliasResolverHook.mjs, loaded via pathToFileURL() instead of a dynamically-built `data:text/javascript,...` URL. The new file is now counted by size-limit as a 5th bin/*.mjs entrypoint. Net +228 = the hook's gzip size (previously hidden inside aliasResolver.mjs because the template literal was compressed away). Security-driven; no shrink opportunity.",
|
||||
"_rebaseline_2026_07_28_v3849_release_preflight": "6762 -> 7666 (+904). Fechamento do ciclo v3.8.49: gzip dos entrypoints bin/*.mjs (size-limit + @size-limit/file) cresceu com o que os merges do ciclo puxam transitivamente para o CLI (novos provedores — 271->290, seletor de protocolo por conexão #8861, catálogos de busca #8814, resiliência). Crescimento legítimo de ciclo, medido localmente com `npm run check:bundle-size` = 7666 (gzip determinístico, bate com o CI). Encolher é dívida separada.",
|
||||
"_rebaseline_2026_08_09_v3850_release_close": "7666 -> 8045 (+379 gzip bytes, +4.9%). Release v3.8.50 close reconciliation measured twice with the real size-limit + @size-limit/file path on tip e0ce95c592. Per-entry measurements remain below their absolute budgets: omniroute.mjs 4380/15000, mcp-server.mjs 1195/5000, nodeRuntimeSupport.mjs 887/8000, reset-password.mjs 1583/6000. The growth accumulated through legitimate CLI/runtime work in this cycle, including global-install ESM alias resolution, Termux cache preparation, and MCP stdio startup hardening; no entrypoint is near its absolute ceiling. The direction:down ratchet stays blocking from this exact measured tip.",
|
||||
"_rebaseline_2026_08_24_ci_green_gates_f95b03d7": "8045 -> 8461 (+416 gzip bytes, +5.2%). CI run 32786966560 (release PR #8875, tip f95b03d7) measured bundleSize=8461 via check:bundle-size --ratchet, above the 8045 baseline left at the v3.8.50 close. The growth comes from the post-freeze back-merge cycle landing in the CLI entrypoints (Synthetic + Kilo Gateway providers, kilo-gateway routing surface). Re-baseline per the gate's own instruction (legitimate cycle growth); shrinking the entrypoints remains separate debt; direction:down ratchet stays blocking from this measured tip.",
|
||||
"_rebaseline_2026_08_27_v3851_volatile_env_warning_11437": "8461 -> 8653 (+192 gzip bytes, +2.3%). Exact paired size-limit measurements on the VPS compared f95b03d709 with release/v3.8.51: only bin/omniroute.mjs changed, 4700 -> 4892; the other three entries remained 1195/983/1583. The growth originates in 943b9aaa84 (#11437), which warns users before a package-local .env is lost on the next global install. The CLI entry remains 4892/15000 bytes (32.6% of its absolute budget). Legitimate bug-fix growth; shrinking stays separate debt and direction:down remains blocking from this measured tip."
|
||||
"_rebaseline_2026_08_24_ci_green_gates_f95b03d7": "8045 -> 8461 (+416 gzip bytes, +5.2%). CI run 32786966560 (release PR #8875, tip f95b03d7) measured bundleSize=8461 via check:bundle-size --ratchet, above the 8045 baseline left at the v3.8.50 close. The growth comes from the post-freeze back-merge cycle landing in the CLI entrypoints (Synthetic + Kilo Gateway providers, kilo-gateway routing surface). Re-baseline per the gate's own instruction (legitimate cycle growth); shrinking the entrypoints remains separate debt; direction:down ratchet stays blocking from this measured tip."
|
||||
},
|
||||
"openapiBreaking": {
|
||||
"value": 4,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<svg viewBox="0 0 1200 350" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Animated terminal demoing the OmniRoute CLI: omniroute providers list (357 providers registered, anthropic, codex, glm, kimi shown active), omniroute combo list (always-on priority, cost-saver, fusion-panel, context-relay) and omniroute health (healthy, 18412 requests in 24h, p95 412ms, circuit breakers 24 closed, 1 half-open, 0 open), cycling over 85 top-level commands: providers, oauth, keys, combo, nodes, models, cache, compression, cost, usage, quota, health, resilience, telemetry, logs, audit, mcp, a2a, cloud, memory, skills, eval, doctor, repl, tunnel, backup, sync, webhooks, policy, pricing, translator, simulate and more.">
|
||||
<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 (354 providers registered, anthropic, codex, glm, kimi shown active), omniroute combo list (always-on priority, cost-saver, fusion-panel, context-relay) and omniroute health (healthy, 18412 requests in 24h, p95 412ms, circuit breakers 24 closed, 1 half-open, 0 open), cycling over 85 top-level commands: providers, oauth, keys, combo, nodes, models, cache, compression, cost, usage, quota, health, resilience, telemetry, logs, audit, mcp, a2a, cloud, memory, skills, eval, doctor, repl, tunnel, backup, sync, webhooks, policy, pricing, translator, simulate and more.">
|
||||
<desc>Compact animated terminal cycling three real OmniRoute CLI commands with a typewriter effect and a scrolling subcommand ticker; the first frame shows the completed providers-list screen.</desc>
|
||||
<defs><clipPath id="tickerClip"><rect x="12" y="304" width="1176" height="40"/></clipPath><clipPath id="tw0"><rect x="64" y="46" height="26" width="0"><animate attributeName="width" calcMode="discrete" values="0;31;61;92;122;153;184;214;245;245" keyTimes="0;0.012;0.018;0.024;0.030;0.036;0.042;0.048;0.054;1" dur="18s" repeatCount="indefinite"/></rect></clipPath><clipPath id="tw1"><rect x="64" y="46" height="26" width="0"><animate attributeName="width" calcMode="discrete" values="0;26;51;76;102;128;153;178;204;204" keyTimes="0;0.348;0.351;0.357;0.363;0.369;0.375;0.381;0.387;1" dur="18s" repeatCount="indefinite"/></rect></clipPath><clipPath id="tw2"><rect x="64" y="46" height="26" width="0"><animate attributeName="width" calcMode="discrete" values="0;20;41;61;82;102;122;143;163;163" keyTimes="0;0.678;0.684;0.690;0.696;0.702;0.708;0.714;0.720;1" dur="18s" repeatCount="indefinite"/></rect></clipPath></defs>
|
||||
<rect width="1200" height="350" fill="#0d1117"/>
|
||||
|
||||
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
@@ -1,4 +1,4 @@
|
||||
<svg viewBox="0 0 1200 780" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Comparison table: OmniRoute versus 9router, OpenRouter, CLIProxyAPI and LiteLLM across 13 capabilities. OmniRoute is the only one with the full set: 357 providers, 90+ free providers built-in, 19 routing strategies, 12-engine token compression, a built-in MCP server with 110 tools, A2A protocol, persistent memory, guardrails, cloud agents, TLS fingerprint stealth, desktop/Termux/PWA, 43 UI locales and 100% MIT self-hosted. 9router has free providers, RTK compression and translation but no MCP, A2A, memory, guardrails, cloud agents or stealth. OpenRouter is a hosted SaaS with 400+ models, guardrails and a hosted MCP but is not self-hosted and lacks A2A, memory, cloud agents and stealth. CLIProxyAPI is a light OAuth proxy with two routing strategies. LiteLLM has 100+ providers, A2A and extensive guardrails but no memory, compression, free tier, stealth or cloud agents.">
|
||||
<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: 354 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 357 providers. Six pillars. Resilient fallback: automatic routing continues while another healthy target is available. Save up to 95 percent of eligible tokens: RTK plus Caveman stacked compression averages about 89 percent on tool-heavy sessions. Zero dollars to start: 90+ providers with a free tier and 56 recurring or keyless free-forever providers. Every tool works: 35 CLI and agent integration records, including Claude Code, Codex, Cursor, Cline, Copilot and Antigravity, through one config. One endpoint: OpenAI, Claude, Gemini and Responses API translation at /v1. Production controls: circuit breakers, TLS stealth, MCP with 110 tools, A2A, memory, guardrails, evals, and 39,000+ static test declarations across 5,100+ tracked test files.">
|
||||
<svg viewBox="0 0 1200 540" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="The OmniRoute promise: one endpoint and 354 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">357 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">354 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 357 providers in</text>
|
||||
<text x="66" y="204" font-size="13.5" fill="#a1a1aa">Auto-fallback across 354 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 357 providers — 90+ free — through one endpoint. Claude Code, Codex, Cursor, Cline, Copilot and Antigravity into free Claude, GPT and Gemini with auto-fallback. RTK + Caveman stacked compression saves 15 to 95 percent of tokens — about 89 percent average on tool-heavy sessions — so you never hit limits. Stats: 357 AI providers, 90+ free tiers, about 1.51B free tokens per month, 15 to 95 percent token savings, 19 routing strategies, zero dollars to start.">
|
||||
<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 354 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: 354 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">357 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">354 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 |
@@ -57,7 +57,7 @@ New tab for extracting content from a URL via `POST /v1/web/fetch` (created in p
|
||||
- Submit → fetch → render `ScrapeResult.tsx`.
|
||||
- `ScrapeResult` renders markdown preview + raw toggle.
|
||||
- Cap: if response body > **256 KB**, UI shows `(truncated, view raw)` and opens raw in a Monaco modal (D21).
|
||||
- Metadata panel: provider (firecrawl/jina-reader/tavily-search/tinyfish/nimble-search/anysearch-search), latency, cost, response size, links count.
|
||||
- Metadata panel: provider (firecrawl/jina-reader/tavily-search/tinyfish/nimble-search), latency, cost, response size, links count.
|
||||
- Uses `useScrapeFetch.ts` hook.
|
||||
|
||||
### Compare Tab
|
||||
@@ -105,7 +105,15 @@ Runs the same query/URL across up to **4 providers in parallel** (D22):
|
||||
`ProviderCatalog.tsx` exposes the full provider list from `GET /api/search/providers`
|
||||
(extended in F4 to include fetch providers):
|
||||
|
||||
| `kind` | `"search"` (20 providers) or `"fetch"` (firecrawl, jina-reader, tavily-search, tinyfish, nimble-search, anysearch-search) |
|
||||
| Field | Source |
|
||||
| ------------------------------ | ------------------------------------------------------------------------------------------ |
|
||||
| `id`, `name` | `searchRegistry.ts` |
|
||||
| `kind` | `"search"` or `"fetch"` (firecrawl, jina-reader, tavily-search, tinyfish, nimble-search) |
|
||||
| `costPerQuery` | Registry data |
|
||||
| `freeMonthlyQuota` | Registry data |
|
||||
| `searchTypes` / `fetchFormats` | Registry data |
|
||||
| `status` | `"configured"` / `"missing"` / `"rate_limited"` — derived at runtime from credential store |
|
||||
| `configureHref` | `/dashboard/providers` |
|
||||
|
||||
The status is **derived at request time** by checking whether credentials exist and whether
|
||||
all keys are currently in cooldown.
|
||||
@@ -128,7 +136,7 @@ Only one backend change was needed for this feature:
|
||||
|
||||
`src/app/api/search/providers/route.ts` was extended to:
|
||||
|
||||
- Include all 6 fetch providers (`firecrawl`, `jina-reader`, `tavily-search`, `tinyfish`, `nimble-search`, `anysearch-search`) in the array.
|
||||
- Include every fetch provider (`firecrawl`, `jina-reader`, `tavily-search`, `tinyfish`, `nimble-search`) in the array.
|
||||
- Add `kind: "search" | "fetch"` to every item.
|
||||
- Add `status: "configured" | "missing" | "rate_limited"` derived from live credential state.
|
||||
- Maintain backward compatibility — existing fields (`id`, `name`, etc.) unchanged.
|
||||
|
||||
@@ -226,21 +226,16 @@ Three build args control what the `builder` stage costs. They are build-time onl
|
||||
| --------------------------- | ------- | ----------------------------------------------------------------------------------- |
|
||||
| `OMNIROUTE_USE_TURBOPACK` | `1` | `0` builds with webpack instead. Lower peak memory, slower. |
|
||||
| `OMNIROUTE_BUILD_MEMORY_MB` | `6144` | V8 heap ceiling (`--max-old-space-size`) for the spawned `next build`. |
|
||||
| `OMNIROUTE_BUILD_WORKERS` | `2` | Feeds `CIRCLE_NODE_TOTAL`; Next derives `workers = N - 1` for page-data collection. |
|
||||
| `OMNIROUTE_BUILD_WORKERS` | `3` | Feeds `CIRCLE_NODE_TOTAL`; Next derives `workers = N - 1` for page-data collection. |
|
||||
|
||||
`OMNIROUTE_BUILD_WORKERS` is the one to raise on a big builder and the one to
|
||||
suspect when a constrained build dies **after** `✓ Compiled successfully`. Each
|
||||
page-data worker is its own process, and so is the parent `next build` itself;
|
||||
a live VPS reproduction (issue #7518) measured each process's peak RSS at
|
||||
~4.5 GB independent of the `NODE_OPTIONS` heap flag (Turbopack compiles in
|
||||
native/Rust memory outside the V8 heap). The default of `2` (→ 1 worker, 2
|
||||
processes total) is sized for the 16 GB / 4 vCPU GitHub-hosted runners the
|
||||
publish pipeline uses. At `8` (→ 7 workers) that runner ran out of memory and
|
||||
buildkit failed the step with `ResourceExhausted: ... cannot allocate memory`;
|
||||
`3` (→ 2 workers) still didn't fit once the per-process RSS was measured
|
||||
directly instead of inferred. `tests/unit/docker-build-memory-budget.test.ts`
|
||||
does the arithmetic against the measured figure and fails if either knob
|
||||
outgrows the runner.
|
||||
page-data worker is its own process and inherits `NODE_OPTIONS`, so the heap
|
||||
ceiling is per process, not per build: the default of `3` (→ 2 workers) is sized
|
||||
for the 16 GB / 4 vCPU GitHub-hosted runners the publish pipeline uses. At `8`
|
||||
(→ 7 workers) that runner ran out of memory and buildkit failed the step with
|
||||
`ResourceExhausted: ... cannot allocate memory`. `tests/unit/docker-build-memory-budget.test.ts`
|
||||
does the arithmetic and fails if either knob outgrows the runner.
|
||||
|
||||
Turbopack compiles in native Rust memory that lives **outside** the V8 heap, so
|
||||
`OMNIROUTE_BUILD_MEMORY_MB` does not bound it. On a host with a memory ceiling the
|
||||
|
||||
@@ -4,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 357 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
> 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 354 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
|
||||
- **357 AI providers** with automatic format translation
|
||||
- **354 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
|
||||
|
||||
- **357-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **354-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 357 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
> 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 354 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
|
||||
- **357 AI providers** with automatic format translation
|
||||
- **354 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
|
||||
|
||||
- **357-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **354-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 357 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
> 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 354 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
|
||||
- **357 AI providers** with automatic format translation
|
||||
- **354 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
|
||||
|
||||
- **357-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **354-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 357 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
> 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 354 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
|
||||
- **357 AI providers** with automatic format translation
|
||||
- **354 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
|
||||
|
||||
- **357-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **354-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 357 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
> 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 354 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
|
||||
- **357 AI providers** with automatic format translation
|
||||
- **354 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
|
||||
|
||||
- **357-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **354-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 357 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
> 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 354 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
|
||||
- **357 AI providers** with automatic format translation
|
||||
- **354 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
|
||||
|
||||
- **357-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **354-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 357 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
> 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 354 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
|
||||
- **357 AI providers** with automatic format translation
|
||||
- **354 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
|
||||
|
||||
- **357-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **354-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 357 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
> 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 354 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
|
||||
- **357 AI providers** with automatic format translation
|
||||
- **354 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
|
||||
|
||||
- **357-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **354-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 357 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
> 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 354 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
|
||||
- **357 AI providers** with automatic format translation
|
||||
- **354 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
|
||||
|
||||
- **357-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **354-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 357 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
> 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 354 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
|
||||
- **357 AI providers** with automatic format translation
|
||||
- **354 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
|
||||
|
||||
- **357-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **354-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 357 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
> 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 354 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
|
||||
- **357 AI providers** with automatic format translation
|
||||
- **354 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
|
||||
|
||||
- **357-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **354-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 357 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
> 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 354 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
|
||||
- **357 AI providers** with automatic format translation
|
||||
- **354 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
|
||||
|
||||
- **357-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **354-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 357 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
> 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 354 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
|
||||
- **357 AI providers** with automatic format translation
|
||||
- **354 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
|
||||
|
||||
- **357-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **354-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 357 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
> 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 354 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
|
||||
- **357 AI providers** with automatic format translation
|
||||
- **354 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
|
||||
|
||||
- **357-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **354-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 357 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
> 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 354 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
|
||||
- **357 AI providers** with automatic format translation
|
||||
- **354 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
|
||||
|
||||
- **357-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **354-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 357 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
> 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 354 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
|
||||
- **357 AI providers** with automatic format translation
|
||||
- **354 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
|
||||
|
||||
- **357-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **354-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 357 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
> 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 354 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
|
||||
- **357 AI providers** with automatic format translation
|
||||
- **354 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
|
||||
|
||||
- **357-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **354-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 357 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
> 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 354 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
|
||||
- **357 AI providers** with automatic format translation
|
||||
- **354 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
|
||||
|
||||
- **357-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **354-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 357 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
> 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 354 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
|
||||
- **357 AI providers** with automatic format translation
|
||||
- **354 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
|
||||
|
||||
- **357-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **354-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 357 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
> 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 354 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
|
||||
- **357 AI providers** with automatic format translation
|
||||
- **354 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
|
||||
|
||||
- **357-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **354-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 357 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
> 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 354 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
|
||||
- **357 AI providers** with automatic format translation
|
||||
- **354 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
|
||||
|
||||
- **357-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **354-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 357 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
> 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 354 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
|
||||
- **357 AI providers** with automatic format translation
|
||||
- **354 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
|
||||
|
||||
- **357-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **354-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 357 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
> 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 354 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
|
||||
- **357 AI providers** with automatic format translation
|
||||
- **354 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
|
||||
|
||||
- **357-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **354-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 357 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
> 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 354 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
|
||||
- **357 AI providers** with automatic format translation
|
||||
- **354 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
|
||||
|
||||
- **357-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **354-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 357 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
> 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 354 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
|
||||
- **357 AI providers** with automatic format translation
|
||||
- **354 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
|
||||
|
||||
- **357-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **354-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 357 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
> 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 354 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
|
||||
- **357 AI providers** with automatic format translation
|
||||
- **354 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
|
||||
|
||||
- **357-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **354-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 357 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
> 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 354 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
|
||||
- **357 AI providers** with automatic format translation
|
||||
- **354 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
|
||||
|
||||
- **357-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **354-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 357 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
> 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 354 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
|
||||
- **357 AI providers** with automatic format translation
|
||||
- **354 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
|
||||
|
||||
- **357-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **354-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 357 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
> 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 354 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
|
||||
- **357 AI providers** with automatic format translation
|
||||
- **354 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
|
||||
|
||||
- **357-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **354-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 357 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
> 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 354 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
|
||||
- **357 AI providers** with automatic format translation
|
||||
- **354 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
|
||||
|
||||
- **357-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **354-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 357 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
> 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 354 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
|
||||
- **357 AI providers** with automatic format translation
|
||||
- **354 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
|
||||
|
||||
- **357-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **354-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 357 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
> 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 354 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
|
||||
- **357 AI providers** with automatic format translation
|
||||
- **354 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
|
||||
|
||||
- **357-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **354-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 357 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
> 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 354 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
|
||||
- **357 AI providers** with automatic format translation
|
||||
- **354 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
|
||||
|
||||
- **357-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **354-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 357 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
> 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 354 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
|
||||
- **357 AI providers** with automatic format translation
|
||||
- **354 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
|
||||
|
||||
- **357-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **354-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 357 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
> 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 354 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
|
||||
- **357 AI providers** with automatic format translation
|
||||
- **354 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
|
||||
|
||||
- **357-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **354-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 357 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
> 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 354 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
|
||||
- **357 AI providers** with automatic format translation
|
||||
- **354 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
|
||||
|
||||
- **357-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **354-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 357 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
> 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 354 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
|
||||
- **357 AI providers** with automatic format translation
|
||||
- **354 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
|
||||
|
||||
- **357-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **354-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 357 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
> 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 354 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
|
||||
- **357 AI providers** with automatic format translation
|
||||
- **354 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
|
||||
|
||||
- **357-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **354-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 357 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
> 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 354 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
|
||||
- **357 AI providers** with automatic format translation
|
||||
- **354 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
|
||||
|
||||
- **357-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **354-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 357 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
> 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 354 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
|
||||
- **357 AI providers** with automatic format translation
|
||||
- **354 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
|
||||
|
||||
- **357-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **354-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 357 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
> 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 354 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
|
||||
- **357 AI providers** with automatic format translation
|
||||
- **354 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
|
||||
|
||||
- **357-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **354-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 357 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
> 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 354 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
|
||||
- **357 AI providers** with automatic format translation
|
||||
- **354 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
|
||||
|
||||
- **357-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **354-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)
|
||||
|
||||
@@ -1199,8 +1199,6 @@ paths:
|
||||
Searches the web, news, or X through a configured provider. Set `provider`
|
||||
to `xquik-search` to use Xquik for X search. The aliases `xquik` and
|
||||
`xquik_search` resolve to the same provider.
|
||||
AnySearch (`anysearch-search`, aliases `anysearch` / `anysearch_search`)
|
||||
provides free fallback-only web search.
|
||||
security:
|
||||
- BearerAuth: []
|
||||
requestBody:
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
# AnySearch provider integration (PR #11690)
|
||||
|
||||
Template: xquik #11370 (merged in release/v3.8.51). Posture: `fallbackOnly`.
|
||||
Upstream proposal: issue diegosouzapw/OmniRoute#11637.
|
||||
|
||||
## 1. Service ground truth (official docs, cross-checked)
|
||||
|
||||
- Base URL: `https://api.anysearch.com` (REST) + `POST /mcp` (MCP, JSON-RPC 2.0).
|
||||
- `POST /v1/search` - params: `query` (required), `max_results` (1-10, default 10), `tag` (`{domain}.{sub_domain}` vertical routing), `zone` (cn/intl), `language`, `params` (structured vertical fields), `format` (json/markdown).
|
||||
- `GET /v1/sub-domains?domain=...` - capability catalog, does NOT count against quota.
|
||||
- `POST /v1/extract` - fetch/extract `{url, title, content}`; strict JSON body, 16 KiB cap.
|
||||
- `POST /v1/auth/email/register` - single-call registration, returns one-time plaintext key `as_sk_...`.
|
||||
- Auth: optional `Authorization: Bearer <as_sk_...>`; anonymous degrades to per-IP limits consuming the daily free quota; invalid key returns 401/403 with NO silent anonymous fallback.
|
||||
- Free tier: 1000 requests/day, 20 QPS per key. Paid tier: unpriced (Coming Soon).
|
||||
- Response envelope: success `{code: 0, message: "success", request_id, data}`; failure `{code: -1, message}`. Auth/quota errors carry no structured `error_code` (the message text is the signal); extract-specific errors do carry `error_code` (`invalid_extract_url`, `extract_failed`). No `Retry-After` header on 429.
|
||||
|
||||
## 2. Touch set (~10 layers, mirrors the xquik footprint)
|
||||
|
||||
| Layer | File | Change |
|
||||
| --------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------- |
|
||||
| Registry entry | `open-sse/config/searchRegistry.ts` | `anysearch-search` entry + aliases `anysearch`, `anysearch_search` |
|
||||
| Executor | `open-sse/handlers/search/anysearchSearch.ts` | `buildAnysearchSearchRequest`, `normalizeAnysearchSearchResponse`, `AnysearchSearchEnvelopeError` |
|
||||
| Dispatch maps | `open-sse/handlers/search.ts` + `searchProxy.ts` | request-builder map + response-normalizer map; envelope error -> 402 (quota) / 502 (other) |
|
||||
| Fetch executor | `open-sse/executors/anysearch-fetch.ts` + `open-sse/handlers/webFetch.ts` | `POST /v1/extract`; union + `WEB_FETCH_PROVIDERS` + dispatch case; quota envelope -> 402 |
|
||||
| UI catalog | `src/shared/constants/providers/search.ts` | metadata entry (`serviceKinds: ["webSearch", "webFetch"]`); authHint documents the 1000/day free tier |
|
||||
| Credential validation | `src/lib/providers/validation/searchProviders.ts` | `SEARCH_VALIDATOR_CONFIGS["anysearch-search"]` (Bearer probe) |
|
||||
| MCP | `open-sse/mcp-server/schemas/tools.ts` | fetch enum + web_search description |
|
||||
| API schema | `src/shared/validation/schemas/apiV1.ts`, `docs/openapi.yaml` | alias canonicalization + provider enum |
|
||||
| Docs | `docs/reference/PROVIDER_REFERENCE.md`, `docs/frameworks/SEARCH_TOOLS_STUDIO.md`, `changelog.d/features/anysearch-search-provider.md` | consistency copies |
|
||||
| Tests | `tests/unit/anysearch-search-provider.test.ts` (8 cases), `tests/unit/executor-anysearch-fetch.test.ts` (2 cases), `search-registry.test.ts`, `search-route.test.ts`, `tests/integration/search-providers-catalog.test.ts`, `tests/snapshots/executors/dispatch-rules.json` | mirror xquik suite; catalog counts 20 search / 6 fetch (coexisting with nimble-search) |
|
||||
|
||||
## 3. Decisions (all reached 2026-08-26)
|
||||
|
||||
1. **Routing posture: `fallbackOnly`.** Rationale: a cost-0 free provider must never dominate automatic cost routing; explicit selection and failover return are unaffected. Precedent: merged xquik entry.
|
||||
2. **Scope: webSearch + webFetch via `POST /v1/extract` in v1.** Aligned with the tavily/exa dual-capability mental model. Vertical surfaces (`tag`, `sub-domains`, `batch_search`) stay out of scope — there is no IR for vertical params today.
|
||||
3. **Quota display: `freeMonthlyQuota: 0`** (xquik-style conservative display). The real allowance (1000 req/day, daily reset) is carried in the UI catalog `authHint` copy instead. Rationale: quota display must match reset semantics; converting a daily cap to a monthly-equivalent (30000) is a false promise the UI cannot honor.
|
||||
4. **searchTypes: `["web"]` only.** No public evidence of a news/images vertical in the AnySearch API; gateways integrating APIs without a documented vertical (e.g. Google Custom Search) expose web only rather than silently mapping news -> web.
|
||||
5. **Key posture:** keyless works; an invalid key returns 401/403 and is never silently downgraded to anonymous (mirrors the upstream contract).
|
||||
6. **Deliberate exclusions:** `FETCH_BACKEND_TO_PROVIDER` / `FetchInterceptionBackend` (chat interception stays firecrawl/jina/tavily); `ANONYMOUS_CAPABLE_WEB_FETCH_PROVIDERS` (anonymous remote endpoints share one IP-limited pool per egress IP, so keyless extract is opt-in, not an auto-routing candidate); `QUOTA_STATUS_PROVIDERS` (AnySearch signals quota via 429/envelope, not 402/403 status).
|
||||
|
||||
## 4. References
|
||||
|
||||
- xquik template: commit a0ceccc, PR #11370 (in-tree at release/v3.8.51).
|
||||
- AnySearch official docs: https://anysearch.com/docs, https://anysearch.com/pricing; MCP catalog mcpservers.org/servers/anysearch-ai/anysearch-mcp-server; skill repo github.com/anysearch-ai/anysearch-skill.
|
||||
- Industry mental model: LiteLLM search docs (registry + unified search() + Perplexity-spec IR), open-webui web search (built-in providers, search_web/fetch_url dual tools), Dify tool plugin pattern; cost-fallback ladder: self-hosted -> free quota -> paid.
|
||||
@@ -1,16 +1,16 @@
|
||||
---
|
||||
title: "Provider Reference"
|
||||
version: 3.8.51
|
||||
lastUpdated: 2026-08-27
|
||||
lastUpdated: 2026-08-26
|
||||
---
|
||||
|
||||
# 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-27
|
||||
> **Last generated:** 2026-08-26
|
||||
|
||||
Total providers: **357**. See category breakdown below.
|
||||
Total providers: **354**. See category breakdown below.
|
||||
|
||||
## Categories
|
||||
|
||||
@@ -122,7 +122,7 @@ Use the dashboard at `/dashboard/providers` to enable, configure, and test each
|
||||
| `zai-web` | `zw` | Z.ai Web | Web cookie | [link](https://chat.z.ai) | Copy the "token" value from chat.z.ai → DevTools → Application → Local Storage. Do not copy cookies; OmniRoute handles the per-request CAPTCHA through its browser transport. | — |
|
||||
| `zenmux-free` | `zmf` | ZenMux Free (Web) | Web cookie | [link](https://zenmux.ai) | Login at zenmux.ai, then export all cookies using EditThisCookie or Cookie-Editor and paste the full Cookie header string here. Refresh every ~30 days. | — |
|
||||
|
||||
## API Key Providers (paid / paid-with-free-credits) (235)
|
||||
## API Key Providers (paid / paid-with-free-credits) (234)
|
||||
|
||||
| ID | Alias | Name | Tags | Website | Notes |
|
||||
|----|-------|------|------|---------|-------|
|
||||
@@ -277,7 +277,6 @@ Use the dashboard at `/dashboard/providers` to enable, configure, and test each
|
||||
| `oci` | `oci` | OCI Generative AI | API key, enterprise | [link](https://www.oracle.com/artificial-intelligence/generative-ai) | Use your OCI Generative AI API key or IAM bearer token. Base URL can be https://inference.generativeai.<region>.oci.oraclecloud.com/openai/v1/. |
|
||||
| `ofoxai` | `ofoxai` | OfoxAI | API key, aggregator | [link](https://ofox.ai) | The current catalog advertises 10+ free models without a public numeric quota; review upstream provenance, retention and training terms before production use. |
|
||||
| `ollama-cloud` | `ollamacloud` | Ollama Cloud | API key | [link](https://ollama.com/settings/keys) | — |
|
||||
| `oneminai` | `1min` | 1min.AI | API key | [link](https://1min.ai) | Create an API key at https://docs.1min.ai/docs/api/create-api-key, then paste it here. |
|
||||
| `openadapter` | `oad` | OpenAdapter | API key | [link](https://openadapter.dev) | Use your OpenAdapter API key in Authorization: Bearer sk-cv-<key>. Fully OpenAI-compatible. API base URL: https://api.openadapter.in/v1. |
|
||||
| `openai` | `openai` | OpenAI | API key | [link](https://platform.openai.com) | — |
|
||||
| `opencode-go` | `opencode-go` | OpenCode Go | API key | [link](https://opencode.ai/go) | — |
|
||||
@@ -381,11 +380,10 @@ 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 (17)
|
||||
## Search Providers (16)
|
||||
|
||||
| ID | Alias | Name | Tags | Website | Notes |
|
||||
|----|-------|------|------|---------|-------|
|
||||
| `anysearch-search` | `anysearch` | AnySearch | Search | [link](https://anysearch.com) | Optional API key from anysearch.com (as_sk_...) - free 1000/day; keyless tier has lower limits |
|
||||
| `brave-search` | `brave-search` | Brave Search | Search | [link](https://brave.com/search/api) | Subscription token from Brave Search API dashboard |
|
||||
| `context7` | `context7` | Context7 (library docs) | Search | [link](https://context7.com) | API key optional (ctx7sk-...) — anonymous tier works without a key; a key raises the rate limit |
|
||||
| `exa-search` | `exa-search` | Exa Search | Search | [link](https://exa.ai) | API key from dashboard.exa.ai |
|
||||
@@ -445,7 +443,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/) (112 implementations)
|
||||
- Executors: [`open-sse/executors/`](../../open-sse/executors/) (110 implementations)
|
||||
- Translators: [`open-sse/translator/`](../../open-sse/translator/)
|
||||
|
||||
## See Also
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
---
|
||||
title: "Guardrails"
|
||||
version: 3.8.51
|
||||
lastUpdated: 2026-08-27
|
||||
version: 3.8.50
|
||||
lastUpdated: 2026-08-24
|
||||
---
|
||||
|
||||
# Guardrails
|
||||
|
||||
> **Source of truth:** `src/lib/guardrails/`
|
||||
> **Last updated:** 2026-08-27 — v3.8.51 (Video Bridge transcript-retention boundary)
|
||||
> **Last updated:** 2026-08-24 — v3.8.50 (Video Bridge visual dedup hardening + focused captions)
|
||||
|
||||
Guardrails enforce safety, policy, and content transformations at the boundary
|
||||
between OmniRoute and upstream providers. Each guardrail can inspect (and
|
||||
@@ -439,69 +439,6 @@ are rendered as untrusted observations alongside the frame captions. Invalid,
|
||||
out-of-range, or provenance-free text is rejected rather than mixed into the
|
||||
caption stream.
|
||||
|
||||
#### Transcript retention boundary
|
||||
|
||||
Transcript text remains available to the live bridge and upstream request, but it is not retained
|
||||
by OmniRoute's logging and memory surfaces. `videoTranscriptLogRedaction.ts` recognizes
|
||||
`transcript` and `audioTranscript` only inside supported video carriers. A field with either name
|
||||
in an ordinary tool argument or application object is preserved, and delimiter-shaped caller
|
||||
prose is not trusted as a bridge result.
|
||||
|
||||
When the Video Bridge produces a description containing validated cues, it emits only a
|
||||
SHA-256/length identity in guardrail metadata. The request pipeline accepts those identities only
|
||||
from a successful, payload-modifying `video-bridge` result and uses them to omit the exact generated
|
||||
description segment after translation or concatenation. Raw transcript text is never placed in the
|
||||
metadata.
|
||||
|
||||
The retention policy covers raw, converted, and provider request logs; provider and client response
|
||||
bodies; detailed pipeline artifacts; call logs, active/pending usage, rejected requests, proxy/error
|
||||
diagnostics, SSE chunks, and Memory extraction. Response bodies and stream chunks for a sensitive
|
||||
request are omitted because an upstream may echo request text. Safe operational metadata such as
|
||||
status, timing, provider, model, headers-presence, token counts, and the fact that redaction occurred
|
||||
remains available. The constant retained marker is `[omitted: video transcript]`.
|
||||
|
||||
Sensitivity is resolved before the guardrail chain from the original client body and resolved again
|
||||
after the chain from the original body, processed body, and trusted description identities. A
|
||||
guardrail that removes or replaces the raw carrier therefore cannot clear the request-scoped bit.
|
||||
Explicit video parts remain sensitive when malformed, and nested carrier objects are inspected under
|
||||
the same bounds. Rejected-request persistence also inspects the body itself, so forgetting to pass the
|
||||
bit at one caller does not expose a raw carrier.
|
||||
|
||||
The request-scoped bit protects OmniRoute-owned application paths in addition to structured payload
|
||||
clones. Native priority, round-robin, pinned, and fusion diagnostics; quality-rejection and live-event
|
||||
logs; terminal combo errors; proxy fast-fail logs; guardrail-registry logs; plugin-dispatcher failure
|
||||
logs; and stream lifecycle callbacks retain only the constant marker when their detail could echo the
|
||||
request. A request-scoped executor logger keeps the diagnostic tag but replaces arbitrary executor
|
||||
messages with the marker and drops attached metadata before those values reach the application
|
||||
logger. Context Relay and Universal Handoff summaries are still generated from a sanitized clone:
|
||||
safe adjacent conversation context remains available while recognized carriers and trusted generated
|
||||
descriptions are omitted. Memory may likewise extract safe request-derived text from the sanitized
|
||||
copy, but response-derived Memory extraction is skipped because an upstream response may echo the
|
||||
transcript. Daily-quota classification still inspects the original upstream error, while sensitive
|
||||
requests use the server-resolved model as the retained lockout key instead of promoting a model token
|
||||
parsed from that diagnostic. These rules affect retained copies, not the live provider request or the
|
||||
functional response returned to the same client.
|
||||
|
||||
Inspection is bounded by depth, aggregate entries, trusted identities, description-prefix
|
||||
occurrences, candidate hashes, and total hashed text. Cycles and binary views are handled without
|
||||
retaining their contents. If a bound is exceeded, or an enumerable getter/proxy throws during
|
||||
inspection, the retained copy fails closed to the omission marker; the live request is not changed.
|
||||
|
||||
Privacy-redacted Responses artifacts cannot reconstruct the omitted transcript portion of
|
||||
`previous_response_id` history. `responsesContinuationStore.ts` therefore reuses only valid,
|
||||
non-truncated retained `input` and `output` arrays, including the explicit omission marker, and never
|
||||
attempts to restore raw transcript text from the pipeline marker. This preserves continuation for the
|
||||
safe portions of the turn while making the missing private segment visible; a client that needs that
|
||||
context again must submit it as part of a new live request.
|
||||
|
||||
Custom guardrails and plugins are privileged, in-process processors: they intentionally receive the
|
||||
live payload so they can inspect, transform, or block it. OmniRoute supplies the server-owned
|
||||
`videoTranscriptSensitive` bit, wraps `GuardrailContext.log`, and protects errors emitted by its native
|
||||
plugin dispatcher. It cannot control a third-party processor that independently writes the live body
|
||||
to `console`, a file, a database, or the network. Operators must audit such code and treat its own
|
||||
sinks as outside the OmniRoute-owned retention boundary; clearing or ignoring the bit does not make
|
||||
the transcript safe to persist.
|
||||
|
||||
An advanced caller may provide an already-authorized `audioTranscript` track
|
||||
for the same video. The fusion seam runs visual and audio observations under
|
||||
one deadline and abort signal, orders them on a common timeline, collapses
|
||||
|
||||
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 357 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
> 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 354 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
|
||||
- **357 AI providers** with automatic format translation
|
||||
- **354 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
|
||||
|
||||
- **357-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **354-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)
|
||||
|
||||
@@ -370,26 +370,6 @@ export const SEARCH_PROVIDERS: Record<string, SearchProviderConfig> = {
|
||||
cacheTTLMs: 5 * 60 * 1000,
|
||||
fallbackOnly: true,
|
||||
},
|
||||
|
||||
// Free public web search for AI agents (https://anysearch.com). fallback-only:
|
||||
// a cost-0 provider never overrides configured paid providers in automatic
|
||||
// selection. max_results is capped at 10 upstream.
|
||||
"anysearch-search": {
|
||||
id: "anysearch-search",
|
||||
name: "AnySearch",
|
||||
baseUrl: "https://api.anysearch.com/v1/search",
|
||||
method: "POST",
|
||||
authType: "apikey",
|
||||
authHeader: "bearer",
|
||||
costPerQuery: 0,
|
||||
freeMonthlyQuota: 0, // free tier is 1000 req/day (daily reset, not monthly) — 0 matches the xquik convention; the daily figure lives in the UI catalog authHint
|
||||
searchTypes: ["web"],
|
||||
defaultMaxResults: 5,
|
||||
maxMaxResults: 10,
|
||||
timeoutMs: 10_000,
|
||||
cacheTTLMs: 5 * 60 * 1000,
|
||||
fallbackOnly: true,
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -439,8 +419,6 @@ export const SEARCH_PROVIDER_ALIASES: Record<string, string> = {
|
||||
x: "x-search",
|
||||
xquik: "xquik-search",
|
||||
xquik_search: "xquik-search",
|
||||
anysearch: "anysearch-search",
|
||||
anysearch_search: "anysearch-search",
|
||||
};
|
||||
|
||||
export function resolveSearchProviderId(providerId: string): string {
|
||||
|
||||
@@ -1,109 +0,0 @@
|
||||
/**
|
||||
* AnySearch Web Fetch Executor
|
||||
*
|
||||
* Fetches readable content from a URL using the AnySearch Extract API.
|
||||
* POST https://api.anysearch.com/v1/extract
|
||||
*
|
||||
* Free tier: 1000 requests/day per key, shared with /v1/search. Bearer auth
|
||||
* is optional upstream - keyless calls use the lower anonymous tier. Routing
|
||||
* stays key-gated; anonymity only applies at call time.
|
||||
* Docs: https://anysearch.com/docs
|
||||
*/
|
||||
|
||||
import { sanitizeErrorMessage, buildErrorBody } from "../utils/error.ts";
|
||||
import type { WebFetchResult, WebFetchFormat, WebFetchCredentials } from "../handlers/webFetch.ts";
|
||||
|
||||
const ANYSEARCH_EXTRACT_URL = "https://api.anysearch.com/v1/extract";
|
||||
const ANYSEARCH_TIMEOUT_MS = 30_000;
|
||||
|
||||
interface AnysearchFetchOptions {
|
||||
url: string;
|
||||
format: WebFetchFormat;
|
||||
includeMetadata: boolean;
|
||||
credentials: WebFetchCredentials;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute an AnySearch extract request.
|
||||
* The upstream contract is strict JSON: a single { url } object with no
|
||||
* unknown fields, capped at 16 KiB - do not add request fields here.
|
||||
*/
|
||||
export async function anysearchFetch(opts: AnysearchFetchOptions): Promise<WebFetchResult> {
|
||||
// format is accepted but unused: AnySearch extract always returns markdown-ish text
|
||||
// (mirroring the context7 pattern of accepting the field without rejecting).
|
||||
const { url, includeMetadata, credentials } = opts;
|
||||
|
||||
const controller = new AbortController();
|
||||
const timeoutId = setTimeout(() => {
|
||||
const err = new Error(`anysearch-fetch timeout after ${ANYSEARCH_TIMEOUT_MS}ms`);
|
||||
err.name = "TimeoutError";
|
||||
controller.abort(err);
|
||||
}, ANYSEARCH_TIMEOUT_MS);
|
||||
|
||||
try {
|
||||
const response = await fetch(ANYSEARCH_EXTRACT_URL, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
...(credentials.apiKey ? { Authorization: `Bearer ${credentials.apiKey}` } : {}),
|
||||
},
|
||||
body: JSON.stringify({ url }),
|
||||
signal: controller.signal,
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const rawError = await response.text().catch(() => `HTTP ${response.status}`);
|
||||
const msg = sanitizeErrorMessage(`AnySearch error ${response.status}: ${rawError}`);
|
||||
const body = buildErrorBody(response.status, msg);
|
||||
return { success: false, status: response.status, error: body.error.message };
|
||||
}
|
||||
|
||||
const data = (await response.json()) as Record<string, unknown>;
|
||||
|
||||
// Envelope: { code, message, data: { url, title, content } } - tolerate the
|
||||
// enveloped and flat shapes before giving up.
|
||||
const envelope =
|
||||
data.data && typeof data.data === "object" && !Array.isArray(data.data)
|
||||
? (data.data as Record<string, unknown>)
|
||||
: data;
|
||||
const code = typeof data.code === "number" ? data.code : 0;
|
||||
if (code !== 0) {
|
||||
const errorMsg = String(data.error_code ?? data.message ?? code);
|
||||
// Quota-shaped envelope errors map to 402 (failover-eligible), mirroring
|
||||
// the search-side AnysearchSearchEnvelopeError → 402 pattern in anysearchSearch.ts.
|
||||
const isQuota = /quota|exceed|limit|balance|credit|exhaust/i.test(errorMsg);
|
||||
const status = isQuota ? 402 : 422;
|
||||
const msg = sanitizeErrorMessage(`AnySearch extract failed: ${errorMsg}`);
|
||||
const body = buildErrorBody(status, msg);
|
||||
return { success: false, status, error: body.error.message };
|
||||
}
|
||||
|
||||
const content = String(envelope.content ?? "");
|
||||
const title = envelope.title != null ? String(envelope.title) : null;
|
||||
|
||||
const metadata = includeMetadata ? { title, description: null } : null;
|
||||
|
||||
return {
|
||||
success: true,
|
||||
data: {
|
||||
provider: "anysearch-search",
|
||||
url,
|
||||
content,
|
||||
links: [],
|
||||
metadata,
|
||||
screenshot_url: null,
|
||||
},
|
||||
};
|
||||
} catch (err: unknown) {
|
||||
if (err instanceof Error && err.name === "AbortError") {
|
||||
const body = buildErrorBody(504, "AnySearch request timed out");
|
||||
return { success: false, status: 504, error: body.error.message };
|
||||
}
|
||||
const msg =
|
||||
err instanceof Error ? sanitizeErrorMessage(err.message) : sanitizeErrorMessage(String(err));
|
||||
const body = buildErrorBody(502, msg);
|
||||
return { success: false, status: 502, error: body.error.message };
|
||||
} finally {
|
||||
clearTimeout(timeoutId);
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
import { HTTP_STATUS, FETCH_TIMEOUT_MS } from "../config/constants.ts";
|
||||
import { getRegistryEntry } from "../config/providerRegistry.ts";
|
||||
import { resolveFetchStartTimeout } from "../utils/fetchStartTimeoutPolicy.ts";
|
||||
import {
|
||||
resolveAlternateFormat,
|
||||
type AlternateFormat,
|
||||
@@ -179,10 +178,10 @@ export type ProviderCredentials = {
|
||||
};
|
||||
|
||||
export type ExecutorLog = {
|
||||
debug?: (tag: string, message: string, data?: Record<string, unknown> | null) => void;
|
||||
info?: (tag: string, message: string, data?: Record<string, unknown> | null) => void;
|
||||
warn?: (tag: string, message: string, data?: Record<string, unknown> | null) => void;
|
||||
error?: (tag: string, message: string, data?: Record<string, unknown> | null) => void;
|
||||
debug?: (tag: string, message: string) => void;
|
||||
info?: (tag: string, message: string) => void;
|
||||
warn?: (tag: string, message: string) => void;
|
||||
error?: (tag: string, message: string) => void;
|
||||
};
|
||||
|
||||
export type ExecuteInput = {
|
||||
@@ -202,10 +201,6 @@ export type ExecuteInput = {
|
||||
* this to apply client-format-aware policies such as `</think>` close-marker
|
||||
* suppression. */
|
||||
clientResponseFormat?: string | null;
|
||||
/** True when upstream diagnostics may echo a video transcript. Executors must
|
||||
* preserve operational responses/errors while omitting those echoes from
|
||||
* retained logs. */
|
||||
videoTranscriptSensitive?: boolean;
|
||||
/** Callback to persist tokens that are proactively refreshed during execution.
|
||||
* Accepts a partial credentials patch (e.g. `{ accessToken, refreshToken }` or
|
||||
* `{ testStatus: "expired", isActive: false }`); the caller merges into the
|
||||
@@ -907,24 +902,9 @@ export class BaseExecutor {
|
||||
clampNestedThinkingBudget(transformedBody, thinkingBudgetClampedMax);
|
||||
}
|
||||
|
||||
// Timeout only covers response start; stream stalls are handled downstream.
|
||||
// #11526: streaming requests cap the headers-wait phase to a client-realistic
|
||||
// ceiling (see fetchStartTimeoutPolicy.ts) — non-streaming keeps the flat default.
|
||||
// Declared outside the try/catch below so the catch's TIMEOUT log (on the
|
||||
// error path) reports the same effective value the fetch actually used.
|
||||
const fetchStartTimeoutPolicy = resolveFetchStartTimeout({
|
||||
baseTimeoutMs: this.getTimeoutMs(),
|
||||
stream,
|
||||
});
|
||||
const fetchStartTimeoutMs = fetchStartTimeoutPolicy.timeoutMs;
|
||||
if (fetchStartTimeoutPolicy.capped) {
|
||||
log?.debug?.(
|
||||
"TIMEOUT",
|
||||
`fetch-start timeout capped ${fetchStartTimeoutPolicy.baseTimeoutMs}ms -> ${fetchStartTimeoutMs}ms (streaming)`
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
// Timeout only covers response start; stream stalls are handled downstream.
|
||||
const fetchStartTimeoutMs = this.getTimeoutMs();
|
||||
const fetchWithStartTimeout = async (requestUrl: string, requestOptions: RequestInit) => {
|
||||
// GHSA-4f49: guard here (not only next to the first buildUrl) so retries
|
||||
// and fallback URLs are validated too, before any bytes leave the host.
|
||||
@@ -1733,7 +1713,7 @@ export class BaseExecutor {
|
||||
// Distinguish timeout errors from other abort errors
|
||||
const err = error instanceof Error ? error : new Error(String(error));
|
||||
if (err.name === "TimeoutError") {
|
||||
log?.warn?.("TIMEOUT", `Fetch timeout after ${fetchStartTimeoutMs}ms on ${url}`);
|
||||
log?.warn?.("TIMEOUT", `Fetch timeout after ${this.getTimeoutMs()}ms on ${url}`);
|
||||
}
|
||||
lastError = err;
|
||||
if (!skipUpstreamRetry && urlIndex + 1 < fallbackCount) {
|
||||
|
||||
@@ -16,7 +16,6 @@ import { prepareToolMessages, buildToolAwareResult } from "../translator/webTool
|
||||
import type { Session } from "../services/sessionPool/session.ts";
|
||||
import { tryBackedChat } from "../services/browserBackedChat.ts";
|
||||
import { sanitizeErrorMessage } from "../utils/error.ts";
|
||||
import { normalizeSystemRole } from "../services/roleNormalizer.ts";
|
||||
|
||||
// Issue #6999: Lightweight circuit breaker for the DuckDuckGo executor.
|
||||
// After CB_THRESHOLD consecutive failures (429, 5xx, or network errors),
|
||||
@@ -560,17 +559,8 @@ export class DuckDuckGoWebExecutor extends BaseExecutor {
|
||||
}
|
||||
}
|
||||
|
||||
// #ddgw defense-in-depth: duckchat/v1/chat accepts only user/assistant roles.
|
||||
// Normalize after catalog resolution so the effective upstream model is used.
|
||||
// This also shields the system tool prompt injected by prepareToolMessages.
|
||||
const normalizedMessages = normalizeSystemRole(
|
||||
messages,
|
||||
"duckduckgo-web",
|
||||
upstreamModel
|
||||
) as typeof messages;
|
||||
|
||||
const sendChat = async (vqdHeaders: DuckDuckGoAuthHeaders): Promise<Response> => {
|
||||
const payload = buildDuckDuckGoPayload(upstreamModel, normalizedMessages);
|
||||
const payload = buildDuckDuckGoPayload(upstreamModel, messages);
|
||||
const response = await fetch(CHAT_URL, {
|
||||
method: "POST",
|
||||
headers: mergeHeadersCaseInsensitive(
|
||||
@@ -796,7 +786,10 @@ export class DuckDuckGoWebExecutor extends BaseExecutor {
|
||||
try {
|
||||
return {
|
||||
vqd4: retry.vqd4,
|
||||
vqdHash1: await solveDuckDuckGoChallenge(retry.vqdHash1, FAKE_HEADERS["User-Agent"]),
|
||||
vqdHash1: await solveDuckDuckGoChallenge(
|
||||
retry.vqdHash1,
|
||||
FAKE_HEADERS["User-Agent"]
|
||||
),
|
||||
status: retry.status,
|
||||
retryAfter: retry.retryAfter,
|
||||
};
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { randomUUID } from "node:crypto";
|
||||
import { redactVideoTranscriptSensitiveText } from "../../src/lib/guardrails/videoTranscriptLogRedaction.ts";
|
||||
import type { KeyHealth } from "../services/apiKeyRotator.ts";
|
||||
|
||||
import { DefaultExecutor } from "./default.ts";
|
||||
@@ -46,11 +45,6 @@ function asRecord(value: unknown): JsonRecord | null {
|
||||
return value && typeof value === "object" && !Array.isArray(value) ? (value as JsonRecord) : null;
|
||||
}
|
||||
|
||||
function retainGlmDiagnostic(error: unknown, input: ExecuteInput): string {
|
||||
const message = error instanceof Error ? error.message : String(error);
|
||||
return redactVideoTranscriptSensitiveText(message, input.videoTranscriptSensitive === true);
|
||||
}
|
||||
|
||||
function getEffectiveKey(credentials: ProviderCredentials): string {
|
||||
const extraKeys = (credentials.providerSpecificData?.extraApiKeys as string[] | undefined) ?? [];
|
||||
if (credentials.apiKey && credentials.connectionId && extraKeys.length > 0) {
|
||||
@@ -471,7 +465,6 @@ export class GlmExecutor extends DefaultExecutor {
|
||||
timeoutMs: STREAM_READINESS_TIMEOUT_MS,
|
||||
provider: this.provider,
|
||||
model: input.model,
|
||||
redactUpstreamDiagnosticForLog: input.videoTranscriptSensitive === true,
|
||||
log: input.log,
|
||||
});
|
||||
response = readiness.response;
|
||||
@@ -566,7 +559,7 @@ export class GlmExecutor extends DefaultExecutor {
|
||||
if (!isRetryableGlmFallbackError(error)) throw error;
|
||||
input.log?.debug?.(
|
||||
"GLM_FALLBACK",
|
||||
`${primaryTransport} error (${retainGlmDiagnostic(error, input)}); trying ${fallbackTransport}`
|
||||
`${primaryTransport} error (${error instanceof Error ? error.message : String(error)}); trying ${fallbackTransport}`
|
||||
);
|
||||
}
|
||||
|
||||
@@ -579,7 +572,7 @@ export class GlmExecutor extends DefaultExecutor {
|
||||
if (!primaryResult) throw error;
|
||||
input.log?.debug?.(
|
||||
"GLM_FALLBACK",
|
||||
`${fallbackTransport} fallback failed (${retainGlmDiagnostic(error, input)}); returning primary response`
|
||||
`${fallbackTransport} fallback failed (${error instanceof Error ? error.message : String(error)}); returning primary response`
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -90,19 +90,13 @@ async function resolveZaiBrowserAttachments(
|
||||
> {
|
||||
try {
|
||||
// Browser-page upload: keep the original bytes/mimeType (no Cursor wire prep).
|
||||
// EncodedImage.mimeType is optional on the wire type, but every producer
|
||||
// reachable here (decodeDataUrl / fetchImageBytes) validates an image/*
|
||||
// string before pushing; the fallback only satisfies the attachment type.
|
||||
const images = await resolveCursorImages(imageUrls, { prepareForWire: false });
|
||||
return {
|
||||
attachments: images.map((image, index) => {
|
||||
const mimeType = image.mimeType ?? "image/jpeg";
|
||||
return {
|
||||
name: zaiImageFileName(mimeType, index),
|
||||
mimeType,
|
||||
buffer: image.data,
|
||||
};
|
||||
}),
|
||||
attachments: images.map((image, index) => ({
|
||||
name: zaiImageFileName(image.mimeType, index),
|
||||
mimeType: image.mimeType,
|
||||
buffer: image.data,
|
||||
})),
|
||||
};
|
||||
} catch (error) {
|
||||
const message =
|
||||
|
||||
@@ -166,11 +166,6 @@ import {
|
||||
runWithCasGuard,
|
||||
} from "../services/tokenRefresh.ts";
|
||||
import { createRequestLogger } from "../utils/requestLogger.ts";
|
||||
import {
|
||||
redactVideoTranscriptSensitiveText,
|
||||
resolveVideoTranscriptLogSensitivity,
|
||||
} from "@/lib/guardrails/videoTranscriptLogRedaction";
|
||||
import { createExecutorRetentionLog } from "./chatCore/executorRetentionLog.ts";
|
||||
import { createPreparedRequestLogger, runWithCapture } from "../utils/providerRequestLogging.ts";
|
||||
import { summarizeToolSources } from "../utils/toolSources.ts";
|
||||
import { applyResponsesPreviousResponseIdPolicy } from "../utils/responsesStatePolicy.ts";
|
||||
@@ -529,22 +524,7 @@ export async function handleChatCore({
|
||||
skipResourcePressureGuard = false,
|
||||
reasoningTransportFallback = "drop",
|
||||
managedLease = null,
|
||||
videoTranscriptSensitive = false,
|
||||
videoTranscriptDescriptionFingerprints = [],
|
||||
}) {
|
||||
videoTranscriptSensitive =
|
||||
videoTranscriptSensitive ||
|
||||
resolveVideoTranscriptLogSensitivity({
|
||||
rawRequestBody: clientRawRequest?.body,
|
||||
processedBody: body,
|
||||
trustedDescriptionFingerprints: videoTranscriptDescriptionFingerprints,
|
||||
});
|
||||
const retainedErrorTextForLog = (error: unknown): string =>
|
||||
redactVideoTranscriptSensitiveText(
|
||||
error instanceof Error ? error.message : String(error),
|
||||
videoTranscriptSensitive
|
||||
);
|
||||
const retainedExecutorLog = createExecutorRetentionLog(log, videoTranscriptSensitive);
|
||||
let { provider, model, extendedContext } = modelInfo;
|
||||
const resilienceSettings = resolveResilienceSettings(cachedSettings);
|
||||
if (!skipResourcePressureGuard) {
|
||||
@@ -657,7 +637,6 @@ export async function handleChatCore({
|
||||
apiKeyInfo,
|
||||
headers: clientRawRequest?.headers,
|
||||
log,
|
||||
videoTranscriptSensitive,
|
||||
});
|
||||
if (pluginGate.blocked === true) {
|
||||
return {
|
||||
@@ -742,7 +721,6 @@ export async function handleChatCore({
|
||||
effectiveServiceTier,
|
||||
startTime,
|
||||
log,
|
||||
videoTranscriptSensitive,
|
||||
});
|
||||
if (idempotencyHit) {
|
||||
return idempotencyHit;
|
||||
@@ -924,8 +902,6 @@ export async function handleChatCore({
|
||||
stage: "registered",
|
||||
correlationId,
|
||||
sessionTag: conversationId || null,
|
||||
videoTranscriptSensitive,
|
||||
videoTranscriptDescriptionFingerprints,
|
||||
}) || generateRequestId();
|
||||
|
||||
// Initialize rate limit settings from persisted DB (once, lazy)
|
||||
@@ -1058,8 +1034,6 @@ export async function handleChatCore({
|
||||
noLogEnabled,
|
||||
correlationId,
|
||||
modelPinned,
|
||||
videoTranscriptSensitive,
|
||||
videoTranscriptDescriptionFingerprints,
|
||||
// Resolved conversationId (open-sse/services/conversationTracker.ts) wins when
|
||||
// present — it's populated for every request now, not just ones where the
|
||||
// client explicitly sent x-omniroute-session-id. The raw header remains a
|
||||
@@ -1182,17 +1156,8 @@ export async function handleChatCore({
|
||||
model,
|
||||
provider: provider || undefined,
|
||||
connectionId: connectionId || credentials?.connectionId || undefined,
|
||||
videoTranscriptSensitive,
|
||||
videoTranscriptDescriptionFingerprints,
|
||||
});
|
||||
const pendingScope = {
|
||||
id: pendingRequestId,
|
||||
model,
|
||||
provider,
|
||||
connectionId: pendingConnId,
|
||||
videoTranscriptSensitive,
|
||||
videoTranscriptDescriptionFingerprints,
|
||||
};
|
||||
const pendingScope = { id: pendingRequestId, model, provider, connectionId: pendingConnId };
|
||||
const providerRequestCapture = createPreparedRequestLogger(reqLogger, pendingScope);
|
||||
// 0. Log client raw request (before format conversion)
|
||||
if (clientRawRequest) {
|
||||
@@ -1238,7 +1203,6 @@ export async function handleChatCore({
|
||||
apiKeyId: apiKeyInfo?.id ?? undefined,
|
||||
cacheDefaultMode: (apiKeyInfo as { cacheDefaultMode?: "legacy" | "bypass" } | null)
|
||||
?.cacheDefaultMode,
|
||||
videoTranscriptSensitive,
|
||||
});
|
||||
if (cacheHit) {
|
||||
return cacheHit;
|
||||
@@ -1518,7 +1482,8 @@ export async function handleChatCore({
|
||||
} catch (err) {
|
||||
log?.debug?.(
|
||||
"COMPRESSION",
|
||||
"Combo compression override lookup skipped: " + retainedErrorTextForLog(err)
|
||||
"Combo compression override lookup skipped: " +
|
||||
(err instanceof Error ? err.message : String(err))
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1527,7 +1492,10 @@ export async function handleChatCore({
|
||||
const { listCompressionCombos } = await import("../../src/lib/db/compressionCombos.ts");
|
||||
namedCombos = buildNamedComboLookup(listCompressionCombos());
|
||||
} catch (err) {
|
||||
log?.debug?.("COMPRESSION", "Named combos load skipped: " + retainedErrorTextForLog(err));
|
||||
log?.debug?.(
|
||||
"COMPRESSION",
|
||||
"Named combos load skipped: " + (err instanceof Error ? err.message : String(err))
|
||||
);
|
||||
}
|
||||
// Phase 3: per-request override. Unknown values fall through in the resolver (never error).
|
||||
const compressionHeader = resolveCompressionHeader(clientRawRequest?.headers ?? null);
|
||||
@@ -1575,7 +1543,8 @@ export async function handleChatCore({
|
||||
} catch (err) {
|
||||
log?.debug?.(
|
||||
"COMPRESSION",
|
||||
"Default compression combo lookup skipped: " + retainedErrorTextForLog(err)
|
||||
"Default compression combo lookup skipped: " +
|
||||
(err instanceof Error ? err.message : String(err))
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1617,7 +1586,10 @@ export async function handleChatCore({
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
log?.debug?.("COMPRESSION", "Output styles skipped: " + retainedErrorTextForLog(err));
|
||||
log?.debug?.(
|
||||
"COMPRESSION",
|
||||
"Output styles skipped: " + (err instanceof Error ? err.message : String(err))
|
||||
);
|
||||
}
|
||||
}
|
||||
const compressionInputBody = body as Record<string, unknown>;
|
||||
@@ -1927,7 +1899,8 @@ export async function handleChatCore({
|
||||
} catch (err) {
|
||||
log?.warn?.(
|
||||
"COMPRESSION",
|
||||
"Compression pipeline error (non-fatal): " + retainedErrorTextForLog(err)
|
||||
"Compression pipeline error (non-fatal): " +
|
||||
(err instanceof Error ? err.message : String(err))
|
||||
);
|
||||
}
|
||||
// --- End Modular Compression Pipeline ---
|
||||
@@ -1981,10 +1954,7 @@ export async function handleChatCore({
|
||||
`Combo context limit: ${resolved.limit} (source=${resolved.source})`
|
||||
);
|
||||
} catch (err) {
|
||||
log?.warn?.(
|
||||
"CONTEXT",
|
||||
"Failed to resolve combo limits for compression: " + retainedErrorTextForLog(err)
|
||||
);
|
||||
log?.warn?.("CONTEXT", "Failed to resolve combo limits for compression: " + err);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2462,21 +2432,13 @@ export async function handleChatCore({
|
||||
try {
|
||||
const { runOnError } = await import("@/lib/plugins/hooks");
|
||||
await runOnError(
|
||||
{
|
||||
requestId: traceId,
|
||||
body,
|
||||
model,
|
||||
provider,
|
||||
apiKeyInfo,
|
||||
metadata: {},
|
||||
videoTranscriptSensitive,
|
||||
},
|
||||
{ requestId: traceId, body, model, provider, apiKeyInfo, metadata: {} },
|
||||
error instanceof Error ? error : new Error(String(error))
|
||||
);
|
||||
} catch (pluginErr) {
|
||||
log?.debug?.(
|
||||
"PLUGIN",
|
||||
`onError hook error (non-fatal): ${retainedErrorTextForLog(pluginErr)}`
|
||||
`onError hook error (non-fatal): ${pluginErr instanceof Error ? pluginErr.message : String(pluginErr)}`
|
||||
);
|
||||
}
|
||||
|
||||
@@ -2488,13 +2450,7 @@ export async function handleChatCore({
|
||||
const message = error?.message || "Invalid request";
|
||||
const errorType = typeof error?.errorType === "string" ? error.errorType : null;
|
||||
|
||||
log?.warn?.(
|
||||
"TRANSLATE",
|
||||
`Request translation failed: ${redactVideoTranscriptSensitiveText(
|
||||
message,
|
||||
videoTranscriptSensitive
|
||||
)}`
|
||||
);
|
||||
log?.warn?.("TRANSLATE", `Request translation failed: ${message}`);
|
||||
|
||||
if (errorType) {
|
||||
trackPendingRequest(model, provider, connectionId, false);
|
||||
@@ -2588,7 +2544,8 @@ export async function handleChatCore({
|
||||
// must never turn an otherwise valid translated request into a 500.
|
||||
log?.warn?.(
|
||||
"COMPRESSION",
|
||||
"Post-translation OmniGlyph skipped: " + retainedErrorTextForLog(error)
|
||||
"Post-translation OmniGlyph skipped: " +
|
||||
(error instanceof Error ? error.message : String(error))
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -2882,7 +2839,7 @@ export async function handleChatCore({
|
||||
const resolveExecutorWithProxy = (prov: string) =>
|
||||
resolveExecutorWithProxyFor(
|
||||
prov,
|
||||
retainedExecutorLog,
|
||||
log,
|
||||
(credentials?.providerSpecificData as Record<string, unknown> | null | undefined) ?? null
|
||||
);
|
||||
|
||||
@@ -2904,7 +2861,7 @@ export async function handleChatCore({
|
||||
}).catch((err: unknown): EnforceDecision => {
|
||||
log?.warn?.(
|
||||
"QUOTA_SHARE",
|
||||
`enforceQuotaShare failed; fail-open: ${retainedErrorTextForLog(err)}`
|
||||
`enforceQuotaShare failed; fail-open: ${err instanceof Error ? err.message : String(err)}`
|
||||
);
|
||||
return { kind: "allow" as const };
|
||||
});
|
||||
@@ -2946,7 +2903,7 @@ export async function handleChatCore({
|
||||
// Outer fail-open guard — should not be reached (inner .catch covers it)
|
||||
log?.warn?.(
|
||||
"QUOTA_SHARE",
|
||||
`[quotaShare] enforceQuotaShare unexpected error; fail-open: ${retainedErrorTextForLog(err)}`
|
||||
`[quotaShare] enforceQuotaShare unexpected error; fail-open: ${err instanceof Error ? err.message : String(err)}`
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -2958,7 +2915,7 @@ export async function handleChatCore({
|
||||
} catch (err) {
|
||||
log?.warn?.(
|
||||
"QUOTA_SHARE",
|
||||
`[quotaShare] could not set soft penalty on candidate: ${retainedErrorTextForLog(err)}`
|
||||
`[quotaShare] could not set soft penalty on candidate: ${err instanceof Error ? err.message : String(err)}`
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -2992,8 +2949,6 @@ export async function handleChatCore({
|
||||
let onPipelineStreamError: streamFailure.PipelineStreamErrorHandler | null = null;
|
||||
let onClientDisconnectFinalize:
|
||||
((event: { reason: string; duration: number }) => boolean) | null = null;
|
||||
const redactStreamDiagnosticsForLog =
|
||||
videoTranscriptSensitive || reqLogger.isVideoTranscriptSensitive();
|
||||
|
||||
// Create stream controller for disconnect detection
|
||||
const streamController = createStreamController({
|
||||
@@ -3025,15 +2980,11 @@ export async function handleChatCore({
|
||||
clientAbortSignal: clientRawRequest?.signal,
|
||||
allowCompletedToolHandoffGrace: isCodexResponsesEcho,
|
||||
clientDisconnectGracePeriodMs: STREAM_DISCONNECT_GRACE_PERIOD_MS,
|
||||
redactStreamDiagnosticsForLog,
|
||||
});
|
||||
|
||||
const dedupRequestBody = { ...translatedBody, model: `${provider}/${model}`, stream };
|
||||
const dedupEnabled = shouldDeduplicate(dedupRequestBody);
|
||||
// Namespaced by the calling API key: dedup hands the SAME response object to
|
||||
// every joiner, so a shared hash across keys is a cross-principal response
|
||||
// leak (GHSA-6c7w-56xp-wpc6).
|
||||
const dedupHash = dedupEnabled ? computeRequestHash(dedupRequestBody, apiKeyInfo?.id) : null;
|
||||
const dedupHash = dedupEnabled ? computeRequestHash(dedupRequestBody) : null;
|
||||
|
||||
const executeProviderRequest = async (modelToCall = effectiveModel, allowDedup = false) => {
|
||||
const execute = async () => {
|
||||
@@ -3158,7 +3109,7 @@ export async function handleChatCore({
|
||||
execCreds?.providerSpecificData
|
||||
),
|
||||
signal: streamController.signal,
|
||||
log: retainedExecutorLog,
|
||||
log,
|
||||
execute: (signal) =>
|
||||
runWithCapture(providerRequestCapture, () =>
|
||||
executor.execute({
|
||||
@@ -3167,7 +3118,7 @@ export async function handleChatCore({
|
||||
stream: upstreamStream,
|
||||
credentials: execCreds,
|
||||
signal,
|
||||
log: retainedExecutorLog,
|
||||
log,
|
||||
extendedContext,
|
||||
upstreamExtraHeaders: buildUpstreamHeadersForExecute(modelToCall),
|
||||
clientHeaders: buildExecutorClientHeaders(
|
||||
@@ -3175,7 +3126,6 @@ export async function handleChatCore({
|
||||
userAgent
|
||||
),
|
||||
clientResponseFormat,
|
||||
videoTranscriptSensitive,
|
||||
onCredentialsRefreshed,
|
||||
skipUpstreamRetry,
|
||||
contextEditing: { enabled: contextEditingEnabled },
|
||||
@@ -3210,7 +3160,7 @@ export async function handleChatCore({
|
||||
invalidateCodexQuotaCache(String(attemptConnectionId));
|
||||
}
|
||||
} catch (err) {
|
||||
const errMessage = retainedErrorTextForLog(err);
|
||||
const errMessage = err instanceof Error ? err.message : String(err);
|
||||
log?.debug?.("CODEX", `Failed to persist codex quota state: ${errMessage}`);
|
||||
}
|
||||
}
|
||||
@@ -3475,7 +3425,7 @@ export async function handleChatCore({
|
||||
execCreds?.providerSpecificData
|
||||
),
|
||||
signal: streamController.signal,
|
||||
log: retainedExecutorLog,
|
||||
log,
|
||||
execute: (signal) =>
|
||||
runWithCapture(providerRequestCapture, () =>
|
||||
executor.execute({
|
||||
@@ -3484,7 +3434,7 @@ export async function handleChatCore({
|
||||
stream: upstreamStream,
|
||||
credentials: execCreds,
|
||||
signal,
|
||||
log: retainedExecutorLog,
|
||||
log,
|
||||
extendedContext,
|
||||
upstreamExtraHeaders: buildUpstreamHeadersForExecute(modelToCall),
|
||||
clientHeaders: buildExecutorClientHeaders(
|
||||
@@ -3492,7 +3442,6 @@ export async function handleChatCore({
|
||||
userAgent
|
||||
),
|
||||
clientResponseFormat,
|
||||
videoTranscriptSensitive,
|
||||
onCredentialsRefreshed,
|
||||
skipUpstreamRetry,
|
||||
contextEditing: { enabled: contextEditingEnabled },
|
||||
@@ -3725,9 +3674,7 @@ export async function handleChatCore({
|
||||
} catch (err) {
|
||||
// Fail-open at Tier 2: Tier 1 already enforced the model/global limit pre-dispatch.
|
||||
// A transient counter read error here must not break an otherwise-valid request.
|
||||
log?.warn?.("TOKEN_LIMIT", "Tier 2 token-limit check failed; allowing request", {
|
||||
error: retainedErrorTextForLog(err),
|
||||
});
|
||||
log?.warn?.("TOKEN_LIMIT", "Tier 2 token-limit check failed; allowing request", { err });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3746,9 +3693,7 @@ export async function handleChatCore({
|
||||
);
|
||||
}
|
||||
} catch (err) {
|
||||
log?.warn?.("GEMINI_RATE_LIMIT", "Pre-dispatch TPM check failed; allowing request", {
|
||||
error: retainedErrorTextForLog(err),
|
||||
});
|
||||
log?.warn?.("GEMINI_RATE_LIMIT", "Pre-dispatch TPM check failed; allowing request", { err });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3898,12 +3843,7 @@ export async function handleChatCore({
|
||||
failureStatus,
|
||||
upstreamErrorCode || (error instanceof Error && error.name ? error.name : "upstream_error")
|
||||
);
|
||||
console.log(
|
||||
`${COLORS.red}[ERROR] ${redactVideoTranscriptSensitiveText(
|
||||
failureMessage,
|
||||
videoTranscriptSensitive
|
||||
)}${COLORS.reset}`
|
||||
);
|
||||
console.log(`${COLORS.red}[ERROR] ${failureMessage}${COLORS.reset}`);
|
||||
if (stream && upstreamErrorCode) {
|
||||
const result = createStreamingErrorResult(
|
||||
failureStatus,
|
||||
@@ -4035,12 +3975,11 @@ export async function handleChatCore({
|
||||
stream: upstreamStream,
|
||||
credentials: getExecutionCredentials(),
|
||||
signal: streamController.signal,
|
||||
log: retainedExecutorLog,
|
||||
log,
|
||||
extendedContext,
|
||||
upstreamExtraHeaders: buildUpstreamHeadersForExecute(retryModelId),
|
||||
clientHeaders: buildExecutorClientHeaders(clientRawRequest?.headers, userAgent),
|
||||
clientResponseFormat,
|
||||
videoTranscriptSensitive,
|
||||
onCredentialsRefreshed,
|
||||
skipUpstreamRetry: isCombo,
|
||||
contextEditing: { enabled: contextEditingEnabled },
|
||||
@@ -4070,13 +4009,9 @@ export async function handleChatCore({
|
||||
// executor throw). Don't swallow — the operator-visible signal "the user
|
||||
// saw 401 even though auth was actually fixed" is much more confusing
|
||||
// than the original 401 alone. Surface at error level with sanitization.
|
||||
const retainedRetryError = redactVideoTranscriptSensitiveText(
|
||||
sanitizeErrorMessage(retryErr),
|
||||
videoTranscriptSensitive
|
||||
);
|
||||
log?.error?.(
|
||||
"TOKEN",
|
||||
`${provider?.toUpperCase()} | retry after refresh failed: ${retainedRetryError}`
|
||||
`${provider?.toUpperCase()} | retry after refresh failed: ${sanitizeErrorMessage(retryErr)}`
|
||||
);
|
||||
}
|
||||
} else {
|
||||
@@ -4193,11 +4128,6 @@ export async function handleChatCore({
|
||||
|
||||
if (signatureRecovery.succeeded) break providerFailure;
|
||||
|
||||
const retainedProviderMessage = redactVideoTranscriptSensitiveText(
|
||||
message,
|
||||
videoTranscriptSensitive
|
||||
);
|
||||
|
||||
// #10281 — tiny-budget reasoning probes (e.g. Claude Code's `/model` check
|
||||
// sends `max_tokens: 1`): the model burns the whole budget on thinking, and
|
||||
// some upstreams (e.g. api.cline.bot for deepseek-v4-flash) answer the empty
|
||||
@@ -4220,7 +4150,7 @@ export async function handleChatCore({
|
||||
});
|
||||
log?.warn?.(
|
||||
"PROBE",
|
||||
`Reasoning probe (max_tokens < ${REASONING_BUFFER_MIN_TRIGGER}) answered with truncated 200 — upstream reported "${retainedProviderMessage}"`
|
||||
`Reasoning probe (max_tokens < ${REASONING_BUFFER_MIN_TRIGGER}) answered with truncated 200 — upstream reported "${message}"`
|
||||
);
|
||||
break providerFailure;
|
||||
}
|
||||
@@ -4249,7 +4179,7 @@ export async function handleChatCore({
|
||||
{
|
||||
testStatus: "banned",
|
||||
isActive: false,
|
||||
lastError: retainedProviderMessage,
|
||||
lastError: message,
|
||||
lastErrorType: errorType,
|
||||
errorCode: String(statusCode),
|
||||
},
|
||||
@@ -4280,7 +4210,7 @@ export async function handleChatCore({
|
||||
) {
|
||||
await updateProviderConnection(errorConnectionId, {
|
||||
lastErrorType: errorType,
|
||||
lastError: retainedProviderMessage,
|
||||
lastError: message,
|
||||
errorCode: statusCode,
|
||||
});
|
||||
console.warn(
|
||||
@@ -4293,7 +4223,7 @@ export async function handleChatCore({
|
||||
{
|
||||
testStatus: "deactivated",
|
||||
isActive: false,
|
||||
lastError: retainedProviderMessage,
|
||||
lastError: message,
|
||||
lastErrorType: errorType,
|
||||
errorCode: String(statusCode),
|
||||
},
|
||||
@@ -4317,7 +4247,7 @@ export async function handleChatCore({
|
||||
errorConnectionId,
|
||||
{
|
||||
testStatus: "credits_exhausted",
|
||||
lastError: retainedProviderMessage,
|
||||
lastError: message,
|
||||
lastErrorType: errorType,
|
||||
errorCode: String(statusCode),
|
||||
},
|
||||
@@ -4365,7 +4295,7 @@ export async function handleChatCore({
|
||||
rateLimitedUntil: kimiRateLimitResetAt,
|
||||
backoffLevel: 0,
|
||||
lastErrorType: PROVIDER_ERROR_TYPES.RATE_LIMITED,
|
||||
lastError: retainedProviderMessage,
|
||||
lastError: message,
|
||||
errorCode: statusCode,
|
||||
});
|
||||
console.warn(
|
||||
@@ -4395,7 +4325,7 @@ export async function handleChatCore({
|
||||
errorConnectionId,
|
||||
{
|
||||
testStatus: "credits_exhausted",
|
||||
lastError: retainedProviderMessage,
|
||||
lastError: message,
|
||||
lastErrorType: errorType,
|
||||
errorCode: String(statusCode),
|
||||
},
|
||||
@@ -4411,14 +4341,14 @@ export async function handleChatCore({
|
||||
// Normal 401 (token/session auth issue): keep account active for refresh/re-auth.
|
||||
await updateProviderConnection(errorConnectionId, {
|
||||
lastErrorType: errorType,
|
||||
lastError: retainedProviderMessage,
|
||||
lastError: message,
|
||||
errorCode: statusCode,
|
||||
});
|
||||
} else if (errorType === PROVIDER_ERROR_TYPES.OAUTH_INVALID_TOKEN) {
|
||||
// OAuth 401 with invalid credentials - token refresh can recover
|
||||
await updateProviderConnection(errorConnectionId, {
|
||||
lastErrorType: errorType,
|
||||
lastError: retainedProviderMessage,
|
||||
lastError: message,
|
||||
errorCode: statusCode,
|
||||
});
|
||||
console.warn(
|
||||
@@ -4428,7 +4358,7 @@ export async function handleChatCore({
|
||||
// Cloud Code 403 with stale project: not a ban, keep account active.
|
||||
await updateProviderConnection(errorConnectionId, {
|
||||
lastErrorType: errorType,
|
||||
lastError: retainedProviderMessage,
|
||||
lastError: message,
|
||||
errorCode: statusCode,
|
||||
});
|
||||
console.warn(
|
||||
@@ -4444,7 +4374,7 @@ export async function handleChatCore({
|
||||
const geoCooldownMs = COOLDOWN_MS.geoBlocked ?? 24 * 60 * 60 * 1000;
|
||||
await updateProviderConnection(errorConnectionId, {
|
||||
lastErrorType: errorType,
|
||||
lastError: retainedProviderMessage,
|
||||
lastError: message,
|
||||
errorCode: statusCode,
|
||||
});
|
||||
// T-PROBE: the 24h exclusion is a routing mutation — a probe must
|
||||
@@ -4469,7 +4399,7 @@ export async function handleChatCore({
|
||||
const byopCooldownMs = COOLDOWN_MS.gcpProjectRequired ?? 24 * 60 * 60 * 1000;
|
||||
await updateProviderConnection(errorConnectionId, {
|
||||
lastErrorType: errorType,
|
||||
lastError: retainedProviderMessage,
|
||||
lastError: message,
|
||||
errorCode: statusCode,
|
||||
});
|
||||
try {
|
||||
@@ -4515,13 +4445,7 @@ export async function handleChatCore({
|
||||
}).catch(() => {});
|
||||
|
||||
const errMsg = formatProviderError(new Error(message), provider, model, statusCode);
|
||||
const retainedErrMsg = formatProviderError(
|
||||
new Error(retainedProviderMessage),
|
||||
provider,
|
||||
model,
|
||||
statusCode
|
||||
);
|
||||
console.log(`${COLORS.red}[ERROR] ${retainedErrMsg}${COLORS.reset}`);
|
||||
console.log(`${COLORS.red}[ERROR] ${errMsg}${COLORS.reset}`);
|
||||
|
||||
// Log Antigravity retry time if available
|
||||
if (retryAfterMs && provider === "antigravity") {
|
||||
@@ -4848,11 +4772,12 @@ export async function handleChatCore({
|
||||
}
|
||||
}
|
||||
} catch (retryErr) {
|
||||
const retainedRetryError = redactVideoTranscriptSensitiveText(
|
||||
retryErr instanceof Error ? retryErr.message : String(retryErr),
|
||||
videoTranscriptSensitive
|
||||
log?.warn?.(
|
||||
"RETRY",
|
||||
`clinepass retry failed: ${
|
||||
retryErr instanceof Error ? retryErr.message : String(retryErr)
|
||||
}`
|
||||
);
|
||||
log?.warn?.("RETRY", `clinepass retry failed: ${retainedRetryError}`);
|
||||
}
|
||||
}
|
||||
if (envError) {
|
||||
@@ -5071,7 +4996,6 @@ export async function handleChatCore({
|
||||
cacheReasoningFromAssistantMessage(msg, provider, model, {
|
||||
scope: reasoningCacheScope,
|
||||
historyMessages: Array.isArray(historyMessages) ? historyMessages : [],
|
||||
videoTranscriptSensitive,
|
||||
});
|
||||
}
|
||||
} catch {
|
||||
@@ -5123,18 +5047,12 @@ export async function handleChatCore({
|
||||
);
|
||||
|
||||
if (memoryOwnerId && memorySettings?.enabled && memorySettings.maxTokens > 0) {
|
||||
const requestMemoryText = extractMemoryTextFromRequestBody(
|
||||
body as Record<string, unknown>,
|
||||
videoTranscriptSensitive,
|
||||
{ trustedDescriptionFingerprints: videoTranscriptDescriptionFingerprints }
|
||||
);
|
||||
const requestMemoryText = extractMemoryTextFromRequestBody(body as Record<string, unknown>);
|
||||
if (requestMemoryText) {
|
||||
extractFacts(requestMemoryText, memoryOwnerId, pipelineSessionId);
|
||||
}
|
||||
|
||||
const memoryText = videoTranscriptSensitive
|
||||
? ""
|
||||
: extractMemoryTextFromResponse(memoryExtractionResponse);
|
||||
const memoryText = extractMemoryTextFromResponse(memoryExtractionResponse);
|
||||
if (memoryText) {
|
||||
extractFacts(memoryText, memoryOwnerId, pipelineSessionId);
|
||||
}
|
||||
@@ -5174,7 +5092,6 @@ export async function handleChatCore({
|
||||
provider,
|
||||
responsePayloadFormat,
|
||||
clientResponseFormat,
|
||||
videoTranscriptSensitive,
|
||||
});
|
||||
const postCallGuardrails = await guardrailRegistry.runPostCallHooks(
|
||||
translatedResponse,
|
||||
@@ -5216,10 +5133,7 @@ export async function handleChatCore({
|
||||
}
|
||||
log?.warn?.(
|
||||
"GUARDRAIL",
|
||||
`Response blocked by ${postCallGuardrails.guardrail || "guardrail"}: ${redactVideoTranscriptSensitiveText(
|
||||
guardrailMessage,
|
||||
videoTranscriptSensitive
|
||||
)}`
|
||||
`Response blocked by ${postCallGuardrails.guardrail || "guardrail"}: ${guardrailMessage}`
|
||||
);
|
||||
finalizePendingScope(pendingScope, {
|
||||
providerResponse: responseBody,
|
||||
@@ -5322,7 +5236,6 @@ export async function handleChatCore({
|
||||
apiKeyId: apiKeyInfo?.id ?? undefined,
|
||||
usage,
|
||||
log,
|
||||
videoTranscriptSensitive,
|
||||
});
|
||||
|
||||
// ── Phase 9.2: Save for idempotency ──
|
||||
@@ -5402,7 +5315,6 @@ export async function handleChatCore({
|
||||
apiKeyInfo,
|
||||
headers: clientRawRequest?.headers,
|
||||
response: { status: 200, data: translatedResponse },
|
||||
videoTranscriptSensitive,
|
||||
});
|
||||
|
||||
// Routing event (feedback foundation) — fire-and-forget, cheap.
|
||||
@@ -5478,7 +5390,6 @@ export async function handleChatCore({
|
||||
provider,
|
||||
model,
|
||||
log,
|
||||
redactUpstreamDiagnosticForLog: videoTranscriptSensitive,
|
||||
});
|
||||
if (streamReadiness.ok === false) {
|
||||
const { response: failureResponse, reason } = streamReadiness;
|
||||
@@ -5610,7 +5521,6 @@ export async function handleChatCore({
|
||||
cacheReasoningFromAssistantMessage(msg, provider, model, {
|
||||
scope: reasoningCacheScope,
|
||||
historyMessages: Array.isArray(historyMessages) ? historyMessages : [],
|
||||
videoTranscriptSensitive,
|
||||
});
|
||||
}
|
||||
} catch {
|
||||
@@ -5643,7 +5553,6 @@ export async function handleChatCore({
|
||||
status: normalizedStreamStatus,
|
||||
error: streamError,
|
||||
errorCode: streamErrorCode,
|
||||
videoTranscriptSensitive,
|
||||
});
|
||||
|
||||
// Track cache token metrics for streaming responses
|
||||
@@ -5766,20 +5675,14 @@ export async function handleChatCore({
|
||||
memorySettings.maxTokens > 0 &&
|
||||
streamStatus === 200
|
||||
) {
|
||||
const requestMemoryText = extractMemoryTextFromRequestBody(
|
||||
body as Record<string, unknown>,
|
||||
videoTranscriptSensitive,
|
||||
{ trustedDescriptionFingerprints: videoTranscriptDescriptionFingerprints }
|
||||
);
|
||||
const requestMemoryText = extractMemoryTextFromRequestBody(body as Record<string, unknown>);
|
||||
if (requestMemoryText) {
|
||||
extractFacts(requestMemoryText, memoryOwnerId, pipelineSessionId);
|
||||
}
|
||||
|
||||
const streamedMemoryText = videoTranscriptSensitive
|
||||
? ""
|
||||
: extractMemoryTextFromResponse(
|
||||
(streamResponseBody ?? null) as Record<string, unknown> | null
|
||||
);
|
||||
const streamedMemoryText = extractMemoryTextFromResponse(
|
||||
(streamResponseBody ?? null) as Record<string, unknown> | null
|
||||
);
|
||||
if (streamedMemoryText) {
|
||||
extractFacts(streamedMemoryText, memoryOwnerId, pipelineSessionId);
|
||||
}
|
||||
@@ -5796,7 +5699,6 @@ export async function handleChatCore({
|
||||
apiKeyId: apiKeyInfo?.id ?? undefined,
|
||||
streamUsage,
|
||||
log,
|
||||
videoTranscriptSensitive,
|
||||
});
|
||||
|
||||
// Plugin onStreamComplete hook — fire-and-forget, fail-open (#9571)
|
||||
@@ -5865,8 +5767,7 @@ export async function handleChatCore({
|
||||
// openai-responses → openai translation still wants the namespace identity
|
||||
// map for #7936-style round-trip closure when the client also speaks
|
||||
// Responses (Codex CLI).
|
||||
requestToolIdentityMap,
|
||||
redactStreamDiagnosticsForLog
|
||||
requestToolIdentityMap
|
||||
);
|
||||
} else if (needsTranslation(targetFormat, clientResponseFormat)) {
|
||||
// Standard translation for other providers
|
||||
@@ -5896,8 +5797,7 @@ export async function handleChatCore({
|
||||
clientResponseFormat,
|
||||
}),
|
||||
customToolNames,
|
||||
requestToolIdentityMap,
|
||||
redactStreamDiagnosticsForLog
|
||||
requestToolIdentityMap
|
||||
);
|
||||
} else {
|
||||
log?.debug?.("STREAM", `Standard passthrough mode`);
|
||||
@@ -5912,8 +5812,7 @@ export async function handleChatCore({
|
||||
apiKeyInfo,
|
||||
handleStreamFailure,
|
||||
clientResponseFormat,
|
||||
requestToolIdentityMap,
|
||||
redactStreamDiagnosticsForLog
|
||||
requestToolIdentityMap
|
||||
);
|
||||
}
|
||||
|
||||
@@ -5925,7 +5824,6 @@ export async function handleChatCore({
|
||||
clientRawRequestHeaders: clientRawRequest?.headers,
|
||||
clientResponseFormat,
|
||||
echoModel,
|
||||
redactStreamDiagnosticsForLog,
|
||||
responseHeaders,
|
||||
});
|
||||
|
||||
@@ -5941,7 +5839,6 @@ export async function handleChatCore({
|
||||
apiKeyInfo,
|
||||
headers: clientRawRequest?.headers,
|
||||
response: { status: 200, streamed: true },
|
||||
videoTranscriptSensitive,
|
||||
});
|
||||
|
||||
return {
|
||||
|
||||
@@ -15,10 +15,6 @@ import { logAuditEvent } from "@/lib/compliance";
|
||||
import { emit } from "@/lib/events/eventBus";
|
||||
import type { RequestCompletedPayload, RequestFailedPayload } from "@/lib/events/types";
|
||||
import { saveCallLog } from "@/lib/usageDb";
|
||||
import {
|
||||
VIDEO_TRANSCRIPT_LOG_OMISSION_MARKER,
|
||||
VIDEO_TRANSCRIPT_REDACTION_PIPELINE_KEY,
|
||||
} from "@/lib/guardrails/videoTranscriptLogRedaction";
|
||||
import { FORMATS } from "../../translator/formats.ts";
|
||||
import { takeEarlyKeepaliveBytes } from "../../utils/earlyKeepaliveByteBuffer.ts";
|
||||
import { cloneBoundedChatLogPayload, truncateForLog } from "./logTruncation.ts";
|
||||
@@ -72,13 +68,7 @@ export type PersistAttemptLogsContext = {
|
||||
model: string | null | undefined;
|
||||
skillRequestId: string;
|
||||
detailedLoggingEnabled: boolean;
|
||||
reqLogger:
|
||||
| {
|
||||
getPipelinePayloads?: () => Record<string, unknown> | undefined;
|
||||
isVideoTranscriptSensitive?: () => boolean;
|
||||
}
|
||||
| null
|
||||
| undefined;
|
||||
reqLogger: { getPipelinePayloads?: () => Record<string, unknown> | undefined } | null | undefined;
|
||||
pendingRequestId: unknown;
|
||||
clientRawRequest: { endpoint?: string } | null | undefined;
|
||||
requestedModel: unknown;
|
||||
@@ -99,34 +89,12 @@ export type PersistAttemptLogsContext = {
|
||||
* explicitly present (never synthesized from skillRequestId) — persisted as call_logs.session_tag
|
||||
* for per-session cost attribution. */
|
||||
sessionTag?: string | null;
|
||||
/** Trusted request state derived from the Video Bridge guardrail result. */
|
||||
videoTranscriptSensitive?: boolean;
|
||||
/** Exact SHA-256 identities of transcript descriptions generated by the guardrail. */
|
||||
videoTranscriptDescriptionFingerprints?: readonly string[];
|
||||
};
|
||||
|
||||
function toConnectionId(value: unknown): string | null {
|
||||
return typeof value === "string" && value.trim().length > 0 ? value.trim() : null;
|
||||
}
|
||||
|
||||
function omitSensitivePipelineResponse(value: unknown): Record<string, unknown> {
|
||||
const record =
|
||||
value && typeof value === "object" && !Array.isArray(value)
|
||||
? (value as Record<string, unknown>)
|
||||
: {};
|
||||
return {
|
||||
...(typeof record.timestamp === "string" ? { timestamp: record.timestamp } : {}),
|
||||
...(typeof record.status === "number" && Number.isFinite(record.status)
|
||||
? { status: record.status }
|
||||
: {}),
|
||||
...(record.headers !== undefined ? { headers: VIDEO_TRANSCRIPT_LOG_OMISSION_MARKER } : {}),
|
||||
...(record.statusText !== undefined
|
||||
? { statusText: VIDEO_TRANSCRIPT_LOG_OMISSION_MARKER }
|
||||
: {}),
|
||||
body: VIDEO_TRANSCRIPT_LOG_OMISSION_MARKER,
|
||||
};
|
||||
}
|
||||
|
||||
function buildAccountRotationMeta(
|
||||
provider: string | null | undefined,
|
||||
initialConnectionId: string | null,
|
||||
@@ -236,8 +204,6 @@ export function persistAttemptLogs(args: PersistAttemptLogsArgs, ctx: PersistAtt
|
||||
correlationId,
|
||||
modelPinned,
|
||||
sessionTag,
|
||||
videoTranscriptSensitive,
|
||||
videoTranscriptDescriptionFingerprints,
|
||||
} = ctx;
|
||||
const initialConnectionId = toConnectionId(connectionId);
|
||||
const finalConnectionId = toConnectionId(credentials?.connectionId) || initialConnectionId;
|
||||
@@ -246,21 +212,8 @@ export function persistAttemptLogs(args: PersistAttemptLogsArgs, ctx: PersistAtt
|
||||
initialConnectionId,
|
||||
finalConnectionId
|
||||
);
|
||||
const transcriptSensitive =
|
||||
videoTranscriptSensitive === true || reqLogger?.isVideoTranscriptSensitive?.() === true;
|
||||
const descriptionLogContext = {
|
||||
trustedDescriptionFingerprints: videoTranscriptDescriptionFingerprints ?? [],
|
||||
};
|
||||
|
||||
const detectedProviderWarnings = extractProviderWarnings(
|
||||
providerResponse,
|
||||
clientResponse,
|
||||
responseBody
|
||||
);
|
||||
const providerWarnings =
|
||||
transcriptSensitive && detectedProviderWarnings.length > 0
|
||||
? [VIDEO_TRANSCRIPT_LOG_OMISSION_MARKER]
|
||||
: detectedProviderWarnings;
|
||||
const providerWarnings = extractProviderWarnings(providerResponse, clientResponse, responseBody);
|
||||
if (providerWarnings.length > 0) {
|
||||
logAuditEvent({
|
||||
action: "provider.warning",
|
||||
@@ -274,7 +227,6 @@ export function persistAttemptLogs(args: PersistAttemptLogsArgs, ctx: PersistAtt
|
||||
model,
|
||||
connectionId: finalConnectionId,
|
||||
httpStatus: status,
|
||||
warningCount: detectedProviderWarnings.length,
|
||||
warnings: providerWarnings,
|
||||
},
|
||||
});
|
||||
@@ -321,51 +273,8 @@ export function persistAttemptLogs(args: PersistAttemptLogsArgs, ctx: PersistAtt
|
||||
};
|
||||
}
|
||||
}
|
||||
if (transcriptSensitive) {
|
||||
pipelinePayloads[VIDEO_TRANSCRIPT_REDACTION_PIPELINE_KEY] = true;
|
||||
for (const requestStage of [
|
||||
"clientRawRequest",
|
||||
"openaiRequest",
|
||||
"providerRequest",
|
||||
] as const) {
|
||||
if (pipelinePayloads[requestStage] !== undefined) {
|
||||
pipelinePayloads[requestStage] = cloneBoundedChatLogPayload(
|
||||
pipelinePayloads[requestStage],
|
||||
0,
|
||||
requestStage !== "clientRawRequest" ? descriptionLogContext : {}
|
||||
) as Record<string, unknown>;
|
||||
}
|
||||
}
|
||||
if (pipelinePayloads.providerResponse !== undefined || providerResponse !== undefined) {
|
||||
pipelinePayloads.providerResponse = omitSensitivePipelineResponse(
|
||||
pipelinePayloads.providerResponse ?? providerResponse
|
||||
);
|
||||
}
|
||||
if (pipelinePayloads.clientResponse !== undefined || clientResponse !== undefined) {
|
||||
pipelinePayloads.clientResponse = omitSensitivePipelineResponse(
|
||||
pipelinePayloads.clientResponse ?? clientResponse
|
||||
);
|
||||
}
|
||||
if (pipelinePayloads.error !== undefined || error) {
|
||||
const errorRecord =
|
||||
pipelinePayloads.error && typeof pipelinePayloads.error === "object"
|
||||
? pipelinePayloads.error
|
||||
: {};
|
||||
pipelinePayloads.error = {
|
||||
...(typeof errorRecord.timestamp === "string"
|
||||
? { timestamp: errorRecord.timestamp }
|
||||
: {}),
|
||||
message: VIDEO_TRANSCRIPT_LOG_OMISSION_MARKER,
|
||||
};
|
||||
}
|
||||
delete pipelinePayloads.streamChunks;
|
||||
}
|
||||
}
|
||||
|
||||
const responseBodyForLog = transcriptSensitive
|
||||
? VIDEO_TRANSCRIPT_LOG_OMISSION_MARKER
|
||||
: responseBody;
|
||||
|
||||
saveCallLog({
|
||||
id: pendingRequestId,
|
||||
method: "POST",
|
||||
@@ -381,12 +290,10 @@ export function persistAttemptLogs(args: PersistAttemptLogsArgs, ctx: PersistAtt
|
||||
attachLogMeta(truncateForLog(body as Record<string, unknown>), {
|
||||
...accountRotationMeta,
|
||||
claudePromptCache: claudeCacheMeta,
|
||||
}),
|
||||
0,
|
||||
descriptionLogContext
|
||||
})
|
||||
),
|
||||
responseBody: cloneBoundedChatLogPayload(
|
||||
attachLogMeta(truncateForLog(responseBodyForLog as Record<string, unknown>), {
|
||||
attachLogMeta(truncateForLog(responseBody as Record<string, unknown>), {
|
||||
...accountRotationMeta,
|
||||
claudePromptCache: claudeCacheMeta
|
||||
? {
|
||||
@@ -398,7 +305,7 @@ export function persistAttemptLogs(args: PersistAttemptLogsArgs, ctx: PersistAtt
|
||||
claudePromptCacheUsage: claudeCacheUsageMeta,
|
||||
})
|
||||
),
|
||||
error: transcriptSensitive && error ? VIDEO_TRANSCRIPT_LOG_OMISSION_MARKER : error || null,
|
||||
error: error || null,
|
||||
sourceFormat,
|
||||
targetFormat,
|
||||
comboName,
|
||||
@@ -425,7 +332,7 @@ export function persistAttemptLogs(args: PersistAttemptLogsArgs, ctx: PersistAtt
|
||||
const lifecycle = resolveRequestLifecycleEvent({
|
||||
traceId,
|
||||
status,
|
||||
error: transcriptSensitive && error ? VIDEO_TRANSCRIPT_LOG_OMISSION_MARKER : error,
|
||||
error,
|
||||
model,
|
||||
provider,
|
||||
comboName,
|
||||
|
||||
@@ -17,10 +17,8 @@
|
||||
*/
|
||||
|
||||
import { getExecutor } from "../../executors/index.ts";
|
||||
import type { ExecuteInput } from "../../executors/base.ts";
|
||||
import { isCliproxyapiDeepModeEnabled } from "../../executors/cliproxyapi.ts";
|
||||
import { isDarioDeepModeEnabled } from "../../executors/dario.ts";
|
||||
import { redactVideoTranscriptSensitiveText } from "../../../src/lib/guardrails/videoTranscriptLogRedaction.ts";
|
||||
import { getCachedSettings } from "@/lib/db/readCache";
|
||||
import { getUpstreamProxyConfigCached } from "./comboContextCache.ts";
|
||||
import type { FallbackBackend } from "@/lib/db/upstreamProxy";
|
||||
@@ -50,11 +48,6 @@ function parseFallbackCodes(raw: unknown): number[] | null {
|
||||
return parsed.length > 0 ? parsed : null;
|
||||
}
|
||||
|
||||
function retainExecutorDiagnostic(error: unknown, input: ExecuteInput): string {
|
||||
const message = error instanceof Error ? error.message : String(error);
|
||||
return redactVideoTranscriptSensitiveText(message, input.videoTranscriptSensitive === true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the CLIProxyAPI-related settings shared by both the direct
|
||||
* `mode: "cliproxyapi"` passthrough leg and the `mode: "fallback"` retry leg:
|
||||
@@ -162,20 +155,25 @@ export async function resolveExecutorWithProxy(
|
||||
const isRetryableStatus = (s: number) => fallbackCodes.includes(s) || s === 0;
|
||||
|
||||
const wrapper = Object.create(nativeExec);
|
||||
wrapper.execute = async (input: ExecuteInput) => {
|
||||
wrapper.execute = async (input: {
|
||||
model: string;
|
||||
body: unknown;
|
||||
stream: boolean;
|
||||
credentials: unknown;
|
||||
signal?: AbortSignal | null;
|
||||
log?: unknown;
|
||||
upstreamExtraHeaders?: Record<string, string> | null;
|
||||
}) => {
|
||||
let result;
|
||||
try {
|
||||
result = await nativeExec.execute(input);
|
||||
} catch (err) {
|
||||
const errMsg = retainExecutorDiagnostic(err, input);
|
||||
log?.info?.(
|
||||
"UPSTREAM_PROXY",
|
||||
`${prov} native error (${errMsg}), retrying via ${backendLabel}`
|
||||
);
|
||||
const errMsg = err instanceof Error ? err.message : String(err);
|
||||
log?.info?.("UPSTREAM_PROXY", `${prov} native error (${errMsg}), retrying via ${backendLabel}`);
|
||||
try {
|
||||
return await proxyExec.execute(input);
|
||||
} catch (proxyErr) {
|
||||
const proxyMsg = retainExecutorDiagnostic(proxyErr, input);
|
||||
const proxyMsg = proxyErr instanceof Error ? proxyErr.message : String(proxyErr);
|
||||
log?.error?.("UPSTREAM_PROXY", `${prov} ${backendLabel} fallback also failed: ${proxyMsg}`);
|
||||
throw proxyErr;
|
||||
}
|
||||
@@ -191,7 +189,7 @@ export async function resolveExecutorWithProxy(
|
||||
try {
|
||||
return await proxyExec.execute(input);
|
||||
} catch (proxyErr) {
|
||||
const proxyMsg = retainExecutorDiagnostic(proxyErr, input);
|
||||
const proxyMsg = proxyErr instanceof Error ? proxyErr.message : String(proxyErr);
|
||||
log?.error?.("UPSTREAM_PROXY", `${prov} ${backendLabel} fallback also failed: ${proxyMsg}`);
|
||||
throw proxyErr;
|
||||
}
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
import { VIDEO_TRANSCRIPT_LOG_OMISSION_MARKER } from "@/lib/guardrails/videoTranscriptLogRedaction";
|
||||
|
||||
import type { ExecutorLog } from "../../executors/base.ts";
|
||||
|
||||
type ExecutorLogMethod = NonNullable<ExecutorLog["debug"]>;
|
||||
|
||||
function retainMethod(
|
||||
owner: ExecutorLog,
|
||||
method: ExecutorLogMethod | undefined
|
||||
): ExecutorLogMethod | undefined {
|
||||
if (!method) return undefined;
|
||||
|
||||
return (tag) => {
|
||||
method.call(owner, tag, VIDEO_TRANSCRIPT_LOG_OMISSION_MARKER);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Executor diagnostics can contain arbitrary provider text, including request echoes.
|
||||
* Keep the original logger for ordinary requests, but give every executor a request-scoped
|
||||
* fail-closed view when the Video Bridge marked the request sensitive. Tags remain useful;
|
||||
* messages and optional structured metadata are replaced/dropped only at the retention seam.
|
||||
*/
|
||||
export function createExecutorRetentionLog(
|
||||
log: ExecutorLog | null | undefined,
|
||||
videoTranscriptSensitive: boolean
|
||||
): ExecutorLog | null | undefined {
|
||||
if (!log || !videoTranscriptSensitive) return log;
|
||||
|
||||
return {
|
||||
debug: retainMethod(log, log.debug),
|
||||
info: retainMethod(log, log.info),
|
||||
warn: retainMethod(log, log.warn),
|
||||
error: retainMethod(log, log.error),
|
||||
};
|
||||
}
|
||||
@@ -124,7 +124,6 @@ export async function checkIdempotencyCache({
|
||||
effectiveServiceTier,
|
||||
startTime,
|
||||
log,
|
||||
videoTranscriptSensitive,
|
||||
}: {
|
||||
clientRawRequest: IdempotencyRequest;
|
||||
provider: string;
|
||||
@@ -133,9 +132,7 @@ export async function checkIdempotencyCache({
|
||||
effectiveServiceTier: EffectiveServiceTier | null | undefined;
|
||||
startTime: number;
|
||||
log: LoggerLike;
|
||||
videoTranscriptSensitive: boolean;
|
||||
}): Promise<{ hit: { success: true; response: Response } | null; idempotencyKey: string | null }> {
|
||||
if (videoTranscriptSensitive) return { hit: null, idempotencyKey: null };
|
||||
// NEXA fusion-idempotency fix: namespace the raw header key (see composeIdempotencyKey).
|
||||
const rawIdempotencyKey = getIdempotencyKey(clientRawRequest?.headers);
|
||||
const idempotencyKey = composeIdempotencyKey({
|
||||
|
||||
@@ -5,10 +5,6 @@ import {
|
||||
getChatLogMaxObjectKeys,
|
||||
getChatLogMaxBodyBytes,
|
||||
} from "@/lib/logEnv";
|
||||
import {
|
||||
omitVideoTranscriptForLog,
|
||||
type VideoTranscriptLogContext,
|
||||
} from "@/lib/guardrails/videoTranscriptLogRedaction";
|
||||
import { estimateSizeFast } from "../../utils/estimateSize.ts";
|
||||
|
||||
export const MEMORY_EXTRACTION_TEXT_LIMIT = 64 * 1024;
|
||||
@@ -26,7 +22,7 @@ export function truncateChatLogText(value: string): string {
|
||||
return `${head}\n[...truncated ${value.length - limit} chars...]\n${tail}`;
|
||||
}
|
||||
|
||||
function cloneBoundedChatLogPayloadValue(value: unknown, depth = 0): unknown {
|
||||
export function cloneBoundedChatLogPayload(value: unknown, depth = 0): unknown {
|
||||
if (value === null || value === undefined) return value;
|
||||
if (typeof value === "string") return truncateChatLogText(value);
|
||||
if (typeof value !== "object") return value;
|
||||
@@ -36,7 +32,7 @@ function cloneBoundedChatLogPayloadValue(value: unknown, depth = 0): unknown {
|
||||
|
||||
if (Array.isArray(value)) {
|
||||
const retained = value.length > maxTailItems ? value.slice(-maxTailItems) : value;
|
||||
const cloned = retained.map((item) => cloneBoundedChatLogPayloadValue(item, depth + 1));
|
||||
const cloned = retained.map((item) => cloneBoundedChatLogPayload(item, depth + 1));
|
||||
if (value.length > maxTailItems) {
|
||||
return [
|
||||
{
|
||||
@@ -51,11 +47,10 @@ function cloneBoundedChatLogPayloadValue(value: unknown, depth = 0): unknown {
|
||||
}
|
||||
|
||||
const result: Record<string, unknown> = {};
|
||||
const record = value as Record<string, unknown>;
|
||||
const entries = Object.entries(record);
|
||||
const entries = Object.entries(value as Record<string, unknown>);
|
||||
const maxKeys = getChatLogMaxObjectKeys();
|
||||
for (const [key, item] of maxKeys > 0 ? entries.slice(0, maxKeys) : entries) {
|
||||
result[key] = cloneBoundedChatLogPayloadValue(item, depth + 1);
|
||||
result[key] = cloneBoundedChatLogPayload(item, depth + 1);
|
||||
}
|
||||
if (maxKeys > 0 && entries.length > maxKeys) {
|
||||
result._omniroute_truncated_keys = entries.length - maxKeys;
|
||||
@@ -63,16 +58,6 @@ function cloneBoundedChatLogPayloadValue(value: unknown, depth = 0): unknown {
|
||||
return result;
|
||||
}
|
||||
|
||||
export function cloneBoundedChatLogPayload(
|
||||
value: unknown,
|
||||
depth = 0,
|
||||
descriptionContext: VideoTranscriptLogContext = {}
|
||||
): unknown {
|
||||
const transcriptSafeValue =
|
||||
depth === 0 ? omitVideoTranscriptForLog(value, descriptionContext) : value;
|
||||
return cloneBoundedChatLogPayloadValue(transcriptSafeValue, depth);
|
||||
}
|
||||
|
||||
/**
|
||||
* Truncate a large object for logging. If its JSON representation exceeds
|
||||
* getChatLogMaxBodyBytes() (default 1MB; CHAT_LOG_MAX_BODY_KB env override),
|
||||
|
||||
@@ -1,39 +1,5 @@
|
||||
import {
|
||||
containsVideoTranscriptForLog,
|
||||
omitVideoTranscriptDerivedTextForMemory,
|
||||
type VideoTranscriptLogContext,
|
||||
} from "../../../src/lib/guardrails/videoTranscriptLogRedaction.ts";
|
||||
import { capMemoryExtractionText, MEMORY_EXTRACTION_TEXT_LIMIT } from "./logTruncation.ts";
|
||||
|
||||
function normalizeMemoryInputText(value: unknown, context: VideoTranscriptLogContext = {}): string {
|
||||
if (typeof value !== "string") return "";
|
||||
return omitVideoTranscriptDerivedTextForMemory(value, context).trim();
|
||||
}
|
||||
|
||||
function extractMemoryTextPart(
|
||||
part: Record<string, unknown>,
|
||||
transcriptSensitive: boolean,
|
||||
context: VideoTranscriptLogContext
|
||||
): string {
|
||||
try {
|
||||
const rawText = typeof part?.text === "string" ? part.text : "";
|
||||
if (!rawText) return "";
|
||||
|
||||
const retainedText = normalizeMemoryInputText(rawText, context);
|
||||
if (!retainedText) return "";
|
||||
if (
|
||||
transcriptSensitive &&
|
||||
containsVideoTranscriptForLog(part, context) &&
|
||||
retainedText === rawText.trim()
|
||||
) {
|
||||
return "";
|
||||
}
|
||||
return retainedText;
|
||||
} catch {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
export function extractMemoryTextFromResponse(
|
||||
response: Record<string, unknown> | null | undefined
|
||||
): string {
|
||||
@@ -63,16 +29,9 @@ export function extractMemoryTextFromResponse(
|
||||
}
|
||||
|
||||
export function extractMemoryTextFromRequestBody(
|
||||
body: Record<string, unknown> | null | undefined,
|
||||
videoTranscriptSensitive = false,
|
||||
context: VideoTranscriptLogContext = {}
|
||||
body: Record<string, unknown> | null | undefined
|
||||
): string {
|
||||
if (!body || typeof body !== "object") return "";
|
||||
// Re-check the structured body at the sink boundary. The explicit bit covers
|
||||
// processed requests whose raw carrier was already replaced; trusted hashes
|
||||
// identify only descriptions emitted by a modified Video Bridge guardrail.
|
||||
const transcriptSensitive =
|
||||
videoTranscriptSensitive || containsVideoTranscriptForLog(body, context);
|
||||
|
||||
const messages = Array.isArray(body.messages) ? body.messages : null;
|
||||
if (messages && messages.length > 0) {
|
||||
@@ -80,16 +39,18 @@ export function extractMemoryTextFromRequestBody(
|
||||
const msg = messages[i] as Record<string, unknown>;
|
||||
if (msg?.role !== "user") continue;
|
||||
|
||||
const messageText = normalizeMemoryInputText(msg.content, context);
|
||||
if (messageText) {
|
||||
return capMemoryExtractionText(messageText);
|
||||
if (typeof msg.content === "string" && msg.content.trim().length > 0) {
|
||||
return capMemoryExtractionText(msg.content.trim());
|
||||
}
|
||||
|
||||
if (Array.isArray(msg.content)) {
|
||||
const text = msg.content
|
||||
.map((part: Record<string, unknown>) =>
|
||||
extractMemoryTextPart(part, transcriptSensitive, context)
|
||||
)
|
||||
.map((part: Record<string, unknown>) => {
|
||||
if (typeof part?.text === "string") return part.text.trim();
|
||||
if (part?.type === "input_text" && typeof part?.text === "string")
|
||||
return part.text.trim();
|
||||
return "";
|
||||
})
|
||||
.filter(Boolean)
|
||||
.join("\n")
|
||||
.trim();
|
||||
@@ -107,15 +68,17 @@ export function extractMemoryTextFromRequestBody(
|
||||
if (role && role !== "user") continue;
|
||||
if (itemType && itemType !== "message") continue;
|
||||
|
||||
const itemText = normalizeMemoryInputText(item?.content, context);
|
||||
if (itemText) {
|
||||
return capMemoryExtractionText(itemText);
|
||||
if (typeof item?.content === "string" && item.content.trim()) {
|
||||
return capMemoryExtractionText(item.content.trim());
|
||||
}
|
||||
if (Array.isArray(item?.content)) {
|
||||
const text = item.content
|
||||
.map((part: Record<string, unknown>) =>
|
||||
extractMemoryTextPart(part, transcriptSensitive, context)
|
||||
)
|
||||
.map((part: Record<string, unknown>) => {
|
||||
if (typeof part?.text === "string") return part.text.trim();
|
||||
if (part?.type === "input_text" && typeof part?.text === "string")
|
||||
return part.text.trim();
|
||||
return "";
|
||||
})
|
||||
.filter(Boolean)
|
||||
.join("\n")
|
||||
.trim();
|
||||
@@ -133,14 +96,15 @@ export function extractMemoryTextFromRequestBody(
|
||||
if (role && role !== "user") return "";
|
||||
if (itemType && itemType !== "message") return "";
|
||||
|
||||
if (typeof item?.content === "string") {
|
||||
return normalizeMemoryInputText(item.content, context);
|
||||
}
|
||||
if (typeof item?.content === "string") return item.content.trim();
|
||||
if (Array.isArray(item?.content)) {
|
||||
return item.content
|
||||
.map((part: Record<string, unknown>) =>
|
||||
extractMemoryTextPart(part, transcriptSensitive, context)
|
||||
)
|
||||
.map((part: Record<string, unknown>) => {
|
||||
if (typeof part?.text === "string") return part.text.trim();
|
||||
if (part?.type === "input_text" && typeof part?.text === "string")
|
||||
return part.text.trim();
|
||||
return "";
|
||||
})
|
||||
.filter(Boolean)
|
||||
.join("\n")
|
||||
.trim();
|
||||
|
||||
@@ -9,8 +9,6 @@
|
||||
* byte-identical to the previous inline block.
|
||||
*/
|
||||
|
||||
import { redactVideoTranscriptSensitiveText } from "@/lib/guardrails/videoTranscriptLogRedaction";
|
||||
|
||||
type LoggerLike =
|
||||
{ info?: (...args: unknown[]) => void; debug?: (...args: unknown[]) => void } | null | undefined;
|
||||
|
||||
@@ -27,7 +25,6 @@ export async function runPluginOnRequestHook(args: {
|
||||
apiKeyInfo: unknown;
|
||||
headers?: Record<string, string | string[] | undefined>;
|
||||
log?: LoggerLike;
|
||||
videoTranscriptSensitive?: boolean;
|
||||
}): Promise<PluginOnRequestGate> {
|
||||
try {
|
||||
const { runOnRequest } = await import("@/lib/plugins/hooks");
|
||||
@@ -39,7 +36,6 @@ export async function runPluginOnRequestHook(args: {
|
||||
apiKeyInfo: args.apiKeyInfo,
|
||||
headers: args.headers,
|
||||
metadata: {},
|
||||
videoTranscriptSensitive: args.videoTranscriptSensitive === true,
|
||||
};
|
||||
const pluginResult = await runOnRequest(pluginCtx);
|
||||
if (pluginResult?.blocked) {
|
||||
@@ -59,11 +55,10 @@ export async function runPluginOnRequestHook(args: {
|
||||
}
|
||||
return { blocked: false, body: pluginResult?.body };
|
||||
} catch (pluginErr) {
|
||||
const retainedPluginError = redactVideoTranscriptSensitiveText(
|
||||
pluginErr instanceof Error ? pluginErr.message : String(pluginErr),
|
||||
args.videoTranscriptSensitive === true
|
||||
args.log?.debug?.(
|
||||
"PLUGIN",
|
||||
`onRequest hook error (non-fatal): ${pluginErr instanceof Error ? pluginErr.message : String(pluginErr)}`
|
||||
);
|
||||
args.log?.debug?.("PLUGIN", `onRequest hook error (non-fatal): ${retainedPluginError}`);
|
||||
return { blocked: false };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,7 +26,6 @@ export async function runPluginOnResponseHook(args: {
|
||||
apiKeyInfo: unknown;
|
||||
headers?: Record<string, string | string[] | undefined>;
|
||||
response: PluginOnResponsePayload;
|
||||
videoTranscriptSensitive?: boolean;
|
||||
}): Promise<void> {
|
||||
try {
|
||||
const { runOnResponse } = await import("@/lib/plugins/hooks");
|
||||
@@ -39,7 +38,6 @@ export async function runPluginOnResponseHook(args: {
|
||||
apiKeyInfo: args.apiKeyInfo,
|
||||
headers: args.headers,
|
||||
metadata: {},
|
||||
videoTranscriptSensitive: args.videoTranscriptSensitive === true,
|
||||
},
|
||||
args.response
|
||||
).catch(() => {});
|
||||
|
||||
@@ -36,7 +36,6 @@ export function buildPostCallGuardrailContext(
|
||||
provider: string | null | undefined;
|
||||
responsePayloadFormat: unknown;
|
||||
clientResponseFormat: unknown;
|
||||
videoTranscriptSensitive?: boolean;
|
||||
},
|
||||
resolveDisabledGuardrails: typeof defaultResolveDisabled = defaultResolveDisabled
|
||||
): GuardrailContext {
|
||||
@@ -58,6 +57,5 @@ export function buildPostCallGuardrailContext(
|
||||
sourceFormat: optionalString(args.responsePayloadFormat),
|
||||
stream: false,
|
||||
targetFormat: optionalString(args.clientResponseFormat),
|
||||
videoTranscriptSensitive: args.videoTranscriptSensitive === true,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
import { generateSignature, getCachedResponse, isCacheableForRead } from "@/lib/semanticCache";
|
||||
import {
|
||||
generateSignature,
|
||||
getCachedResponse,
|
||||
isCacheableForRead,
|
||||
} from "@/lib/semanticCache";
|
||||
import { calculateCost } from "@/lib/usage/costCalculator";
|
||||
import { trackPendingRequest } from "@/lib/usageDb";
|
||||
import { synthesizeOpenAiSseFromJson } from "../../utils/jsonToSse.ts";
|
||||
@@ -21,7 +25,6 @@ export async function checkSemanticCache({
|
||||
persistAttemptLogs,
|
||||
apiKeyId,
|
||||
cacheDefaultMode,
|
||||
videoTranscriptSensitive,
|
||||
}: {
|
||||
semanticCacheEnabled: boolean;
|
||||
// Only the fields this read path actually touches are named; everything else
|
||||
@@ -39,9 +42,7 @@ export async function checkSemanticCache({
|
||||
persistAttemptLogs: (args: unknown) => void;
|
||||
apiKeyId?: string | null;
|
||||
cacheDefaultMode?: "legacy" | "bypass" | null;
|
||||
videoTranscriptSensitive: boolean;
|
||||
}) {
|
||||
if (videoTranscriptSensitive) return null;
|
||||
// Per-key bypass: skip cache lookup entirely when the API key opts out.
|
||||
if (cacheDefaultMode === "bypass") return null;
|
||||
if (semanticCacheEnabled && isCacheableForRead(body, clientRawRequest?.headers)) {
|
||||
|
||||