Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 61 additions & 8 deletions .github/actions/check-component-ids/componentId-registry.js
Original file line number Diff line number Diff line change
Expand Up @@ -1870,10 +1870,24 @@ module.exports = {
"mlflow.mcp_registry.bindings.view_toggle": "",
"mlflow.mcp_registry.card": "",
"mlflow.mcp_registry.card_grid.pagination": "",
"mlflow.mcp_registry.compare.status": "",
"mlflow.mcp_registry.compare.switch_sides": "",
"mlflow.mcp_registry.compare.switch_sides.tooltip": "",
"mlflow.mcp_registry.create.display_name": "",
"mlflow.mcp_registry.create.server_json": "",
"mlflow.mcp_registry.create.source": "",
"mlflow.mcp_registry.create.status": "",
"mlflow.mcp_registry.create.tag.add": "",
"mlflow.mcp_registry.create.tag.add.tooltip": "",
"mlflow.mcp_registry.create.tag.value": "",
"mlflow.mcp_registry.create.tools": "",
"mlflow.mcp_registry.create_binding_button": "",
"mlflow.mcp_registry.create_server_button": "",
"mlflow.mcp_registry.create_server_version.error": "",
"mlflow.mcp_registry.create_server_version.modal": "",
"mlflow.mcp_registry.detail.actions": "",
"mlflow.mcp_registry.detail.actions.delete": "",
"mlflow.mcp_registry.detail.actions.edit_display_name": "",
"mlflow.mcp_registry.detail.add_binding": "",
"mlflow.mcp_registry.detail.binding.card": "",
"mlflow.mcp_registry.detail.binding.delete": "",
Expand All @@ -1886,16 +1900,54 @@ module.exports = {
"mlflow.mcp_registry.detail.delete_server_modal": "",
"mlflow.mcp_registry.detail.delete_version": "",
"mlflow.mcp_registry.detail.delete_version_modal": "",
"mlflow.mcp_registry.detail.edit_status": "",
"mlflow.mcp_registry.detail.display_name_input": "",
"mlflow.mcp_registry.detail.edit_version": "",
"mlflow.mcp_registry.detail.error": "",
"mlflow.mcp_registry.detail.repository": "",
"mlflow.mcp_registry.detail.status_select": "",
"mlflow.mcp_registry.detail.toggle_json": "",
"mlflow.mcp_registry.detail.update_status_error": "",
"mlflow.mcp_registry.detail.update_status_modal": "",
"mlflow.mcp_registry.detail.update_status_terminal": "",
"mlflow.mcp_registry.detail.usage_example_modal": "",
"mlflow.mcp_registry.detail.use_version": "",
"mlflow.mcp_registry.detail.select_baseline.tooltip": "",
"mlflow.mcp_registry.detail.select_compared.tooltip": "",
"mlflow.mcp_registry.detail.tags.edit": "",
"mlflow.mcp_registry.detail.update_display_name_error": "",
"mlflow.mcp_registry.detail.update_display_name_modal": "",
"mlflow.mcp_registry.detail.copy_package_identifier": "",
"mlflow.mcp_registry.detail.copy_package_identifier_button": "",
"mlflow.mcp_registry.detail.copy_remote_url": "",
"mlflow.mcp_registry.detail.copy_remote_url_button": "",
"mlflow.mcp_registry.detail.env_var_required": "",
"mlflow.mcp_registry.detail.env_var_secret": "",
"mlflow.mcp_registry.detail.header_required": "",
"mlflow.mcp_registry.detail.header_secret": "",
"mlflow.mcp_registry.detail.package_registry_tag": "",
"mlflow.mcp_registry.detail.raw_json.copy": "",
"mlflow.mcp_registry.detail.raw_json.copy_button": "",
"mlflow.mcp_registry.detail.raw_json.toggle": "",
"mlflow.mcp_registry.detail.raw_tools_json.copy": "",
"mlflow.mcp_registry.detail.raw_tools_json.copy_button": "",
"mlflow.mcp_registry.detail.raw_tools_json.toggle": "",
"mlflow.mcp_registry.detail.remote_transport_tag": "",
"mlflow.mcp_registry.detail.toggle_env_vars": "",
"mlflow.mcp_registry.detail.toggle_packages": "",
"mlflow.mcp_registry.detail.toggle_tools": "",
"mlflow.mcp_registry.detail.tool_annotation_tag": "",
"mlflow.mcp_registry.detail.tool_input_schema.copy": "",
"mlflow.mcp_registry.detail.tool_input_schema.copy_button": "",
"mlflow.mcp_registry.detail.tool_input_schema.toggle": "",
"mlflow.mcp_registry.detail.tool_name_tag": "",
"mlflow.mcp_registry.detail.tool_output_schema.copy": "",
"mlflow.mcp_registry.detail.tool_output_schema.copy_button": "",
"mlflow.mcp_registry.detail.tool_output_schema.toggle": "",
"mlflow.mcp_registry.detail.version_tabs": "",
"mlflow.mcp_registry.detail.version.add_metadata": "",
"mlflow.mcp_registry.detail.version.description.error": "",
"mlflow.mcp_registry.detail.version.description.modal": "",
"mlflow.mcp_registry.detail.version.description.textarea": "",
"mlflow.mcp_registry.detail.version.edit_display_name_input": "",
"mlflow.mcp_registry.detail.version.edit_metadata": "",
"mlflow.mcp_registry.detail.version.edit_status_select": "",
"mlflow.mcp_registry.detail.version.edit_tools_input": "",
"mlflow.mcp_registry.detail.version.edit_version_error": "",
"mlflow.mcp_registry.detail.version.edit_version_modal": "",
"mlflow.mcp_registry.detail.version.tools_validation_error": "",
"mlflow.mcp_registry.detail.version_status": "",
"mlflow.mcp_registry.detail.version_status_tag": "",
"mlflow.mcp_registry.detail.versions.header": "",
Expand All @@ -1909,6 +1961,7 @@ module.exports = {
"mlflow.mcp_registry.table.header": "",
"mlflow.mcp_registry.table.name_link": "",
"mlflow.mcp_registry.table.pagination": "",
"mlflow.mcp_registry.table.tag.edit": "",
"mlflow.mcp_registry.tabs": "",
"mlflow.mcp_registry.view_toggle": "",

Expand Down
61 changes: 37 additions & 24 deletions mlflow/server/js/src/common/components/AliasSelect.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type { Dispatch } from 'react';
import { useCallback, useState } from 'react';

import { LegacySelect, useDesignSystemTheme } from '@databricks/design-system';
import type { TagColors } from '@databricks/design-system';

import { AliasTag } from './AliasTag';
import { FormattedMessage, useIntl } from 'react-intl';
Expand All @@ -17,6 +18,8 @@ export const AliasSelect = ({
version,
aliasToVersionMap,
disabled,
pinnedAliases,
pinnedAliasColor,
}: {
renderKey: any;
disabled: boolean;
Expand All @@ -25,6 +28,8 @@ export const AliasSelect = ({
draftAliases: string[];
version: string;
aliasToVersionMap: Record<string, string>;
pinnedAliases?: string[];
pinnedAliasColor?: TagColors;
}) => {
const intl = useIntl();
const [dropdownVisible, setDropdownVisible] = useState(false);
Expand All @@ -50,14 +55,16 @@ export const AliasSelect = ({
)
// After sanitization, filter out invalid aliases
// so we won't get empty values
.filter((alias) => alias.length > 0);
.filter((alias) => alias.length > 0)
// Exclude pinned aliases from the draft set
.filter((alias) => !pinnedAliases?.includes(alias));

// Remove duplicates that might result from varying letter case
const uniqueAliases = Array.from(new Set(sanitizedAliases));
setDraftAliases(uniqueAliases);
setDropdownVisible(false);
},
[setDraftAliases],
[setDraftAliases, pinnedAliases],
);

return (
Expand All @@ -82,33 +89,39 @@ export const AliasSelect = ({
dangerouslySetAntdProps={{
dropdownMatchSelectWidth: true,
// eslint-disable-next-line @databricks/no-unstable-nested-components -- go/no-nested-components
tagRender: ({ value }) => (
<AliasTag
compact
css={{ marginTop: 2 }}
closable
onClose={() => removeFromEditedAliases(value.toString())}
value={value.toString()}
/>
),
tagRender: ({ value }) => {
const isPinned = pinnedAliases?.includes(value.toString());
return (
<AliasTag
compact
css={{ marginTop: 2 }}
closable={!isPinned}
onClose={isPinned ? undefined : () => removeFromEditedAliases(value.toString())}
value={value.toString()}
color={isPinned ? pinnedAliasColor || 'turquoise' : undefined}
/>
);
},
}}
onDropdownVisibleChange={setDropdownVisible}
open={dropdownVisible}
value={draftAliases || []}
value={[...(pinnedAliases ?? []), ...(draftAliases || [])]}
>
{existingAliases.map((alias) => (
<LegacySelect.Option key={alias} value={alias} data-testid="model-alias-option">
<div key={alias} css={{ display: 'flex', marginRight: theme.spacing.xs }}>
<div css={{ flex: 1 }}>{alias}</div>
<div>
<FormattedMessage
defaultMessage="This version"
description="Model registry > model version alias select > Indicator for alias of selected version"
/>
{existingAliases
.filter((alias) => !pinnedAliases?.includes(alias))
.map((alias) => (
<LegacySelect.Option key={alias} value={alias} data-testid="model-alias-option">
<div key={alias} css={{ display: 'flex', marginRight: theme.spacing.xs }}>
<div css={{ flex: 1 }}>{alias}</div>
<div>
<FormattedMessage
defaultMessage="This version"
description="Model registry > model version alias select > Indicator for alias of selected version"
/>
</div>
</div>
</div>
</LegacySelect.Option>
))}
</LegacySelect.Option>
))}
{Object.entries(aliasToVersionMap)
.filter(([, otherVersion]) => otherVersion !== version)
.map(([alias, aliasedVersion]) => (
Expand Down
12 changes: 10 additions & 2 deletions mlflow/server/js/src/common/components/AliasTag.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,22 @@ import type { TagProps } from '@databricks/design-system';

type ModelVersionAliasTagProps = { value: string; compact?: boolean } & Pick<
TagProps,
'closable' | 'onClose' | 'className'
'closable' | 'onClose' | 'className' | 'color'
>;

// When displayed in compact mode (e.g. within <Select>), constrain the width to 160 pixels
const COMPACT_MODE_MAX_WIDTH = 160;
const REGULAR_MAX_WIDTH = 300;
const TAG_SYMBOL = '@';

export const AliasTag = ({ value, closable, onClose, className, compact = false }: ModelVersionAliasTagProps) => {
export const AliasTag = ({
value,
closable,
onClose,
className,
compact = false,
color,
}: ModelVersionAliasTagProps) => {
const { theme } = useDesignSystemTheme();
return (
<Tag
Expand All @@ -24,6 +31,7 @@ export const AliasTag = ({ value, closable, onClose, className, compact = false
closable={closable}
onClose={onClose}
title={value}
color={color}
>
<span
css={{
Expand Down
7 changes: 7 additions & 0 deletions mlflow/server/js/src/common/hooks/useEditAliasesModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { isEqual } from 'lodash';
import { useCallback, useMemo, useState } from 'react';

import { Alert, Button, LegacyForm, Modal, useDesignSystemTheme } from '@databricks/design-system';
import type { TagColors } from '@databricks/design-system';
import { Typography } from '@databricks/design-system';
import { AliasSelect } from '../components/AliasSelect';
import { FormattedMessage } from 'react-intl';
Expand All @@ -21,13 +22,17 @@ export const useEditAliasesModal = ({
getTitle,
description,
reservedAliases = [],
pinnedLatestVersion,
pinnedAliasColor,
}: {
aliases: AliasMap;
onSuccess?: () => void;
onSave: (currentlyEditedVersion: string, existingAliases: string[], draftAliases: string[]) => Promise<any>;
getTitle: (version: string) => React.ReactNode;
description?: React.ReactNode;
reservedAliases?: string[];
pinnedLatestVersion?: string;
pinnedAliasColor?: TagColors;
}) => {
const [isLoading, setIsLoading] = useState(false);
const [showModal, setShowModal] = useState(false);
Expand Down Expand Up @@ -158,6 +163,8 @@ export const useEditAliasesModal = ({
draftAliases={draftAliases}
existingAliases={existingAliases}
setDraftAliases={setDraftAliases}
pinnedAliases={pinnedLatestVersion === currentlyEditedVersion ? ['latest'] : undefined}
pinnedAliasColor={pinnedAliasColor}
/>
</LegacyForm.Item>
<div css={{ display: 'flex', flexDirection: 'column', gap: theme.spacing.xs }}>
Expand Down
29 changes: 20 additions & 9 deletions mlflow/server/js/src/common/hooks/useEditKeyValueTagsModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,14 @@ export const useEditKeyValueTagsModal = <T extends { tags?: KeyValueEntity[] }>(
allAvailableTags,
valueRequired = false,
title,
saveButtonLabel,
}: {
onSuccess?: () => void;
saveTagsHandler: (editedEntity: T, existingTags: KeyValueEntity[], newTags: KeyValueEntity[]) => Promise<any>;
allAvailableTags?: string[];
valueRequired?: boolean;
title?: React.ReactNode;
saveButtonLabel?: string;
}) => {
const editedEntityRef = useRef<T>();
const [errorMessage, setErrorMessage] = useState<string>('');
Expand Down Expand Up @@ -175,7 +177,12 @@ export const useEditKeyValueTagsModal = <T extends { tags?: KeyValueEntity[] }>(
})}
</Button>
{showPopoverMessage ? (
<UnsavedTagPopoverTrigger formValues={formValues} isLoading={isLoading} onSaveTask={saveTags} />
<UnsavedTagPopoverTrigger
formValues={formValues}
isLoading={isLoading}
onSaveTask={saveTags}
saveButtonLabel={saveButtonLabel}
/>
) : (
<Tooltip
content={
Expand All @@ -196,10 +203,11 @@ export const useEditKeyValueTagsModal = <T extends { tags?: KeyValueEntity[] }>(
type="primary"
onClick={saveTags}
>
{intl.formatMessage({
defaultMessage: 'Save tags',
description: 'Key-value tag editor modal > Manage Tag save button',
})}
{saveButtonLabel ??
intl.formatMessage({
defaultMessage: 'Save tags',
description: 'Key-value tag editor modal > Manage Tag save button',
})}
</Button>
</Tooltip>
)}
Expand Down Expand Up @@ -304,10 +312,12 @@ function UnsavedTagPopoverTrigger({
isLoading,
formValues,
onSaveTask,
saveButtonLabel,
}: {
isLoading: boolean;
formValues: any;
onSaveTask: () => void;
saveButtonLabel?: string;
}) {
const intl = useIntl();
const { theme } = useDesignSystemTheme();
Expand All @@ -334,10 +344,11 @@ function UnsavedTagPopoverTrigger({
loading={isLoading}
type="primary"
>
{intl.formatMessage({
defaultMessage: 'Save tags',
description: 'Key-value tag editor modal > Manage Tag save button',
})}
{saveButtonLabel ??
intl.formatMessage({
defaultMessage: 'Save tags',
description: 'Key-value tag editor modal > Manage Tag save button',
})}
</Button>
</Popover.Trigger>
<Popover.Content align="end" aria-label={shownText}>
Expand Down
1 change: 1 addition & 0 deletions mlflow/server/js/src/common/utils/ErrorUtils.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ class ErrorUtils {
CHAT_SESSIONS: 'Chat Sessions',
MODEL_SERVING: 'Model Serving',
RUN_TRACKING: 'Run Tracking',
MCP_REGISTRY: 'MCP Registry',
};
}

Expand Down
Loading
Loading