Skip to content

feat(apollo-react): add ModelPicker Material component#879

Open
denispetre wants to merge 4 commits into
mainfrom
feature/model-picker
Open

feat(apollo-react): add ModelPicker Material component#879
denispetre wants to merge 4 commits into
mainfrom
feature/model-picker

Conversation

@denispetre

@denispetre denispetre commented Jul 3, 2026

Copy link
Copy Markdown

Summary

Ports the LLM ModelPicker (LLM-3802) from apollo-design-system into this repo as packages/apollo-react/src/material/components/ap-model-picker/. The component is unchanged in design and behavior; only repo-toolchain adaptations were made.

What it is

A fully controlled picker for LLM Gateway Discovery models: search, Category ⇆ Provider grouping (Custom Models/BYO always first, per-vendor sections ordered by lifecycle), lifecycle chips (Recommended/Preview/Deprecating/Routes to … substitutions/Custom/Out-of-region), built-in folder switcher, and a "Use custom model" footer CTA. Per-product customization is all props (filter, friendlyNameFor, customTagsFor badges, render slots) — no forks.

Platform-aware: given a requestContext, the picker gates BYO management on the AiTrustLayerByoLlm entitlement itself (fail-closed) and fetches the user's Orchestrator folders (enableFolders). Recommended/Preview read straight off the Discovery DTO.

A11y: WAI-ARIA listbox with aria-activedescendant keyboard navigation and live-region announcements. Theming: every color via --color-* CSS variables with @uipath/apollo-core token fallbacks — no ThemeProvider required. Performance: memoized rows + auto-virtualization above 120 visible options.

Full production guide in the component README.md.

Repo adaptations (behavior-preserving)

  • i18n: raw Lingui hooks replaced with this repo's useSafeLingui (never throws in providerless hosts); utils thread a structural PickerTranslator contract that both real I18n instances and the safe fallback satisfy. Catalog entry added to lingui.config.ts; catalogs extracted for all 13 locales.
  • Tests: ported jest → vitest (52 tests, all passing on happy-dom + React 19).
  • React 19: RefObject<T | null> typings in useModelPickerState.
  • Formatting/linting: biome (biome check clean); storybook copy scrubbed of em dashes per repo rule.
  • New dependency: @tanstack/react-virtual (headless virtualizer for the 500-model case).
  • Internal wiki links and repo paths removed from docs/comments for the public repo.

Note on MUI usage

This component is MUI 5-based, which is why it lands under material/components (the maintenance namespace) rather than apollo-wind. It ports an existing, shipped design 1:1; a Tailwind/apollo-wind rewrite would be a separate effort.

Testing

  • vitest: 52/52 passing (src/material/components/ap-model-picker)
  • biome check: clean
  • pnpm --filter @uipath/apollo-react build: succeeds, including declaration generation
  • Stories appear under Apollo React/Material (Maintenance Only)/Components/ModelPicker (20+ stories: kitchen sink, dark mode via CSS variables, folder scoping, substitution, unknown-model fallback, 500-model stress)

Known issues found while integrating (pre-existing, not addressed here)

  • pnpm i18n:extract fails on a duplicate message id in ap-chat (autopilot-chat.error.multiple-files has two different default translations). I extracted scoped to src/material/components/ap-model-picker to work around it.
  • apollo-core's build:fonts script uses single-quoted globs, which breaks under Windows cmd.

Replaces UiPath/apollo-design-system#5276.

image image image

🤖 Generated with Claude Code

Ports the LLM ModelPicker into the Material component set as
ap-model-picker, unchanged in design and behavior:

- controlled picker over LLM Gateway Discovery models with search,
  Category/Provider grouping (BYO always first), lifecycle chips,
  folder switcher, and a Use custom model footer CTA
- per-product customization via props: filter, friendlyNameFor,
  customTagsFor badges, render slots
- platform-aware hooks: BYO management gated on the AiTrustLayerByoLlm
  entitlement (fail closed) and Orchestrator folder fetching
- WAI-ARIA listbox pattern with aria-activedescendant keyboard nav
- CSS-variable theming with apollo-core token fallbacks, no
  ThemeProvider required
- memoized rows + virtualization above 120 options
  (new dependency: @tanstack/react-virtual)

Repo adaptations only: useSafeLingui instead of raw Lingui hooks (new
PickerTranslator contract), vitest test suite (52 tests), biome
formatting, a lingui catalog entry, and React 19 ref typings.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings July 3, 2026 08:00
@github-actions

github-actions Bot commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Review Updated (PT)
apollo-design 🟢 Ready Preview, Logs Jul 03, 2026, 01:35:41 AM
apollo-docs 🟢 Ready Preview, Logs Jul 03, 2026, 01:35:41 AM
apollo-landing 🟢 Ready Preview, Logs Jul 03, 2026, 01:35:41 AM
apollo-vertex 🟢 Ready Preview, Logs Jul 03, 2026, 01:35:41 AM

@github-actions

github-actions Bot commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

Dependency License Review

  • 1945 package(s) scanned
  • ✅ No license issues found
  • ⚠️ 2 package(s) excluded (see details below)
License distribution
License Packages
MIT 1715
ISC 89
Apache-2.0 55
BSD-3-Clause 27
BSD-2-Clause 23
BlueOak-1.0.0 8
MPL-2.0 4
MIT-0 3
CC0-1.0 3
MIT OR Apache-2.0 2
(MIT OR Apache-2.0) 2
Unlicense 2
LGPL-3.0-or-later 1
Python-2.0 1
CC-BY-4.0 1
(MPL-2.0 OR Apache-2.0) 1
Unknown 1
Artistic-2.0 1
(WTFPL OR MIT) 1
(BSD-2-Clause OR MIT OR Apache-2.0) 1
CC-BY-3.0 1
0BSD 1
(MIT OR CC0-1.0) 1
MIT AND ISC 1
Excluded packages
Package Version License Reason
@img/sharp-libvips-linux-x64 1.2.4 LGPL-3.0-or-later LGPL pre-built binary, not linked
khroma 2.1.0 Unknown MIT per GitHub repo, missing license field in package.json

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Adds a new Material (maintenance) ModelPicker component to @uipath/apollo-react, ported from apollo-design-system, including supporting hooks (Discovery fetch, platform entitlements/folders), primitives, and tests; updates i18n and dependencies accordingly.

Changes:

  • Introduces ap-model-picker/ (component, primitives, hooks, utils/types, Storybook-ready docs) and exports it from the material components barrel.
  • Adds Vitest test coverage for the picker primitives, utils, and platform/discovery hooks.
  • Adds @tanstack/react-virtual and updates Lingui config + lockfile to support the new component and virtualization.

Reviewed changes

Copilot reviewed 39 out of 40 changed files in this pull request and generated 9 comments.

Show a summary per file
File Description
pnpm-lock.yaml Lockfile updates for @tanstack/react-virtual and dependency graph changes.
packages/apollo-react/package.json Adds @tanstack/react-virtual dependency.
packages/apollo-react/lingui.config.ts Registers a new Lingui catalog for the ModelPicker component.
packages/apollo-react/src/material/components/index.ts Exports the new ap-model-picker module from the material components barrel.
packages/apollo-react/src/material/components/ap-model-picker/index.ts Public barrel exports for ModelPicker, primitives, hooks, types, and utils.
packages/apollo-react/src/material/components/ap-model-picker/ModelPicker.tsx Main controlled picker UI, grouping toggle, footer CTA, and wiring to hooks/primitives.
packages/apollo-react/src/material/components/ap-model-picker/ModelPicker.test.tsx Component-level interaction and keyboard navigation tests.
packages/apollo-react/src/material/components/ap-model-picker/ModelTagChip.tsx Semantic chip renderer for derived/custom model tags.
packages/apollo-react/src/material/components/ap-model-picker/README.md Full usage guide and API reference for consumers.
packages/apollo-react/src/material/components/ap-model-picker/i18n.ts Central message descriptors + translator contract for component i18n.
packages/apollo-react/src/material/components/ap-model-picker/types.ts DTO/types for Discovery models, tags, groups, and request context.
packages/apollo-react/src/material/components/ap-model-picker/useDiscoveryModels.ts Minimal fetch+state hook for LLM Gateway Discovery models.
packages/apollo-react/src/material/components/ap-model-picker/useModelPickerState.ts State controller for open/query/grouping/selection/keyboard navigation.
packages/apollo-react/src/material/components/ap-model-picker/usePlatformAccess.ts Minimal fetch+state hooks for folders and BYO entitlement checks.
packages/apollo-react/src/material/components/ap-model-picker/usePlatformAccess.test.tsx Tests for folder fetch + entitlement check hook behavior.
packages/apollo-react/src/material/components/ap-model-picker/utils.ts Model grouping, filtering, tag derivation, and substitution helpers.
packages/apollo-react/src/material/components/ap-model-picker/utils.test.ts Unit tests for grouping/tag derivation/filtering/substitution helpers.
packages/apollo-react/src/material/components/ap-model-picker/primitives/FolderSwitcher.tsx Folder scope UI (pill + popper menu).
packages/apollo-react/src/material/components/ap-model-picker/primitives/FolderSwitcher.test.tsx Folder switcher interaction tests.
packages/apollo-react/src/material/components/ap-model-picker/primitives/GroupHeader.tsx Group header row (static + collapsible modes).
packages/apollo-react/src/material/components/ap-model-picker/primitives/ModelOptionRow.tsx Single option row rendering + default BYO actions.
packages/apollo-react/src/material/components/ap-model-picker/primitives/OptionList.tsx Grouped and virtualized option list renderers + option DOM id helper.
packages/apollo-react/src/material/components/ap-model-picker/primitives/OptionList.test.tsx Tests for grouped/virtual list rendering and behaviors.
packages/apollo-react/src/material/components/ap-model-picker/primitives/PickerPopup.tsx Popper+Paper dropdown shell used by the picker.
packages/apollo-react/src/material/components/ap-model-picker/primitives/PickerSearchInput.tsx Search input toolbar control with combobox ARIA wiring.
packages/apollo-react/src/material/components/ap-model-picker/primitives/PickerTrigger.tsx Trigger button rendering selected/unknown/placeholder states + inline tags.
packages/apollo-react/src/material/components/ap-model-picker/locales/en.json New ModelPicker catalog (currently empty values).
packages/apollo-react/src/material/components/ap-model-picker/locales/de.json New ModelPicker catalog (currently empty values).
packages/apollo-react/src/material/components/ap-model-picker/locales/es.json New ModelPicker catalog (currently empty values).
packages/apollo-react/src/material/components/ap-model-picker/locales/es-MX.json New ModelPicker catalog (currently empty values).
packages/apollo-react/src/material/components/ap-model-picker/locales/fr.json New ModelPicker catalog (currently empty values).
packages/apollo-react/src/material/components/ap-model-picker/locales/ja.json New ModelPicker catalog (currently empty values).
packages/apollo-react/src/material/components/ap-model-picker/locales/ko.json New ModelPicker catalog (currently empty values).
packages/apollo-react/src/material/components/ap-model-picker/locales/pt.json New ModelPicker catalog (currently empty values).
packages/apollo-react/src/material/components/ap-model-picker/locales/pt-BR.json New ModelPicker catalog (currently empty values).
packages/apollo-react/src/material/components/ap-model-picker/locales/ru.json New ModelPicker catalog (currently empty values).
packages/apollo-react/src/material/components/ap-model-picker/locales/tr.json New ModelPicker catalog (currently empty values).
packages/apollo-react/src/material/components/ap-model-picker/locales/zh-CN.json New ModelPicker catalog (currently empty values).
packages/apollo-react/src/material/components/ap-model-picker/locales/zh-TW.json New ModelPicker catalog (currently empty values).
Files not reviewed (1)
  • pnpm-lock.yaml: Generated file

Comment thread packages/apollo-react/src/material/components/ap-model-picker/locales/en.json Outdated
@github-actions

github-actions Bot commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

📊 Coverage + size by package

Per-package coverage and bundle size on this PR. New-line coverage = of the source lines this PR adds or changes, the % hit by tests.

Package Coverage New-line coverage Packed (gzip) Unpacked vs main
@uipath/apollo-core 9.0% 43.82 MB 57.31 MB ±0
@uipath/apollo-react 35.6% 68.5% (435/635) 7.33 MB 27.94 MB +59.2 KB
@uipath/apollo-wind 40.1% 392.5 KB 2.55 MB +1 B
@uipath/ap-chat 85.8% 43.43 MB 55.91 MB +18.4 KB

"Coverage" is each package's own coverage.include scope (e.g. apollo-core instruments only scripts/). "Packed"/"Unpacked" come from npm pack --dry-run and only cover built packages — "—" means not measured this run (package not affected / not built). "vs main" is the packed (gzipped) delta against the last successful main build (the package-sizes artifact from the Release workflow); "—" there means no main baseline was available this run. The baseline is main's latest build, not this PR's exact merge-base, so it includes any drift since the branch diverged. Packages with no vitest config are omitted.

denispetre and others added 2 commits July 3, 2026 11:14
…MUI theme

- guard the dev-only duplicate-folder warning with `typeof process` so
  Vite-served hosts (Storybook) don't hit `process is not defined`
- bake the mini-chip variant styles into ModelTagChip via sx (CSS
  variables + Apollo light fallbacks) instead of relying on the Apollo
  MuiChip theme overrides — chips now honor the picker's
  no-ThemeProvider contract and render correctly in dark themes

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
MenuItem inherited MUI's default (light) text color, which reads
dark-on-dark in dark hosts without a ThemeProvider — same class of bug
as the chip variants. Paint it from --color-foreground like the rest
of the picker.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings July 3, 2026 08:15

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 39 out of 40 changed files in this pull request and generated 7 comments.

Files not reviewed (1)
  • pnpm-lock.yaml: Generated file

Comment thread packages/apollo-react/src/material/components/ap-model-picker/locales/en.json Outdated
- fill en.json with English defaults so hosts that load the catalog
  don't render blank strings (matches ap-chat)
- resolve host-filtered selections from the raw catalog so the trigger
  keeps showing the stored model (documented contract; new test)
- deep-camelize Discovery DTO keys; nested PascalCase (ModelDetails,
  CostDetails, DeprecationDetails, ...) previously stayed raw
- reset state and abort in-flight requests when useDiscoveryModels /
  useUserFolders / useCanManageByo are disabled mid-flight
- guard formatDate against Invalid Date leaking into chip labels
- useId() for picker DOM ids instead of a module counter
- aria-haspopup="menu" on the folder switcher; role="button" on the
  collapsible group header

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@denispetre

Copy link
Copy Markdown
Author

Triage of the Copilot review — all findings addressed in a35aa17 except one dismissed with reasoning:

Finding Verdict Resolution
locales/en.json all empty strings Confirmed Filled with the English defaults from the source descriptors (38/38), matching the ap-chat/ap-tool-call convention; catalogs recompiled.
normalizeKeys only camel-cases top-level keys Confirmed Now recurses through nested objects/arrays (ModelDetails.CostDetails, DeprecationDetails, RoutingDetails, …).
Filtered-out selection renders as "nothing selected", contradicting the docs Confirmed (code had drifted from its own comment) Selection now falls back to the raw catalog; selected widened to DiscoveryModel | null; regression test added.
useDiscoveryModels / useUserFolders / useCanManageByo keep stale state when disabled Confirmed Disabling now aborts in-flight requests and resets models/folders/loading/error (and canManage).
formatDate try/catch can't catch Invalid Date Confirmed Explicit Number.isNaN(date.getTime()) guard, falls back to the raw ISO string.
Module-level pickerIdCounter vs useId() Confirmed Switched to useId() (SSR/concurrent-safe, matches ApRichTextEditor).
aria-haspopup="listbox" on a menu popup; collapsible header div without a role Confirmed aria-haspopup="menu" on the folder switcher; role="button" on the collapsible group header.
process.env in the dev-only warning Already fixed in 714d3f6 Guarded with typeof process === 'undefined'.
PickerTranslator._ typed as (descriptor: MessageDescriptor) => string "fails excess-property checks" for { id, message, values } Dismissed Lingui v5's MessageDescriptor includes values?: Record<string, unknown>, so those call sites type-check as-is — tsc -b passes with zero errors across the package.

53/53 vitest, biome clean, tsc -b clean after the changes.

🤖 Generated with Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

pkg:apollo-react size:XXL 1,000+ changed lines.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants