diff --git a/.github/pr-assets/4823-opper-provider-catalog.png b/.github/pr-assets/4823-opper-provider-catalog.png
new file mode 100644
index 0000000000..374c7b3924
Binary files /dev/null and b/.github/pr-assets/4823-opper-provider-catalog.png differ
diff --git a/.github/pr-assets/4823-opper-provider-note.png b/.github/pr-assets/4823-opper-provider-note.png
new file mode 100644
index 0000000000..a37239de6e
Binary files /dev/null and b/.github/pr-assets/4823-opper-provider-note.png differ
diff --git a/docs-site/src/content/docs/guides/providers.md b/docs-site/src/content/docs/guides/providers.md
index b7e8438c3c..90e192a97a 100644
--- a/docs-site/src/content/docs/guides/providers.md
+++ b/docs-site/src/content/docs/guides/providers.md
@@ -395,7 +395,7 @@ selectors, then retry. Signing in from a machine with no existing `kiro-cli` ses
## 3. API-key catalog
-opencodex ships 94 built-in presets: 78 key-based, 12 OAuth, three local, and one default
+opencodex ships 95 built-in presets: 79 key-based, 12 OAuth, three local, and one default
ChatGPT-forward preset. The dashboard's **Add provider** picker opens a key provider's dashboard,
validates the key, and stores it; validation is provider-specific. Notable entries:
@@ -428,6 +428,15 @@ token group allows (`gpt-5.5` and `gpt-5.1-codex` are seeded). Register at
pins the row near the top of the Add provider picker and marks it as a sponsor, and nothing else about
routing or defaults changes.
+**Opper** is the EU-hosted AI gateway from Opper AI (Stockholm): one key (created at
+[platform.opper.ai](https://platform.opper.ai)) and one OpenAI-compatible endpoint in front of 700+
+models from 30+ providers. Bare model ids such as `claude-sonnet-4-6` or `gpt-5.5` are *pools*: Opper
+picks the provider and region per request, so the seeded ids stay valid as routes come and go. A
+`vendor/model` id (`anthropic/claude-sonnet-4-6`, `aws/claude-sonnet-4-6-eu`) pins one route instead.
+The catalogue is discovered live from `/v3/compat/models` with your key; the public list, including
+region-pinned EU routes, is at [opper.ai/models](https://opper.ai/models). Opper is also a
+[models.dev](https://models.dev) provider (`opper`), which uses the same bare pool ids.
+
| Provider | Base URL |
| --- | --- |
| **OpenAI (API key)** | `https://api.openai.com/v1` |
@@ -476,6 +485,7 @@ routing or defaults changes.
| Xiaomi MiMo | `https://api.xiaomimimo.com/anthropic` |
| Xiaomi MiMo (OpenAI Chat) | `https://api.xiaomimimo.com/v1` |
| Kilo | `https://api.kilo.ai/api/gateway` |
+| Opper | `https://api.opper.ai/v3/compat` |
| GitLab Duo | `https://cloud.gitlab.com/ai/v1/proxy/openai/v1` |
| Cloudflare AI Gateway | `https://gateway.ai.cloudflare.com/v1/{account-id}/{gateway}/anthropic` |
| …and more | opencode zen, Vercel AI Gateway, Venice, NanoGPT, Synthetic, Qianfan, Alibaba, Parallel, ZenMux, LiteLLM |
diff --git a/gui/public/provider-icons/README.md b/gui/public/provider-icons/README.md
index 22d329d513..3f0878f924 100644
--- a/gui/public/provider-icons/README.md
+++ b/gui/public/provider-icons/README.md
@@ -208,6 +208,7 @@ Published as SVG and committed with only comments, `
`/`` and
- `digitalocean.svg` — `digitalocean.com` favicon, 32x32.
- `featherless.svg` — `featherless.ai/favicon.svg`, 256x256.
- `kilo.svg` — `kilo.ai/favicon/favicon.svg`, 32x32. Keeps its `oklch()` plate.
+- `opper.svg` — supplied in PR #4823 by an Opper employee, 315x315 single-ink mark.
- `nanogpt.svg` — `nano-gpt.com/logo.svg`, 181x187, gradient.
- `nebius.svg` — `nebius.com/favicon/favicon.svg`, 96x96.
- `neuralwatt.svg` — the site's Webflow-hosted brand asset, 32x32.
diff --git a/gui/public/provider-icons/opper.svg b/gui/public/provider-icons/opper.svg
new file mode 100644
index 0000000000..45500826c3
--- /dev/null
+++ b/gui/public/provider-icons/opper.svg
@@ -0,0 +1,3 @@
+
diff --git a/gui/src/provider-icons.ts b/gui/src/provider-icons.ts
index 78055e5df1..e7906e773c 100644
--- a/gui/src/provider-icons.ts
+++ b/gui/src/provider-icons.ts
@@ -51,6 +51,7 @@ const PROVIDER_ICON_ALIASES: Record = {
"opencode-go": "opencode.svg",
"opencode-zen": "opencode.svg",
openrouter: "openrouter-color.svg",
+ opper: "opper.svg",
qianfan: "qianfan-color.svg",
/*
* Qoder Global and Qoder CN are one brand on two operators (BRIGHT ZENITH
@@ -144,6 +145,7 @@ const PROVIDER_DISPLAY_NAMES: Record = {
"github-copilot": "GitHub Copilot",
"gitlab-duo": "GitLab Duo",
openrouter: "OpenRouter",
+ opper: "Opper",
"opencode-go": "OpenCode Go",
"opencode-free": "OpenCode Free",
"opencode-zen": "OpenCode Zen",
@@ -231,6 +233,7 @@ const MASKED_PROVIDER_ICONS: ReadonlySet = new Set([
"nous.svg",
"novita.svg",
"packycode.svg",
+ "opper.svg",
"siliconflow.svg",
"synthetic.svg",
"zenmux.svg",
diff --git a/scripts/test-layout/layout.json b/scripts/test-layout/layout.json
index c361c4c0c8..5446a94e51 100644
--- a/scripts/test-layout/layout.json
+++ b/scripts/test-layout/layout.json
@@ -1055,6 +1055,7 @@
"opencode-zen-rate-limit.test.ts": "providers",
"openrouter-provider-routing.test.ts": "providers",
"openrouter-quota-reset-cooldown-4024.test.ts": "providers",
+ "opper-provider.test.ts": "providers",
"optional-shutdown-hooks.test.ts": "lib",
"orcarouter-provider.test.ts": "providers",
"outbound-body-guard.test.ts": "server",
diff --git a/src/providers/registry/entries-extended.ts b/src/providers/registry/entries-extended.ts
index 068d5087a2..6a8a6d1ee8 100644
--- a/src/providers/registry/entries-extended.ts
+++ b/src/providers/registry/entries-extended.ts
@@ -98,6 +98,10 @@ import {
DIGITALOCEAN_CHAT_COMPLETION_MODELS,
SCALEWAY_SERVERLESS_CHAT_MODELS,
SCALEWAY_MODEL_INPUT_MODALITIES,
+ OPPER_MODELS,
+ OPPER_MODEL_CONTEXT_WINDOWS,
+ OPPER_MODEL_MAX_OUTPUT_TOKENS,
+ OPPER_MODEL_INPUT_MODALITIES,
} from "./model-seeds";
export const PROVIDER_REGISTRY_EXTENDED: readonly ProviderRegistryEntry[] = [
@@ -1023,6 +1027,30 @@ export const PROVIDER_REGISTRY_EXTENDED: readonly ProviderRegistryEntry[] = [
noJsonSchemaModels: [...DEEPSEEK_GATEWAY_THINKING_MODELS, ...OPENCODE_FREE_DEEPSEEK_MODELS],
},
{ id: "vercel-ai-gateway", label: "Vercel AI Gateway", baseUrl: "https://ai-gateway.vercel.sh/v1", adapter: "openai-chat", authKind: "key", dashboardUrl: "https://vercel.com/dashboard" },
+ {
+ // Opper: EU-hosted AI gateway (Opper AI AB, Stockholm). One OpenAI-compatible endpoint and one
+ // key in front of 30+ upstream providers. Seeded ids are Opper *pools* (bare names such as
+ // `claude-sonnet-4-6`): the gateway chooses the provider/region per request, and a
+ // `vendor/model` id (`anthropic/claude-sonnet-4-6`, `aws/claude-sonnet-4-6-eu`) pins one route.
+ // The original provider author reported on 2026-09-08 that GET /v3/compat/models answers 401
+ // without a key, so the default discovery URL doubles as key validation. Windows, output caps
+ // and modalities live in model-seeds.ts (smallest value / shared modality across each pool's
+ // members); live discovery owns which models exist.
+ id: "opper",
+ label: "Opper",
+ adapter: "openai-chat",
+ baseUrl: "https://api.opper.ai/v3/compat",
+ authKind: "key",
+ dashboardUrl: "https://platform.opper.ai",
+ liveModels: true,
+ preserveCustomDestination: true,
+ defaultModel: "claude-sonnet-4-6",
+ models: OPPER_MODELS,
+ modelContextWindows: OPPER_MODEL_CONTEXT_WINDOWS,
+ modelMaxOutputTokens: OPPER_MODEL_MAX_OUTPUT_TOKENS,
+ modelInputModalities: OPPER_MODEL_INPUT_MODALITIES,
+ note: "EU-hosted AI gateway: one OpenAI-compatible endpoint and one key in front of 30+ providers. Bare model ids are pools (claude-sonnet-4-6, gpt-5.5) and Opper picks the route per request; vendor/model ids (anthropic/claude-sonnet-4-6) pin one provider. The catalogue is discovered live from /v3/compat/models with your key; the public list is at opper.ai/models. Token rates are the model providers' rates with no markup; Opper charges a 3% fee when you buy credits.",
+ },
{
id: "opencode-free",
label: "OpenCode Free",
diff --git a/src/providers/registry/model-seeds.ts b/src/providers/registry/model-seeds.ts
index f18b73cb57..ed5f6b1c4a 100644
--- a/src/providers/registry/model-seeds.ts
+++ b/src/providers/registry/model-seeds.ts
@@ -960,3 +960,47 @@ export const CLINE_PASS_TEXT_ONLY_MODELS = CLINE_PASS_MODALITY_KNOWN_MODELS.filt
export const CLINE_PASS_MODEL_INPUT_MODALITIES: Record = Object.fromEntries(
CLINE_PASS_MODALITY_KNOWN_MODELS.map(id => [id, CLINE_PASS_IMAGE_MODELS.has(id) ? ["text", "image"] : ["text"]]),
);
+
+// Opper seed: bare *pool* names. A pool is every provider Opper serves that model through; Opper
+// picks the route per request. Each name is the `.model` of a `pooled: true` entry in the public
+// catalogue snapshot supplied by the original provider author
+// (https://api.opper.ai/v3/models?limit=2000, captured 2026-09-14); `vendor/model` ids
+// (anthropic/claude-sonnet-4-6) pin one route and stay valid, they are just not seeded.
+export const OPPER_MODELS = [
+ "claude-sonnet-4-6",
+ "claude-opus-5",
+ "gpt-5.5",
+ "gpt-5.4-mini",
+ "gemini-3.8-flash",
+ "deepseek-v4-pro",
+ "kimi-k3",
+ "mistral-large-2512",
+];
+// Smallest value across each pool's members in that snapshot, capped at the lab model's own limit
+// (kimi-k3 output); live discovery owns which models exist.
+export const OPPER_MODEL_CONTEXT_WINDOWS: Record = {
+ "claude-sonnet-4-6": 1_000_000,
+ "claude-opus-5": 1_000_000,
+ "gpt-5.5": 1_050_000,
+ "gpt-5.4-mini": 400_000,
+ "gemini-3.8-flash": 1_048_576,
+ "deepseek-v4-pro": 1_000_000,
+ "kimi-k3": 1_048_576,
+ "mistral-large-2512": 256_000,
+};
+export const OPPER_MODEL_MAX_OUTPUT_TOKENS: Record = {
+ "claude-sonnet-4-6": 64_000,
+ "claude-opus-5": 128_000,
+ "gpt-5.5": 128_000,
+ "gpt-5.4-mini": 128_000,
+ "gemini-3.8-flash": 65_536,
+ "deepseek-v4-pro": 65_536,
+ "kimi-k3": 131_072,
+ "mistral-large-2512": 8_192,
+};
+// Pools whose members do not all accept image input (deepseek-v4-pro: no member does; kimi-k3: the
+// sference route is text-only), so the shared modality set is text.
+export const OPPER_TEXT_ONLY_MODELS = ["deepseek-v4-pro", "kimi-k3"];
+export const OPPER_MODEL_INPUT_MODALITIES: Record = Object.fromEntries(
+ OPPER_MODELS.map(id => [id, OPPER_TEXT_ONLY_MODELS.includes(id) ? ["text"] : ["text", "image"]]),
+);
diff --git a/structure/providers/xai-grok.md b/structure/providers/xai-grok.md
index 5bd4a83a55..0a6b292c7a 100644
--- a/structure/providers/xai-grok.md
+++ b/structure/providers/xai-grok.md
@@ -127,6 +127,9 @@ overrides remain authoritative. First-party `deepseek-chat`, `deepseek-reasoner`
The Crusoe fixed-key registry row, discovery predicate, effort ladder, and input-modality map are
also provider-scoped and do not alter xAI model metadata, OAuth routing, or wire behavior.
+The Opper pool seeds in `src/providers/registry/entries-extended.ts` are also provider-scoped and
+do not alter xAI discovery, model metadata, or transport behavior.
+
Native Chat applies qualifying effort ceilings independently of model pins; pin selection precedes the cap and only pins or cap rewrites enter wire mapping. The [catalog effort contract](../catalog.md#ultra-reasoning-level) records the V1/compaction exemptions and caller-preservation boundary.
Combo child requests normalize effort and thinking controls against the selected target while retaining reasoning summaries; strict unknown targets preserve caller controls. The [Responses transport owner](../transports/responses.md) documents this boundary, and native Chat removes effort only for an explicit empty declaration or no-reasoning model.
diff --git a/structure/subagents.md b/structure/subagents.md
index 91ea815fbe..89d9e85848 100644
--- a/structure/subagents.md
+++ b/structure/subagents.md
@@ -390,6 +390,10 @@ vision support from a provider-wide multimodal label.
[Anthropic seed image metadata](runtime.md#capability-aware-image-admission) supplies missing capability evidence; subagent selection and eligibility rules remain unchanged.
+Opper's fallback pool seeds carry provider-scoped text/image declarations from
+`src/providers/registry/model-seeds.ts`. They feed the same capability-aware image admission and
+do not change subagent selection, roster order, or eligibility.
+
Provider-scoped approval reviewer settings are projected by the [catalog owner](catalog.md#provider-scoped-approval-reviewer); this surface retains its existing routing, transport and account-selection behavior.
Renamed fixed-key providers receive [missing reasoning metadata](catalog.md#renamed-destination-reasoning-metadata) during derivation; explicit per-model entries and provider defaults retain precedence.
diff --git a/structure/transports/inventory.md b/structure/transports/inventory.md
index 55dec2695d..71563d6c2e 100644
--- a/structure/transports/inventory.md
+++ b/structure/transports/inventory.md
@@ -40,7 +40,7 @@ surface is listed here so a maintainer can find the owner without grepping:
| OAuth account failover | `src/oauth/generic-account-failover.ts`, `src/oauth/anthropic-routing.ts` | Reactive pre-output 429 recovery is presence-driven with 2+ eligible accounts. Pool and `oauthAccountFailover` flags govern proactive routing, not the reactive retry: a disabled Anthropic pool recovers through quota ordering rather than its dormant strategy, and a per-provider `enabled` beats the global default in either direction. |
| OAuth login callback (inbound) | `src/oauth/callback-server.ts` | Every response, including non-callback 404s, closes its connection so a pooled socket cannot deliver a later login to a retired flow on the same callback port. |
| Alibaba regions | `src/providers/alibaba-region-backup.ts`, `src/providers/alibaba-region-migration.ts`, `src/providers/alibaba-region-startup.ts` | Region migration backs up before rewriting and is idempotent across restarts. |
-| Discovery and quota | `src/providers/model-discovery.ts`, `src/providers/quota.ts`, `src/providers/registry.ts` | Discovery rejects a response over 4 MiB or past 2,000 raw rows before caching it. Provider-scoped hints fill capabilities omitted by live rosters; OpenCode Go's `deepseek-v4.1-flash` keeps its 1,048,576-token context window. Codex quota DTOs suppress retired Spark evidence under the [OpenAI scope contract](../providers/openai-tiers.md#public-provider-contract), retaining ordinary custom windows. |
+| Discovery and quota | `src/providers/model-discovery.ts`, `src/providers/quota.ts`, `src/providers/registry.ts` | Discovery rejects a response over 4 MiB or past 2,000 raw rows before caching it. Provider-scoped hints fill capabilities omitted by live rosters; OpenCode Go's `deepseek-v4.1-flash` keeps its 1,048,576-token context window. The fixed-key Opper preset uses the shared OpenAI Chat adapter at `https://api.opper.ai/v3/compat`, discovers models through its conventional authenticated `/models` path, preserves an older same-named custom destination, and falls back to bare pool ids while passing vendor-prefixed ids through unchanged. Codex quota DTOs suppress retired Spark evidence under the [OpenAI scope contract](../providers/openai-tiers.md#public-provider-contract), retaining ordinary custom windows. |
The registry's first-party `deepseek-flash` row declares native `text` and `image` input, so image
requests bypass the vision sidecar by default; explicit `noVisionModels` or text-only declarations
diff --git a/tests/fixtures/test-layout-expected.json b/tests/fixtures/test-layout-expected.json
index d59eeb0c83..9564514726 100644
--- a/tests/fixtures/test-layout-expected.json
+++ b/tests/fixtures/test-layout-expected.json
@@ -881,6 +881,7 @@
"opencode-zen-rate-limit.test.ts": "providers",
"openrouter-provider-routing.test.ts": "providers",
"openrouter-quota-reset-cooldown-4024.test.ts": "providers",
+ "opper-provider.test.ts": "providers",
"optional-shutdown-hooks.test.ts": "lib",
"orcarouter-provider.test.ts": "providers",
"outbound-body-guard.test.ts": "server",
diff --git a/tests/providers/opper-provider.test.ts b/tests/providers/opper-provider.test.ts
new file mode 100644
index 0000000000..61eabc9f21
--- /dev/null
+++ b/tests/providers/opper-provider.test.ts
@@ -0,0 +1,106 @@
+import { describe, expect, test } from "bun:test";
+import { KEY_LOGIN_PROVIDERS } from "../../src/oauth/key-providers";
+import { deriveProviderPresets, providerConfigSeed } from "../../src/providers/derive";
+import { resolveProviderModelDiscoveryUrl } from "../../src/providers/model-discovery";
+import { PROVIDER_REGISTRY } from "../../src/providers/registry";
+import { routeModel } from "../../src/router";
+import type { OcxConfig } from "../../src/types";
+import { en } from "../../gui/src/i18n/en";
+import { interpolate, type TFn } from "../../gui/src/i18n/shared";
+import { formatProviderDisplayName, providerIconPaint, providerIconSrc } from "../../gui/src/provider-icons";
+
+const englishT: TFn = (key, vars) => interpolate(en[key], vars);
+const OPPER_BASE_URL = "https://api.opper.ai/v3/compat";
+
+function registryEntry() {
+ const entry = PROVIDER_REGISTRY.find(row => row.id === "opper");
+ if (!entry) throw new Error("missing opper registry entry");
+ return entry;
+}
+
+describe("Opper gateway provider", () => {
+ test("is a fixed OpenAI-compatible API-key gateway seeded with bare pool ids", () => {
+ const entry = registryEntry();
+ expect(entry).toMatchObject({
+ label: "Opper",
+ adapter: "openai-chat",
+ baseUrl: OPPER_BASE_URL,
+ authKind: "key",
+ liveModels: true,
+ preserveCustomDestination: true,
+ defaultModel: "claude-sonnet-4-6",
+ });
+ expect(entry.allowBaseUrlOverride).toBeUndefined();
+ expect(entry.models).toContain(entry.defaultModel!);
+ for (const id of entry.models ?? []) {
+ // Pool names carry no vendor prefix; Opper resolves the route per request.
+ expect(id).toMatch(/^[a-z0-9.-]+$/);
+ expect(entry.modelContextWindows?.[id]).toBeGreaterThan(0);
+ expect(entry.modelMaxOutputTokens?.[id]).toBeGreaterThan(0);
+ expect(entry.modelMaxOutputTokens?.[id]).toBeLessThanOrEqual(entry.modelContextWindows?.[id] ?? 0);
+ expect(entry.modelInputModalities?.[id]?.[0]).toBe("text");
+ }
+ // Only pools whose every member takes images advertise the image modality.
+ expect(entry.modelInputModalities?.["claude-sonnet-4-6"]).toEqual(["text", "image"]);
+ expect(entry.modelInputModalities?.["deepseek-v4-pro"]).toEqual(["text"]);
+ expect(entry.modelInputModalities?.["kimi-k3"]).toEqual(["text"]);
+ });
+
+ test("derives the key-login preset and config seed from the registry row", () => {
+ const entry = registryEntry();
+ expect(KEY_LOGIN_PROVIDERS.opper).toMatchObject({
+ label: "Opper",
+ adapter: "openai-chat",
+ baseUrl: OPPER_BASE_URL,
+ defaultModel: "claude-sonnet-4-6",
+ });
+ expect(deriveProviderPresets().find(row => row.id === "opper")).toMatchObject({ auth: "key" });
+ const seed = providerConfigSeed(entry);
+ expect(seed).toMatchObject({
+ adapter: "openai-chat",
+ baseUrl: OPPER_BASE_URL,
+ liveModels: true,
+ models: entry.models,
+ modelContextWindows: entry.modelContextWindows,
+ });
+ expect(seed).not.toHaveProperty("preserveCustomDestination");
+ expect(seed).not.toHaveProperty("note");
+ });
+
+ test("uses the authenticated compat model list for key validation", () => {
+ // The original provider author reported that /v3/compat/models answers 401 without a key;
+ // this test statically pins the resulting registry policy and URL, not that upstream behavior.
+ expect(registryEntry().apiKeyValidation).toBeUndefined();
+ expect(resolveProviderModelDiscoveryUrl(
+ "opper",
+ { adapter: "openai-chat", baseUrl: OPPER_BASE_URL, authMode: "key" },
+ OPPER_BASE_URL,
+ `${OPPER_BASE_URL}/models`,
+ )).toBe("https://api.opper.ai/v3/compat/models");
+ });
+
+ test("routes the provider prefix and forwards pool and pinned model ids unchanged", () => {
+ const config: OcxConfig = {
+ port: 10100,
+ defaultProvider: "opper",
+ providers: {
+ opper: { ...providerConfigSeed(registryEntry()), apiKey: "test-key" },
+ },
+ };
+ // Bare pool id: Opper picks the provider.
+ const pooled = routeModel(config, "opper/claude-sonnet-4-6");
+ expect(pooled.modelId).toBe("claude-sonnet-4-6");
+ expect(pooled.provider.baseUrl).toBe(OPPER_BASE_URL);
+ expect(pooled.provider.adapter).toBe("openai-chat");
+ // Vendor-prefixed id pins one route; the extra slash must survive the provider split.
+ const pinned = routeModel(config, "opper/anthropic/claude-sonnet-4-6");
+ expect(pinned.modelId).toBe("anthropic/claude-sonnet-4-6");
+ expect(pinned.provider.baseUrl).toBe(OPPER_BASE_URL);
+ });
+
+ test("ships a masked single-ink mark and a brand display name in the dashboard", () => {
+ expect(providerIconSrc("opper")).toBe("/provider-icons/opper.svg");
+ expect(providerIconPaint(providerIconSrc("opper"))).toBe("mask");
+ expect(formatProviderDisplayName("opper", englishT)).toBe("Opper");
+ });
+});
diff --git a/tests/providers/provider-registry-parity.test.ts b/tests/providers/provider-registry-parity.test.ts
index 21ff647de0..5e53824a84 100644
--- a/tests/providers/provider-registry-parity.test.ts
+++ b/tests/providers/provider-registry-parity.test.ts
@@ -38,7 +38,7 @@ const EXPECTED_KEY_PROVIDER_IDS = [
"deepseek", "cerebras", "chutes", "deepinfra", "hyperbolic", "nscale", "vultr", "baseten", "commandcode", "sambanova", "nebius", "crusoe", "digitalocean", "scaleway", "featherless", "novita", "together", "fireworks", "firepass", "moonshot",
"huggingface", "nvidia", "venice", "zai", "zhipu-bigmodel", "zhipu-bigmodel-coding", "zhipu-bigmodel-responses", "nanogpt", "synthetic", "siliconflow", "qwen-cloud", "tencent-coding-plan",
"volcengine", "volcengine-coding-plan", "volcengine-agent-plan", "qianfan", "alibaba", "alibaba-token-plan", "alibaba-token-plan-intl", "parallel", "zenmux", "litellm", "ollama-cloud", "mistral",
- "minimax", "minimax-cn", "kimi-code", "opencode-zen", "vercel-ai-gateway",
+ "minimax", "minimax-cn", "kimi-code", "opencode-zen", "vercel-ai-gateway", "opper",
"opencode-free", "xiaomi", "xiaomi-mimo", "kilo", "mimo-free", "mimo", "cloudflare-ai-gateway", "cloudflare-workers-ai", "gitlab-duo",
"qoder", "qoder-cn", "codebuddy", "codebuddy-cn",
];