From 4710ef78b1b0a4a0462c93a7c19234dd3065a7ab Mon Sep 17 00:00:00 2001 From: Nathan Walston Date: Fri, 12 Jun 2026 11:52:34 -0700 Subject: [PATCH 1/6] feat(studio): add column visibility toggle to test cases table Lifts the EditColumnsMenu pattern from ExperimentGroupDataView onto ExperimentSessionsDataView. All columns start visible; users can toggle them via the Columns dropdown in the table toolbar. Signed-off-by: Nathan Walston --- .../ExperimentSessionsDataView/index.tsx | 20 +++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/web/packages/studio/src/components/dataViews/ExperimentSessionsDataView/index.tsx b/web/packages/studio/src/components/dataViews/ExperimentSessionsDataView/index.tsx index 981b8792bc..d526771f64 100644 --- a/web/packages/studio/src/components/dataViews/ExperimentSessionsDataView/index.tsx +++ b/web/packages/studio/src/components/dataViews/ExperimentSessionsDataView/index.tsx @@ -1,7 +1,10 @@ // SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 -import { Root as DataViewRoot } from '@nemo/common/src/components/DataView/internal'; +import { + Root as DataViewRoot, + EditColumnsMenu, +} from '@nemo/common/src/components/DataView/internal'; import { StudioDataView } from '@nemo/common/src/components/DataView/StudioDataView'; import { RelativeTime } from '@nemo/common/src/components/RelativeTime'; import { StatusBadge } from '@nemo/common/src/components/StatusBadge'; @@ -14,6 +17,7 @@ import { Empty } from '@studio/components/dataViews/ExperimentSessionsDataView/E import { useWorkspaceFromPath } from '@studio/hooks/useWorkspaceFromPath'; import { tooltipClassName } from '@studio/styles/common'; import { keepPreviousData } from '@tanstack/react-query'; +import { Columns3 } from 'lucide-react'; import { type ComponentProps, type FC, useMemo } from 'react'; type SessionRow = ExperimentSessionResponse & { _rowId: string }; @@ -33,7 +37,7 @@ export const ExperimentSessionsDataView: FC = ( experimentGroupName, }) => { const workspace = useWorkspaceFromPath(); - const dataViewState = useStudioDataViewState({}); + const dataViewState = useStudioDataViewState({ columnVisibility: {} }); const { data: experiment } = useGetExperiment(workspace, experimentName); const page = dataViewState.pagination.state.pageIndex + 1; @@ -174,6 +178,18 @@ export const ExperimentSessionsDataView: FC = ( } + > + <> + + Columns + + + } attributes={{ DataViewRoot: { data: tableData, From 6c6938e09cd4213fa11cf62333e8bb4bea830592 Mon Sep 17 00:00:00 2001 From: Nathan Walston Date: Fri, 12 Jun 2026 12:03:16 -0700 Subject: [PATCH 2/6] feat(studio): add column visibility, search, and status filter to test cases table Lifts the EditColumnsMenu pattern from ExperimentGroupDataView onto ExperimentSessionsDataView. Adds a search bar (by test_case_id) and a status column filter, both wired through to the list sessions API. Signed-off-by: Nathan Walston --- .../ExperimentSessionsDataView/index.tsx | 22 ++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/web/packages/studio/src/components/dataViews/ExperimentSessionsDataView/index.tsx b/web/packages/studio/src/components/dataViews/ExperimentSessionsDataView/index.tsx index d526771f64..c8ecb50736 100644 --- a/web/packages/studio/src/components/dataViews/ExperimentSessionsDataView/index.tsx +++ b/web/packages/studio/src/components/dataViews/ExperimentSessionsDataView/index.tsx @@ -11,7 +11,10 @@ import { StatusBadge } from '@nemo/common/src/components/StatusBadge'; import { useStudioDataViewState } from '@nemo/common/src/hooks/useStudioDataViewState'; import { snakeCaseToTitleCase } from '@nemo/common/src/utils/formatters'; import { useGetExperiment, useListExperimentSessions } from '@nemo/sdk/generated/platform/api'; -import type { ExperimentSessionResponse } from '@nemo/sdk/generated/platform/schema'; +import type { + ExperimentSessionFilter, + ExperimentSessionResponse, +} from '@nemo/sdk/generated/platform/schema'; import { Text, Tooltip } from '@nvidia/foundations-react-core'; import { Empty } from '@studio/components/dataViews/ExperimentSessionsDataView/Empty'; import { useWorkspaceFromPath } from '@studio/hooks/useWorkspaceFromPath'; @@ -37,7 +40,7 @@ export const ExperimentSessionsDataView: FC = ( experimentGroupName, }) => { const workspace = useWorkspaceFromPath(); - const dataViewState = useStudioDataViewState({ columnVisibility: {} }); + const dataViewState = useStudioDataViewState({ columnVisibility: {} }); const { data: experiment } = useGetExperiment(workspace, experimentName); const page = dataViewState.pagination.state.pageIndex + 1; @@ -46,7 +49,16 @@ export const ExperimentSessionsDataView: FC = ( const { data: sessionsResponse, isLoading } = useListExperimentSessions( workspace, experimentName, - { page, page_size: pageSize }, + { + page, + page_size: pageSize, + filter: { + ...dataViewState.apiFilter.filter, + ...(dataViewState.debouncedSearchBar && { + test_case_id: dataViewState.debouncedSearchBar, + }), + }, + }, { query: { placeholderData: keepPreviousData } } ); @@ -134,6 +146,9 @@ export const ExperimentSessionsDataView: FC = ( accessor('status', { header: 'Status', enableSorting: false, + meta: { + filter: { type: 'text', label: 'Status', placeholder: 'Filter by status' }, + }, cell: ({ row }) => , }), accessor( @@ -178,6 +193,7 @@ export const ExperimentSessionsDataView: FC = ( Date: Fri, 12 Jun 2026 12:16:47 -0700 Subject: [PATCH 3/6] fix(studio): use single-select status filter with client-side instant feedback Replaces the text input with a radio-select of the four known API status values (labels match the StatusBadge display). Client-side filtering is applied immediately against the current page while the debounced API request catches up. Signed-off-by: Nathan Walston --- .../ExperimentSessionsDataView/index.tsx | 24 +++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/web/packages/studio/src/components/dataViews/ExperimentSessionsDataView/index.tsx b/web/packages/studio/src/components/dataViews/ExperimentSessionsDataView/index.tsx index c8ecb50736..11906feb4a 100644 --- a/web/packages/studio/src/components/dataViews/ExperimentSessionsDataView/index.tsx +++ b/web/packages/studio/src/components/dataViews/ExperimentSessionsDataView/index.tsx @@ -74,6 +74,17 @@ export const ExperimentSessionsDataView: FC = ( [sessionsData] ); + // Client-side status filter for instant feedback while the debounced API request catches up. + const immediateStatusFilter = dataViewState.columnFiltering.state.find((f) => f.id === 'status') + ?.value as string | undefined; + const visibleTableData = useMemo( + () => + immediateStatusFilter + ? tableData.filter((row) => row.status === immediateStatusFilter) + : tableData, + [tableData, immediateStatusFilter] + ); + // One column per evaluator. The experiment's `evaluator_names` is the // authoritative, stable set across all sessions; we union in any score keys // present in the current page's data so a column never goes missing while the @@ -147,7 +158,16 @@ export const ExperimentSessionsDataView: FC = ( header: 'Status', enableSorting: false, meta: { - filter: { type: 'text', label: 'Status', placeholder: 'Filter by status' }, + filter: { + type: 'single-select', + label: 'Status', + options: [ + { value: 'success', label: 'Completed' }, + { value: 'error', label: 'Error' }, + { value: 'cancelled', label: 'Cancelled' }, + { value: 'unknown', label: 'Unknown' }, + ], + }, }, cell: ({ row }) => , }), @@ -208,7 +228,7 @@ export const ExperimentSessionsDataView: FC = ( } attributes={{ DataViewRoot: { - data: tableData, + data: visibleTableData, totalCount, requestStatus: isLoading && !sessionsData ? 'loading' : undefined, }, From 605c141c60db405d9f6b7f103a4bfbcce06b1702 Mon Sep 17 00:00:00 2001 From: Nathan Walston Date: Fri, 12 Jun 2026 12:25:42 -0700 Subject: [PATCH 4/6] fix(studio): update agent_names/agent_versions to singular after API change Signed-off-by: Nathan Walston --- .../dataViews/ExperimentGroupDataView/index.tsx | 10 ++++------ .../ExperimentDetailRoute/ExperimentDetailMetrics.tsx | 4 ++-- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/web/packages/studio/src/components/dataViews/ExperimentGroupDataView/index.tsx b/web/packages/studio/src/components/dataViews/ExperimentGroupDataView/index.tsx index 97f2f0e417..e235c7a3e8 100644 --- a/web/packages/studio/src/components/dataViews/ExperimentGroupDataView/index.tsx +++ b/web/packages/studio/src/components/dataViews/ExperimentGroupDataView/index.tsx @@ -132,15 +132,13 @@ export const ExperimentGroupDataView: FC = ({ ); }, }), - accessor((original) => original.agent_names?.join(', '), { - id: 'agent_names', - header: 'Agent Names', + accessor('agent_name', { + header: 'Agent Name', enableSorting: false, cell: ({ getValue }) => {getValue() || '-'}, }), - accessor((original) => original.agent_versions?.join(', '), { - id: 'agent_versions', - header: 'Agent Versions', + accessor('agent_version', { + header: 'Agent Version', enableSorting: false, cell: ({ getValue }) => {getValue() || '-'}, }), diff --git a/web/packages/studio/src/routes/ExperimentDetailRoute/ExperimentDetailMetrics.tsx b/web/packages/studio/src/routes/ExperimentDetailRoute/ExperimentDetailMetrics.tsx index 14f48d6323..f3211635e4 100644 --- a/web/packages/studio/src/routes/ExperimentDetailRoute/ExperimentDetailMetrics.tsx +++ b/web/packages/studio/src/routes/ExperimentDetailRoute/ExperimentDetailMetrics.tsx @@ -27,8 +27,8 @@ export const ExperimentDetailMetrics: FC = ({ expe return (
From ae6f284b2ab45d183c7934adca0515710865a516 Mon Sep 17 00:00:00 2001 From: Nathan Walston Date: Fri, 12 Jun 2026 12:49:59 -0700 Subject: [PATCH 5/6] =?UTF-8?q?revert(studio):=20restore=20agent=5Fnames/a?= =?UTF-8?q?gent=5Fversions=20=E2=80=94=20SDK=20is=20generated=20from=20cur?= =?UTF-8?q?rent=20API=20schema?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Nathan Walston --- .../dataViews/ExperimentGroupDataView/index.tsx | 10 ++++++---- .../ExperimentDetailRoute/ExperimentDetailMetrics.tsx | 4 ++-- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/web/packages/studio/src/components/dataViews/ExperimentGroupDataView/index.tsx b/web/packages/studio/src/components/dataViews/ExperimentGroupDataView/index.tsx index e235c7a3e8..97f2f0e417 100644 --- a/web/packages/studio/src/components/dataViews/ExperimentGroupDataView/index.tsx +++ b/web/packages/studio/src/components/dataViews/ExperimentGroupDataView/index.tsx @@ -132,13 +132,15 @@ export const ExperimentGroupDataView: FC = ({ ); }, }), - accessor('agent_name', { - header: 'Agent Name', + accessor((original) => original.agent_names?.join(', '), { + id: 'agent_names', + header: 'Agent Names', enableSorting: false, cell: ({ getValue }) => {getValue() || '-'}, }), - accessor('agent_version', { - header: 'Agent Version', + accessor((original) => original.agent_versions?.join(', '), { + id: 'agent_versions', + header: 'Agent Versions', enableSorting: false, cell: ({ getValue }) => {getValue() || '-'}, }), diff --git a/web/packages/studio/src/routes/ExperimentDetailRoute/ExperimentDetailMetrics.tsx b/web/packages/studio/src/routes/ExperimentDetailRoute/ExperimentDetailMetrics.tsx index f3211635e4..14f48d6323 100644 --- a/web/packages/studio/src/routes/ExperimentDetailRoute/ExperimentDetailMetrics.tsx +++ b/web/packages/studio/src/routes/ExperimentDetailRoute/ExperimentDetailMetrics.tsx @@ -27,8 +27,8 @@ export const ExperimentDetailMetrics: FC = ({ expe return (
From aed60d62d2e5b652f5349659313a3f36756e76f9 Mon Sep 17 00:00:00 2001 From: Nathan Walston Date: Fri, 12 Jun 2026 13:23:31 -0700 Subject: [PATCH 6/6] fix(studio): update search placeholder and filtered empty state for test cases table Signed-off-by: Nathan Walston --- .../ExperimentSessionsDataView/index.tsx | 37 ++++++++++++++++--- 1 file changed, 31 insertions(+), 6 deletions(-) diff --git a/web/packages/studio/src/components/dataViews/ExperimentSessionsDataView/index.tsx b/web/packages/studio/src/components/dataViews/ExperimentSessionsDataView/index.tsx index 11906feb4a..61a05fdb85 100644 --- a/web/packages/studio/src/components/dataViews/ExperimentSessionsDataView/index.tsx +++ b/web/packages/studio/src/components/dataViews/ExperimentSessionsDataView/index.tsx @@ -8,6 +8,7 @@ import { import { StudioDataView } from '@nemo/common/src/components/DataView/StudioDataView'; import { RelativeTime } from '@nemo/common/src/components/RelativeTime'; import { StatusBadge } from '@nemo/common/src/components/StatusBadge'; +import { TableEmptyState } from '@nemo/common/src/components/TableEmptyState'; import { useStudioDataViewState } from '@nemo/common/src/hooks/useStudioDataViewState'; import { snakeCaseToTitleCase } from '@nemo/common/src/utils/formatters'; import { useGetExperiment, useListExperimentSessions } from '@nemo/sdk/generated/platform/api'; @@ -232,13 +233,37 @@ export const ExperimentSessionsDataView: FC = ( totalCount, requestStatus: isLoading && !sessionsData ? 'loading' : undefined, }, + DataViewSearchBar: { placeholder: 'Search case...' }, DataViewTableContent: { - renderEmptyState: () => ( - '} - /> - ), + renderEmptyState: () => { + const hasActiveFilters = + !!dataViewState.searchBar.state || dataViewState.columnFiltering.state.length > 0; + if (hasActiveFilters) { + return ( + + Change your filters and try again, or{' '} + + . + + } + /> + ); + } + return ( + '} + /> + ); + }, }, }} />