Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ The format is based on Keep a Changelog and this project follows Semantic Versio
- **Onboarding prompt scope** — the Run page completion handoff now appears only for the onboarding first-run step, canceling the onboarding-launched server drawer stops setup with an explicit normal-mode notice, and the three-step welcome layout is centered.
- **Built-in template reload after DB clear** — clearing the database from Settings now reloads the built-in benchmark library immediately, keeping shipped templates available without restarting the backend.
- **Catalog empty server card** — the empty Servers catalog now presents a dashed first-server card with the add action instead of a centered empty-state panel.
- **Catalog model auto-selection** — opening the Models catalog without a server filter now selects the first available inference server so discovered models render immediately.
- **Run empty preview** — the empty Run workspace now shows a dummy benchmark result with sample model, prompt, metrics, and audit rows instead of a generic empty panel.

## [0.9.0] - 2026-06-17
Expand Down
18 changes: 17 additions & 1 deletion frontend/src/pages/Catalog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -613,6 +613,12 @@ export function Catalog({
const selectedQuantizations = useMemo(() => new Set(parseCsv(searchParams.get('quantization'))), [searchParams]);
const selectedFormats = useMemo(() => new Set(parseCsv(searchParams.get('format'))), [searchParams]);
const selectedCapabilities = useMemo(() => new Set(parseCsv(searchParams.get('capabilities'))), [searchParams]);
const autoSelectedModelServer = useMemo(() => {
return servers.find((server) => server.inference_server.active && !server.inference_server.archived)
?? servers.find((server) => !server.inference_server.archived)
?? servers[0]
?? null;
}, [servers]);
const maxParamCount = useMemo(() => {
const v = searchParams.get('maxParams');
return v !== null ? Number(v) : null;
Expand All @@ -626,6 +632,16 @@ export function Catalog({
}, { replace: true });
}, [activeTab, inspectorModelId, inspectorServerId, navigate]);

useEffect(() => {
if (activeTab !== 'models' || inspectorServerId || inspectorModelId || searchParams.has('servers') || !autoSelectedModelServer) {
return;
}
const next = new URLSearchParams(searchParams);
next.set('tab', 'models');
next.set('servers', autoSelectedModelServer.inference_server.server_id);
setSearchParams(next, { replace: true });
}, [activeTab, autoSelectedModelServer, inspectorModelId, inspectorServerId, searchParams, setSearchParams]);

useEffect(() => {
if (searchParams.get('startOnboarding') !== '1') {
return;
Expand Down Expand Up @@ -903,7 +919,7 @@ export function Catalog({
setServerStageCollapsed={setServerStageCollapsed}
onToggleServer={toggleServerSelection}
onClearServers={() => updateQuery((params) => {
params.delete('servers');
params.set('servers', '');
params.delete('family');
params.delete('quantization');
params.delete('format');
Expand Down
9 changes: 4 additions & 5 deletions frontend/tests/e2e/models.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,19 +64,18 @@ async function seedModel(
}
}

test('models tab requires a selected server before showing model cards', async ({ page, request }) => {
const server = await createInferenceServer(request);
test('models tab auto-selects an available server before showing model cards', async ({ page, request }) => {
const server = await createInferenceServer(request, { display_name: `! Auto-select ${Date.now()}` });
await seedModel(request, server.inference_server.server_id, {
model_id: '/lmstudio-community/Qwen3-Coder-30B-A3B-Instruct-MLX-6bit',
display_name: 'Qwen3 Coder',
format: 'MLX'
});

await page.goto('/catalog?tab=models');
await expect(page.getByRole('heading', { name: 'Select a server to see its models' })).toBeVisible();

await page.locator('.server-filter-row').filter({ hasText: server.inference_server.display_name }).click();
await expect(page.locator('.catalog-model-card').filter({ hasText: 'Qwen3 Coder' })).toBeVisible();
await expect(page.locator('.server-filter-row.is-selected').filter({ hasText: server.inference_server.display_name })).toBeVisible();
await expect(page).toHaveURL(new RegExp(`servers=${encodeURIComponent(server.inference_server.server_id)}`));

await archiveInferenceServer(request, server.inference_server.server_id);
});
Expand Down
Loading