Filter gateway experiments from the experiment list page#21130
Filter gateway experiments from the experiment list page#21130TomeHirata merged 2 commits intomasterfrom
Conversation
Gateway experiments (tagged with mlflow.experiment.sourceType=GATEWAY) are now excluded from the experiment list page. This is implemented by: 1. Adding GATEWAY to ExperimentSourceType enum in experimentPage.common-utils.ts 2. Exporting isGatewayExperiment() helper function 3. Filtering gateway experiments out in useExperimentListQuery.ts 4. Adding a test to verify the filtering behavior Co-authored-by: TomeHirata <33407409+TomeHirata@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
This PR filters out gateway experiments (auto-created when gateway endpoints have usage tracking enabled) from the experiment list page to reduce UI clutter. Gateway experiments are identified by the tag mlflow.experiment.sourceType=GATEWAY and filtered client-side before rendering.
Changes:
- Added
GATEWAYenum value andisGatewayExperiment()helper function to identify gateway experiments - Implemented client-side filtering in the experiment list query hook to exclude gateway experiments
- Added unit test to verify gateway experiments are properly filtered out
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
experimentPage.common-utils.ts |
Added GATEWAY to ExperimentSourceType enum and created isGatewayExperiment() helper that checks for the gateway source type tag |
useExperimentListQuery.ts |
Modified sorting logic to filter out gateway experiments before separating demo and non-demo experiments |
useExperimentListQuery.test.tsx |
Added test case verifying gateway experiments are excluded from the returned experiment list |
| const nonGateway = experiments.filter((e) => !isGatewayExperiment(e)); | ||
| const demo = nonGateway.filter(isDemoExperiment); | ||
| const nonDemo = nonGateway.filter((e) => !isDemoExperiment(e)); | ||
| return [...demo, ...nonDemo]; |
There was a problem hiding this comment.
🟡 MODERATE: Client-side filtering reduces the page size inconsistently — if the API returns 10 experiments but 3 are gateway experiments, users only see 7 on that page. This creates a confusing UX where page sizes vary unpredictably.
Consider either:
- Filtering on the server side by adding gateway experiments to the search filter
- Documenting this behavior if it's acceptable for the product
There was a problem hiding this comment.
Here's a video demonstrating the page size inconsistency:
gateway-experiment-page-size-bug.mp4
There was a problem hiding this comment.
Good call, missed this message. Filed #21138 for migrating into the server side filter.
There was a problem hiding this comment.
@TomeHirata Can you share #21138 in the relevant slack thread, which I think would've caught the issue.
There was a problem hiding this comment.
For PRs like this, it’s risky to judge solo. Getting another set of eyes would help.
There was a problem hiding this comment.
Sure, I noticed a challenge to do it on the server. Can you take a look at #21146?
|
|
||
| expect(result.current.data).toHaveLength(2); | ||
| expect(result.current.data?.map((e) => e.experimentId)).toEqual(['1', '3']); | ||
| }); |
There was a problem hiding this comment.
🟢 NIT: Test coverage could be expanded to include edge cases: experiments that are both demo and gateway, or a page where all experiments are gateway experiments. While unlikely in practice, these scenarios would verify the filtering logic handles all cases correctly.
| }); | |
| }); | |
| it('filters out experiments that are both demo and GATEWAY source type', async () => { | |
| mockSearchExperiments.mockResolvedValueOnce({ | |
| experiments: [ | |
| { | |
| experimentId: '1', | |
| name: 'Regular Experiment', | |
| artifactLocation: '/artifacts/1', | |
| lifecycleStage: 'active', | |
| lastUpdateTime: Date.now(), | |
| creationTime: Date.now(), | |
| tags: [], | |
| allowedActions: [], | |
| }, | |
| { | |
| experimentId: '2', | |
| name: 'Demo Gateway Experiment', | |
| artifactLocation: '/artifacts/2', | |
| lifecycleStage: 'active', | |
| lastUpdateTime: Date.now(), | |
| creationTime: Date.now(), | |
| tags: [ | |
| { key: 'mlflow.experimentType', value: 'DEMO' }, | |
| { key: 'mlflow.experiment.sourceType', value: 'GATEWAY' }, | |
| ], | |
| allowedActions: [], | |
| }, | |
| { | |
| experimentId: '3', | |
| name: 'Another Regular Experiment', | |
| artifactLocation: '/artifacts/3', | |
| lifecycleStage: 'active', | |
| lastUpdateTime: Date.now(), | |
| creationTime: Date.now(), | |
| tags: [], | |
| allowedActions: [], | |
| }, | |
| ], | |
| }); | |
| const { result } = renderHook(() => useExperimentListQuery(), { | |
| wrapper: createWrapper(), | |
| }); | |
| await waitFor(() => expect(result.current.isLoading).toBe(false)); | |
| expect(result.current.data).toHaveLength(2); | |
| expect(result.current.data?.map((e) => e.experimentId)).toEqual(['1', '3']); | |
| }); | |
| it('returns an empty list when all experiments are GATEWAY source type', async () => { | |
| mockSearchExperiments.mockResolvedValueOnce({ | |
| experiments: [ | |
| { | |
| experimentId: '1', | |
| name: 'Gateway Experiment 1', | |
| artifactLocation: '/artifacts/1', | |
| lifecycleStage: 'active', | |
| lastUpdateTime: Date.now(), | |
| creationTime: Date.now(), | |
| tags: [{ key: 'mlflow.experiment.sourceType', value: 'GATEWAY' }], | |
| allowedActions: [], | |
| }, | |
| { | |
| experimentId: '2', | |
| name: 'Gateway Experiment 2', | |
| artifactLocation: '/artifacts/2', | |
| lifecycleStage: 'active', | |
| lastUpdateTime: Date.now(), | |
| creationTime: Date.now(), | |
| tags: [{ key: 'mlflow.experiment.sourceType', value: 'GATEWAY' }], | |
| allowedActions: [], | |
| }, | |
| ], | |
| }); | |
| const { result } = renderHook(() => useExperimentListQuery(), { | |
| wrapper: createWrapper(), | |
| }); | |
| await waitFor(() => expect(result.current.isLoading).toBe(false)); | |
| expect(result.current.data).toHaveLength(0); | |
| expect(result.current.data).toEqual([]); | |
| }); |
|
Documentation preview for a2765b4 is available at: More info
|
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: TomeHirata <33407409+TomeHirata@users.noreply.github.com> Signed-off-by: Samraj Moorjani <samraj.moorjani@databricks.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: TomeHirata <33407409+TomeHirata@users.noreply.github.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: TomeHirata <33407409+TomeHirata@users.noreply.github.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: TomeHirata <33407409+TomeHirata@users.noreply.github.com>
Gateway experiments (auto-created with
mlflow.experiment.sourceType=GATEWAYwhen a gateway endpoint has usage tracking enabled) were appearing alongside user experiments on the experiment list page, adding noise.Changes
experimentPage.common-utils.ts: AddedGATEWAYtoExperimentSourceTypeenum; exported newisGatewayExperiment()helper that detects experiments tagged withmlflow.experiment.sourceType=GATEWAYuseExperimentListQuery.ts: Filters out gateway experiments from the returned list before rendering (client-side, consistent with the existing demo-experiment reordering pattern)useExperimentListQuery.test.tsx: Added test verifying gateway experiments are excluded from returned dataHow is this PR tested?
Does this PR require documentation update?
Does this PR require updating the MLflow Skills repository?
Release Notes
Is this a user-facing change?
Gateway experiments are now hidden from the experiment list page to reduce noise.
What component(s), interfaces, languages, and integrations does this PR affect?
Components
area/uiux: Front-end, user experience, plotting, JavaScript, JavaScript dev serverHow should the PR be classified in the release notes? Choose one:
rn/feature- A new user-facing feature worth mentioning in the release notesShould this PR be included in the next patch release?
🔒 GitHub Advanced Security automatically protects Copilot coding agent pull requests. You can protect all pull requests by enabling Advanced Security for your repositories. Learn more about Advanced Security.