Skip to content

fix(studio): Performance optimization for ModelSelect model fetch - #950

Merged
steramae-nvidia merged 3 commits into
mainfrom
steramae/model-fetch-perf
Jul 29, 2026
Merged

fix(studio): Performance optimization for ModelSelect model fetch#950
steramae-nvidia merged 3 commits into
mainfrom
steramae/model-fetch-perf

Conversation

@steramae-nvidia

@steramae-nvidia steramae-nvidia commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

Signed-off-by: Sean Teramae steramae@nvidia.com

Summary by CodeRabbit

  • New Features
    • Added workspace-aware model search and selection, including URN-based preselection.
    • Introduced a virtualized, debounced, progressively paged model dropdown for smoother browsing.
    • Updated Studio model pickers across chat, compare, config, and builder flows to use the workspace-scoped search experience.
  • Bug Fixes
    • Improved progressive paging so results don’t stall when client-side filtering removes a page.
    • Refined compare preselection and routing behavior for better accuracy.
  • Tests
    • Added/expanded coverage for searching, paging, dropdown virtualization, and builder template auto-fill.

@steramae-nvidia
steramae-nvidia requested review from a team as code owners July 28, 2026 18:28
@steramae-nvidia

steramae-nvidia commented Jul 28, 2026

Copy link
Copy Markdown
Contributor Author

This change is part of the following stack:

Change managed by git-spice.

@github-actions github-actions Bot added the fix label Jul 28, 2026
@github-actions

github-actions Bot commented Jul 28, 2026

Copy link
Copy Markdown
Contributor
Suite Lines Covered Line Rate Branch Rate
Unit Tests 28023/35820 78.2% 62.7%
Integration Tests 16264/34538 47.1% 19.6%

@coderabbitai

coderabbitai Bot commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Model selection now uses workspace-scoped, paginated search with entity lookup and virtualized dropdown rendering. Studio components consume shared search state, provider filters, and workspace props, while Data Designer autofills seeded models through workspace candidate resolution.

Changes

Model selection flow

Layer / File(s) Summary
Search and entity hooks
web/packages/common/src/api/models/*
Adds entity lookup and paginated model search with server filters, client predicates, grouping, and automatic page advancement.
Searchable virtualized dropdown
web/packages/common/src/components/ModelSelectV2/*
Adds debounced search, virtualized rows, incremental loading, entity-backed selections, deferred details, and progressive-loading coverage.
Studio selector integrations
web/packages/studio/src/components/*, web/packages/studio/src/routes/DeploymentsListRoute/*, web/packages/studio/src/components/sidePanels/*
Migrates model pickers to workspace-scoped search and removes preloaded model-group props.
Builder model autofill
web/packages/studio/src/routes/DataDesignerJobBuildRoute/*
Replaces route-level model loading with workspace-based candidate fetching and seeded-model autofill.
Model comparison routing
web/packages/studio/src/routes/ModelCompareRoute/*
Uses model search for availability and entity lookup for query-parameter preselection.

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant WorkspaceModelSelect
  participant useModelSearch
  participant ModelDropdownList
  participant ModelCompareRoute

  User->>WorkspaceModelSelect: Open and search models
  WorkspaceModelSelect->>useModelSearch: Request workspace-scoped results
  useModelSearch-->>WorkspaceModelSelect: Groups, loading, and pagination state
  ModelDropdownList->>useModelSearch: Load additional page
  useModelSearch-->>ModelDropdownList: Append model results
  ModelCompareRoute->>useModelSearch: Check model availability
  ModelCompareRoute->>useModelEntity: Resolve query model
  useModelEntity-->>ModelCompareRoute: Return selected entity
Loading

Possibly related PRs

Suggested reviewers: htolentino-nvidia, marcusds, aray12, mckornfield

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title matches the main theme: optimizing ModelSelect model fetching in studio.
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Fix failing CI checks
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch steramae/model-fetch-perf

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 6

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
web/packages/studio/src/components/ModelConfigPanel/index.tsx (1)

114-125: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Missing hasModelProvider filter allows selecting providerless models.

Unlike ModelChatPanel/ModelColumnSelect, this WorkspaceModelSelect has no include={hasModelProvider}. handleModelChange then sets provider via providerForSelection, which yields '' for such models — and validateModels never checks provider presence, so the job build proceeds and fails at submission time with "the model does not have a provider" (per the comment in models.ts). See consolidated comment.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web/packages/studio/src/components/ModelConfigPanel/index.tsx` around lines
114 - 125, Update the WorkspaceModelSelect usage in ModelConfigPanel to pass
hasModelProvider through its include filter, matching ModelChatPanel and
ModelColumnSelect, so providerless models cannot be selected and
handleModelChange only receives valid provider-backed models.
🧹 Nitpick comments (3)
web/packages/studio/src/routes/ModelCompareRoute/index.tsx (1)

4-8: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Group external and internal imports separately.

The new @nemo/common imports are interleaved with external packages and @studio aliases. As per coding guidelines, group external libraries first, then internal modules, then relative imports.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web/packages/studio/src/routes/ModelCompareRoute/index.tsx` around lines 4 -
8, Reorder the imports in ModelCompareRoute so external package imports come
first, followed by internal `@nemo/common` and `@studio` alias imports, with
relative imports last. Keep the imported symbols unchanged.

Source: Coding guidelines

web/packages/studio/src/routes/DataDesignerJobBuildRoute/useJobBuilder.ts (1)

109-138: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Redundant duplicate "first page" fetches across pending models.

fetchAutoFillCandidates is called once per pending model, and each call independently re-fetches the same unfiltered first page of the workspace. With multiple pending models this fires duplicate identical requests. Consider fetching the shared first page once and reusing it across all pending models.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web/packages/studio/src/routes/DataDesignerJobBuildRoute/useJobBuilder.ts`
around lines 109 - 138, Update the auto-fill effect around
fetchAutoFillCandidates so the workspace’s unfiltered first-page candidates are
fetched once and reused for every pending model, while preserving
preferred-model-specific resolution behavior. Avoid issuing duplicate identical
requests from the pending.map flow, and continue applying each model’s resolved
value through the existing setValue logic.
web/packages/common/src/api/models/useModelEntity.ts (1)

22-31: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add tests for useModelEntity's enabled-gating logic.

Logic is correct but untested (unlike useModelSearch.test.tsx). The enabled && !!parts?.workspace && !!parts?.name gate and malformed-URN handling are exactly the kind of boolean logic that regresses silently.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web/packages/common/src/api/models/useModelEntity.ts` around lines 22 - 31,
Add tests for useModelEntity covering enabled=true with a valid URN,
enabled=false, and malformed or missing URNs. Assert that useModelsGetModel
receives the parsed workspace/name and that its query.enabled reflects enabled
&& !!parts?.workspace && !!parts?.name, including disabled calls when required.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@web/packages/studio/src/components/PromptTuningForm/ModelDetailsSection/index.tsx`:
- Around line 41-45: Surface the useModelSearch error in ModelDetailsSection by
applying the existing useSetFieldErrorOnApiError pattern used by
JudgeModelSelect. Pass the search error and the appropriate model field/form
context so failed prompt-tuneable model searches set a visible field error
instead of being silently ignored.

In `@web/packages/studio/src/components/sidePanels/MetricRunSidePanel/index.tsx`:
- Around line 164-165: Update the model search flow in the MetricRunSidePanel
component to surface modelSearch.error to the user, matching the existing
error-handling behavior used by ModelDetailsSection. Preserve the current
workspace, open, and modelSelectOpen search enablement logic.

In `@web/packages/studio/src/routes/DataDesignerJobBuildRoute/models.ts`:
- Around line 55-89: Update fetchAutoFillCandidates so both preferred matches
and the initial model page only include entities with a non-empty
model_providers value before grouping them. Ensure resolveTemplateModel can only
receive provider-backed models, while preserving the existing deduplication and
workspace grouping behavior.

In `@web/packages/studio/src/routes/ModelCompareRoute/index.tsx`:
- Around line 51-75: Update the preselection commit in the effect using
preselectModel so it only calls setModels when preselectModel.workspace matches
workspace and hasModelProvider(preselectModel) returns true. Keep the existing
pending and didPreselectRef behavior, and mark preselection complete after the
model has been validated or rejected.
- Around line 37-40: Update the ModelCompareRoute logic around availableModels
and hasNoModels to handle availableModels.error separately from an exhausted
successful search. Render the error/retry state when a search fails, and set the
no-models condition only when loading is complete, no error exists, the search
is exhausted, and models is empty.
- Around line 62-75: Update the preselection logic in the useEffect around
didPreselectRef so the guard is reset when the URL’s model selection changes,
including transitions from no modelParam to a modelParam and between different
model values. Track the last processed preselectModel URN or equivalent
selection state, while preserving the existing early returns and model update
behavior for already-processed selections.

---

Outside diff comments:
In `@web/packages/studio/src/components/ModelConfigPanel/index.tsx`:
- Around line 114-125: Update the WorkspaceModelSelect usage in ModelConfigPanel
to pass hasModelProvider through its include filter, matching ModelChatPanel and
ModelColumnSelect, so providerless models cannot be selected and
handleModelChange only receives valid provider-backed models.

---

Nitpick comments:
In `@web/packages/common/src/api/models/useModelEntity.ts`:
- Around line 22-31: Add tests for useModelEntity covering enabled=true with a
valid URN, enabled=false, and malformed or missing URNs. Assert that
useModelsGetModel receives the parsed workspace/name and that its query.enabled
reflects enabled && !!parts?.workspace && !!parts?.name, including disabled
calls when required.

In `@web/packages/studio/src/routes/DataDesignerJobBuildRoute/useJobBuilder.ts`:
- Around line 109-138: Update the auto-fill effect around
fetchAutoFillCandidates so the workspace’s unfiltered first-page candidates are
fetched once and reused for every pending model, while preserving
preferred-model-specific resolution behavior. Avoid issuing duplicate identical
requests from the pending.map flow, and continue applying each model’s resolved
value through the existing setValue logic.

In `@web/packages/studio/src/routes/ModelCompareRoute/index.tsx`:
- Around line 4-8: Reorder the imports in ModelCompareRoute so external package
imports come first, followed by internal `@nemo/common` and `@studio` alias imports,
with relative imports last. Keep the imported symbols unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: ec58eb88-1701-4dc0-a011-d496ba2a78f3

📥 Commits

Reviewing files that changed from the base of the PR and between 48a2614 and 4614483.

📒 Files selected for processing (35)
  • web/packages/common/src/api/models/useModelEntity.ts
  • web/packages/common/src/api/models/useModelSearch.test.tsx
  • web/packages/common/src/api/models/useModelSearch.ts
  • web/packages/common/src/components/ModelSelectV2/ModelDropdown.test.tsx
  • web/packages/common/src/components/ModelSelectV2/ModelDropdown.tsx
  • web/packages/common/src/components/ModelSelectV2/ModelDropdownItem.tsx
  • web/packages/common/src/components/ModelSelectV2/ModelDropdownList.tsx
  • web/packages/common/src/components/ModelSelectV2/ModelSelectV2.stories.tsx
  • web/packages/common/src/components/ModelSelectV2/ModelSelectV2.tsx
  • web/packages/common/src/components/ModelSelectV2/WorkspaceModelSelect.tsx
  • web/packages/common/src/components/ModelSelectV2/index.tsx
  • web/packages/common/src/components/ModelSelectV2/types.ts
  • web/packages/common/src/utils/models.ts
  • web/packages/studio/src/components/AddModelPalette/AddModelPalette.stories.tsx
  • web/packages/studio/src/components/AddModelPalette/index.tsx
  • web/packages/studio/src/components/ModelChatPanel/ModelChatPanel.test.tsx
  • web/packages/studio/src/components/ModelChatPanel/index.tsx
  • web/packages/studio/src/components/ModelCompareChat/index.tsx
  • web/packages/studio/src/components/ModelComparePrompts/ModelColumnSelect.tsx
  • web/packages/studio/src/components/ModelComparePrompts/ModelCompareTable.tsx
  • web/packages/studio/src/components/ModelComparePrompts/index.tsx
  • web/packages/studio/src/components/ModelComparePrompts/types.ts
  • web/packages/studio/src/components/ModelConfigPanel/index.tsx
  • web/packages/studio/src/components/NewCustomizationForm/ModelSelectionSection.tsx
  • web/packages/studio/src/components/PromptTuningForm/ModelDetailsSection/index.tsx
  • web/packages/studio/src/components/evaluation/JudgeModelSelect.tsx
  • web/packages/studio/src/components/sidePanels/MetricRunSidePanel/index.tsx
  • web/packages/studio/src/routes/DataDesignerJobBuildRoute/BuilderConfigPane.tsx
  • web/packages/studio/src/routes/DataDesignerJobBuildRoute/BuilderPalette.tsx
  • web/packages/studio/src/routes/DataDesignerJobBuildRoute/index.tsx
  • web/packages/studio/src/routes/DataDesignerJobBuildRoute/models.test.ts
  • web/packages/studio/src/routes/DataDesignerJobBuildRoute/models.ts
  • web/packages/studio/src/routes/DataDesignerJobBuildRoute/useJobBuilder.ts
  • web/packages/studio/src/routes/DeploymentsListRoute/CreateDeploymentSidePanel/WorkspaceSourceFields.tsx
  • web/packages/studio/src/routes/ModelCompareRoute/index.tsx
💤 Files with no reviewable changes (2)
  • web/packages/studio/src/components/ModelComparePrompts/types.ts
  • web/packages/studio/src/components/ModelCompareChat/index.tsx

Comment thread web/packages/studio/src/components/PromptTuningForm/ModelDetailsSection/index.tsx Outdated
Comment thread web/packages/studio/src/routes/DataDesignerJobBuildRoute/models.ts
Comment thread web/packages/studio/src/routes/ModelCompareRoute/index.tsx
Comment thread web/packages/studio/src/routes/ModelCompareRoute/index.tsx Outdated
Comment thread web/packages/studio/src/routes/ModelCompareRoute/index.tsx Outdated

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 1

🧹 Nitpick comments (1)
web/packages/common/src/components/ModelSelectV2/ModelDropdownItem.tsx (1)

125-168: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Avoid model.adapters! — restructure for narrowing instead of asserting.

hasAdapters aliases model.adapters && model.adapters.length > 0, but TS's aliased-condition narrowing doesn't propagate to non-readonly object properties, hence the ! at line 168. Extracting model.adapters into a local const first narrows normally without an assertion, per guideline: "Use type assertions sparingly. Prefer type guards and narrowing in TypeScript."

♻️ Proposed fix
-  const hasAdapters = !hideAdapters && model.adapters && model.adapters.length > 0;
+  const adapters = hideAdapters ? undefined : model.adapters;
+  const hasAdapters = adapters !== undefined && adapters.length > 0;

And later:

-            {sortAdaptersByNewest(model.adapters!).map((adapter) => (
+            {sortAdaptersByNewest(adapters).map((adapter) => (
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web/packages/common/src/components/ModelSelectV2/ModelDropdownItem.tsx`
around lines 125 - 168, In ModelDropdownItem, extract model.adapters into a
local constant and use that value for the hasAdapters check and adapter
iteration. Replace sortAdaptersByNewest(model.adapters!) with the narrowed local
collection, preserving the existing adapter rendering behavior without a
non-null assertion.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@web/packages/studio/src/routes/DataDesignerJobBuildRoute/useJobBuilder.test.tsx`:
- Around line 75-81: Update the useJobBuilder test setup to seed a model with a
non-empty provider instead of models: []. Assert getBuilderValues().models
retains that seeded model unchanged, while preserving the expectation that
mockListModels is not called.

---

Nitpick comments:
In `@web/packages/common/src/components/ModelSelectV2/ModelDropdownItem.tsx`:
- Around line 125-168: In ModelDropdownItem, extract model.adapters into a local
constant and use that value for the hasAdapters check and adapter iteration.
Replace sortAdaptersByNewest(model.adapters!) with the narrowed local
collection, preserving the existing adapter rendering behavior without a
non-null assertion.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 87b49875-fe11-4a33-872b-581d0bac199b

📥 Commits

Reviewing files that changed from the base of the PR and between 4614483 and 1ddf950.

📒 Files selected for processing (6)
  • web/packages/common/src/components/ModelSelectV2/ModelDropdown.test.tsx
  • web/packages/common/src/components/ModelSelectV2/ModelDropdown.tsx
  • web/packages/common/src/components/ModelSelectV2/ModelDropdownItem.tsx
  • web/packages/common/src/components/ModelSelectV2/ModelDropdownList.tsx
  • web/packages/studio/src/routes/DataDesignerJobBuildRoute/useJobBuilder.test.tsx
  • web/packages/studio/src/routes/DataDesignerJobBuildRoute/useJobBuilder.ts
🚧 Files skipped from review as they are similar to previous changes (3)
  • web/packages/studio/src/routes/DataDesignerJobBuildRoute/useJobBuilder.ts
  • web/packages/common/src/components/ModelSelectV2/ModelDropdownList.tsx
  • web/packages/common/src/components/ModelSelectV2/ModelDropdown.tsx

@coderabbitai coderabbitai Bot 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.

🧹 Nitpick comments (1)
web/packages/studio/src/routes/ModelCompareRoute/index.test.tsx (1)

4-10: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Move the external import before internal aliases.

react-router-dom is an external dependency but appears after internal @nemo and @studio imports. As per coding guidelines, group imports as external libraries, then internal modules, then relative imports.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web/packages/studio/src/routes/ModelCompareRoute/index.test.tsx` around lines
4 - 10, Reorder the imports in the ModelCompareRoute test so the external
react-router-dom import appears before the internal `@nemo` and `@studio` aliases,
preserving the existing imports and grouping conventions.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@web/packages/studio/src/routes/ModelCompareRoute/index.test.tsx`:
- Around line 4-10: Reorder the imports in the ModelCompareRoute test so the
external react-router-dom import appears before the internal `@nemo` and `@studio`
aliases, preserving the existing imports and grouping conventions.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 37be1eca-82c8-47b2-8aab-9db6f5c0f5d3

📥 Commits

Reviewing files that changed from the base of the PR and between 1ddf950 and 552f2e7.

📒 Files selected for processing (6)
  • web/packages/studio/src/components/PromptTuningForm/ModelDetailsSection/index.tsx
  • web/packages/studio/src/components/sidePanels/MetricRunSidePanel/index.tsx
  • web/packages/studio/src/routes/DataDesignerJobBuildRoute/models.ts
  • web/packages/studio/src/routes/DataDesignerJobBuildRoute/useJobBuilder.test.tsx
  • web/packages/studio/src/routes/ModelCompareRoute/index.test.tsx
  • web/packages/studio/src/routes/ModelCompareRoute/index.tsx
🚧 Files skipped from review as they are similar to previous changes (5)
  • web/packages/studio/src/routes/ModelCompareRoute/index.tsx
  • web/packages/studio/src/components/sidePanels/MetricRunSidePanel/index.tsx
  • web/packages/studio/src/routes/DataDesignerJobBuildRoute/models.ts
  • web/packages/studio/src/components/PromptTuningForm/ModelDetailsSection/index.tsx
  • web/packages/studio/src/routes/DataDesignerJobBuildRoute/useJobBuilder.test.tsx

Signed-off-by: Sean Teramae <steramae@nvidia.com>
Signed-off-by: Sean Teramae <steramae@nvidia.com>
Signed-off-by: Sean Teramae <steramae@nvidia.com>
@steramae-nvidia
steramae-nvidia force-pushed the steramae/model-fetch-perf branch from 552f2e7 to bb7a44a Compare July 29, 2026 16:37

@htolentino-nvidia htolentino-nvidia 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.

LGTM, paginates fast!

@steramae-nvidia
steramae-nvidia added this pull request to the merge queue Jul 29, 2026
Merged via the queue into main with commit b671a20 Jul 29, 2026
62 of 98 checks passed
@steramae-nvidia
steramae-nvidia deleted the steramae/model-fetch-perf branch July 29, 2026 21:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants