mirror of
https://github.com/t8y2/dbx.git
synced 2026-10-22 08:47:26 +00:00
fix(ai): refresh model options after adding config
This commit is contained in:
@@ -55,6 +55,7 @@ import { useQueryStore } from "@/stores/queryStore";
|
||||
import { useToast } from "@/composables/useToast";
|
||||
import { useNavigationTargets } from "@/composables/useNavigationTargets";
|
||||
import { buildAiContext, runAgentStream, isVectorDbType, isValidActionForMode, defaultActionForMode, type AiAction, type AiAssistantMode, type AiSqlFileContext, type CustomPromptContext } from "@/lib/ai/ai";
|
||||
import { getAiConfigModelIds, isAiConfigModelCandidate } from "@/lib/ai/aiConfigCandidates";
|
||||
import { orderAiConfigsForDisplay } from "@/lib/ai/aiConfigOrdering";
|
||||
import { normalizeClaudeCodeReasoningLevel } from "@/lib/ai/aiModelEffort";
|
||||
import { ACTIVE_TEMPLATES_TOTAL_MAX, promptTemplateCharacterCount } from "@/types/promptTemplate";
|
||||
@@ -297,14 +298,7 @@ const modelSearchQuery = ref("");
|
||||
|
||||
// Configured providers for quick switching - get from aiConfigs
|
||||
const configuredProviders = computed(() => {
|
||||
const providers = orderAiConfigsForDisplay(
|
||||
settings.aiConfigs.filter((c) => {
|
||||
// Check directly if config has required fields
|
||||
const preset = AI_PROVIDER_PRESETS[c.provider];
|
||||
if (c.provider === "codex-cli" || c.provider === "claude-code-cli") return true;
|
||||
return !!c.endpoint?.trim() && !!c.model?.trim() && (!preset.requiresApiKey || !!c.apiKey?.trim());
|
||||
}),
|
||||
);
|
||||
const providers = orderAiConfigsForDisplay(settings.aiConfigs.filter((config) => isAiConfigModelCandidate(config, AI_PROVIDER_PRESETS[config.provider].requiresApiKey)));
|
||||
// Apply search filter - hide providers with no matching models
|
||||
if (modelSearchQuery.value.trim()) {
|
||||
const query = modelSearchQuery.value.trim().toLowerCase();
|
||||
@@ -334,12 +328,7 @@ const activeFullConfig = computed(() => {
|
||||
function getModelsForConfig(configId: string): string[] {
|
||||
const config = settings.aiConfigs.find((c) => c.id === configId);
|
||||
if (!config) return [];
|
||||
const models = config.models?.map((m) => m.name) || [];
|
||||
// Always include the current model
|
||||
if (config.model && !models.includes(config.model)) {
|
||||
return [config.model, ...models];
|
||||
}
|
||||
return models;
|
||||
return getAiConfigModelIds(config);
|
||||
}
|
||||
|
||||
function getConfigModelOptionIds(configId: string): string[] {
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
Reference in New Issue
Block a user