fix(model): require provider readiness for saved default selection

This commit is contained in:
Jeremy 2026-04-12 12:24:49 -05:00
parent e247f2fe61
commit c96d01acb7
2 changed files with 100 additions and 18 deletions

View file

@ -0,0 +1,78 @@
import assert from "node:assert/strict";
import { describe, it } from "node:test";
import type { Api, Model } from "@gsd/pi-ai";
import type { ModelRegistry } from "./model-registry.js";
import { findInitialModel } from "./model-resolver.js";
function makeModel(provider: string, id: string): Model<Api> {
return {
id,
name: id,
provider,
api: "openai-responses",
reasoning: false,
input: ["text"],
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
contextWindow: 128000,
maxTokens: 8192,
} as Model<Api>;
}
function makeRegistry(opts: {
readyProviders?: Set<string>;
byProviderAndId?: Map<string, Model<Api>>;
available?: Model<Api>[];
}): ModelRegistry {
const readyProviders = opts.readyProviders ?? new Set<string>();
const byProviderAndId = opts.byProviderAndId ?? new Map<string, Model<Api>>();
const available = opts.available ?? [];
return {
find: (provider: string, modelId: string) => byProviderAndId.get(`${provider}/${modelId}`),
getAvailable: async () => available,
isProviderRequestReady: (provider: string) => readyProviders.has(provider),
} as unknown as ModelRegistry;
}
describe("findInitialModel auth gating for saved defaults", () => {
it("uses saved default when provider is request-ready", async () => {
const saved = makeModel("anthropic", "claude-opus-4-6");
const registry = makeRegistry({
readyProviders: new Set(["anthropic"]),
byProviderAndId: new Map([[`anthropic/claude-opus-4-6`, saved]]),
available: [saved],
});
const result = await findInitialModel({
scopedModels: [],
isContinuing: false,
defaultProvider: "anthropic",
defaultModelId: "claude-opus-4-6",
modelRegistry: registry,
});
assert.equal(result.model?.provider, "anthropic");
assert.equal(result.model?.id, "claude-opus-4-6");
});
it("skips saved default when provider is not request-ready and falls back to available", async () => {
const staleDefault = makeModel("anthropic", "claude-opus-4-6");
const fallback = makeModel("openai", "gpt-5.4");
const registry = makeRegistry({
readyProviders: new Set(["openai"]),
byProviderAndId: new Map([[`anthropic/claude-opus-4-6`, staleDefault]]),
available: [fallback],
});
const result = await findInitialModel({
scopedModels: [],
isContinuing: false,
defaultProvider: "anthropic",
defaultModelId: "claude-opus-4-6",
modelRegistry: registry,
});
assert.equal(result.model?.provider, "openai");
assert.equal(result.model?.id, "gpt-5.4");
});
});

View file

@ -504,27 +504,31 @@ export async function findInitialModel(options: {
// 3. Try saved default from settings
if (defaultProvider && defaultModelId) {
const found = modelRegistry.find(defaultProvider, defaultModelId);
if (found) {
// Check if the provider's recommended default is a higher-capability variant
// of the saved model (e.g. saved "claude-opus-4-6" vs recommended "claude-opus-4-6-extended").
// If so, prefer the recommended variant to avoid using a smaller context window (#1125).
const recommendedId = defaultModelPerProvider[defaultProvider as KnownProvider];
if (recommendedId && recommendedId !== defaultModelId && recommendedId.startsWith(defaultModelId)) {
const recommended = modelRegistry.find(defaultProvider, recommendedId);
if (recommended) {
model = recommended;
if (defaultThinkingLevel) {
thinkingLevel = defaultThinkingLevel;
// Guard against stale settings defaults: only use the saved provider/model
// if the provider is actually request-ready (auth/OAuth/CLI ready).
if (modelRegistry.isProviderRequestReady(defaultProvider)) {
const found = modelRegistry.find(defaultProvider, defaultModelId);
if (found) {
// Check if the provider's recommended default is a higher-capability variant
// of the saved model (e.g. saved "claude-opus-4-6" vs recommended "claude-opus-4-6-extended").
// If so, prefer the recommended variant to avoid using a smaller context window (#1125).
const recommendedId = defaultModelPerProvider[defaultProvider as KnownProvider];
if (recommendedId && recommendedId !== defaultModelId && recommendedId.startsWith(defaultModelId)) {
const recommended = modelRegistry.find(defaultProvider, recommendedId);
if (recommended) {
model = recommended;
if (defaultThinkingLevel) {
thinkingLevel = defaultThinkingLevel;
}
return { model, thinkingLevel, fallbackMessage: undefined };
}
return { model, thinkingLevel, fallbackMessage: undefined };
}
model = found;
if (defaultThinkingLevel) {
thinkingLevel = defaultThinkingLevel;
}
return { model, thinkingLevel, fallbackMessage: undefined };
}
model = found;
if (defaultThinkingLevel) {
thinkingLevel = defaultThinkingLevel;
}
return { model, thinkingLevel, fallbackMessage: undefined };
}
}