diff --git a/docs/reference/PROVIDER_REFERENCE.md b/docs/reference/PROVIDER_REFERENCE.md index c67e794c70c8..21146b3cd6c1 100644 --- a/docs/reference/PROVIDER_REFERENCE.md +++ b/docs/reference/PROVIDER_REFERENCE.md @@ -1,14 +1,14 @@ --- title: "Provider Reference" version: 3.8.52 -lastUpdated: 2026-10-17 +lastUpdated: 2026-10-18 --- # 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-10-17 +> **Last generated:** 2026-10-18 Total providers: **374**. See category breakdown below. @@ -44,7 +44,7 @@ Use the dashboard at `/dashboard/providers` to enable, configure, and test each | `codex-app-server` | `cxa` | OpenAI Codex (App-Server) | No-auth | [link](https://developers.openai.com/codex/cli) | No token stored by OmniRoute. The Codex CLI app-server manages its own ChatGPT sign-in (~/.codex/auth.json, auto-refreshed). Use “Sign in with ChatGPT” if the CLI is not yet authenticated. | — | | `devin-cli-agentic` | `dva` | Devin CLI Agentic Bridge | No-auth | [link](https://docs.devin.ai/work-with-devin/devin-cli) | Authentication is owned by the official Devin CLI in its isolated bridge volume. | emulated | | `duckduckgo-web` | `ddgw` | DuckDuckGo AI Chat | No-auth | [link](https://duckduckgo.com/duckchat) | No credentials required — DuckDuckGo AI Chat is anonymous and free. | emulated | -| `uncloseai` | `unc` | UncloseAI | No-auth | [link](https://uncloseai.com) | No auth required. API accepts any non-empty string as key for identification. If older built-in models return 404, use Available Models → Import from /models or Auto-Sync; verified live model: Lorbus/Qwen3.6-27B-int4-AutoRound. | emulated | +| `uncloseai` | `unc` | UncloseAI | No-auth | [link](https://uncloseai.com) | No auth required. API accepts any non-empty string as key for identification. If older built-in models return 404, use Available Models → Import from /models or Auto-Sync; verified live model: turboderp/Qwen3.8-27B-exl3. | emulated | | `veoaifree-web` | `veo-free` | Veo AI Free | No-auth, video | [link](https://veoaifree.com) | No auth required. Rate limited to 6 requests/hour per IP. | — | | `zcode` | `zc` | ZCode (GLM Coding Plan) | No-auth | [link](https://zcode.z.ai) | No API key stored by OmniRoute. The local ZCode app-server uses the existing builtin:zai-coding-plan login. | — | @@ -462,7 +462,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/) (153 implementations) +- Executors: [`open-sse/executors/`](../../open-sse/executors/) (154 implementations) - Translators: [`open-sse/translator/`](../../open-sse/translator/) ## See Also diff --git a/open-sse/config/freeModelCatalog.data.ts b/open-sse/config/freeModelCatalog.data.ts index d847d94240e0..81beb3d9d845 100644 --- a/open-sse/config/freeModelCatalog.data.ts +++ b/open-sse/config/freeModelCatalog.data.ts @@ -432,10 +432,10 @@ export const FREE_MODEL_BUDGETS: FreeModelBudget[] = [ { provider: "t3-web", modelId: "qwen3-32b", displayName: "Qwen3 32B (via t3.chat)", monthlyTokens: 0, creditTokens: 0, freeType: "recurring-daily", poolKey: "t3-web", tos: "avoid" }, { provider: "t3-web", modelId: "kimi-k2", displayName: "Kimi K2 (via t3.chat)", monthlyTokens: 0, creditTokens: 0, freeType: "recurring-daily", poolKey: "t3-web", tos: "avoid" }, { provider: "tencent", modelId: "hunyuan-pro", displayName: "Hunyuan Pro", monthlyTokens: 0, creditTokens: 0, freeType: "recurring-uncapped", poolKey: "tencent", tos: "caution" }, - // 2026-09-15 refresh: upstream /v1/models serves a single live model; the - // three previously catalogued ids 404 on /v1/chat/completions (see the - // matching comment in providers/registry/uncloseai/index.ts). - { provider: "uncloseai", modelId: "Lorbus/Qwen3.6-27B-int4-AutoRound", displayName: "Qwen3.6 27B int4 AutoRound (🆓 Free)", monthlyTokens: 0, creditTokens: 0, freeType: "keyless", poolKey: "uncloseai", tos: "caution" }, + // 2026-10-16 refresh: upstream /v1/models serves a single live model; the + // previously catalogued id 404s on /v1/chat/completions (see the matching + // comment in providers/registry/uncloseai/index.ts). + { provider: "uncloseai", modelId: "turboderp/Qwen3.8-27B-exl3", displayName: "Qwen3.8 27B exl3 (🆓 Free)", monthlyTokens: 0, creditTokens: 0, freeType: "keyless", poolKey: "uncloseai", tos: "caution" }, { provider: "vertex", modelId: "gemini-3.1-pro-preview", displayName: "Gemini 3.1 Pro Preview (Vertex)", monthlyTokens: 0, creditTokens: 300000000, freeType: "one-time-initial", poolKey: "vertex", tos: "caution" }, { provider: "vertex", modelId: "gemini-3.1-flash-lite", displayName: "Gemini 3.1 Flash Lite (Vertex)", monthlyTokens: 0, creditTokens: 300000000, freeType: "one-time-initial", poolKey: "vertex", tos: "caution" }, { provider: "vertex", modelId: "gemini-3-flash-preview", displayName: "Gemini 3 Flash Preview (Vertex)", monthlyTokens: 0, creditTokens: 300000000, freeType: "one-time-initial", poolKey: "vertex", tos: "caution" }, diff --git a/open-sse/config/providers/registry/uncloseai/index.ts b/open-sse/config/providers/registry/uncloseai/index.ts index 31028d8a43e7..cc840578333b 100644 --- a/open-sse/config/providers/registry/uncloseai/index.ts +++ b/open-sse/config/providers/registry/uncloseai/index.ts @@ -10,15 +10,25 @@ export const uncloseaiProvider: RegistryEntry = { authType: "optional", authHeader: "bearer", toolCalling: "emulated", + // The roster rotates every few weeks (see the models[] comment below), so the + // context window must NOT hang off a pinned model id: resolveTokenLimit() would + // miss a newly-rotated id, fall through to the generic 128000 catch-all, and let + // enforceOutputTokenBudget() hand out a completion budget that overflows the real + // 65536 window (upstream then 400s: "context length 65536 ... you requested + // 65537"). A provider-level default survives the rotation — any uncloseai model + // resolves to 65536 even with no discovery/import run. Per-model discovery + // (max_model_len → auto:discovery override) still wins when a sync has run. + defaultContextLength: 65536, models: [ - // 2026-09-15 refresh — upstream rotated its roster: /v1/models now serves - // exactly one model, and every previously catalogued id (adamo1139/ - // Hermes-3-Llama-3.1-8B-FP8-Dynamic, qwen3.6:27b, gemma4:31b) returns - // 404 "model does not exist" on /v1/chat/completions. The live id below - // was confirmed with a real completion (vllm backend, max_model_len 65536). + // 2026-10-16 refresh — upstream rotated its roster again (it does this every + // few weeks): /v1/models now serves exactly one model, and the previously + // catalogued id (Lorbus/Qwen3.6-27B-int4-AutoRound) returns 404 "model does + // not exist" on /v1/chat/completions. The live id below was read straight + // from GET https://hermes.ai.unturf.com/v1/models (vllm backend, + // max_model_len 65536). { - id: "Lorbus/Qwen3.6-27B-int4-AutoRound", - name: "Qwen3.6 27B int4 AutoRound (🆓 Free)", + id: "turboderp/Qwen3.8-27B-exl3", + name: "Qwen3.8 27B exl3 (🆓 Free)", contextLength: 65536, }, ], diff --git a/src/shared/constants/providers/noauth.ts b/src/shared/constants/providers/noauth.ts index 4334ed331823..2e45ae39fba0 100644 --- a/src/shared/constants/providers/noauth.ts +++ b/src/shared/constants/providers/noauth.ts @@ -147,7 +147,7 @@ export const NOAUTH_PROVIDERS = { serviceKinds: ["llm"], toolCalling: "emulated", authHint: - "No auth required. API accepts any non-empty string as key for identification. If older built-in models return 404, use Available Models → Import from /models or Auto-Sync; verified live model: Lorbus/Qwen3.6-27B-int4-AutoRound.", + "No auth required. API accepts any non-empty string as key for identification. If older built-in models return 404, use Available Models → Import from /models or Auto-Sync; verified live model: turboderp/Qwen3.8-27B-exl3.", freeNote: "Free forever — no signup, no credit card. OpenAI-compatible endpoints.", notice: { text: "UncloseAI needs no API key. API accepts any non-empty string as key for identification. If older built-in models return 404, use Available Models → Import from /models or Auto-Sync.", diff --git a/tests/unit/combo-builder-options-route.test.ts b/tests/unit/combo-builder-options-route.test.ts index 86a244d9842d..a9460d3952e5 100644 --- a/tests/unit/combo-builder-options-route.test.ts +++ b/tests/unit/combo-builder-options-route.test.ts @@ -216,7 +216,7 @@ test("combo builder options route includes no-auth provider (uncloseai) even wit assert.ok(noAuth.models.length > 0, "uncloseai should expose its built-in models"); // Spot-check a known built-in model from providerRegistry.ts assert.ok( - noAuth.models.some((m: any) => m.id === "Lorbus/Qwen3.6-27B-int4-AutoRound"), + noAuth.models.some((m: any) => m.id === "turboderp/Qwen3.8-27B-exl3"), "the built-in model should be among uncloseai models" ); // #2901: no-auth providers route under their alias ("unc/"), not their id. diff --git a/tests/unit/models-catalog-route.test.ts b/tests/unit/models-catalog-route.test.ts index 6558ccf73c30..2a663f854f82 100644 --- a/tests/unit/models-catalog-route.test.ts +++ b/tests/unit/models-catalog-route.test.ts @@ -176,11 +176,11 @@ test("v1 models catalog includes display names by default", async () => { new Request("http://localhost/api/v1/models") ); const body = (await response.json()) as any; - const model = body.data.find((item) => item.id === "unc/Lorbus/Qwen3.6-27B-int4-AutoRound"); + const model = body.data.find((item) => item.id === "unc/turboderp/Qwen3.8-27B-exl3"); assert.equal(response.status, 200); assert.ok(model); - assert.equal(model.name, "Qwen3.6 27B int4 AutoRound (🆓 Free)"); + assert.equal(model.name, "Qwen3.8 27B exl3 (🆓 Free)"); }); test("v1 models catalog omits display names when the feature flag is disabled", async () => { @@ -191,12 +191,12 @@ test("v1 models catalog omits display names when the feature flag is disabled", new Request("http://localhost/api/v1/models") ); const body = (await response.json()) as any; - const model = body.data.find((item) => item.id === "unc/Lorbus/Qwen3.6-27B-int4-AutoRound"); + const model = body.data.find((item) => item.id === "unc/turboderp/Qwen3.8-27B-exl3"); assert.equal(response.status, 200); assert.ok(model); assert.equal("name" in model, false); - assert.equal(model.root, "Lorbus/Qwen3.6-27B-int4-AutoRound"); + assert.equal(model.root, "turboderp/Qwen3.8-27B-exl3"); } finally { featureFlagsDb.removeFeatureFlagOverride("MODEL_CATALOG_INCLUDE_NAMES"); } diff --git a/tests/unit/uncloseai-registry-refresh.test.ts b/tests/unit/uncloseai-registry-refresh.test.ts index f43ca6670fd4..f42e8378c875 100644 --- a/tests/unit/uncloseai-registry-refresh.test.ts +++ b/tests/unit/uncloseai-registry-refresh.test.ts @@ -4,18 +4,27 @@ import assert from "node:assert/strict"; import { getRegistryEntry } from "../../open-sse/config/providerRegistry.ts"; import { FREE_MODEL_BUDGETS } from "../../open-sse/config/freeModelCatalog.ts"; +// Dynamic imports (mirrors tests/unit/deepseek-flash-context-limit-13922.test.ts): +// contextManager pulls in the model-capability/DB chain, which must not run at +// module scope. +const { resolveTokenLimit } = await import("../../open-sse/services/contextManager.ts"); +const { enforceOutputTokenBudget } = + await import("../../open-sse/handlers/chatCore/outputTokenBudget.ts"); + /** - * 2026-09-15: uncloseai (hermes.ai.unturf.com) rotated its roster — /v1/models - * went down to a single live model and every previously catalogued id started - * returning 404 "model does not exist" on /v1/chat/completions, so requests - * routed through the static seed failed while the provider itself was healthy - * (a real completion against the live id succeeds). The seed is only the - * offline fallback (live discovery via NAMED_OPENAI_STYLE_PROVIDERS), but the - * dashboard serves it whenever discovery has not run, so the stale ids must - * not survive in the registry or the free-model catalog. + * 2026-10-16: uncloseai (hermes.ai.unturf.com) rotated its roster again — + * /v1/models serves a single live model and the previously catalogued id + * (Lorbus/Qwen3.6-27B-int4-AutoRound) started returning 404 "model does not + * exist" on /v1/chat/completions, so requests routed through the static seed + * failed while the provider itself was healthy (a real completion against the + * live id succeeds). The seed is only the offline fallback (live discovery via + * NAMED_OPENAI_STYLE_PROVIDERS), but the dashboard serves it whenever discovery + * has not run, so the stale ids must not survive in the registry or the + * free-model catalog. Same rotation as the 2026-09-15 entry below. */ -const LIVE_MODEL = "Lorbus/Qwen3.6-27B-int4-AutoRound"; +const LIVE_MODEL = "turboderp/Qwen3.8-27B-exl3"; const DEAD_MODELS = [ + "Lorbus/Qwen3.6-27B-int4-AutoRound", "adamo1139/Hermes-3-Llama-3.1-8B-FP8-Dynamic", "qwen3.6:27b", "gemma4:31b", @@ -55,3 +64,43 @@ test("free-model catalog carries exactly the live uncloseai row", () => { assert.equal(stillListed, false, `retired id ${dead} must not remain in the free catalog`); } }); + +/** + * The rotating roster must not take the context window down with it. + * + * resolveTokenLimit() step 3 (provider-registry `defaultContextLength`) is the + * source that survives a rotation: the pinned `models[]` id goes stale every few + * weeks, and a freshly rotated id is absent from the registry *and* from any + * synced discovery row, so with no provider-level window it falls through to the + * generic 128000 catch-all. enforceOutputTokenBudget() then derives available + * output from 128000, which overstates the real 65536 window, and the vLLM + * upstream rejects the request — observed live: "This model's maximum context + * length is 65536 tokens ... you requested 65537" (33537 input + 32000 max_tokens). + * Per-model discovery (max_model_len → auto:discovery override) still wins when a + * sync has run; this is the floor that works with no discovery at all. + */ +test("uncloseai declares a provider-level window that survives a roster rotation", () => { + assert.equal(getRegistryEntry("uncloseai")?.defaultContextLength, 65536); +}); + +test("any uncloseai model id — rotated or not yet seen — resolves to the 65536 window", () => { + for (const id of [LIVE_MODEL, ...DEAD_MODELS, "some/future-model-not-in-registry"]) { + const resolved = resolveTokenLimit("uncloseai", id); + assert.equal(resolved.limit, 65536, `${id} must resolve to the real window`); + assert.equal(resolved.specific, true, `${id} must not fall through to the generic default`); + } +}); + +test("the observed overflow (33537 input + 32000 max_tokens) is clamped to fit 65536", () => { + const input = 33537; + const budget = enforceOutputTokenBudget( + { max_tokens: 32000 }, + input, + resolveTokenLimit("uncloseai", LIVE_MODEL).limit + ); + assert.equal(budget.ok, true); + if (budget.ok !== true) return; + const maxTokens = (budget.body as Record).max_tokens as number; + assert.equal(maxTokens, 65536 - input); + assert.ok(input + maxTokens <= 65536, "prompt + completion must fit the window"); +});