diff --git a/README.md b/README.md index 528ca049d7..450865fcf3 100644 --- a/README.md +++ b/README.md @@ -255,14 +255,7 @@ All **18** strategies β€” mix & match per combo step: | 🚦 **Policy** | `hard` (block over share) Β· `soft` (deprioritize) Β· `burst` (use idle headroom) | | 🧱 **Cap** | absolute ceiling per key, independent of mode | -``` -Pool "team-codex" Β· 1 Codex Pro account Β· 3 keys Β· 5-hour window - β”œβ”€ alice weight 50 β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘ ≀ 50% of the shared 5h quota - β”œβ”€ bob weight 30 β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘ ≀ 30% - └─ ci-bot weight 20 β–ˆβ–ˆβ–ˆβ–ˆβ–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘ ≀ 20% -Generous mode (<50% pool used) β†’ idle shares are lent out -Strict mode (β‰₯50% pool used) β†’ each key held to its fair share -``` +OmniRoute key pool 'team-codex': one Codex Pro account shared by 3 keys over a 5-hour window. alice weight 50 (up to 50% of the shared 5h quota), bob weight 30, ci-bot weight 20. In generous mode (under 50% pool used) idle shares are lent out; once the pool crosses 50% strict mode holds each key to its fair share. Enforced in the hot path **before** the request leaves OmniRoute, with per-(key, model) caps + session stickiness for prompt-cache integrity (now with a per-combo / global disable toggle). πŸ“– [Quota Sharing Engine](docs/routing/QUOTA_SHARE.md) @@ -276,14 +269,7 @@ Strict mode (β‰₯50% pool used) β†’ each key held to its fair share | πŸ’€ **Connection cooldown** | one account / key | Skips a rate-limited key while other keys keep serving | | 🎯 **Model lockout** | provider + model | Quarantines just one quota-limited model, not the whole connection | -``` -Combo: "always-on" Strategy: priority - 1. cc/claude-opus-4-7 ← subscription (use it fully) - 2. cx/gpt-5.5 ← second subscription - 3. glm/glm-5.1 ← cheap backup ($0.5/1M) - 4. kr/claude-sonnet-4.5 ← FREE, unlimited (never fails) -Result: 4 layers of fallback = zero downtime -``` +OmniRoute combo 'always-on' with priority strategy: requests go to 1. cc/claude-opus-4-7 (subscription, use it fully); on failure fall through to 2. cx/gpt-5.5 (second subscription), then 3. glm/glm-5.1 (cheap backup at $0.5/1M), then 4. kr/claude-sonnet-4.5 (free, unlimited, never fails). Result: 4 layers of fallback = zero downtime. πŸ“– [Auto-Combo Engine](docs/routing/AUTO-COMBO.md) Β· [Resilience Guide](docs/architecture/RESILIENCE_GUIDE.md) diff --git a/changelog.d/maintenance/7626-readme-pool-combo-svg.md b/changelog.d/maintenance/7626-readme-pool-combo-svg.md new file mode 100644 index 0000000000..14ebdc8f25 --- /dev/null +++ b/changelog.d/maintenance/7626-readme-pool-combo-svg.md @@ -0,0 +1 @@ +- **README animated diagrams** (`docs/diagrams/pool-fair-share.svg`, `docs/diagrams/combo-always-on.svg`): the last two ASCII blocks in the root README β€” the `"team-codex"` key-pool fair-share example and the `"always-on"` priority combo β€” are now hand-authored SMIL SVG animations (16s loops, DESIGN_SYSTEM.md palette, GitHub ``-sandbox safe), completing the set started by tier-cascade.svg (#7615). Full block copy preserved verbatim in the `img` alt text; both registered in `docs/diagrams/README.md`. diff --git a/changelog.d/maintenance/v3849-campaign-basereds.md b/changelog.d/maintenance/v3849-campaign-basereds.md new file mode 100644 index 0000000000..a0ff2e4ea8 --- /dev/null +++ b/changelog.d/maintenance/v3849-campaign-basereds.md @@ -0,0 +1 @@ +- **maintenance(quality):** clear v3.8.49 owner-PR-campaign base-reds on `release/v3.8.49` (accumulated because fast-gates PRβ†’release skip the complexity/golden/file-size ratchets, surfaced only on re-sync): regenerate the `provider/translate-path` golden snapshot for the new providers; re-point the `#6772` connId-prefix test off the now-reserved `fta` alias (claimed by freetheai #7602 β€” built-in aliases shadow custom-node prefixes by design); bump the complexity baseline 2056β†’2058 for the new provider dispatch handlers. No behavior change. diff --git a/config/quality/complexity-baseline.json b/config/quality/complexity-baseline.json index 361666034a..6917dbb5e4 100644 --- a/config/quality/complexity-baseline.json +++ b/config/quality/complexity-baseline.json @@ -1,6 +1,7 @@ { "_comment": "Catraca de complexidade (check-complexity.mjs, ESLint core rules complexity>=15 e max-lines-per-function>80 sobre src+open-sse+electron+bin via eslint.complexity.config.mjs). Conta total de violacoes; so pode cair. --update ratcheta.", - "count": 2056, + "count": 2058, + "_rebaseline_2026_07_17_v3849_ownerprs_providers": "2056->2058 (+2). v3.8.49 owner-PR merge campaign own-growth: the new provider handlers/dispatch branches merged this cycle (freetheai/felo/notion/segmind/deepinfra/novita/msdesigner image+video handlers, each adding a format-dispatch guard) pushed cyclomatic violations 2056->2058. Fast-gates PR->release do not run the complexity ratchet, so this surfaced only on re-sync. Spread across the new leaf handlers (not a single extractable function); measured on the release tip. Structural shrink tracked in #3501.", "_rebaseline_2026_07_10_v3847_merge_burst": "2053->2054 (+1). Drift herdado do merge burst do dia em release/v3.8.47 (campanha /implement-prs: ~36 PRs mergeados β€” Γ³rfΓ£os, features do dono, ports). O check:complexity NΓƒO roda no fast-path PR->release, entΓ£o o ramo acumulou o +1 sem rebaselinar (mesma famΓ­lia de todos os rebaselines abaixo). Trust-but-verify: medido 2054 no tip da release pΓ³s-burst; a ΓΊnica funΓ§Γ£o flagada nova Γ© prΓ©-existente (getResolvedModelCapabilities em modelCapabilities.ts, jΓ‘ >teto antes de #6714). Nenhum PR Γ³rfΓ£o/feature introduz violaΓ§Γ£o NOVA β€” os fixes deste ciclo sΓ£o complexity-net-zero. Rebaseline aprovado pelo dono (2026-07-10) para destravar o FQG dos ~7 Γ³rfΓ£os verdes-exceto-complexity. Tighten via --update next cycle.", "_rebaseline_2026_07_10_gcf_v3_2": "2054->2056 (+2). PR feat/headroom-gcf-v3.2-nested-flattening: own growth from re-vendoring the GCF (Headroom) codec to spec v3.2 (nested flattening). The 2 new over-threshold functions are the v3.2 `>`-path flatten/unflatten walk in the vendored generic-profile encode/decode paths (open-sse/services/compression/engines/headroom/gcf/{generic,decode_generic}.ts). This is imported third-party code kept byte-faithful to upstream gcf-typescript, not extractable without diverging from the vendored source; local measures 2055 on the merged tree; frozen at 2056 = the base's CI-observed 2054 + this PR's 2 new functions, matching the documented local-vs-CI off-by-one convention (see _rebaseline_2026_07_02_v3844_ci_observed) so the GitHub runner stays green. Round-trip guarded by tests/unit/compression/headroom-smartcrusher.test.ts (deep-nested case). Structural shrink belongs upstream in gcf, not here.", "_rebaseline_2026_07_08_6556_inherited_drift": "2052->2053 (+1). PR #6556 (omniglyph engine): drift herdado do merge burst da base (a catraca nao roda no fast-path PR->release, mesmo padrao dos rebaselines v3.8.44/46). Trust-but-verify: o proprio codigo do PR e complexity-net-zero β€” as 2 violacoes que ele introduzia (runCompressionAsync complexity 17 apos o branch do modo omniglyph; OmniglyphContextPageClient 161 linhas) foram CORRIGIDAS por extracao real (engines/omniglyphSingleMode.ts + split do componente em section components), medido: 2055->2053 local; base pura origin/release/v3.8.47 mede 2053 identico. Tighten via --update next cycle.", diff --git a/docs/diagrams/README.md b/docs/diagrams/README.md index 2de1805fe3..18543d6959 100644 --- a/docs/diagrams/README.md +++ b/docs/diagrams/README.md @@ -30,6 +30,8 @@ inside GitHub's `` sandbox: | File | Used in | Notes | | ---------------------------------------- | ---------------- | ---------------------------------------------------------------------------- | | [tier-cascade.svg](./tier-cascade.svg) | README.md (root) | Animated 4-tier auto-fallback cascade (16s loop, 4 acts). Edit the SVG directly β€” there is no `.mmd` source. | +| [pool-fair-share.svg](./pool-fair-share.svg) | README.md (root) | Animated key-pool fair-share quota (generous β†’ strict, 16s loop). Edit the SVG directly β€” there is no `.mmd` source. | +| [combo-always-on.svg](./combo-always-on.svg) | README.md (root) | Animated priority-combo fallback (4 layers, 16s loop). Edit the SVG directly β€” there is no `.mmd` source. | ## How to update diff --git a/docs/diagrams/combo-always-on.svg b/docs/diagrams/combo-always-on.svg new file mode 100644 index 0000000000..0278375d73 --- /dev/null +++ b/docs/diagrams/combo-always-on.svg @@ -0,0 +1,230 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + COMBO + "always-on" + Strategy: priority + try 1 first · fall through + on failure + + + REQUESTS + + + + + + + + + + + + + + + + + + + 1 + cc/claude-opus-4-7 + subscription · use it fully + + + + + + + + + + + + 2 + cx/gpt-5.5 + second subscription + + + + + + + + + + + + 3 + glm/glm-5.1 + cheap backup · $0.5/1M + + + + + + + + + + + + 4 + kr/claude-sonnet-4.5 + FREE · unlimited · never fails + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + fails → next + + fails → next + + fails → next + + + UPTIME + + + + + + + + + ALWAYS ON + + Result: 4 layers of fallback = zero downtime + diff --git a/docs/diagrams/pool-fair-share.svg b/docs/diagrams/pool-fair-share.svg new file mode 100644 index 0000000000..85e2d1ca16 --- /dev/null +++ b/docs/diagrams/pool-fair-share.svg @@ -0,0 +1,204 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Pool "team-codex" + 1 Codex Pro account Β· 3 keys Β· 5-hour window + + + POOL USED + + + + + + + + + + + 50% + 0 + 100% + + + + + + + + + GENEROUS Β· <50% USED + + + + + + + + STRICT Β· ≥50% USED + + + + + + + + + + alice + weight 50 + bob + weight 30 + ci-bot + weight 20 + + + + + + + + + + + + + + + + + + + + + + + + + + ≤ 50% of the shared 5h quota + ≤ 30% + ≤ 20% + + + + + + + + + + + + + + + + + + + + + + lent + + + + + + + + + + + + + + + + + + + + + + + + + idle + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Generous mode (<50% pool used) → idle shares are lent out + + + + + Strict mode (≥50% pool used) → each key held to its fair share + Enforced in the hot path — before the request leaves OmniRoute Β· per-(key, model) caps Β· session stickiness + diff --git a/tests/snapshots/provider/translate-path.json b/tests/snapshots/provider/translate-path.json index 41f9b431d3..1242dc13d5 100644 --- a/tests/snapshots/provider/translate-path.json +++ b/tests/snapshots/provider/translate-path.json @@ -1600,6 +1600,29 @@ "stream": "https://api.freemodel.dev/v1/chat/completions" } }, + "freetheai": { + "format": "openai", + "headers": { + "apiKey": { + "Accept": "text/event-stream", + "Authorization": "Bearer ", + "Content-Type": "application/json" + }, + "nonStream": { + "Authorization": "Bearer ", + "Content-Type": "application/json" + }, + "oauth": { + "Accept": "text/event-stream", + "Authorization": "Bearer ", + "Content-Type": "application/json" + } + }, + "url": { + "nonStream": "https://api.freetheai.xyz/v1/chat/completions", + "stream": "https://api.freetheai.xyz/v1/chat/completions" + } + }, "friendliai": { "format": "openai", "headers": { diff --git a/tests/unit/model-connid-prefix-normalization-6772.test.ts b/tests/unit/model-connid-prefix-normalization-6772.test.ts index de9b1c5107..430aa1a7c0 100644 --- a/tests/unit/model-connid-prefix-normalization-6772.test.ts +++ b/tests/unit/model-connid-prefix-normalization-6772.test.ts @@ -1,7 +1,7 @@ /** * PROBE for issue #6772 β€” custom OpenAI-compat / 400s when the * connection has a user-defined `prefix` and the listed model id (from /api/models) - * already carries that prefix baked in ("fta/vova/gpt-5.5"). + * already carries that prefix baked in ("custpfx6772/vova/gpt-5.5"). * * Root cause hypothesis: in src/sse/services/model.ts getModelInfo(), when a client * addresses the connection by its raw internal node id (`/...`), the matching @@ -25,7 +25,7 @@ const modelsDb = await import("../../src/lib/db/models.ts"); const { getModelInfo } = await import("../../src/sse/services/model.ts"); const CONN_ID = "openai-compatible-chat-97b0e595-probe6772"; -const PREFIX = "fta"; +const PREFIX = "custpfx6772"; // was "fta"; freetheai (#7602) claimed the "fta" built-in alias, which by design shadows custom-node prefixes const RAW_MODEL_ID = "vova/gpt-5.5"; // upstream's own model id already has a slash test.before(async () => { @@ -53,7 +53,7 @@ test.after(() => { fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true }); }); -test("#6772 baseline: bare alias form `fta/vova/gpt-5.5` resolves to the raw model id", async () => { +test("#6772 baseline: bare alias form `custpfx6772/vova/gpt-5.5` resolves to the raw model id", async () => { const info = (await getModelInfo(`${PREFIX}/${RAW_MODEL_ID}`)) as { provider?: string; model?: string;