Conversation
|
@Gkrumbach07 Thank you for the contribution! Could you fix the following issue(s)? ⚠ DCO checkThe DCO check failed. Please sign off your commit(s) by following the instructions here. See https://github.com/mlflow/mlflow/blob/master/CONTRIBUTING.md#sign-your-work for more details. |
|
Will add screenshots / video in a sec |
f9479a0 to
78b80dd
Compare
32988a0 to
59886b1
Compare
0730f36 to
6cf60c4
Compare
b5247eb to
6ccc89e
Compare
6ccc89e to
47219d4
Compare
|
@Gkrumbach07 a few UX things:
|
47219d4 to
948728e
Compare
|
@Gkrumbach07 one other thing I noticed is if there is an API error when creating a workspace through the UI (e.g. if the workspace provider doesn't support workspace creation), there is no error notification. |
948728e to
3be539c
Compare
46bb816 to
56b2e5b
Compare
|
@Gkrumbach07 manually testing seems good. I had GPT 5.2 Codex review it: Findings
async function fetchServerFeatures(): Promise<ServerFeatures> {
try {
const response = await fetch(getAjaxUrl(SERVER_FEATURES_ENDPOINT));
if (response.status === 404) {
return { workspacesEnabled: Boolean(process.env['MLFLOW_ENABLE_WORKSPACES']) };
}export const getDefaultHeaders = (cookieStr: any) => {
const cookieHeaders = getDefaultHeadersFromCookies(cookieStr);
// Forward Authorization header for OAuth/Kubernetes integration
const authHeader = typeof sessionStorage !== 'undefined' ? sessionStorage.getItem('mlflow-auth-header') : null;
// getActiveWorkspace() returns null if workspaces feature is not enabled
const workspace = getActiveWorkspace();
return {
...cookieHeaders,
...(authHeader ? { Authorization: authHeader } : {}),
...(workspace ? { 'X-MLFLOW-WORKSPACE': workspace } : {}),
};
};
useEffect(() => {
if (!workspacesEnabled) {
setActiveWorkspace(null);
return;
}
const workspaceFromPath = extractWorkspaceFromPathname(location.pathname);
const activeWorkspace = getActiveWorkspace();
if (isLoading) {
return;
}
// Validate workspace from path
if (workspaceFromPath) {
const isValid = workspaces.some((w) => w.name === workspaceFromPath);
if (!isValid) {
setActiveWorkspace(null);
navigate('/', { replace: true });
return;
} useEffect(() => {
if (!workspacesEnabled || isLoading || workspaces.length === 0 || !currentWorkspace) {
return;
}
const currentWorkspaceExists = workspaces.some((ws) => ws.name === currentWorkspace);
if (!currentWorkspaceExists) {
// Get first available workspace as fallback (or 'default' if available)
const fallbackWorkspace = workspaces.find((ws) => ws.name === 'default') || workspaces[0];
if (fallbackWorkspace) {
setActiveWorkspace(fallbackWorkspace.name);
const currentSection = getNavigationSection(location.pathname);
navigate(`/workspaces/${encodeURIComponent(fallbackWorkspace.name)}${currentSection}`);
}
}
}, [workspacesEnabled, isLoading, workspaces, currentWorkspace, location.pathname, navigate]);
export const extractWorkspaceFromPathname = (pathname: string): string | null => {
if (!pathname || !pathname.startsWith(WORKSPACE_PREFIX)) {
return null;
}
const segments = pathname.split('/');
if (segments.length < 3 || !segments[2]) {
return null;
}
const workspaceName = decodeURIComponent(segments[2]);
// Validate workspace name format
if (!WORKSPACE_NAME_PATTERN.test(workspaceName)) {
return null;
} |
d7be8ea to
1fded7b
Compare
|
@mprahl I have addressed your review |
mlflow/server/js/src/common/components/WorkspacePermissionError.tsx
Outdated
Show resolved
Hide resolved
|
/lgtm |
|
Documentation preview for f883189 is available at: More info
|
2111598 to
f883189
Compare
| const currentWorkspace = extractWorkspaceFromSearchParams(searchParams); | ||
|
|
||
| // Fetch workspaces using custom hook | ||
| const { workspaces, isLoading, isError, refetch } = useWorkspaces(workspacesEnabled); |
There was a problem hiding this comment.
If the workspaces are persisted in local storage anyway, should we use that as a cache and load workspaces only after the dropdown is opened? This could save some API calls
There was a problem hiding this comment.
useWorkspaces(workspacesEnabled) uses react query and therefore it is cached there. I am hesitant to lean on local storage for cache as that has no internal invalidation so we have to then deal with
- when do we refresh the dropdown
- what if a user looses access, one is deleted outside their browser, ...
mlflow/server/js/src/experiment-tracking/components/ExperimentListView.tsx
Outdated
Show resolved
Hide resolved
mlflow/server/js/src/experiment-tracking/pages/experiment-page-tabs/ExperimentPageTabs.test.tsx
Outdated
Show resolved
Hide resolved
|
|
||
| const CONFIGURE_EXPERIMENT_SNIPPET = `import mlflow | ||
| const getConfigureExperimentSnippet = (workspace?: string | null) => { | ||
| const workspaceLine = workspace ? `mlflow.set_workspace("${workspace}")\n` : ''; |
There was a problem hiding this comment.
@B-Step62 can you confirm if it's ok to introduce those changes to snippets already?
mlflow/server/js/src/shared/web-shared/genai-traces-table/utils/FetchUtils.ts
Outdated
Show resolved
Hide resolved
...ing/components/experiment-logged-models/ExperimentLoggedModelListPageColumnSelector.test.tsx
Outdated
Show resolved
Hide resolved
...rver/js/src/shared/web-shared/model-trace-explorer/hooks/useUpdateTraceTagsMutation.test.tsx
Show resolved
Hide resolved
96d5dd4 to
00caa6d
Compare
|
I addressed comments |
0fd2dae to
a075c62
Compare
aca57a2 to
29f8002
Compare
f4a6b9e to
84f4bb9
Compare
|
Hi @Gkrumbach07 in case the workspace is removed from the query params when users switch to the Chart View on the models tab. |
84f4bb9 to
69436a9
Compare
hubertzub-db
left a comment
There was a problem hiding this comment.
Took another pass, this looks good! Thanks a lot for redoing everything to use query params instead of a prefix!
- Left a few minor questions
- A major one: what's the exact issue/error that made you add
<TooltipProvider>to many tests in the app? In general,<TooltipProvider>should be automatically added by<DesignSystemProvider>- if it doesn't, it indicates some deeper problem in design system we'd like to investigate - CC @daniellok-db would you like to take a quick look at this PR as well?
| // eslint-disable-next-line no-restricted-globals | ||
| const fetchFn = fetch; |
There was a problem hiding this comment.
can we keep this fetchFn abstraction? this helps sync with the other codebase
| const getTabNameFromRoutePath = (pathname: string) => | ||
| ExperimentPageRoutePathToTabNameMap | ||
| // Find the first route path that matches the given pathname | ||
| // Note: With query param-based workspace routing, pathname no longer contains workspace prefix |
There was a problem hiding this comment.
I think this comment is not needed now
|
|
||
| export const setActiveWorkspace = (workspace: string | null) => { | ||
| activeWorkspace = workspace; | ||
| listeners.forEach((listener) => listener(activeWorkspace)); |
There was a problem hiding this comment.
Is it absolutely necessary for workspace switching to be a dynamic action that updates stuff using this pubsub pattern, instead of just hard reloading the entire UI app with a new query parameter? It may sound barbaric, but it could make it a way simpler and less prone to bugs. WDYT?
There was a problem hiding this comment.
So the ONLY reason for the pub sub is because the react query cache is not under the router. I can move things around so that is the case and no need for pub sub.
Otherwise I guess we could hard refresh, but it makes a very jumpy UI every time a workflow changes
There was a problem hiding this comment.
i did the refresh pattern
…ery param routing Signed-off-by: Gage Krumbach <gkrumbach@gmail.com> Co-Authored-By: Matt Prahl <mprahl@users.noreply.github.com>
- Remove redundant TooltipProvider from all test files (DesignSystemProvider already includes RadixTooltipProvider internally) - Refactor workspace switching from pub/sub to hard reload for simplicity and to eliminate redirect race conditions - Restore fetchFn abstraction in FetchUtils.ts for codebase sync - Remove stale workspace routing comment - Fix upstream bug in useMlflowTraces.test.tsx (fetchFn -> fetchAPI) Signed-off-by: Gage Krumbach <gkrumbach@gmail.com>
69436a9 to
69548ef
Compare
|
Addressed all feedback from the latest review:
|
hubertzub-db
left a comment
There was a problem hiding this comment.
@Gkrumbach07 thanks for all the changes and fixes!
Stamping as it looks good in the current form, I'll defer to @daniellok-db for further review
There was a problem hiding this comment.
Overall LGTM but there seems to be some bug with links to the chat sessions page, e.g.
My suspicion is that this is due to the packages in src/shared/web-shared having their own individual copies of RoutingUtils. Unfortunately this is a quirk of our setup where the UI has some upstream dependencies (we cannot import from @mlflow/mlflow from within that folder as it will cause conflicts). See:
Could you copy paste the changes you made over to those files too?
Doesn't have to block the merge, I'll leave it up to @B-Step62 but we should definitely follow up at least in a future PR








Related Issues/PRs
#1464
#5844
Design document: https://docs.google.com/document/d/1IbFfceCmWV3knJfc0ninn58EXLVbHUh1meV_pknOM40/edit
What changes are proposed in this pull request?
This PR adds the frontend UI components for multi-workspace support in MLflow.
The new workspace UI behavior is only activated when the server's
/ajax-api/2.0/mlflow/server-featuresendpoint returns{ "workspacesEnabled": true }. When workspaces are not enabled (default behavior), the UI operates exactly as before with no visible changes.New Components:
WorkspaceSelector- Dropdown component with search and tooltips for selecting workspacesWorkspacePermissionError- Error page displayed when accessing an unauthorized workspaceuseWorkspaceshook - Custom hook for fetching available workspaces from the APIServerFeaturesContext- Context provider for detecting server feature flags via/ajax-api/2.0/mlflow/server-featuresWorkspaceUtils- Utilities for workspace state management and URL handlingWorkspaceRouteUtils- Utilities for prefixing routes with workspace pathsCore Changes:
MlflowRouter- Added workspace-aware routing withWorkspaceRouterSynccomponent (only active when workspaces enabled)app.tsx- IntegratedServerFeaturesProviderfor feature detectionFetchUtils- AddedX-MLFLOW-WORKSPACEheader injection for all API requests (only when workspace is set)graphql/client- Added workspace header support for GraphQL requestsBehavior when workspaces are enabled:
/#/workspaces/team-a/experimentsX-MLFLOW-WORKSPACEheader (not URL path)Behavior when workspaces are disabled (default):
X-MLFLOW-WORKSPACEheader sentHow is this PR tested?
New tests added for:
ServerFeaturesContext.test.tsx- Tests feature detection and workspace enabled/disabled statesWorkspaceRouteUtils.test.ts- Tests route prefixing logicArtifactUtils.test.ts- Tests workspace header injectiongraphql/client.test.ts- Tests GraphQL workspace header supportDoes this PR require documentation update?
Documentation will be added in a follow-up PR once the full workspace feature is complete.
Release Notes
Is this a user-facing change?
Added multi-workspace UI support allowing users to switch between different workspaces via a dropdown selector in the MLflow header. This feature is only enabled when the MLflow server is configured with workspace support (determined by the
/ajax-api/2.0/mlflow/server-featuresendpoint). When workspaces are not enabled, the UI behaves exactly as before.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/none- No description will be included. The PR will be mentioned only by the PR number in the "Small Bugfixes and Documentation Updates" sectionrn/breaking-change- The PR will be mentioned in the "Breaking Changes" sectionrn/feature- A new user-facing feature worth mentioning in the release notesrn/bug-fix- A user-facing bug fix worth mentioning in the release notesrn/documentation- A user-facing documentation change worth mentioning in the release notesShould this PR be included in the next patch release?