*/
const FAMILY_COMPAT: Record<Family, Record<string, unknown>> = {
qwen: { thinkingFormat: "qwen", supportsReasoningEffort: false, cacheControlFormat: "anthropic" },
deepseek: { thinkingFormat: "deepseek", requiresReasoningContentOnAssistantMessages: true },
zai: { thinkingFormat: "zai", supportsReasoningEffort: false },
// glm depth is reasoning_effort, no enable_thinking: thinking is on by default here and
// glm-5.3 rejects enable_thinking false outright
zai: { supportsReasoningEffort: true },
other: {},
};
/**
* Model Studio's own levels, not the vendors' defaults.
*
* DeepSeek here takes `reasoning_effort` with low/medium/high/xhigh/max (default high),
* so every level maps to itself. Qwen and GLM take only a boolean `enable_thinking`;
* depth would need `thinking_budget`, which the OpenAI-completions path cannot send, so
* they offer off and high and nothing in between.
* DeepSeek takes the full `reasoning_effort` ladder with high as its default. Qwen takes a
* boolean `enable_thinking` only. GLM takes `reasoning_effort` too, with a value set that
* varies by model, so the family entry is a superset and `MODEL_THINKING_LEVELS` narrows it.
*/
const THINKING_LEVELS: Record<Family, Record<string, string | null> | undefined> = {
deepseek: { minimal: null, low: "low", medium: "medium", high: "high", xhigh: "xhigh", max: "max" },
qwen: { minimal: null, low: null, medium: null, high: "high" },
zai: { minimal: null, low: null, medium: null, high: "high" },
zai: { off: "none", minimal: "minimal", low: "low", medium: "medium", high: "high", xhigh: "xhigh", max: "max" },
other: undefined,
};
/** glm-5.3 takes only low/high/max, and 400s on every attempt to stop it thinking */
const MODEL_THINKING_LEVELS: Record<string, Record<string, string | null>> = {
"glm-5.3": { off: null, minimal: null, low: "low", medium: null, high: "high", xhigh: null, max: "max" },
};
const thinkingLevelsFor = (id: string): Record<string, string | null> | undefined =>
MODEL_THINKING_LEVELS[id] ?? THINKING_LEVELS[familyOf(id)];
export const baseUrlFromEnv = (): string => (process.env.ALIBABA_BASE_URL || DEFAULT_BASE_URL).replace(/\/+$/, "");
const readFile = (path: string): string | undefined => {
try {
supportsDeveloperRole: false,
maxTokensField: "max_tokens" as const,
...FAMILY_COMPAT[familyOf(m.id)],
},
...(m.reasoning && THINKING_LEVELS[familyOf(m.id)] ? { thinkingLevelMap: THINKING_LEVELS[familyOf(m.id)] } : {}),
...(m.reasoning && thinkingLevelsFor(m.id) ? { thinkingLevelMap: thinkingLevelsFor(m.id) } : {}),
}));
// entitlement can only hide, never add, so an id gained after the last bake stays
// invisible until regen.ts runs