mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-01 21:02:12 +03:00
fix(providers): remove deprecated MiMo v2 entries (#6248)
chore(providers): remove deprecated MiMo V2 catalog entries (superseded by V2.5); realign provider-catalog tests. Merged — thank you, @backryun! Base-reds only. Integrated into release/v3.8.45.
This commit is contained in:
@@ -33,6 +33,7 @@
|
||||
### 📝 Maintenance
|
||||
|
||||
- **i18n(it):** add 118 missing Italian (`it`) translations (net-additive — no existing keys dropped, valid JSON), improving Italian UI coverage. ([#6212](https://github.com/diegosouzapw/OmniRoute/pull/6212) — thanks @serverless83)
|
||||
- **chore(providers):** remove deprecated **MiMo V2** model entries from the catalogs (xiaomi-mimo, opencode-go, zenmux-free, audio TTS) — the upstream V2 line is superseded by MiMo V2.5; drops `mimo-v2-tts`, `mimo-v2-pro`, `mimo-v2-omni`, `mimo-v2-flash`, `mimo-v2-flash-free` and realigns the provider-catalog tests. ([#6248](https://github.com/diegosouzapw/OmniRoute/pull/6248) — thanks @backryun)
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -416,7 +416,6 @@ export const AUDIO_SPEECH_PROVIDERS: Record<string, AudioProvider> = {
|
||||
{ id: "mimo-v2.5-tts", name: "MiMo V2.5 TTS" },
|
||||
{ id: "mimo-v2.5-tts-voicedesign", name: "MiMo V2.5 Voice Design" },
|
||||
{ id: "mimo-v2.5-tts-voiceclone", name: "MiMo V2.5 Voice Clone" },
|
||||
{ id: "mimo-v2-tts", name: "MiMo V2 TTS" },
|
||||
],
|
||||
},
|
||||
};
|
||||
@@ -512,10 +511,7 @@ export function parseSpeechModel(modelStr: string | null, dynamicProviders?: Aud
|
||||
return parseAudioModel(modelStr, AUDIO_SPEECH_PROVIDERS, dynamicProviders);
|
||||
}
|
||||
|
||||
export function parseTranslationModel(
|
||||
modelStr: string | null,
|
||||
dynamicProviders?: AudioProvider[]
|
||||
) {
|
||||
export function parseTranslationModel(modelStr: string | null, dynamicProviders?: AudioProvider[]) {
|
||||
return parseAudioModel(modelStr, AUDIO_TRANSLATION_PROVIDERS, dynamicProviders);
|
||||
}
|
||||
|
||||
|
||||
@@ -25,8 +25,6 @@ export const opencode_goProvider: RegistryEntry = {
|
||||
{ id: "kimi-k2.5", name: "Kimi K2.5" },
|
||||
{ id: "mimo-v2.5-pro", name: "MiMo-V2.5-Pro" },
|
||||
{ id: "mimo-v2.5", name: "MiMo-V2.5" },
|
||||
{ id: "mimo-v2-pro", name: "MiMo-V2-Pro" },
|
||||
{ id: "mimo-v2-omni", name: "MiMo-V2-Omni" },
|
||||
// #3110: MiniMax M3 via OpenCode Go tier
|
||||
{
|
||||
id: "minimax-m3",
|
||||
|
||||
@@ -9,7 +9,7 @@ import type { RegistryEntry } from "../../shared.ts";
|
||||
* for all API requests as a query parameter.
|
||||
*
|
||||
* Models available on the free tier (5 Flows/5h, 38.64 Flows/week):
|
||||
* DeepSeek V3.2, GLM 4.7 Flash Free, MiMo V2 Flash Free, and others.
|
||||
* DeepSeek V3.2, GLM 4.7 Flash Free, and others.
|
||||
*
|
||||
* Short alias "zmf" is distinct from the paid "zenmux" (alias "zm") which
|
||||
* uses API-key auth against the OpenAI-compatible endpoint.
|
||||
@@ -27,7 +27,6 @@ export const zenmux_freeProvider: RegistryEntry = {
|
||||
{ id: "deepseek/deepseek-reasoner", name: "DeepSeek V3.2 (Thinking)", supportsReasoning: true },
|
||||
{ id: "deepseek/deepseek-v4-pro", name: "DeepSeek V4 Pro", supportsReasoning: true },
|
||||
{ id: "kuaishou/kat-coder-pro-v1-free", name: "KAT Coder Pro V1 Free" },
|
||||
{ id: "xiaomi/mimo-v2-flash-free", name: "MiMo V2 Flash Free" },
|
||||
{ id: "z-ai/glm-4.7-flash-free", name: "GLM 4.7 Flash Free" },
|
||||
{ id: "stepfun/step-3.5-flash-free", name: "Step 3.5 Flash Free" },
|
||||
{ id: "inclusionai/ling-1t", name: "Ling 1T" },
|
||||
|
||||
@@ -435,9 +435,6 @@ export const CHAT_OPENAI_COMPAT_MODELS: Record<string, RegistryModel[]> = {
|
||||
"xiaomi-mimo": [
|
||||
{ id: "mimo-v2.5-pro", name: "MiMo-V2.5-Pro", contextLength: 1048576, maxOutputTokens: 131072 },
|
||||
{ id: "mimo-v2.5", name: "MiMo-V2.5", contextLength: 1048576, maxOutputTokens: 131072 },
|
||||
{ id: "mimo-v2-pro", name: "MiMo-V2-Pro", contextLength: 262144, maxOutputTokens: 131072 },
|
||||
{ id: "mimo-v2-omni", name: "MiMo-V2-Omni", contextLength: 262144, maxOutputTokens: 131072 },
|
||||
{ id: "mimo-v2-flash", name: "MiMo-V2-Flash", contextLength: 262144, maxOutputTokens: 65536 },
|
||||
],
|
||||
gitlawb: [
|
||||
{ id: "mimo-v2.5-pro", name: "MiMo-V2.5-Pro", contextLength: 1048576, maxOutputTokens: 131072 },
|
||||
|
||||
@@ -315,7 +315,7 @@ export const WEB_COOKIE_PROVIDERS = {
|
||||
website: "https://zenmux.ai",
|
||||
hasFree: true,
|
||||
freeNote:
|
||||
"Free tier (5 Flows/5h, 38.64 Flows/week) — DeepSeek V3.2, GLM 4.7 Flash Free, MiMo V2 Flash Free and more. No subscription required.",
|
||||
"Free tier (5 Flows/5h, 38.64 Flows/week) — DeepSeek V3.2, GLM 4.7 Flash Free and more. No subscription required.",
|
||||
authHint:
|
||||
"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.",
|
||||
},
|
||||
|
||||
@@ -228,8 +228,8 @@ describe("OpencodeExecutor", () => {
|
||||
// Register new models
|
||||
registerModel("opencode-go", { id: "glm-5.1", name: "GLM-5.1", contextLength: 204800 });
|
||||
registerModel("opencode-go", { id: "kimi-k2.6", name: "Kimi K2.6" });
|
||||
registerModel("opencode-go", { id: "mimo-v2-pro", name: "MiMo V2 Pro" });
|
||||
registerModel("opencode-go", { id: "mimo-v2-omni", name: "MiMo V2 Omni" });
|
||||
registerModel("opencode-go", { id: "mimo-v2.5-pro", name: "MiMo V2.5 Pro" });
|
||||
registerModel("opencode-go", { id: "mimo-v2.5", name: "MiMo V2.5" });
|
||||
|
||||
// glm-5.1
|
||||
const glm51 = await goExecutor.execute(createInput("glm-5.1"));
|
||||
@@ -239,13 +239,13 @@ describe("OpencodeExecutor", () => {
|
||||
const kimi26 = await goExecutor.execute(createInput("kimi-k2.6"));
|
||||
assert.equal(kimi26.url, "https://opencode.ai/zen/go/v1/chat/completions");
|
||||
|
||||
// mimo-v2-pro
|
||||
const mimoPro = await goExecutor.execute(createInput("mimo-v2-pro"));
|
||||
// mimo-v2.5-pro
|
||||
const mimoPro = await goExecutor.execute(createInput("mimo-v2.5-pro"));
|
||||
assert.equal(mimoPro.url, "https://opencode.ai/zen/go/v1/chat/completions");
|
||||
|
||||
// mimo-v2-omni
|
||||
const mimoOmni = await goExecutor.execute(createInput("mimo-v2-omni"));
|
||||
assert.equal(mimoOmni.url, "https://opencode.ai/zen/go/v1/chat/completions");
|
||||
// mimo-v2.5
|
||||
const mimo = await goExecutor.execute(createInput("mimo-v2.5"));
|
||||
assert.equal(mimo.url, "https://opencode.ai/zen/go/v1/chat/completions");
|
||||
});
|
||||
|
||||
it("routes opencode-go qwen models to claude messages endpoint", async () => {
|
||||
@@ -278,21 +278,21 @@ describe("OpencodeExecutor", () => {
|
||||
it("routes opencode-go catalog-only models to chat completions", async () => {
|
||||
// Register new models
|
||||
registerModel("opencode-go", { id: "glm-6-max", name: "GLM-6 Max" });
|
||||
registerModel("opencode-go", { id: "mimo-v2-pro", name: "MiMo-V2-Pro" });
|
||||
registerModel("opencode-go", { id: "mimo-v2-omni", name: "MiMo-V2-Omni" });
|
||||
registerModel("opencode-go", { id: "mimo-v2.5-pro", name: "MiMo-V2.5-Pro" });
|
||||
registerModel("opencode-go", { id: "mimo-v2.5", name: "MiMo-V2.5" });
|
||||
registerModel("opencode-go", { id: "hy3-preview", name: "Hunyuan3 Preview" });
|
||||
|
||||
// glm-6-max
|
||||
const glm6 = await goExecutor.execute(createInput("glm-6-max"));
|
||||
assert.equal(glm6.url, "https://opencode.ai/zen/go/v1/chat/completions");
|
||||
|
||||
// mimo-v2-pro
|
||||
const mimoPro = await goExecutor.execute(createInput("mimo-v2-pro"));
|
||||
// mimo-v2.5-pro
|
||||
const mimoPro = await goExecutor.execute(createInput("mimo-v2.5-pro"));
|
||||
assert.equal(mimoPro.url, "https://opencode.ai/zen/go/v1/chat/completions");
|
||||
|
||||
// mimo-v2-omni
|
||||
const mimoOmni = await goExecutor.execute(createInput("mimo-v2-omni"));
|
||||
assert.equal(mimoOmni.url, "https://opencode.ai/zen/go/v1/chat/completions");
|
||||
// mimo-v2.5
|
||||
const mimo = await goExecutor.execute(createInput("mimo-v2.5"));
|
||||
assert.equal(mimo.url, "https://opencode.ai/zen/go/v1/chat/completions");
|
||||
|
||||
// hy3-preview
|
||||
const hy3 = await goExecutor.execute(createInput("hy3-preview"));
|
||||
|
||||
@@ -11,14 +11,15 @@
|
||||
import test from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
|
||||
const { opencode_goProvider } = await import(
|
||||
"../../open-sse/config/providers/registry/opencode/go/index.ts"
|
||||
);
|
||||
const { opencode_goProvider } =
|
||||
await import("../../open-sse/config/providers/registry/opencode/go/index.ts");
|
||||
|
||||
function modelIds(): string[] {
|
||||
return (opencode_goProvider.models ?? []).map((m) => m.id);
|
||||
}
|
||||
|
||||
const DEPRECATED_MIMO_V2_MODELS = ["mimo-v2-pro", "mimo-v2-omni"];
|
||||
|
||||
test("opencode-go advertises glm-5.2 (official Go endpoint addition)", () => {
|
||||
assert.ok(
|
||||
modelIds().includes("glm-5.2"),
|
||||
@@ -33,6 +34,13 @@ test("opencode-go advertises kimi-k2.7-code (live API rejects plain kimi-k2.7 fo
|
||||
);
|
||||
});
|
||||
|
||||
test("opencode-go does not advertise deprecated MiMo V2 models", () => {
|
||||
const ids = modelIds();
|
||||
for (const modelId of DEPRECATED_MIMO_V2_MODELS) {
|
||||
assert.ok(!ids.includes(modelId), `${modelId} is deprecated`);
|
||||
}
|
||||
});
|
||||
|
||||
test("opencode-go preserves the pre-existing minimax-m3 and qwen routing via targetFormat=claude", () => {
|
||||
// Routing through the /messages endpoint is OmniRoute's declarative
|
||||
// equivalent of upstream's MESSAGES_FORMAT_MODELS set; the alignment
|
||||
|
||||
@@ -11,14 +11,19 @@ import {
|
||||
} from "../../src/shared/validation/schemas.ts";
|
||||
import { validateBody } from "../../src/shared/validation/helpers.ts";
|
||||
|
||||
test("xiaomi-mimo registry uses the current default base URL and MiMo V2.5 + V2 models", () => {
|
||||
const DEPRECATED_MIMO_V2_MODELS = ["mimo-v2-pro", "mimo-v2-omni", "mimo-v2-flash", "mimo-v2-tts"];
|
||||
|
||||
test("xiaomi-mimo registry uses the current default base URL and MiMo V2.5 models", () => {
|
||||
const entry = REGISTRY["xiaomi-mimo"];
|
||||
|
||||
assert.ok(entry, "xiaomi-mimo should exist in registry");
|
||||
assert.equal(entry.baseUrl, "https://api.xiaomimimo.com/v1");
|
||||
for (const modelId of DEPRECATED_MIMO_V2_MODELS) {
|
||||
assert.ok(!entry.models.some((model) => model.id === modelId), `${modelId} is deprecated`);
|
||||
}
|
||||
assert.deepEqual(
|
||||
entry.models.map((model) => model.id),
|
||||
["mimo-v2.5-pro", "mimo-v2.5", "mimo-v2-pro", "mimo-v2-omni", "mimo-v2-flash"]
|
||||
["mimo-v2.5-pro", "mimo-v2.5"]
|
||||
);
|
||||
});
|
||||
|
||||
@@ -32,7 +37,7 @@ test("xiaomi-mimo TTS models are registered in the audio speech registry", () =>
|
||||
assert.equal(provider.format, "xiaomi-mimo-tts");
|
||||
assert.deepEqual(
|
||||
provider.models.map((model) => model.id),
|
||||
["mimo-v2.5-tts", "mimo-v2.5-tts-voicedesign", "mimo-v2.5-tts-voiceclone", "mimo-v2-tts"]
|
||||
["mimo-v2.5-tts", "mimo-v2.5-tts-voicedesign", "mimo-v2.5-tts-voiceclone"]
|
||||
);
|
||||
assert.ok(
|
||||
getAllAudioModels().some(
|
||||
@@ -98,14 +103,9 @@ test("xiaomi-mimo update schema accepts custom regional baseUrl", () => {
|
||||
}
|
||||
});
|
||||
|
||||
test("only MiMo V2.5 and V2-Omni report vision; the *-pro/flash chat models are text-only", () => {
|
||||
// Corrected: Xiaomi documents ONLY mimo-v2.5 and mimo-v2-omni as image-capable
|
||||
// (mimo.mi.com .../image-understanding). models.dev mislabels the *-pro models
|
||||
// (hermes-agent#18884); see the hard override in src/lib/modelCapabilities.ts.
|
||||
test("registered Xiaomi MiMo V2.5 chat models keep capability overrides", () => {
|
||||
// models.dev mislabels the *-pro models (hermes-agent#18884); see the hard override in
|
||||
// src/lib/modelCapabilities.ts.
|
||||
assert.equal(getModelSpec("mimo-v2.5")?.supportsVision, true);
|
||||
assert.equal(getModelSpec("mimo-v2-omni")?.supportsVision, true);
|
||||
// text-only chat models
|
||||
assert.equal(getModelSpec("mimo-v2.5-pro")?.supportsVision, false);
|
||||
assert.equal(getModelSpec("mimo-v2-pro")?.supportsVision, false);
|
||||
assert.equal(getModelSpec("mimo-v2-flash")?.supportsVision, undefined);
|
||||
});
|
||||
|
||||
@@ -36,6 +36,10 @@ test("zenmux-free WEB_COOKIE_PROVIDERS entry is marked as free-tier", () => {
|
||||
>;
|
||||
assert.equal(p.hasFree, true);
|
||||
assert.ok(typeof p.freeNote === "string" && (p.freeNote as string).length > 0);
|
||||
assert.ok(
|
||||
!(p.freeNote as string).includes("MiMo V2 Flash Free"),
|
||||
"free note must not advertise deprecated MiMo V2 Flash Free"
|
||||
);
|
||||
assert.ok(typeof p.authHint === "string" && (p.authHint as string).length > 0);
|
||||
});
|
||||
|
||||
@@ -52,12 +56,11 @@ test("zenmux-free is present in the provider REGISTRY with correct shape", () =>
|
||||
assert.equal(r.authHeader, "cookie");
|
||||
});
|
||||
|
||||
test("zenmux-free registry has 12 models including the 3 key free-tier ones", () => {
|
||||
test("zenmux-free registry excludes deprecated MiMo V2 Flash Free", () => {
|
||||
const r = REGISTRY["zenmux-free"];
|
||||
assert.ok(r.models && r.models.length > 0, "must have at least one model");
|
||||
const ids = r.models.map((m) => m.id);
|
||||
|
||||
// The 3 main models mentioned in the upstream PR
|
||||
assert.ok(
|
||||
ids.includes("deepseek/deepseek-chat"),
|
||||
"deepseek/deepseek-chat (DeepSeek V3.2) must be registered"
|
||||
@@ -66,11 +69,8 @@ test("zenmux-free registry has 12 models including the 3 key free-tier ones", ()
|
||||
ids.includes("z-ai/glm-4.7-flash-free"),
|
||||
"z-ai/glm-4.7-flash-free (GLM 4.7 Flash Free) must be registered"
|
||||
);
|
||||
assert.ok(
|
||||
ids.includes("xiaomi/mimo-v2-flash-free"),
|
||||
"xiaomi/mimo-v2-flash-free (MiMo V2 Flash Free) must be registered"
|
||||
);
|
||||
assert.equal(r.models.length, 12, "must have exactly 12 models");
|
||||
assert.ok(!ids.includes("xiaomi/mimo-v2-flash-free"), "xiaomi/mimo-v2-flash-free is deprecated");
|
||||
assert.equal(r.models.length, 11, "must have exactly 11 models");
|
||||
});
|
||||
|
||||
test("zenmux-free model names are human-readable strings", () => {
|
||||
@@ -115,7 +115,7 @@ test("ZenmuxFreeExecutor returns 401 when ctoken is missing from cookies", async
|
||||
} as Parameters<typeof executor.execute>[0]);
|
||||
assert.ok(result.response instanceof Response);
|
||||
assert.equal(result.response.status, 401);
|
||||
const body = await result.response.json() as Record<string, unknown>;
|
||||
const body = (await result.response.json()) as Record<string, unknown>;
|
||||
const errMsg = (body?.error as Record<string, unknown>)?.message as string;
|
||||
assert.ok(errMsg && typeof errMsg === "string", "error.message must be present");
|
||||
assert.ok(errMsg.includes("ctoken"), "error must mention ctoken");
|
||||
@@ -162,10 +162,7 @@ test("ZenmuxFreeExecutor injects Cookie header from credentials when ctoken is p
|
||||
const req = intercepted[0];
|
||||
|
||||
// URL must include ctoken as query param
|
||||
assert.ok(
|
||||
req.url.includes("ctoken=my-ctoken-value"),
|
||||
"ctoken must appear in the request URL"
|
||||
);
|
||||
assert.ok(req.url.includes("ctoken=my-ctoken-value"), "ctoken must appear in the request URL");
|
||||
// Cookie header must carry the full cookie string
|
||||
assert.ok(req.headers["cookie"], "Cookie header must be set");
|
||||
assert.ok(
|
||||
@@ -200,7 +197,7 @@ test("ZenmuxFreeExecutor handles upstream 401 and returns clean error (not raw m
|
||||
} as Parameters<typeof executor.execute>[0]);
|
||||
assert.ok(result.response instanceof Response);
|
||||
assert.equal(result.response.status, 401);
|
||||
const body = await result.response.json() as Record<string, unknown>;
|
||||
const body = (await result.response.json()) as Record<string, unknown>;
|
||||
const errMsg = ((body?.error as Record<string, unknown>)?.message as string) || "";
|
||||
// Hard Rule #12 — no stack traces
|
||||
assert.ok(!errMsg.includes("at /"), "error must not contain stack trace path");
|
||||
|
||||
Reference in New Issue
Block a user