feat(xiaomi-mimo): server-assisted desktop login, five account clusters, v2.6 models

Reproduce the MiMo Desktop login surface server-side so headless/Docker
deployments can link a Xiaomi account without the Desktop client. The
account session (passToken) is captured during the proxied login and
stored per connection.

- Five account clusters (cn/sgp/ams/ru/in): per-region mimo-server host
  and SSO sid, unknown region falls back to sgp
- mimo-v2.6-pro/flash/pro-ultraspeed dual-route models: account-service
  route when desktop credentials exist, cloud API (sk- key) otherwise;
  drops obsolete mimo-x-*-preview ids
- Desktop ServiceTokenManager 2-phase handshake (single serviceLogin with
  target sid, raw 64-bit nonce preserved), per-region session cache
- reasoning_effort bridged to output_config.effort; i18n runtime now
  observes characterData mutations so React text rewrites get translated
- Security hardening on the login proxy: session travels only in the
  httpOnly cookie (never in the URL), proxy branch requires dashboard
  auth, authorization/proxy-authorization never forwarded upstream, and
  upstream Set-Cookie is not replayed onto the app origin
This commit is contained in:
wismyzhizi
2026-09-23 09:43:39 +07:00
committed by decolua
parent 6af26a9ee8
commit 910db749aa
16 changed files with 1687 additions and 352 deletions

View File

@@ -1,10 +1,15 @@
import { DefaultExecutor } from "./default.js";
import { getMimoAccountCookie, invalidateMimoAccountCookieCache, MIMO_API_BASE, MIMO_API_UA } from "../shared/mimoAccount.js";
import { getMimoAccountCookie, invalidateMimoAccountCookieCache, resolveMimoServerBase, MIMO_API_UA } from "../shared/mimoAccount.js";
// Desktop-exclusive Preview models. These are served by the account service's
// /api/route proxy, authorized by the Xiaomi account session (NOT the sk- key).
// See shared/mimoAccount.js for the session handshake.
const PREVIEW_MODELS = new Set(["mimo-x-pro-preview", "mimo-x-flash-preview"]);
// Dual-route v2.6 models.
// v2.6 models dynamically route to the account service when desktop session credentials
// (mimoPassToken or account cookie) are present to consume weekly quota, falling back to
// the cloud API (sk- key) otherwise.
const ACCOUNT_MODELS = new Set([
"mimo-v2.6-pro",
"mimo-v2.6-flash",
"mimo-v2.6-pro-ultraspeed",
]);
// Session cookie resolved in execute() (async) and read back by buildHeaders()
// (sync — BaseExecutor.execute does not await it). Carried on the per-request
@@ -23,15 +28,24 @@ export class XiaomiMimoExecutor extends DefaultExecutor {
super("xiaomi-mimo");
}
static isPreviewModel(model) {
return PREVIEW_MODELS.has(bareModel(model));
static isAccountRoute(model, credentials) {
const bare = bareModel(model);
if (!ACCOUNT_MODELS.has(bare)) return false;
return Boolean(
credentials?.[COOKIE_KEY] ||
credentials?.providerSpecificData?.mimoPassToken
);
}
isAccountRoute(model, credentials) {
return XiaomiMimoExecutor.isAccountRoute(model, credentials);
}
buildUrl(model, stream, urlIndex = 0, credentials = null) {
// Preview models live on the account-service route, which is not one of the
// Account route models live on the account-service route, which is not one of the
// declared transports — resolve it before the default runtimeTransport path.
if (XiaomiMimoExecutor.isPreviewModel(model)) {
return `${MIMO_API_BASE}/api/route/chat/completions`;
if (this.isAccountRoute(model, credentials)) {
return `${resolveMimoServerBase(credentials?.providerSpecificData)}/api/route/chat/completions`;
}
// Cloud API models keep default handling, so a Claude-format client reaches
// the /anthropic/v1/messages transport.
@@ -39,8 +53,8 @@ export class XiaomiMimoExecutor extends DefaultExecutor {
}
buildHeaders(credentials, stream = true, url, model) {
if (XiaomiMimoExecutor.isPreviewModel(model) && credentials?.[COOKIE_KEY]) {
// Preview models authenticate with the account-session cookie, not the key.
if (this.isAccountRoute(model, credentials) && credentials?.[COOKIE_KEY]) {
// Account route models authenticate with the account-session cookie, not the key.
return {
"Content-Type": "application/json",
Accept: stream ? "text/event-stream" : "application/json",
@@ -52,17 +66,22 @@ export class XiaomiMimoExecutor extends DefaultExecutor {
}
transformRequest(model, body, stream, credentials) {
// super runs stripUnsupportedParams, which flattens Preview content-part
// super runs stripUnsupportedParams, which flattens content-part
// arrays (see the xiaomi-mimo rule in translator/concerns/paramSupport.js).
const out = super.transformRequest(model, body, stream, credentials);
// Preview models: thinking/params get defaults only — never override what the
// caller set explicitly. (body.model is already `xiaomi/<id>` via upstreamModelId.)
if (XiaomiMimoExecutor.isPreviewModel(model)) {
if (out.thinking == null) out.thinking = { type: "enabled" };
// Account route models: bridge reasoning_effort to official output_config.effort
// (matches MiMo Desktop app.asar behavior).
if (this.isAccountRoute(model, credentials)) {
const rawEffort = out.reasoning_effort || body?.reasoning_effort || body?.output_config?.effort;
if (rawEffort) {
delete out.reasoning_effort;
const norm = String(rawEffort).toLowerCase() === "xhigh" ? "high" : String(rawEffort).toLowerCase();
out.output_config = { ...(out.output_config || {}), effort: norm };
}
if (out.temperature == null) out.temperature = 1.0;
if (out.top_p == null) out.top_p = 0.95;
if (!out.max_tokens) out.max_tokens = 4096;
}
return out;
@@ -70,13 +89,11 @@ export class XiaomiMimoExecutor extends DefaultExecutor {
async execute(args) {
const { model, credentials, proxyOptions = null } = args;
if (!XiaomiMimoExecutor.isPreviewModel(model)) return super.execute(args);
if (!this.isAccountRoute(model, credentials)) return super.execute(args);
const cookie = await getMimoAccountCookie(credentials?.providerSpecificData, proxyOptions);
if (!cookie) {
throw new Error(
"Xiaomi MiMo account session unavailable. Sign in to MiMo Desktop once so its passToken is present, then retry.",
);
return super.execute(args);
}
credentials[COOKIE_KEY] = cookie;
const result = await super.execute(args);
@@ -94,6 +111,6 @@ export class XiaomiMimoExecutor extends DefaultExecutor {
}
}
export const __test__ = { PREVIEW_MODELS, bareModel, COOKIE_KEY };
export const __test__ = { ACCOUNT_MODELS, bareModel, COOKIE_KEY };
export default XiaomiMimoExecutor;

View File

@@ -2,9 +2,9 @@ import { CLAUDE_API_HEADERS } from "../shared.js";
// Dual auth (same pattern as kimi):
// - API key (sk-...) → cloud API on api.xiaomimimo.com
// - Desktop account/OAuth → same cloud host, plus the Desktop-exclusive Preview
// models served by the account-service route on mimo-server-cn.xiaomimimo.com
// (authorized by a Xiaomi account session cookie, not the key).
// - Desktop account/OAuth → same cloud host, plus the dual-route v2.6 models
// served by the account-service route (mimo-server-<cluster>.xiaomimimo.com),
// authorized by a Xiaomi account session cookie, not the key.
// Endpoint is picked per model in the executor, same as opencode-go's /responses split.
export default {
id: "xiaomi-mimo",
@@ -30,6 +30,16 @@ export default {
category: "oauth",
authModes: ["oauth", "apikey"],
hasOAuth: true,
// Keys are cluster-specific. MiMo Desktop declares five regions
// (CN/SGP/AMS/RU/IN) — host + sid follow mimo-server-<code> / mimo<code>.
regions: [
{ id: "cn", label: "China (中国大陆)" },
{ id: "sgp", label: "Singapore (新加坡)" },
{ id: "ams", label: "Europe · Amsterdam (欧洲)" },
{ id: "ru", label: "Russia (俄罗斯)" },
{ id: "in", label: "India (印度)" },
],
defaultRegion: "sgp",
serviceKinds: ["llm", "tts"],
transport: {
baseUrl: "https://api.xiaomimimo.com/v1/chat/completions",
@@ -50,10 +60,10 @@ export default {
},
],
models: [
// Desktop-exclusive — served by the account-service route, which only accepts
// OpenAI format, so supportedFormats pins them to the openai transport.
{ id: "mimo-x-pro-preview", name: "MiMo-X-Pro-Preview", upstreamModelId: "xiaomi/mimo-x-pro-preview", supportedFormats: ["openai"] },
{ id: "mimo-x-flash-preview", name: "MiMo-X-Flash-Preview", upstreamModelId: "xiaomi/mimo-x-flash-preview", supportedFormats: ["openai"] },
// Cloud API & Desktop dual-route models (prefers the desktop account quota when available)
{ id: "mimo-v2.6-pro", name: "MiMo V2.6 Pro", upstreamModelId: "xiaomi/mimo-v2.6-pro", supportedFormats: ["openai"] },
{ id: "mimo-v2.6-flash", name: "MiMo V2.6 Flash", upstreamModelId: "xiaomi/mimo-v2.6-flash", supportedFormats: ["openai"] },
{ id: "mimo-v2.6-pro-ultraspeed", name: "MiMo V2.6 Pro UltraSpeed", upstreamModelId: "xiaomi/mimo-v2.6-pro-ultraspeed", supportedFormats: ["openai"] },
// Cloud API models (api.xiaomimimo.com/v1)
{ id: "mimo-v2.5-pro", name: "MiMo V2.5 Pro" },
{ id: "mimo-v2.5", name: "MiMo V2.5" },

View File

@@ -41,6 +41,7 @@ const PATTERN_THINKING = [
{ provider: "codex", pattern: "*gpt-5.6-terra*", levels: [...CODEX_GPT_5_6_LEVELS, "ultra"] },
{ provider: "codex", pattern: "*gpt-5.6-luna*", levels: CODEX_GPT_5_6_LEVELS },
{ pattern: "*codex*", levels: ["low", "medium", "high", "xhigh"] }, // codex cannot disable thinking
{ pattern: "*mimo*v2.6*", levels: ["none", "low", "medium", "high", "xhigh"] },
// DeepSeek v4.* (Alibaba MaaS, probed live): effort low|medium|high|xhigh|max
// all 200 via output_config.effort; "none" is a 400 on the anthropic route
// (disable thinking instead). none kept for the picker = disable.

View File

@@ -8,20 +8,42 @@ import { proxyAwareFetch } from "../utils/proxyFetch.js";
* Xiaomi MiMo account-session helpers (used for weekly quota).
*
* The weekly quota endpoint lives on the account service domain and is authorized
* by an account session cookie, NOT the sk- API key. Acquiring that cookie mirrors
* MiMo Desktop: a passToken (persisted in Desktop's cookie store) is exchanged via
* the passportapi SSO, then authorized for the `mimopc` service, and finally stamped
* by the mimo-server /api/sts callback into a `serviceToken` cookie.
* by an account session cookie, NOT the sk- API key. Acquiring that cookie is a
* 1:1 port of MiMo Desktop's ServiceTokenManager (app.asar) — the GOLD STANDARD:
*
* Flow (verified against MiMo Desktop traffic):
* 1. GET {api}/api/user/xiaomi/me -> 302 to account SSO (sid=mimopc)
* 2. GET account /pass/serviceLogin?sid=passportapi&_json=true -> nonce/ssecurity
* 3. GET {location}&clientSign=... -> account-level serviceToken
* 4. GET account /pass/serviceLogin?sid=mimopc&callback=<sts>&_json=true
* 5. GET {api}/api/sts?...&ticket... -> Set-Cookie: serviceToken (mimopc scope)
* getServiceToken(sid) / refreshServiceToken(sid):
* PHASE 1: GET https://account.xiaomi.com/pass/serviceLogin
* ?_locale=zh_CN&_snsNone=true&sid=<clusterSid>&_json=true
* Cookie: {userId, passToken, cUserId}
* -> {code, location, ssecurity, nonce, bSecondValidation, notificationUrl}
* -> code !== 0 is an error (never silent)
* PHASE 2: GET {location}&clientSign=sha1(nonce & ssecurity), follow the
* redirect chain absorbing Set-Cookie -> serviceToken
*
* sid is per-cluster (SID_BY_REGION): CN = mimopc, SGP = mimosgp.
*/
const API_BASE = "https://mimo-server-cn.xiaomimimo.com";
// Account-service cluster hosts. MiMo Desktop declares five regions
// (rn = {CN, SGP, RU, IN, EU}); the EU cluster is deployed in Amsterdam.
// Host + sid naming is unified: mimo-server-<code> / sid = mimo<code>
// (ams is the only non-country code). Verified live via /api/user/xiaomi/me.
const API_BASE_BY_REGION = {
cn: "https://mimo-server-cn.xiaomimimo.com",
sgp: "https://mimo-server-sgp.xiaomimimo.com",
ams: "https://mimo-server-ams.xiaomimimo.com",
ru: "https://mimo-server-ru.xiaomimimo.com",
in: "https://mimo-server-in.xiaomimimo.com",
};
const DEFAULT_API_BASE = API_BASE_BY_REGION.sgp;
// Cluster service sid — 1:1 with the host code: mimo<code>.
// Unknown/absent region falls back to SGP (the international/open cluster).
const SID_BY_REGION = { cn: "mimopc", sgp: "mimosgp", ams: "mimoams", ru: "mimoru", in: "mimoin" };
function sidForRegion(region) {
const r = String(region || "").toLowerCase();
return SID_BY_REGION[r] || SID_BY_REGION.sgp;
}
const API_BASE = DEFAULT_API_BASE;
const ACCOUNT_HOST = "account.xiaomi.com";
const API_UA =
"miNative PC/Normal Windows_NT/10.0.19045 SDKV/1.0.0 DEVT/PC DEVS/Windows APP/miaccount_desktop APPV/0.1.0";
@@ -112,65 +134,106 @@ function cookieHeader(jar) {
.join("; ");
}
/**
* Resolve the account-service base URL for a connection.
* @param {object|null} providerSpecificData - may carry `region` ("cn"|"sgp"|"ams"|"ru"|"in")
*/
export function resolveMimoServerBase(providerSpecificData = null) {
const region = String(providerSpecificData?.region || "").toLowerCase();
return API_BASE_BY_REGION[region] || DEFAULT_API_BASE;
}
/**
* Exchange a passToken for a mimo-server service session cookie.
* Primary path mirrors the Desktop ServiceTokenManager (app.asar):
* PHASE 1: GET /pass/serviceLogin?_locale=zh_CN&_snsNone=true&sid=<clusterSid>&_json=true
* Cookie {userId,passToken,cUserId} -> {code,location,ssecurity,nonce}
* PHASE 2: GET {location}&clientSign=sha1(nonce&ssecurity), follow the chain
* (manual, absorbing Set-Cookie) -> serviceToken
* sid is per-cluster (SID_BY_REGION): cn=mimopc, sgp=mimosgp, ams=mimoams, ru=mimoru, in=mimoin.
* @returns {Promise<string|null>} Cookie header value, or null on failure.
*/
async function acquireServiceCookie(passJar, proxyOptions) {
async function acquireServiceCookie(passJar, proxyOptions, apiBase = DEFAULT_API_BASE, region = "sgp") {
const r = String(region || "").toLowerCase();
// Hard constraint: CN is ALWAYS direct (ignores proxy even if set)
const effectiveProxy = r === "cn" ? null : proxyOptions;
const sid = sidForRegion(r);
const viaDesktop = await acquireViaDesktopPhases(passJar, effectiveProxy, apiBase, sid);
if (viaDesktop) console.log(`[mimoAccount] desktop 2-phase OK (sid=${sid})`);
return viaDesktop;
}
async function acquireViaDesktopPhases(passJar, proxyOptions, apiBase, sid) {
const failLog = (reason) => console.log(`[mimoAccount] desktopPhase fail: ${reason}`);
const jar = { ...passJar };
const ck = () => cookieHeader(jar);
// 1. Unauthenticated API call -> 302 carrying the sts callback (sid=mimopc)
const r1 = await proxyAwareFetch(
`${API_BASE}/api/user/xiaomi/me`,
{ redirect: "manual", headers: { "User-Agent": API_UA, Cookie: ck() } },
// PHASE 1 — single serviceLogin call with the TARGET sid (no passportapi
// prelude; ssecurity/nonce come straight from this response).
// Desktop only sends: userId, passToken, cUserId (no extra cookies)
const p1Jar = {};
if (jar.userId) p1Jar.userId = jar.userId;
if (jar.passToken) p1Jar.passToken = jar.passToken;
if (jar.cUserId) p1Jar.cUserId = jar.cUserId;
const p1Url = `https://${ACCOUNT_HOST}/pass/serviceLogin?_locale=zh_CN&_snsNone=true&sid=${encodeURIComponent(sid)}&_json=true`;
const p1 = await proxyAwareFetch(
p1Url,
{ headers: { Cookie: cookieHeader(p1Jar), "User-Agent": SSO_UA, Accept: "application/json" } },
proxyOptions,
);
const redirect = r1.headers.get("location");
if (!redirect) return null;
const stsCallback = new URL(redirect).searchParams.get("callback");
if (!stsCallback) return null;
const raw = await p1.text();
const clean = raw.replace(/^&&&START&&&/, "");
// Nonce > 2^53 loses precision in JSON.parse — extract raw literal for signing
const rawNonce = clean.match(/"nonce"\s*:\s*(\d+)/)?.[1];
let j = null;
try { j = JSON.parse(clean); } catch { /* handled below */ }
if (rawNonce && j) j.nonce = rawNonce;
// 2. passportapi SSO phase 1 -> nonce + ssecurity
const sso1 = await proxyAwareFetch(
`https://${ACCOUNT_HOST}/pass/serviceLogin?sid=passportapi&_json=true`,
{ headers: { Cookie: ck(), "User-Agent": SSO_UA, Accept: "application/json" } },
proxyOptions,
);
const j1 = JSON.parse((await sso1.text()).replace(/^&&&START&&&/, ""));
const nonce = j1.nonce || (j1.location ? new URL(j1.location).searchParams.get("nonce") : null);
if (!nonce || !j1.location) return null;
if (!j || typeof j.code !== "number" || j.code !== 0 || !j.location || !j.nonce || !j.ssecurity) {
failLog(
`phase1 sid=${sid} http=${p1.status} code=${j?.code ?? "?"} hasLoc=${!!j?.location}`
+ ` secondValidation=${j?.bSecondValidation ?? "?"} notificationUrl=${j?.notificationUrl ? "present" : "no"}`
+ ` body=${JSON.stringify(raw.slice(0, 200))}`,
);
return null;
}
absorbSetCookie(jar, p1);
// 3. passportapi SSO phase 2 -> account-level serviceToken
const sso2 = await proxyAwareFetch(
`${j1.location}&clientSign=${signatureClientSign(nonce, j1.ssecurity)}`,
{ redirect: "manual", headers: { Cookie: ck(), "User-Agent": SSO_UA } },
proxyOptions,
);
absorbSetCookie(jar, sso2);
// PHASE 2 — clientSign the redirect, follow the redirect chain server-side.
// ⚠️ CRITICAL DESKTOP SPEC (app.asar / SSO_curl.cpp line 728: cookies.clear()):
// Phase 2 MUST NOT send ANY Cookie header! The server returns 200 OK with Set-Cookie: serviceToken!
const sep = j.location.includes("?") ? "&" : "?";
let current = `${j.location}${sep}clientSign=${signatureClientSign(rawNonce || j.nonce, j.ssecurity)}`;
// 4. mimopc SSO -> sts callback carrying a ticket
const sso3 = await proxyAwareFetch(
`https://${ACCOUNT_HOST}/pass/serviceLogin?sid=mimopc&callback=${encodeURIComponent(stsCallback)}&_json=true`,
{ headers: { Cookie: ck(), "User-Agent": SSO_UA, Accept: "application/json" } },
proxyOptions,
);
const j3 = JSON.parse((await sso3.text()).replace(/^&&&START&&&/, ""));
absorbSetCookie(jar, sso3);
if (!j3?.location || !/\/api\/sts/.test(j3.location)) return null;
for (let hop = 0; hop < 8; hop++) {
const res = await proxyAwareFetch(
current,
{ redirect: "manual", headers: { "User-Agent": SSO_UA } },
proxyOptions,
);
absorbSetCookie(jar, res);
const loc = res.headers.get("location");
if (res.status >= 300 && res.status < 400 && loc) {
current = new URL(loc, current).toString();
continue;
}
break;
}
// 5. sts callback -> Set-Cookie: serviceToken (mimopc scope)
const sts = await proxyAwareFetch(
j3.location,
{ redirect: "manual", headers: { "User-Agent": API_UA, Cookie: ck() } },
proxyOptions,
);
absorbSetCookie(jar, sts);
const sidKey = `${sid}_serviceToken`;
if (!jar.serviceToken && jar[sidKey]) {
jar.serviceToken = jar[sidKey];
}
const needed = ["serviceToken", "mimopc_ph", "mimopc_slh", "userId"];
if (!jar.serviceToken) return null;
if (!jar.serviceToken) {
failLog(`phase2 no serviceToken sid=${sid} jar=[${Object.keys(jar).join(",")}]`);
return null;
}
const out = {};
for (const k of needed) if (jar[k]) out[k] = jar[k];
for (const [k, v] of Object.entries(jar)) {
if (!v) continue;
if (k === "serviceToken" || k === "userId" || /_(ph|slh)$/.test(k)) out[k] = v;
}
return cookieHeader(out);
}
@@ -179,13 +242,15 @@ async function acquireServiceCookie(passJar, proxyOptions) {
* @param {object|null} providerSpecificData - may carry `mimoPassToken` override
*/
async function getServiceCookie(providerSpecificData, proxyOptions) {
const apiBase = resolveMimoServerBase(providerSpecificData);
const passJar = providerSpecificData?.mimoPassToken
? { passToken: providerSpecificData.mimoPassToken, userId: providerSpecificData.mimoUserId, cUserId: providerSpecificData.mimoCUserId }
: await readDesktopAccountCookies();
if (!passJar) return { cookie: null, reason: "no-pass-token" };
// One cached session per passToken — accounts/connections rotate independently.
const key = crypto.createHash("sha256").update(passJar.passToken).digest("hex");
// One cached session per passToken+cluster — accounts/connections rotate
// independently, and the same passToken maps to different sessions per region.
const key = crypto.createHash("sha256").update(`${apiBase}|${passJar.passToken}`).digest("hex");
const cached = _cache.get(key);
if (cached && Date.now() - cached.at < COOKIE_TTL_MS) {
@@ -202,8 +267,9 @@ async function getServiceCookie(providerSpecificData, proxyOptions) {
const promise = (async () => {
try {
return await acquireServiceCookie(passJar, proxyOptions);
} catch {
return await acquireServiceCookie(passJar, proxyOptions, apiBase, providerSpecificData?.region);
} catch (e) {
console.log(`[mimoAccount] acquire threw: ${e?.message || e} | ${String(e?.stack || "").split("\n").slice(1, 4).join(" <- ")}`);
return null; // network/parse failure — callers degrade, never throw
} finally {
_inflight.delete(key);
@@ -234,7 +300,8 @@ export async function getMimoAccountCookie(providerSpecificData = null, proxyOpt
try {
const { cookie } = await getServiceCookie(providerSpecificData, proxyOptions);
return cookie;
} catch {
} catch (e) {
console.log(`[mimoAccount] getMimoAccountCookie threw: ${e?.message || e} | ${String(e?.stack || "").split("\n").slice(1, 4).join(" <- ")}`);
return null;
}
}
@@ -250,7 +317,7 @@ export async function getMimoAccountUsage(providerSpecificData = null, proxyOpti
}
try {
const res = await proxyAwareFetch(
`${API_BASE}/api/user/usage`,
`${resolveMimoServerBase(providerSpecificData)}/api/user/usage`,
{ headers: { "User-Agent": API_UA, Cookie: cookie, Accept: "application/json" }, signal: AbortSignal.timeout(10000) },
proxyOptions,
);

View File

@@ -14,9 +14,6 @@ const STRIP_RULES = [
{ provider: "github", match: (m) => /claude/i.test(m) && !/claude.*(opus|sonnet).*4\.6/i.test(m), drop: ["thinking", "reasoning_effort"] },
// Cloudflare Workers AI: content must be plain string, rejects OpenAI content-part array (#1926)
{ provider: "cloudflare-ai", flattenContent: true },
// MiMo Desktop Preview models (account-service route): content must be plain string,
// rejects OpenAI content-part array. Cloud models keep their parts (mimo-v2-omni is multi-modal).
{ provider: "xiaomi-mimo", match: /preview/i, flattenContent: true },
{ provider: "volcengine-ark", match: /glm-5/i, clampToModelMaxOutput: true },
// VolcEngine Ark caps the Kimi family at max_tokens <= 32768, but the model's
// advertised ceiling is far higher (Kimi-K2.7-Code resolves to maxOutput 262144),