Skip to content

[DT-3313] [3/6] Add filters to the AI Models and Workspaces tabs - #3938

Merged
kevinmarete merged 7 commits into
developfrom
km-dt-3313-model-workspace-filters
Sep 17, 2026
Merged

kevinmarete merged 7 commits into
developfrom
km-dt-3313-model-workspace-filters

Conversation

@kevinmarete

@kevinmarete kevinmarete commented Sep 15, 2026 •

Copy link
Copy Markdown
Contributor

Addresses

DT-3313 — 3 of 6, splitting #3926 per review. Stacked on #3936. Security risk: no.

Summary

Mechanical, following the pattern #3936 establishes. AI Models gains Format, License, Cloud and Tags; Workspaces gains Cloud and Access alongside the Tools and Platform it already had. Both grids gain a Cloud column, so the new filter acts on something the user can see.

Each new filter is a checkbox group whose options come from the full corpus (#3936), a matchAny clause, and a client-side row predicate. The predicate is not redundant: the Elasticsearch clauses only decide which studies enter the shared aggregation, so without it a study with one matching model contributes all of its models to the grid — and to the tab-count badge, which is derived from the same function.

Also carries the fix for the blank page reported on #3926. Studies indexed before Cloud became a creatable multi-select hold it as a bare string rather than an array, and the new chip column called .join on it, which threw and blanked the whole grid — only these two tabs, since they're the two with a Cloud column. cloud, tags, tools and trainedOnDatasets are now normalized through toStringArray, and the raw ES types say cloud?: string[] | string so the normalization can't be skipped. Verified against the dev corpus.

Mutation-checked: short-circuiting the modelCloud predicate fails 2 tests, and making toStringArray drop the bare-string case — i.e. reintroducing the blank-page bug — fails 4.

@github-actions

github-actions Bot commented Sep 15, 2026 •

Copy link
Copy Markdown
Contributor

Coverage Report for DUOS Coverage Report

Status Category Percentage Covered / Total
🔵 Lines 90.39% 12084 / 13368
🔵 Statements 89.89% 12901 / 14352
🔵 Functions 87.58% 3581 / 4089
🔵 Branches 82.73% 7531 / 9103
File Coverage
File Stmts Branches Functions Lines Uncovered Lines
Changed Files
src/components/data_library/filterRegistry.ts 86.8% 81.81% 96.82% 86.7% 183, 251, 311-318, 488-495, 582-586, 606-620, 636-645, 681-690, 699-713, 768, 802
src/components/data_library/assets/definition.ts 100% 100% 100% 100%
src/components/data_library/assets/modelAsset.ts 100% 86% 100% 100%
src/components/data_library/assets/workspaceAsset.ts 100% 84.78% 100% 100%
src/components/data_library/columns/modelColumns.tsx 92.3% 75% 87.5% 91.66% 54
src/components/data_library/columns/workspaceColumns.tsx 100% 75% 100% 100%
src/hooks/useLibraryPageState.ts 91.75% 78.33% 89.79% 91.2% 233, 236, 250, 287, 302, 322-326
src/hooks/useLibraryUrlState.ts 95.1% 92.55% 100% 94.81% 188, 199-201, 214, 221, 331
src/libs/dataLibraryFilterConfig.ts 100% 100% 100% 100%
src/types/elastic.ts 100% 100% 100% 100%
src/types/library.ts 100% 100% 100% 100%
Generated in workflow #7254 for commit 25a6e02 by the Vitest Coverage Report Action

@kevinmarete kevinmarete changed the title [DT-3313] Add filters to the AI Models and Workspaces tabs [DT-3313] [3/6] Add filters to the AI Models and Workspaces tabs Sep 15, 2026
@kevinmarete
kevinmarete force-pushed the km-dt-3313-model-workspace-filters branch from 3cc2985 to b91316e Compare September 15, 2026 23:45
@kevinmarete
kevinmarete requested a lite review from Copilot September 15, 2026 23:47

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Unresolved issues affect filter visibility, URL persistence, and global Cloud search.

Get a fresh assessment by requesting another Copilot review.

Pull request overview

Adds configuration-driven filters for AI Models and Workspaces, including Cloud columns and legacy scalar-value normalization.

Changes:

  • Adds filter definitions, corpus-derived options, query clauses, and row predicates.
  • Adds Cloud columns and normalizes scalar-or-array fields.
  • Extends types and tests for filtering, rendering, and normalization.

Outstanding review findings:

  • modelAsset.ts — moderate (1 vote): Include model Cloud values in global search.
  • workspaceAsset.ts — moderate (1 vote): Include workspace Cloud values in global search.
  • filterRegistry.ts — moderate (1 vote): Register all six filters for URL parsing and serialization.
  • filterRegistry.ts — nit (2 votes): Add query-clause path and multi-value OR assertions.
  • dataLibraryFilterConfig.ts — moderate (2 votes): Add the six filters to CHECKBOX_FILTER_KEYS and cover panel rendering.
  • library.ts — moderate (2 votes): Add the six filters to ARRAY_FILTER_PARAM_CONFIG with round-trip coverage.
File summaries
File Reviewed changes
test/components/data_library/LibraryFilters.spec.tsx Updates filter fixtures.
test/components/data_library/filterRegistry.spec.ts Tests filter registration.
test/components/data_library/columns/workspaceColumns.spec.tsx Tests workspace Cloud rendering.
test/components/data_library/columns/modelColumns.spec.tsx Tests model Cloud rendering.
test/components/data_library/assets/workspaceAsset.spec.ts Tests workspace normalization and predicates.
test/components/data_library/assets/modelAsset.spec.ts Tests model normalization and predicates.
src/types/library.ts Adds model and workspace filter state types.
src/types/elastic.ts Supports scalar or array Cloud values.
src/libs/dataLibraryFilterConfig.ts Registers visible filters.
src/hooks/useLibraryPageState.ts Derives full-corpus filter options.
src/components/data_library/filterRegistry.ts Defines filter controls and query clauses.
src/components/data_library/columns/workspaceColumns.tsx Adds the workspace Cloud column.
src/components/data_library/columns/modelColumns.tsx Adds the model Cloud column.
src/components/data_library/assets/workspaceAsset.ts Normalizes and filters workspace rows.
src/components/data_library/assets/modelAsset.ts Normalizes and filters model rows.
src/components/data_library/assets/definition.ts Adds shared string-array normalization.
Review details

Suppressed comments (3)

src/components/data_library/assets/modelAsset.ts:91

  • The new Cloud column is not represented in this asset's searchFields, which are folded into ALL_SEARCH_FIELDS for the library's global search. Searching for a provider such as AWS or GCP therefore cannot match a model through its Cloud value; add study.assets.models.cloud to the search fields as well as the filter/column integration.
          cloud: toStringArray(model.cloud),

src/components/data_library/assets/workspaceAsset.ts:101

  • The new Cloud column is not represented in this asset's searchFields, which are folded into ALL_SEARCH_FIELDS for the library's global search. Searching for a provider such as AWS or GCP therefore cannot match a workspace through its Cloud value; add study.assets.workspaces.cloud to the search fields as well as the filter/column integration.
          cloud: toStringArray(workspace.cloud),

src/components/data_library/filterRegistry.ts:50

  • The URL-state parameter registry in src/hooks/useLibraryUrlState.ts still omits all six new array keys from both parsing and serialization. Selecting any of these filters is therefore lost when the URL state re-parses, and a URL cannot restore one; add entries for these keys and a round-trip test.
  'modelFormat',
  'modelLicense',
  'modelCloud',
  'modelTags',
  'workspaceCloud',
  'workspaceAccess',
  • Files reviewed: 16/16 changed files
  • Comments generated: 3
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread src/libs/dataLibraryFilterConfig.ts
Comment thread src/types/library.ts
Comment thread src/components/data_library/filterRegistry.ts
@kevinmarete
kevinmarete force-pushed the km-dt-3313-full-corpus-options branch from 8ac602d to c045362 Compare September 15, 2026 23:53
@kevinmarete
kevinmarete force-pushed the km-dt-3313-model-workspace-filters branch from b91316e to bfac3ec Compare September 15, 2026 23:55
@kevinmarete
kevinmarete requested a lite review from Copilot September 16, 2026 00:14

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Approval recommended

Only minor test documentation and coverage nits remain.

Review details

Suppressed comments (2)

test/hooks/useLibraryUrlState.spec.tsx:160

  • This comment is now inverted: all six keys are present in ARRAY_FILTER_PARAM_CONFIG, so they do parse and serialize. Please update the explanation to describe the regression these tests guard against; as written it tells future maintainers that the implementation is still broken.
  // Registered in FilterState but absent from ARRAY_FILTER_PARAM_CONFIG, these
  // parse to nothing and never serialize, so a selection dies on reload.

test/hooks/useLibraryUrlState.spec.tsx:168

  • This test is named “round-trips” but only seeds the URL and asserts parsed state; it never calls updateState or inspects location.search, so a missing serializer entry would still pass all six cases. Add a serializer assertion for these keys (or split parsing and serialization cases) so the new URL mapping is covered in both directions.
  ])('round-trips %s through the URL', (key, value) => {
  • Files reviewed: 19/19 changed files
  • Comments generated: 0 new
  • Review effort level: Lite

@kevinmarete
kevinmarete force-pushed the km-dt-3313-full-corpus-options branch from bbb9e93 to 1bf6217 Compare September 16, 2026 00:27
@kevinmarete
kevinmarete force-pushed the km-dt-3313-model-workspace-filters branch from 2736cdb to 40a2720 Compare September 16, 2026 00:28
@kevinmarete
kevinmarete requested a lite review from Copilot September 16, 2026 00:40

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

Moderate findings remain for Cloud free-text search coverage and URL serialization test coverage.

Review details

Suppressed comments (7)

src/components/data_library/assets/modelAsset.ts:24

  • The new Cloud filter/column is not included in modelAsset.searchFields, so the library's free-text search (which uses the union in ALL_SEARCH_FIELDS) cannot find a model whose only matching value is its cloud provider. Add study.assets.models.cloud to the model search fields and cover it in the corresponding search-fields test.
  if (filters.modelCloud.length > 0 && !(model.cloud || []).some(cloud => filters.modelCloud.includes(cloud))) {

src/components/data_library/assets/workspaceAsset.ts:36

  • The new Cloud filter/column is not included in workspaceAsset.searchFields, so the library's free-text search (which uses the union in ALL_SEARCH_FIELDS) cannot find a workspace whose only matching value is its cloud provider. Add study.assets.workspaces.cloud to the workspace search fields and cover it in the corresponding search-fields test.
  if (filters.workspaceCloud.length > 0 && !(workspace.cloud || []).some(cloud => filters.workspaceCloud.includes(cloud))) {

src/components/data_library/columns/modelColumns.tsx:48

  • The new Cloud column is not included in modelAsset.searchFields. ALL_SEARCH_FIELDS is built from these arrays and reused for the global multi_match, so searching for a provider such as AWS will not find a model whose only matching field is Cloud, unlike the other model fields exposed here. Add study.assets.models.cloud to the model search fields.
  chipListColumn<ModelAsset>('cloud', 'Cloud', row => row.cloud || [], 130),

src/components/data_library/columns/workspaceColumns.tsx:91

  • The new Cloud column is not included in workspaceAsset.searchFields. Because ALL_SEARCH_FIELDS is assembled from those arrays and drives the global multi_match, a search for AWS/GCP will not find a workspace whose only matching field is Cloud, even though the other displayed workspace fields are searchable. Add study.assets.workspaces.cloud to the workspace search fields.
  chipListColumn<WorkspaceAsset>('cloud', 'Cloud', row => row.cloud || [], 130),

test/components/data_library/LibraryFilters.spec.tsx:632

  • This test comment refers to a CHECKBOX_FILTER_KEYS list that does not exist; LibraryFilters now derives checkbox keys from FilterState and checks section.control. The stale explanation can mislead maintainers about where a new filter must be registered.
// A key registered as a checkbox in filterRegistry still renders nothing unless
// LibraryFilters recognises it in CHECKBOX_FILTER_KEYS, so assert on the panel
// rather than on getFilterSectionsForAsset's output.

test/hooks/useLibraryUrlState.spec.tsx:168

  • This test is named as a URL round-trip but only parses an initial URL; it never calls updateState and checks the serialized search params. A regression in serializeArrayFiltersToUrl for any of these new keys would still pass, so exercise an update and assert the resulting URL as well.
  ])('round-trips %s through the URL', (key, value) => {

test/hooks/useLibraryUrlState.spec.tsx:160

  • This explanatory comment is now backwards: all six keys are present in ARRAY_FILTER_PARAM_CONFIG immediately above the test, and that is why the test succeeds. Update the comment to describe the URL round-trip regression being covered rather than claiming the keys are absent.
  // Registered in FilterState but absent from ARRAY_FILTER_PARAM_CONFIG, these
  // parse to nothing and never serialize, so a selection dies on reload.
  • Files reviewed: 18/18 changed files
  • Comments generated: 0 new
  • Review effort level: Lite

@kevinmarete
kevinmarete force-pushed the km-dt-3313-model-workspace-filters branch from 40a2720 to 4b01393 Compare September 16, 2026 01:06
@kevinmarete kevinmarete self-assigned this Sep 16, 2026
@kevinmarete
kevinmarete force-pushed the km-dt-3313-model-workspace-filters branch from f91e841 to bd9f2bb Compare September 16, 2026 01:11
@kevinmarete
kevinmarete requested a lite review from Copilot September 16, 2026 01:36

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Approval recommended

No unresolved blocking issues were identified.

Review details
  • Files reviewed: 18/18 changed files
  • Comments generated: 0 new
  • Review effort level: Lite

@kevinmarete
kevinmarete force-pushed the km-dt-3313-model-workspace-filters branch from 996d185 to b60e18b Compare September 16, 2026 01:50
@kevinmarete
kevinmarete force-pushed the km-dt-3313-full-corpus-options branch from fe3f3e3 to d518a95 Compare September 16, 2026 01:57
@kevinmarete
kevinmarete force-pushed the km-dt-3313-model-workspace-filters branch 2 times, most recently from 00e0d51 to e249c6d Compare September 16, 2026 02:23
@kevinmarete
kevinmarete force-pushed the km-dt-3313-full-corpus-options branch from 6089499 to f2c1770 Compare September 16, 2026 02:49
@kevinmarete
kevinmarete force-pushed the km-dt-3313-model-workspace-filters branch from e249c6d to 0ab07cc Compare September 16, 2026 02:54
@kevinmarete
kevinmarete force-pushed the km-dt-3313-model-workspace-filters branch from 0ab07cc to fffe7fd Compare September 16, 2026 02:55
@kevinmarete
kevinmarete marked this pull request as ready for review September 16, 2026 03:08
@kevinmarete
kevinmarete requested a review from a team as a code owner September 16, 2026 03:08
@kevinmarete
kevinmarete requested review from fboulnois and otchet-broad and removed request for a team September 16, 2026 03:08
@kevinmarete
kevinmarete force-pushed the km-dt-3313-model-workspace-filters branch from fffe7fd to 1b19568 Compare September 16, 2026 15:51

@fboulnois fboulnois left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few suggestions identified with Claude:

1. New model filters use case-sensitive exact matching on free-text fields: src/components/data_library/assets/modelAsset.ts:14

The new model filters (modelFormat, modelLicense, modelCloud, modelTags) re-check rows with exact, case-sensitive includes, but all four are free-text fields — AiModelAddEdit renders format/license as plain text inputs, and tags/cloud as creatable selects.

Consequence: "PyTorch" and "pytorch" become two separate checkboxes; selecting one drops the other's rows, even though the analyzed match_phrase clause already admitted both studies.

Every other free-text/corpus-derived filter in the repo uses includesIgnoreCase (workspacePlatform, clinicalTrialRegistry, clinicalTrialInterventionType, biospecimenDataUse); only enum-backed keys use exact includes.

Fix: use includesIgnoreCase for these four keys.

2. workspaceAccess uses exact matching on an unvalidated free-text field: src/components/data_library/assets/workspaceAsset.ts:40

Same class as above, but sharper: workspaceAccess uses exact includes on the line immediately after workspacePlatform uses includesIgnoreCase. access is a bare FormField with no options and no validator at all — the most variance-prone field of the set.

Fix: use includesIgnoreCase here too.

3. modelCloud and workspaceCloud share the label 'Cloud': src/components/data_library/filterRegistry.ts:534

Both keys label as 'Cloud'. On the Workspaces tab, a carried-over model filter renders as "Cloud: AWS" under "Filters from other views", visually identical to the tab's own unchecked "Cloud" section.

The repo already disambiguates its one other label collision ('Datasets Cited (Presentations)?' vs '(Publications)?').

Fix: disambiguate one of the two labels.

@kevinmarete

Copy link
Copy Markdown
Contributor Author

All three fixed in ec18dc3e, thanks.

1 & 2. includesIgnoreCase for modelFormat, modelLicense, modelCloud, modelTags and workspaceAccess. Added a local copy of the helper rather than hoisting it — that would have reached into clinicalTrialAsset/biospecimenAsset, which belong to an earlier PR in the stack. Four tests pin the casing behaviour.
3. Now Cloud (AI Models) and Cloud (Workspaces); I qualified both, since leaving either bare still reads ambiguously on the other tab.

The same collision class turned up twice more downstream — Access/Format on #3939 and Status on #3940, both fixed there, with a test that fails on any new duplicate label.

@otchet-broad otchet-broad left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

Base automatically changed from km-dt-3313-full-corpus-options to develop September 17, 2026 17:40
kevinmarete and others added 7 commits September 17, 2026 13:44
Models gains Format, License, Cloud and Tags; Workspaces gains Cloud and
Access alongside the Tools and Platform it already had. Both grids gain a
Cloud column so the new filter has something visible to act on.

The Elasticsearch clauses only decide which studies enter the shared
aggregation, so each row is re-checked client-side — otherwise a study with
one matching model would contribute all of its models to the grid and to the
tab-count badge derived from the same function.

`cloud`, `tags`, `tools` and `trainedOnDatasets` are normalized to arrays.
Studies indexed before Cloud became a creatable multi-select carry a bare
string, and the chip column's `.join` threw on those, blanking the whole
grid. The raw ES types now say `cloud?: string[] | string` so the
normalization cannot be skipped.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Both were registered in filterRegistry but missing from the two lists that
actually wire a filter up, so they were dead on arrival:

- `CHECKBOX_FILTER_KEYS` in LibraryFilters gates rendering, and an
  unrecognized key falls through to `null` — none of the six sections
  appeared in the panel.
- `ARRAY_FILTER_PARAM_CONFIG` in useLibraryUrlState drives both parse and
  serialize, so a selection never reached the URL and died on reload.

The existing tests asserted `getFilterSectionsForAsset`'s output, which was
correct throughout, and so passed either way. They now assert the rendered
panel and a URL round-trip instead, plus the Elasticsearch field path each
clause targets — a wrong `study.assets.*` path type-checks and silently
matches nothing.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Option lists are built from trimmed values while the new predicates compared
raw ones, so selecting an option built from `"  ONNX "` dropped the only row
that produced it. The transforms now trim, which keeps rows, options and the
grid on the same values; the older tools/platform filters never hit this
because `includesIgnoreCase` does a substring match.

Cloud was displayed and filterable but absent from `searchFields`, so a
free-text search for a provider could not find a model or workspace whose
only matching field was Cloud.

The URL test claimed a round trip while only parsing, and its comment claimed
the keys were unregistered; it now asserts parse, serialize and clear.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
format, license, cloud, tags and access are all free text — plain inputs or
creatable selects — so the corpus can carry "PyTorch" and "pytorch" as two
options. The analyzed match_phrase clause admits both studies, but the row pass
used exact `includes`, so checking one dropped the other's rows.

Use `includesIgnoreCase`, as every other free-text filter here already does.
Disambiguate the two 'Cloud' labels while they are in reach: a carried-over
model filter rendered as a chip identical to the Workspaces tab's own section.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@kevinmarete
kevinmarete force-pushed the km-dt-3313-model-workspace-filters branch from ec18dc3 to 25a6e02 Compare September 17, 2026 17:50
@sonarqubecloud

Copy link
Copy Markdown

@kevinmarete
kevinmarete merged commit 996f16a into develop Sep 17, 2026
16 checks passed
@kevinmarete
kevinmarete deleted the km-dt-3313-model-workspace-filters branch September 17, 2026 17:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants