@@ -8,6 +8,7 @@
|
||||
|
||||
### 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 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 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 };
|
||||
}
|
||||
|
||||
|
||||
@@ -344,6 +344,7 @@ describe("resolveCliModel", () => {
|
||||
};
|
||||
const registry = {
|
||||
getAll: () => [...allModels, zaiModel, gatewayModel],
|
||||
hasConfiguredAuth: () => true,
|
||||
} as unknown as Parameters<typeof resolveCliModel>[0]["modelRegistry"];
|
||||
|
||||
const result = resolveCliModel({
|
||||
@@ -356,6 +357,46 @@ describe("resolveCliModel", () => {
|
||||
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)", () => {
|
||||
const registry = {
|
||||
getAll: () => allModels,
|
||||
|
||||
Reference in New Issue
Block a user