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 <cursoragent@cursor.com>
This commit is contained in:
@@ -549,6 +549,7 @@ export const PROVIDER_MODELS = {
|
|||||||
],
|
],
|
||||||
"xiaomi-tokenplan": [
|
"xiaomi-tokenplan": [
|
||||||
{ id: "mimo-v2.5-pro", name: "MiMo V2.5 Pro" },
|
{ 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.5", name: "MiMo V2.5" },
|
||||||
{ id: "mimo-v2-pro", name: "MiMo V2 Pro" },
|
{ id: "mimo-v2-pro", name: "MiMo V2 Pro" },
|
||||||
{ id: "mimo-v2-omni", name: "MiMo V2 Omni" },
|
{ id: "mimo-v2-omni", name: "MiMo V2 Omni" },
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { BaseExecutor } from "./base.js";
|
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 { OAUTH_ENDPOINTS, buildKimiHeaders } from "../config/appConstants.js";
|
||||||
import { buildClineHeaders } from "../../src/shared/utils/clineAuth.js";
|
import { buildClineHeaders } from "../../src/shared/utils/clineAuth.js";
|
||||||
import { getCachedClaudeHeaders } from "../utils/claudeHeaderCache.js";
|
import { getCachedClaudeHeaders } from "../utils/claudeHeaderCache.js";
|
||||||
@@ -60,9 +60,6 @@ export class DefaultExecutor extends BaseExecutor {
|
|||||||
case "gemini":
|
case "gemini":
|
||||||
return `${this.config.baseUrl}/${model}:${stream ? "streamGenerateContent?alt=sse" : "generateContent"}`;
|
return `${this.config.baseUrl}/${model}:${stream ? "streamGenerateContent?alt=sse" : "generateContent"}`;
|
||||||
default: {
|
default: {
|
||||||
if (this.provider === "xiaomi-tokenplan") {
|
|
||||||
return `${resolveXiaomiTokenplanBaseUrl(credentials)}/chat/completions`;
|
|
||||||
}
|
|
||||||
const url = this.config.baseUrl;
|
const url = this.config.baseUrl;
|
||||||
if (url?.includes("{accountId}")) {
|
if (url?.includes("{accountId}")) {
|
||||||
const accountId = credentials?.providerSpecificData?.accountId;
|
const accountId = credentials?.providerSpecificData?.accountId;
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import { GrokWebExecutor } from "./grok-web.js";
|
|||||||
import { PerplexityWebExecutor } from "./perplexity-web.js";
|
import { PerplexityWebExecutor } from "./perplexity-web.js";
|
||||||
import { OllamaLocalExecutor } from "./ollama-local.js";
|
import { OllamaLocalExecutor } from "./ollama-local.js";
|
||||||
import { CommandCodeExecutor } from "./commandcode.js";
|
import { CommandCodeExecutor } from "./commandcode.js";
|
||||||
|
import { XiaomiTokenplanExecutor } from "./xiaomi-tokenplan.js";
|
||||||
import { DefaultExecutor } from "./default.js";
|
import { DefaultExecutor } from "./default.js";
|
||||||
|
|
||||||
const executors = {
|
const executors = {
|
||||||
@@ -37,6 +38,7 @@ const executors = {
|
|||||||
"perplexity-web": new PerplexityWebExecutor(),
|
"perplexity-web": new PerplexityWebExecutor(),
|
||||||
"ollama-local": new OllamaLocalExecutor(),
|
"ollama-local": new OllamaLocalExecutor(),
|
||||||
commandcode: new CommandCodeExecutor(),
|
commandcode: new CommandCodeExecutor(),
|
||||||
|
"xiaomi-tokenplan": new XiaomiTokenplanExecutor(),
|
||||||
};
|
};
|
||||||
|
|
||||||
const defaultCache = new Map();
|
const defaultCache = new Map();
|
||||||
@@ -70,3 +72,4 @@ export { GrokWebExecutor } from "./grok-web.js";
|
|||||||
export { PerplexityWebExecutor } from "./perplexity-web.js";
|
export { PerplexityWebExecutor } from "./perplexity-web.js";
|
||||||
export { OllamaLocalExecutor } from "./ollama-local.js";
|
export { OllamaLocalExecutor } from "./ollama-local.js";
|
||||||
export { CommandCodeExecutor } from "./commandcode.js";
|
export { CommandCodeExecutor } from "./commandcode.js";
|
||||||
|
export { XiaomiTokenplanExecutor } from "./xiaomi-tokenplan.js";
|
||||||
|
|||||||
19
open-sse/executors/xiaomi-tokenplan.js
Normal file
19
open-sse/executors/xiaomi-tokenplan.js
Normal file
@@ -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`;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,7 +5,7 @@ import { COLORS } from "../utils/stream.js";
|
|||||||
import { createStreamController } from "../utils/streamHandler.js";
|
import { createStreamController } from "../utils/streamHandler.js";
|
||||||
import { refreshWithRetry } from "../services/tokenRefresh.js";
|
import { refreshWithRetry } from "../services/tokenRefresh.js";
|
||||||
import { createRequestLogger } from "../utils/requestLogger.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 { createErrorResult, parseUpstreamError, formatProviderError } from "../utils/error.js";
|
||||||
import { HTTP_STATUS } from "../config/runtimeConfig.js";
|
import { HTTP_STATUS } from "../config/runtimeConfig.js";
|
||||||
import { handleBypassRequest } from "../utils/bypassHandler.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 modelTargetFormat = getModelTargetFormat(alias, model);
|
||||||
const targetFormat = modelTargetFormat || getTargetFormat(provider);
|
const targetFormat = modelTargetFormat || getTargetFormat(provider);
|
||||||
const stripList = getModelStrip(alias, model);
|
const stripList = getModelStrip(alias, model);
|
||||||
|
const upstreamModel = getModelUpstreamId(alias, model);
|
||||||
|
|
||||||
// Inject provider-level thinking config override (only if client hasn't set)
|
// 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)
|
// 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;
|
let toolNameMap;
|
||||||
if (passthrough) {
|
if (passthrough) {
|
||||||
log?.debug?.("PASSTHROUGH", `${clientTool} → ${provider} | native lossless`);
|
log?.debug?.("PASSTHROUGH", `${clientTool} → ${provider} | native lossless`);
|
||||||
translatedBody = { ...body, model };
|
translatedBody = { ...body, model: upstreamModel };
|
||||||
} else {
|
} 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) {
|
if (!translatedBody) {
|
||||||
trackPendingRequest(model, provider, connectionId, false, true);
|
trackPendingRequest(model, provider, connectionId, false, true);
|
||||||
return createErrorResult(HTTP_STATUS.BAD_REQUEST, `Failed to translate request for ${sourceFormat} → ${targetFormat}`);
|
return createErrorResult(HTTP_STATUS.BAD_REQUEST, `Failed to translate request for ${sourceFormat} → ${targetFormat}`);
|
||||||
}
|
}
|
||||||
toolNameMap = translatedBody._toolNameMap;
|
toolNameMap = translatedBody._toolNameMap;
|
||||||
delete 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).
|
// Dedupe duplicate built-in tools when equivalent MCP tools are present (Claude clients only).
|
||||||
|
|||||||
Reference in New Issue
Block a user