Merge pull request #5560 from haoqixu/fix-5552
fix(coding-agent): parse :thinking suffix from custom model IDs in fallback path
This commit is contained in:
@@ -340,9 +340,10 @@ export interface ResolveCliModelResult {
|
|||||||
export function resolveCliModel(options: {
|
export function resolveCliModel(options: {
|
||||||
cliProvider?: string;
|
cliProvider?: string;
|
||||||
cliModel?: string;
|
cliModel?: string;
|
||||||
|
cliThinking?: string;
|
||||||
modelRegistry: ModelRegistry;
|
modelRegistry: ModelRegistry;
|
||||||
}): ResolveCliModelResult {
|
}): ResolveCliModelResult {
|
||||||
const { cliProvider, cliModel, modelRegistry } = options;
|
const { cliProvider, cliModel, cliThinking, modelRegistry } = options;
|
||||||
|
|
||||||
if (!cliModel) {
|
if (!cliModel) {
|
||||||
return { model: undefined, warning: undefined, error: undefined };
|
return { model: undefined, warning: undefined, error: undefined };
|
||||||
@@ -451,12 +452,28 @@ export function resolveCliModel(options: {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (provider) {
|
if (provider) {
|
||||||
const fallbackModel = buildFallbackModel(provider, pattern, availableModels);
|
// Parse thinking level suffix from the pattern before building the fallback model,
|
||||||
|
// but only when --thinking is not explicitly provided.
|
||||||
|
// e.g. "zai-org/GLM-5.1-FP8:high" → modelId="zai-org/GLM-5.1-FP8", fallbackThinking="high"
|
||||||
|
let fallbackPattern = pattern;
|
||||||
|
let fallbackThinking: ThinkingLevel | undefined;
|
||||||
|
if (!cliThinking) {
|
||||||
|
const lastColon = pattern.lastIndexOf(":");
|
||||||
|
if (lastColon !== -1) {
|
||||||
|
const suffix = pattern.substring(lastColon + 1);
|
||||||
|
if (isValidThinkingLevel(suffix)) {
|
||||||
|
fallbackPattern = pattern.substring(0, lastColon);
|
||||||
|
fallbackThinking = suffix;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const fallbackModel = buildFallbackModel(provider, fallbackPattern, availableModels);
|
||||||
if (fallbackModel) {
|
if (fallbackModel) {
|
||||||
const fallbackWarning = warning
|
const fallbackWarning = warning
|
||||||
? `${warning} Model "${pattern}" not found for provider "${provider}". Using custom model id.`
|
? `${warning} Model "${fallbackPattern}" not found for provider "${provider}". Using custom model id.`
|
||||||
: `Model "${pattern}" not found for provider "${provider}". Using custom model id.`;
|
: `Model "${fallbackPattern}" not found for provider "${provider}". Using custom model id.`;
|
||||||
return { model: fallbackModel, thinkingLevel: undefined, warning: fallbackWarning, error: undefined };
|
return { model: fallbackModel, thinkingLevel: fallbackThinking, warning: fallbackWarning, error: undefined };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -360,6 +360,7 @@ function buildSessionOptions(
|
|||||||
const resolved = resolveCliModel({
|
const resolved = resolveCliModel({
|
||||||
cliProvider: parsed.provider,
|
cliProvider: parsed.provider,
|
||||||
cliModel: parsed.model,
|
cliModel: parsed.model,
|
||||||
|
cliThinking: parsed.thinking,
|
||||||
modelRegistry,
|
modelRegistry,
|
||||||
});
|
});
|
||||||
if (resolved.warning) {
|
if (resolved.warning) {
|
||||||
|
|||||||
@@ -370,6 +370,127 @@ describe("resolveCliModel", () => {
|
|||||||
expect(result.model?.provider).toBe("openrouter");
|
expect(result.model?.provider).toBe("openrouter");
|
||||||
expect(result.model?.id).toBe("qwen/qwen3-coder:exacto");
|
expect(result.model?.id).toBe("qwen/qwen3-coder:exacto");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("custom model fallback with :thinking suffix (#5552)", () => {
|
||||||
|
// Models for a provider that has registered models but the specific model ID
|
||||||
|
// is not in the registry (triggers buildFallbackModel path).
|
||||||
|
const neuralwattModel: Model<"anthropic-messages"> = {
|
||||||
|
id: "some-base-model",
|
||||||
|
name: "Some Base Model",
|
||||||
|
api: "anthropic-messages",
|
||||||
|
provider: "neuralwatt",
|
||||||
|
baseUrl: "https://api.neuralwatt.com",
|
||||||
|
reasoning: false,
|
||||||
|
input: ["text"],
|
||||||
|
cost: { input: 1, output: 2, cacheRead: 0.1, cacheWrite: 1 },
|
||||||
|
contextWindow: 128000,
|
||||||
|
maxTokens: 8192,
|
||||||
|
};
|
||||||
|
|
||||||
|
const modelsWithNeuralwatt = [...allModels, neuralwattModel];
|
||||||
|
|
||||||
|
test("strips :thinking suffix from custom model id in fallback path", () => {
|
||||||
|
const registry = {
|
||||||
|
getAll: () => modelsWithNeuralwatt,
|
||||||
|
} as unknown as Parameters<typeof resolveCliModel>[0]["modelRegistry"];
|
||||||
|
|
||||||
|
const result = resolveCliModel({
|
||||||
|
cliModel: "neuralwatt/zai-org/GLM-5.1-FP8:high",
|
||||||
|
modelRegistry: registry,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.error).toBeUndefined();
|
||||||
|
expect(result.model?.provider).toBe("neuralwatt");
|
||||||
|
// The :high suffix must NOT leak into the model id sent to the API
|
||||||
|
expect(result.model?.id).toBe("zai-org/GLM-5.1-FP8");
|
||||||
|
expect(result.thinkingLevel).toBe("high");
|
||||||
|
});
|
||||||
|
|
||||||
|
test("custom model without thinking suffix works normally in fallback path", () => {
|
||||||
|
const registry = {
|
||||||
|
getAll: () => modelsWithNeuralwatt,
|
||||||
|
} as unknown as Parameters<typeof resolveCliModel>[0]["modelRegistry"];
|
||||||
|
|
||||||
|
const result = resolveCliModel({
|
||||||
|
cliModel: "neuralwatt/zai-org/GLM-5.1-FP8",
|
||||||
|
modelRegistry: registry,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.error).toBeUndefined();
|
||||||
|
expect(result.model?.provider).toBe("neuralwatt");
|
||||||
|
expect(result.model?.id).toBe("zai-org/GLM-5.1-FP8");
|
||||||
|
expect(result.thinkingLevel).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("all valid thinking levels work in fallback path", () => {
|
||||||
|
const registry = {
|
||||||
|
getAll: () => modelsWithNeuralwatt,
|
||||||
|
} as unknown as Parameters<typeof resolveCliModel>[0]["modelRegistry"];
|
||||||
|
|
||||||
|
for (const level of ["off", "minimal", "low", "medium", "high", "xhigh"]) {
|
||||||
|
const result = resolveCliModel({
|
||||||
|
cliModel: `neuralwatt/zai-org/GLM-5.1-FP8:${level}`,
|
||||||
|
modelRegistry: registry,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.error).toBeUndefined();
|
||||||
|
expect(result.model?.id).toBe("zai-org/GLM-5.1-FP8");
|
||||||
|
expect(result.thinkingLevel).toBe(level);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
test("invalid thinking suffix on custom model is treated as part of model id", () => {
|
||||||
|
const registry = {
|
||||||
|
getAll: () => modelsWithNeuralwatt,
|
||||||
|
} as unknown as Parameters<typeof resolveCliModel>[0]["modelRegistry"];
|
||||||
|
|
||||||
|
const result = resolveCliModel({
|
||||||
|
cliModel: "neuralwatt/zai-org/GLM-5.1-FP8:banana",
|
||||||
|
modelRegistry: registry,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.error).toBeUndefined();
|
||||||
|
expect(result.model?.provider).toBe("neuralwatt");
|
||||||
|
// Invalid suffix stays in the id (it's not a thinking level)
|
||||||
|
expect(result.model?.id).toBe("zai-org/GLM-5.1-FP8:banana");
|
||||||
|
expect(result.thinkingLevel).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("explicit --provider with custom model:thinking strips suffix correctly", () => {
|
||||||
|
const registry = {
|
||||||
|
getAll: () => modelsWithNeuralwatt,
|
||||||
|
} as unknown as Parameters<typeof resolveCliModel>[0]["modelRegistry"];
|
||||||
|
|
||||||
|
const result = resolveCliModel({
|
||||||
|
cliProvider: "neuralwatt",
|
||||||
|
cliModel: "zai-org/GLM-5.1-FP8:high",
|
||||||
|
modelRegistry: registry,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.error).toBeUndefined();
|
||||||
|
expect(result.model?.provider).toBe("neuralwatt");
|
||||||
|
expect(result.model?.id).toBe("zai-org/GLM-5.1-FP8");
|
||||||
|
expect(result.thinkingLevel).toBe("high");
|
||||||
|
});
|
||||||
|
|
||||||
|
test("with explicit --thinking, :suffix is kept as part of model id", () => {
|
||||||
|
const registry = {
|
||||||
|
getAll: () => modelsWithNeuralwatt,
|
||||||
|
} as unknown as Parameters<typeof resolveCliModel>[0]["modelRegistry"];
|
||||||
|
|
||||||
|
const result = resolveCliModel({
|
||||||
|
cliModel: "neuralwatt/zai-org/GLM-5.1-FP8:high",
|
||||||
|
cliThinking: "medium",
|
||||||
|
modelRegistry: registry,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.error).toBeUndefined();
|
||||||
|
expect(result.model?.provider).toBe("neuralwatt");
|
||||||
|
// :high is kept as part of the model id since --thinking was explicit
|
||||||
|
expect(result.model?.id).toBe("zai-org/GLM-5.1-FP8:high");
|
||||||
|
expect(result.thinkingLevel).toBeUndefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("default model selection", () => {
|
describe("default model selection", () => {
|
||||||
|
|||||||
Reference in New Issue
Block a user