Files
openclaw/extensions/openrouter/models.ts
rain ad1d8bf990 fix(openrouter): apply strict9 ids to Mistral routes
Fixes #58012.

Applies strict9 replay tool call id sanitization to OpenRouter Mistral-family model routes, including unprefixed Mistral/Codestral/Devstral aliases, while preserving existing passthrough behavior for Gemini and other OpenRouter-backed routes.

Adds focused unit coverage plus a live OpenRouter model catalog test so new Mistral-family routes are checked against the replay policy. Also keeps the current core lint gate green by switching the tool schema cache key sort to a non-mutating sorted array.

Co-authored-by: Pluviobyte <Pluviobyte@users.noreply.github.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-28 16:14:32 +01:00

38 lines
1.2 KiB
TypeScript

import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/string-coerce-runtime";
const OPENROUTER_MISTRAL_MODEL_PREFIXES = [
"mistralai/",
"mistral/",
"mistral-",
"codestral-",
"devstral-",
"ministral-",
"mixtral-",
"pixtral-",
"voxtral-",
] as const;
export function normalizeOpenRouterModelId(modelId: unknown): string | undefined {
if (typeof modelId !== "string") {
return undefined;
}
const normalized = normalizeLowercaseStringOrEmpty(modelId);
return normalized.startsWith("openrouter/") ? normalized.slice("openrouter/".length) : normalized;
}
export function isOpenRouterMistralModelId(modelId: unknown): boolean {
const normalized = normalizeOpenRouterModelId(modelId);
return Boolean(
normalized && OPENROUTER_MISTRAL_MODEL_PREFIXES.some((prefix) => normalized.startsWith(prefix)),
);
}
export function isOpenRouterDeepSeekV4ModelId(modelId: unknown): boolean {
const normalized = normalizeOpenRouterModelId(modelId);
if (!normalized?.startsWith("deepseek/")) {
return false;
}
const deepSeekModelId = normalized.slice("deepseek/".length).split(":", 1)[0];
return deepSeekModelId === "deepseek-v4-flash" || deepSeekModelId === "deepseek-v4-pro";
}