fix(ai): refresh model options after adding config

This commit is contained in:
t8y2 committed 2026-07-22 23:09:54 +08:00
1 parent 22ccdef594
commit e6224ec0b0
3 files changed
+74 -14

No files matched your search

@@ -0,0 +1,54 @@
import { describe, expect, it } from "vitest";
import { getAiConfigModelIds, isAiConfigModelCandidate } from "@/lib/ai/aiConfigCandidates";
import type { AiConfig } from "@/types/ai";
function config(overrides: Partial<AiConfig> = {}): AiConfig {
return {
provider: "openai",
apiKey: "secret",
authMethod: "bearer",
endpoint: "https://api.example.com/v1",
model: "gpt-default",
apiStyle: "completions",
...overrides,
};
}
describe("isAiConfigModelCandidate", () => {
it("accepts discovered models when the default model is empty", () => {
expect(isAiConfigModelCandidate(config({ model: "", models: [{ name: "gpt-new" }] }), true)).toBe(true);
});
it("keeps endpoint and required API key validation", () => {
expect(isAiConfigModelCandidate(config({ endpoint: "" }), true)).toBe(false);
expect(isAiConfigModelCandidate(config({ apiKey: "" }), true)).toBe(false);
});
it.each(["codex-cli", "claude-code-cli"] as const)("keeps %s configs eligible without endpoint, API key, or model metadata", (provider) => {
expect(
isAiConfigModelCandidate(
config({
provider,
endpoint: "",
apiKey: "",
model: "",
models: [],
}),
false,
),
).toBe(true);
});
});
describe("getAiConfigModelIds", () => {
it("includes the default model once while preserving configured model order", () => {
expect(
getAiConfigModelIds(
config({
model: "gpt-default",
models: [{ name: "gpt-fast" }, { name: "gpt-default" }, { name: "gpt-default" }],
}),
),
).toEqual(["gpt-fast", "gpt-default"]);
});
});
@@ -0,0 +1,17 @@
import type { AiConfig } from "@/types/ai";
const CLI_PROVIDERS = new Set<AiConfig["provider"]>(["codex-cli", "claude-code-cli"]);
export function getAiConfigModelIds(config: Pick<AiConfig, "model" | "models">): string[] {
const configuredModels = [...new Set(config.models?.map((model) => model.name) ?? [])];
return config.model && !configuredModels.includes(config.model) ? [config.model, ...configuredModels] : configuredModels;
}
export function isAiConfigModelCandidate(config: AiConfig, requiresApiKey: boolean): boolean {
// CLI providers resolve their model and credentials externally, so keep the existing eligibility bypass.
if (CLI_PROVIDERS.has(config.provider)) return true;
if (!config.endpoint?.trim() || (requiresApiKey && !config.apiKey?.trim())) return false;
// A discovered model list is sufficient even before the user chooses a default model.
return getAiConfigModelIds(config).some((model) => !!model.trim());
}