From 40cfa63eb8e1806e8ce88fe04ea5f83dcb605d51 Mon Sep 17 00:00:00 2001 From: Mr_NoboDy Date: Sat, 6 Jun 2026 10:36:03 +0700 Subject: [PATCH] feat(xiaomi-tokenplan): add Claude-native MiMo V2.5 Pro alias via dedicated executor Add mimo-v2.5-pro-claude alias routing to the Xiaomi TokenPlan Anthropic-compatible /anthropic/v1/messages endpoint. Logic lives in a dedicated XiaomiTokenplanExecutor (config-driven via targetFormat) instead of the shared DefaultExecutor. Co-authored-by: Cursor --- open-sse/config/providerModels.js | 1 + open-sse/executors/default.js | 5 +---- open-sse/executors/index.js | 3 +++ open-sse/executors/xiaomi-tokenplan.js | 19 +++++++++++++++++++ open-sse/handlers/chatCore.js | 9 +++++---- 5 files changed, 29 insertions(+), 8 deletions(-) create mode 100644 open-sse/executors/xiaomi-tokenplan.js diff --git a/open-sse/config/providerModels.js b/open-sse/config/providerModels.js index 8bd89d6a..11e5ebe4 100644 --- a/open-sse/config/providerModels.js +++ b/open-sse/config/providerModels.js @@ -549,6 +549,7 @@ export const PROVIDER_MODELS = { ], "xiaomi-tokenplan": [ { id: "mimo-v2.5-pro", name: "MiMo V2.5 Pro" }, + { id: "mimo-v2.5-pro-claude", name: "MiMo V2.5 Pro (Claude Native)", targetFormat: "claude", upstreamModelId: "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" }, diff --git a/open-sse/executors/default.js b/open-sse/executors/default.js index 634b82e8..1af130c2 100644 --- a/open-sse/executors/default.js +++ b/open-sse/executors/default.js @@ -1,5 +1,5 @@ import { BaseExecutor } from "./base.js"; -import { PROVIDERS, resolveXiaomiTokenplanBaseUrl } from "../config/providers.js"; +import { PROVIDERS } from "../config/providers.js"; import { OAUTH_ENDPOINTS, buildKimiHeaders } from "../config/appConstants.js"; import { buildClineHeaders } from "../../src/shared/utils/clineAuth.js"; import { getCachedClaudeHeaders } from "../utils/claudeHeaderCache.js"; @@ -60,9 +60,6 @@ export class DefaultExecutor extends BaseExecutor { case "gemini": return `${this.config.baseUrl}/${model}:${stream ? "streamGenerateContent?alt=sse" : "generateContent"}`; default: { - if (this.provider === "xiaomi-tokenplan") { - return `${resolveXiaomiTokenplanBaseUrl(credentials)}/chat/completions`; - } const url = this.config.baseUrl; if (url?.includes("{accountId}")) { const accountId = credentials?.providerSpecificData?.accountId; diff --git a/open-sse/executors/index.js b/open-sse/executors/index.js index 78372cbf..4a443903 100644 --- a/open-sse/executors/index.js +++ b/open-sse/executors/index.js @@ -15,6 +15,7 @@ import { GrokWebExecutor } from "./grok-web.js"; import { PerplexityWebExecutor } from "./perplexity-web.js"; import { OllamaLocalExecutor } from "./ollama-local.js"; import { CommandCodeExecutor } from "./commandcode.js"; +import { XiaomiTokenplanExecutor } from "./xiaomi-tokenplan.js"; import { DefaultExecutor } from "./default.js"; const executors = { @@ -37,6 +38,7 @@ const executors = { "perplexity-web": new PerplexityWebExecutor(), "ollama-local": new OllamaLocalExecutor(), commandcode: new CommandCodeExecutor(), + "xiaomi-tokenplan": new XiaomiTokenplanExecutor(), }; const defaultCache = new Map(); @@ -70,3 +72,4 @@ export { GrokWebExecutor } from "./grok-web.js"; export { PerplexityWebExecutor } from "./perplexity-web.js"; export { OllamaLocalExecutor } from "./ollama-local.js"; export { CommandCodeExecutor } from "./commandcode.js"; +export { XiaomiTokenplanExecutor } from "./xiaomi-tokenplan.js"; diff --git a/open-sse/executors/xiaomi-tokenplan.js b/open-sse/executors/xiaomi-tokenplan.js new file mode 100644 index 00000000..259da07f --- /dev/null +++ b/open-sse/executors/xiaomi-tokenplan.js @@ -0,0 +1,19 @@ +import { DefaultExecutor } from "./default.js"; +import { resolveXiaomiTokenplanBaseUrl } from "../config/providers.js"; +import { getModelTargetFormat } from "../config/providerModels.js"; +import { FORMATS } from "../translator/formats.js"; + +export class XiaomiTokenplanExecutor extends DefaultExecutor { + constructor() { + super("xiaomi-tokenplan"); + } + + // Claude-native aliases route to the Anthropic-compatible messages endpoint + buildUrl(model, stream, urlIndex = 0, credentials = null) { + const baseUrl = resolveXiaomiTokenplanBaseUrl(credentials); + if (getModelTargetFormat(model, model) === FORMATS.CLAUDE) { + return `${baseUrl.replace(/\/v1\/?$/, "/anthropic/v1")}/messages`; + } + return `${baseUrl}/chat/completions`; + } +} diff --git a/open-sse/handlers/chatCore.js b/open-sse/handlers/chatCore.js index 8d606091..2a2abde1 100644 --- a/open-sse/handlers/chatCore.js +++ b/open-sse/handlers/chatCore.js @@ -5,7 +5,7 @@ import { COLORS } from "../utils/stream.js"; import { createStreamController } from "../utils/streamHandler.js"; import { refreshWithRetry } from "../services/tokenRefresh.js"; import { createRequestLogger } from "../utils/requestLogger.js"; -import { getModelTargetFormat, getModelStrip, PROVIDER_ID_TO_ALIAS } from "../config/providerModels.js"; +import { getModelTargetFormat, getModelStrip, getModelUpstreamId, PROVIDER_ID_TO_ALIAS } from "../config/providerModels.js"; import { createErrorResult, parseUpstreamError, formatProviderError } from "../utils/error.js"; import { HTTP_STATUS } from "../config/runtimeConfig.js"; import { handleBypassRequest } from "../utils/bypassHandler.js"; @@ -41,6 +41,7 @@ export async function handleChatCore({ body, modelInfo, credentials, log, onCred const modelTargetFormat = getModelTargetFormat(alias, model); const targetFormat = modelTargetFormat || getTargetFormat(provider); const stripList = getModelStrip(alias, model); + const upstreamModel = getModelUpstreamId(alias, model); // Inject provider-level thinking config override (only if client hasn't set) // on/off → extended type (body.thinking), none/low/medium/high → effort type (body.reasoning_effort) @@ -89,16 +90,16 @@ export async function handleChatCore({ body, modelInfo, credentials, log, onCred let toolNameMap; if (passthrough) { log?.debug?.("PASSTHROUGH", `${clientTool} → ${provider} | native lossless`); - translatedBody = { ...body, model }; + translatedBody = { ...body, model: upstreamModel }; } else { - translatedBody = translateRequest(sourceFormat, targetFormat, model, body, stream, credentials, provider, reqLogger, stripList, connectionId, clientTool); + translatedBody = translateRequest(sourceFormat, targetFormat, upstreamModel, body, stream, credentials, provider, reqLogger, stripList, connectionId, clientTool); if (!translatedBody) { trackPendingRequest(model, provider, connectionId, false, true); return createErrorResult(HTTP_STATUS.BAD_REQUEST, `Failed to translate request for ${sourceFormat} → ${targetFormat}`); } toolNameMap = translatedBody._toolNameMap; delete translatedBody._toolNameMap; - translatedBody.model = model; + translatedBody.model = upstreamModel; } // Dedupe duplicate built-in tools when equivalent MCP tools are present (Claude clients only).