feat(dashboard): fix Playground account selector & CLI Tools dynamic model listing

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
This commit is contained in:
diegosouzapw
2026-03-25 18:17:48 -03:00
parent 1c5c62e311
commit fb97c11140
2 changed files with 50 additions and 11 deletions

View File

@@ -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;
};

View File

@@ -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 && (
<div className="flex-1 w-full">
<label className="block text-xs font-medium text-text-muted mb-1.5 uppercase tracking-wider">
Account