@@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
|
- Fixed `--model` resolution for authenticated custom model IDs whose slash prefix matches an unauthenticated built-in provider ([#5643](https://github.com/earendil-works/pi/issues/5643)).
|
||||||
- Fixed `/share` and `/export` HTML exports to use the active fallback theme when the configured custom theme no longer exists ([#5596](https://github.com/earendil-works/pi/issues/5596)).
|
- Fixed `/share` and `/export` HTML exports to use the active fallback theme when the configured custom theme no longer exists ([#5596](https://github.com/earendil-works/pi/issues/5596)).
|
||||||
- Fixed custom fallback model IDs with `:<thinking>` suffixes to preserve the requested thinking level when the provider template model does not advertise reasoning ([#5552](https://github.com/earendil-works/pi/issues/5552)).
|
- Fixed custom fallback model IDs with `:<thinking>` suffixes to preserve the requested thinking level when the provider template model does not advertise reasoning ([#5552](https://github.com/earendil-works/pi/issues/5552)).
|
||||||
|
|
||||||
|
|||||||
@@ -422,6 +422,27 @@ export function resolveCliModel(options: {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (model) {
|
if (model) {
|
||||||
|
// If provider inference matched an unauthenticated provider/model pair, prefer
|
||||||
|
// one exact raw model-id match that is authenticated. This keeps
|
||||||
|
// "provider/model" syntax preferred when usable, but handles models whose
|
||||||
|
// literal id starts with a known provider name (for example
|
||||||
|
// commandcode model id "xiaomi/mimo-v2.5-pro").
|
||||||
|
if (inferredProvider) {
|
||||||
|
const rawExactMatches = availableModels.filter(
|
||||||
|
(m) => m.id.toLowerCase() === cliModel.toLowerCase() && !modelsAreEqual(m, model),
|
||||||
|
);
|
||||||
|
if (rawExactMatches.length > 0 && !modelRegistry.hasConfiguredAuth(model)) {
|
||||||
|
const authenticatedRawMatches = rawExactMatches.filter((m) => modelRegistry.hasConfiguredAuth(m));
|
||||||
|
if (authenticatedRawMatches.length === 1) {
|
||||||
|
return {
|
||||||
|
model: authenticatedRawMatches[0],
|
||||||
|
thinkingLevel: undefined,
|
||||||
|
warning: undefined,
|
||||||
|
error: undefined,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
return { model, thinkingLevel, warning, error: undefined };
|
return { model, thinkingLevel, warning, error: undefined };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -344,6 +344,7 @@ describe("resolveCliModel", () => {
|
|||||||
};
|
};
|
||||||
const registry = {
|
const registry = {
|
||||||
getAll: () => [...allModels, zaiModel, gatewayModel],
|
getAll: () => [...allModels, zaiModel, gatewayModel],
|
||||||
|
hasConfiguredAuth: () => true,
|
||||||
} as unknown as Parameters<typeof resolveCliModel>[0]["modelRegistry"];
|
} as unknown as Parameters<typeof resolveCliModel>[0]["modelRegistry"];
|
||||||
|
|
||||||
const result = resolveCliModel({
|
const result = resolveCliModel({
|
||||||
@@ -356,6 +357,46 @@ describe("resolveCliModel", () => {
|
|||||||
expect(result.model?.id).toBe("glm-5");
|
expect(result.model?.id).toBe("glm-5");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("prefers an authenticated exact raw model id over an unauthenticated inferred provider", () => {
|
||||||
|
const commandcodeModel: Model<"anthropic-messages"> = {
|
||||||
|
id: "xiaomi/mimo-v2.5-pro",
|
||||||
|
name: "Xiaomi MiMo via Commandcode",
|
||||||
|
api: "anthropic-messages",
|
||||||
|
provider: "commandcode",
|
||||||
|
baseUrl: "https://example.invalid",
|
||||||
|
reasoning: false,
|
||||||
|
input: ["text"],
|
||||||
|
cost: { input: 1, output: 2, cacheRead: 0.1, cacheWrite: 1 },
|
||||||
|
contextWindow: 128000,
|
||||||
|
maxTokens: 8192,
|
||||||
|
};
|
||||||
|
const xiaomiModel: Model<"anthropic-messages"> = {
|
||||||
|
id: "mimo-v2.5-pro",
|
||||||
|
name: "Xiaomi MiMo",
|
||||||
|
api: "anthropic-messages",
|
||||||
|
provider: "xiaomi",
|
||||||
|
baseUrl: "https://api.xiaomimimo.com",
|
||||||
|
reasoning: false,
|
||||||
|
input: ["text"],
|
||||||
|
cost: { input: 1, output: 2, cacheRead: 0.1, cacheWrite: 1 },
|
||||||
|
contextWindow: 128000,
|
||||||
|
maxTokens: 8192,
|
||||||
|
};
|
||||||
|
const registry = {
|
||||||
|
getAll: () => [...allModels, commandcodeModel, xiaomiModel],
|
||||||
|
hasConfiguredAuth: (model: Model<"anthropic-messages">) => model.provider === "commandcode",
|
||||||
|
} as unknown as Parameters<typeof resolveCliModel>[0]["modelRegistry"];
|
||||||
|
|
||||||
|
const result = resolveCliModel({
|
||||||
|
cliModel: "xiaomi/mimo-v2.5-pro",
|
||||||
|
modelRegistry: registry,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.error).toBeUndefined();
|
||||||
|
expect(result.model?.provider).toBe("commandcode");
|
||||||
|
expect(result.model?.id).toBe("xiaomi/mimo-v2.5-pro");
|
||||||
|
});
|
||||||
|
|
||||||
test("resolves provider-prefixed fuzzy patterns (openrouter/qwen -> openrouter model)", () => {
|
test("resolves provider-prefixed fuzzy patterns (openrouter/qwen -> openrouter model)", () => {
|
||||||
const registry = {
|
const registry = {
|
||||||
getAll: () => allModels,
|
getAll: () => allModels,
|
||||||
|
|||||||
Reference in New Issue
Block a user