feat(models): wire /api/models dynamic discovery to model selector dialog
Fetches live Claude model list from /api/models on mount; falls back to hardcoded CLAUDE_MODELS.OPTIONS when the endpoint is unavailable. Closes #1
This commit is contained in:
parent
a1517e9f26
commit
18516fd488
1 changed files with 43 additions and 17 deletions
|
|
@ -11,6 +11,7 @@ import {
|
|||
GEMINI_MODELS,
|
||||
PROVIDERS,
|
||||
} from "../../../../../shared/modelConstants";
|
||||
import { authenticatedFetch } from "../../../../utils/api";
|
||||
import type { ProjectSession, LLMProvider } from "../../../../types/app";
|
||||
import { NextTaskBanner } from "../../../task-master";
|
||||
import {
|
||||
|
|
@ -30,6 +31,8 @@ import {
|
|||
const MOD_KEY =
|
||||
typeof navigator !== "undefined" && /Mac|iPhone|iPad/.test(navigator.platform) ? "⌘" : "Ctrl";
|
||||
|
||||
type ModelOption = { value: string; label: string };
|
||||
|
||||
type ProviderSelectionEmptyStateProps = {
|
||||
selectedSession: ProjectSession | null;
|
||||
currentSessionId: string | null;
|
||||
|
|
@ -53,22 +56,15 @@ type ProviderSelectionEmptyStateProps = {
|
|||
type ProviderGroup = {
|
||||
id: LLMProvider;
|
||||
name: string;
|
||||
models: { value: string; label: string }[];
|
||||
models: ModelOption[];
|
||||
};
|
||||
|
||||
const PROVIDER_GROUPS: ProviderGroup[] = PROVIDERS.map((p) => ({
|
||||
const STATIC_PROVIDER_GROUPS: ProviderGroup[] = PROVIDERS.map((p) => ({
|
||||
id: p.id as LLMProvider,
|
||||
name: p.name,
|
||||
models: p.models.OPTIONS,
|
||||
}));
|
||||
|
||||
function getModelConfig(p: LLMProvider) {
|
||||
if (p === "claude") return CLAUDE_MODELS;
|
||||
if (p === "codex") return CODEX_MODELS;
|
||||
if (p === "gemini") return GEMINI_MODELS;
|
||||
return CURSOR_MODELS;
|
||||
}
|
||||
|
||||
function getCurrentModel(
|
||||
p: LLMProvider,
|
||||
c: string,
|
||||
|
|
@ -111,10 +107,36 @@ export default function ProviderSelectionEmptyState({
|
|||
const { t } = useTranslation("chat");
|
||||
const { isWindowsServer } = useServerPlatform();
|
||||
const [dialogOpen, setDialogOpen] = useState(false);
|
||||
const [claudeModelOptions, setClaudeModelOptions] = useState<ModelOption[]>(CLAUDE_MODELS.OPTIONS);
|
||||
|
||||
// Fetch live Claude model list from the server; fall back to static constants on failure.
|
||||
useEffect(() => {
|
||||
authenticatedFetch("/api/models")
|
||||
.then((res) => {
|
||||
if (!res.ok) return;
|
||||
return res.json();
|
||||
})
|
||||
.then((data) => {
|
||||
if (Array.isArray(data?.claude) && data.claude.length > 0) {
|
||||
setClaudeModelOptions(data.claude);
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
// Static fallback already set as initial state.
|
||||
});
|
||||
}, []);
|
||||
|
||||
const providerGroups = useMemo<ProviderGroup[]>(
|
||||
() =>
|
||||
STATIC_PROVIDER_GROUPS.map((g) =>
|
||||
g.id === "claude" ? { ...g, models: claudeModelOptions } : g,
|
||||
),
|
||||
[claudeModelOptions],
|
||||
);
|
||||
|
||||
const visibleProviderGroups = useMemo(
|
||||
() => (isWindowsServer ? PROVIDER_GROUPS.filter((p) => p.id !== "cursor") : PROVIDER_GROUPS),
|
||||
[isWindowsServer],
|
||||
() => (isWindowsServer ? providerGroups.filter((p) => p.id !== "cursor") : providerGroups),
|
||||
[isWindowsServer, providerGroups],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
|
|
@ -137,12 +159,16 @@ export default function ProviderSelectionEmptyState({
|
|||
);
|
||||
|
||||
const currentModelLabel = useMemo(() => {
|
||||
const config = getModelConfig(provider);
|
||||
const found = config.OPTIONS.find(
|
||||
(o: { value: string; label: string }) => o.value === currentModel,
|
||||
);
|
||||
return found?.label || currentModel;
|
||||
}, [provider, currentModel]);
|
||||
const options: ModelOption[] =
|
||||
provider === "claude"
|
||||
? claudeModelOptions
|
||||
: provider === "codex"
|
||||
? CODEX_MODELS.OPTIONS
|
||||
: provider === "gemini"
|
||||
? GEMINI_MODELS.OPTIONS
|
||||
: CURSOR_MODELS.OPTIONS;
|
||||
return options.find((o) => o.value === currentModel)?.label ?? currentModel;
|
||||
}, [provider, currentModel, claudeModelOptions]);
|
||||
|
||||
const setModelForProvider = useCallback(
|
||||
(providerId: LLMProvider, modelValue: string) => {
|
||||
|
|
|
|||
Loading…
Reference in a new issue