From fb97c1114013ecc37ad91056f06f37760387ba78 Mon Sep 17 00:00:00 2001 From: diegosouzapw Date: Wed, 25 Mar 2026 18:17:48 -0300 Subject: [PATCH] feat(dashboard): fix Playground account selector & CLI Tools dynamic model listing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Playground: - loadConnections() was parsing wrong API response shape (expected providers[].connections[] but API returns flat connections[]) - Account selector now shows for any provider with ≥1 connection - Uses conn.email as name fallback for OAuth providers CLI Tools: - getAllAvailableModels() now also fetches from /v1/models API - Dynamic models supplement static PROVIDER_MODELS definitions - Fixes providers like Kiro, OpenCode Zen showing 0 models --- .../cli-tools/CLIToolsPageClient.tsx | 40 +++++++++++++++++++ .../(dashboard)/dashboard/playground/page.tsx | 21 +++++----- 2 files changed, 50 insertions(+), 11 deletions(-) diff --git a/src/app/(dashboard)/dashboard/cli-tools/CLIToolsPageClient.tsx b/src/app/(dashboard)/dashboard/cli-tools/CLIToolsPageClient.tsx index 4cded35570..e9e5a6e28d 100644 --- a/src/app/(dashboard)/dashboard/cli-tools/CLIToolsPageClient.tsx +++ b/src/app/(dashboard)/dashboard/cli-tools/CLIToolsPageClient.tsx @@ -33,6 +33,7 @@ export default function CLIToolsPageClient({ machineId }) { const [apiKeys, setApiKeys] = useState([]); const [toolStatuses, setToolStatuses] = useState({}); const [statusesLoaded, setStatusesLoaded] = useState(false); + const [dynamicModels, setDynamicModels] = useState([]); const translateOrFallback = useCallback( (key, fallback, values = undefined) => { try { @@ -49,6 +50,7 @@ export default function CLIToolsPageClient({ machineId }) { loadCloudSettings(); fetchApiKeys(); fetchToolStatuses(); + fetchDynamicModels(); }, []); const loadCloudSettings = async () => { @@ -107,6 +109,18 @@ export default function CLIToolsPageClient({ machineId }) { } }; + const fetchDynamicModels = async () => { + try { + const res = await fetch("/v1/models"); + if (res.ok) { + const data = await res.json(); + setDynamicModels(data?.data || []); + } + } catch (error) { + console.log("Error fetching dynamic models:", error); + } + }; + const getActiveProviders = () => { return connections.filter((c) => c.isActive !== false); }; @@ -116,6 +130,7 @@ export default function CLIToolsPageClient({ machineId }) { const models = []; const seenModels = new Set(); + // First: add static models from the constants activeProviders.forEach((conn) => { const alias = PROVIDER_ID_TO_ALIAS[conn.provider] || conn.provider; const providerModels = getModelsByProviderId(conn.provider); @@ -135,6 +150,31 @@ export default function CLIToolsPageClient({ machineId }) { }); }); + // Second: add dynamic models from /v1/models (fills gaps for Kiro, OpenCode, custom providers) + const activeProviderIds = new Set(activeProviders.map((c) => c.provider)); + const activeAliases = new Set( + activeProviders.map((c) => PROVIDER_ID_TO_ALIAS[c.provider] || c.provider) + ); + dynamicModels.forEach((dm) => { + const modelId = dm.id || dm; + if (seenModels.has(modelId)) return; + // Parse alias/model format + const slashIdx = modelId.indexOf("/"); + if (slashIdx === -1) return; + const alias = modelId.substring(0, slashIdx); + const bareModel = modelId.substring(slashIdx + 1); + if (!activeAliases.has(alias) && !activeProviderIds.has(alias)) return; + seenModels.add(modelId); + models.push({ + value: modelId, + label: modelId, + provider: alias, + alias: alias, + connectionName: "", + modelId: bareModel, + }); + }); + return models; }; diff --git a/src/app/(dashboard)/dashboard/playground/page.tsx b/src/app/(dashboard)/dashboard/playground/page.tsx index eff6bbbfdd..6edcf7da73 100644 --- a/src/app/(dashboard)/dashboard/playground/page.tsx +++ b/src/app/(dashboard)/dashboard/playground/page.tsx @@ -225,16 +225,15 @@ export default function PlaygroundPage() { .then((res) => res.json()) .then((data) => { const allConns: ConnectionOption[] = []; - for (const p of data?.providers || []) { - if (p.id !== provider) continue; - for (const conn of p.connections || []) { - allConns.push({ - id: conn.id, - name: conn.name || conn.id, - provider: p.id, - authType: conn.authType || "apiKey", - }); - } + // API returns flat { connections: [...] } — each has a .provider field + for (const conn of data?.connections || []) { + if (conn.provider !== provider) continue; + allConns.push({ + id: conn.id, + name: conn.name || conn.email || conn.id, + provider: conn.provider, + authType: conn.authType || "apiKey", + }); } setConnections(allConns); setSelectedConnection(""); @@ -528,7 +527,7 @@ export default function PlaygroundPage() { )} {/* Account/Connection — shown when provider has multiple connections */} - {!isSearchEndpoint && connections.length > 1 && ( + {!isSearchEndpoint && connections.length >= 1 && (