Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(ui): delete objects from the ui #3261

Merged
merged 38 commits into from
Jan 10, 2025
Merged
Show file tree
Hide file tree
Changes from 34 commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
69a8da7
chore(ui): add frontend handling for deleted objects
gtarpenning Dec 16, 2024
fe1e350
undo
gtarpenning Dec 16, 2024
4069205
lint
gtarpenning Dec 16, 2024
873dc27
better delete modal design
gtarpenning Dec 19, 2024
0162f69
better error handling and multi-delete support
gtarpenning Dec 19, 2024
314e57c
better style
gtarpenning Dec 19, 2024
03b3910
add max to show for multi delete
gtarpenning Dec 19, 2024
62d0f32
Merge branch 'master' into griffin/frontend-objects-deletino
gtarpenning Jan 3, 2025
9a16ad5
lint
gtarpenning Jan 3, 2025
e81a313
Merge branch 'master' into griffin/frontend-objects-deletino
gtarpenning Jan 6, 2025
77ffe64
clean
gtarpenning Jan 6, 2025
eea58b0
refactor for clarity
gtarpenning Jan 6, 2025
b25cd1b
only show delete buttons for admins
gtarpenning Jan 6, 2025
a58565d
chore(weave): soft deletion for objects server (#3259)
gtarpenning Jan 6, 2025
6ce18b7
chore(weave): allow call.feedback.add() for annotations (#3323)
gtarpenning Jan 6, 2025
ee4dde1
working
gtarpenning Jan 7, 2025
77d5772
Merge branch 'master' into griffin/frontend-objects-deletino
gtarpenning Jan 7, 2025
66c0dad
merge
gtarpenning Jan 8, 2025
4796f40
review comments
gtarpenning Jan 8, 2025
119571b
dont regex error str
gtarpenning Jan 8, 2025
3fadc2d
better error
gtarpenning Jan 8, 2025
f819f80
add number response
gtarpenning Jan 8, 2025
0e97e19
cleanup
gtarpenning Jan 8, 2025
629287b
lint
gtarpenning Jan 8, 2025
d2c051a
wip
gtarpenning Jan 8, 2025
c8d67fb
simpler error handling
gtarpenning Jan 8, 2025
121cb37
allow error handling to happen naturally
gtarpenning Jan 8, 2025
3248926
Merge branch 'master' into griffin/frontend-objects-deletino
gtarpenning Jan 9, 2025
e218391
Merge branch 'master' into griffin/frontend-objects-deletino
gtarpenning Jan 9, 2025
1d25acd
Merge branch 'master' into griffin/frontend-objects-deletino
gtarpenning Jan 9, 2025
59c9a32
eval compare page robust to model delete
gtarpenning Jan 9, 2025
5820182
add delete button to dataset page
gtarpenning Jan 9, 2025
33b354d
lint
gtarpenning Jan 9, 2025
0893dd4
cosmetics
gtarpenning Jan 9, 2025
a8dcfbd
Merge branch 'master' into griffin/frontend-objects-deletino
gtarpenning Jan 10, 2025
59716f1
handle eval compare page with deleted dataset
gtarpenning Jan 10, 2025
387d6c3
bump
gtarpenning Jan 10, 2025
17278be
Merge branch 'master' into griffin/frontend-objects-deletino
gtarpenning Jan 10, 2025
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
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {Icon, IconName, IconNames} from '../../../Icon';
import {useWeaveflowRouteContext} from '../Browse3/context';
import {Link} from '../Browse3/pages/common/Links';
import {useWFHooks} from '../Browse3/pages/wfReactInterface/context';
import {isObjDeleteError} from '../Browse3/pages/wfReactInterface/utilities';
import {
ObjectVersionKey,
OpVersionKey,
Expand Down Expand Up @@ -125,6 +126,11 @@ export const SmallRef: FC<{
}
const objectVersion = useObjectVersion(objVersionKey);
const opVersion = useOpVersion(opVersionKey);

const isDeleted =
isObjDeleteError(objectVersion?.error) ||
isObjDeleteError(opVersion?.error);

const versionIndex =
objectVersion.result?.versionIndex ?? opVersion.result?.versionIndex;

Expand Down Expand Up @@ -177,13 +183,14 @@ export const SmallRef: FC<{
overflow: 'hidden',
whiteSpace: 'nowrap',
textOverflow: 'ellipsis',
textDecoration: isDeleted ? 'line-through' : 'none',
}}>
{label}
</Box>
)}
</Box>
);
if (refTypeQuery.loading) {
if (refTypeQuery.loading || isDeleted) {
return Item;
}
if (!isArtifactRef && !isWeaveObjRef) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
ScrollableTabContent,
SimplePageLayoutWithHeader,
} from '../pages/common/SimplePageLayout';
import {DeleteObjectButtonWithModal} from '../pages/ObjectVersionPage';
import {TabUseDataset} from '../pages/TabUseDataset';
import {useWFHooks} from '../pages/wfReactInterface/context';
import {objectVersionKeyToRefUri} from '../pages/wfReactInterface/utilities';
Expand All @@ -21,7 +22,8 @@ import {CustomWeaveTypeProjectContext} from '../typeViews/CustomWeaveTypeDispatc

export const DatasetVersionPage: React.FC<{
objectVersion: ObjectVersionSchema;
}> = ({objectVersion}) => {
showDeleteButton?: boolean;
}> = ({objectVersion, showDeleteButton}) => {
const {useRootObjectVersions, useRefsData} = useWFHooks();
const entityName = objectVersion.entity;
const projectName = objectVersion.project;
Expand Down Expand Up @@ -73,7 +75,7 @@ export const DatasetVersionPage: React.FC<{
}
headerContent={
<Tailwind>
<div className="grid w-full auto-cols-max grid-flow-col gap-[16px] text-[14px]">
<div className="grid w-full grid-flow-col grid-cols-[auto_auto_1fr] gap-[16px] text-[14px]">
<div className="block">
<p className="text-moon-500">Name</p>
<ObjectVersionsLink
Expand Down Expand Up @@ -106,6 +108,11 @@ export const DatasetVersionPage: React.FC<{
<p className="text-moon-500">Version</p>
<p>{objectVersionIndex}</p>
</div>
{showDeleteButton && (
<div className="ml-auto mr-0">
<DeleteObjectButtonWithModal objVersionSchema={objectVersion} />
</div>
)}
</div>
</Tailwind>
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
GridRowId,
} from '@mui/x-data-grid-pro';
import {Button} from '@wandb/weave/components/Button';
import {parseRef} from '@wandb/weave/react';
import _ from 'lodash';
import React, {
Dispatch,
Expand All @@ -21,6 +22,7 @@ import React, {
import {parseRefMaybe} from '../../../../../../react';
import {LoadingDots} from '../../../../../LoadingDots';
import {Browse2OpDefCode} from '../../../Browse2/Browse2OpDefCode';
import {objectRefDisplayName} from '../../../Browse2/SmallRef';
import {isWeaveRef} from '../../filters/common';
import {StyledDataGrid} from '../../StyledDataGrid';
import {
Expand Down Expand Up @@ -161,10 +163,15 @@ export const ObjectViewer = ({

const refValues: RefValues = {};
for (const [r, v] of _.zip(refs, resolvedRefData)) {
if (!r || !v) {
if (!r) {
// Shouldn't be possible
continue;
}
if (!v) {
// Value for ref not found, must be deleted
refValues[r] = deletedRefValuePlaceholder(r);
continue;
}
let val = r;
if (v == null) {
console.error('Error resolving ref', r);
Expand Down Expand Up @@ -725,3 +732,18 @@ const useTruncatedData = (data: Data) => {

return {truncatedData, truncatedStore, setTruncatedData, setTruncatedStore};
};

// Placeholder value for deleted refs
const DELETED_REF_KEY = '_weave_deleted_ref';
const deletedRefValuePlaceholder = (
ref: string
): {[DELETED_REF_KEY]: string} => {
const parsedRef = parseRef(ref);
const refString = objectRefDisplayName(parsedRef).label;
return {[DELETED_REF_KEY]: refString};
};
export const maybeGetDeletedRefValuePlaceholderFromRow = (
row: any
): string | undefined => {
return row.value?.[DELETED_REF_KEY];
};
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import React, {FC, MouseEvent} from 'react';
import {Button} from '../../../../../Button';
import {Tooltip} from '../../../../../Tooltip';
import {CursorBox} from './CursorBox';
import {maybeGetDeletedRefValuePlaceholderFromRow} from './ObjectViewer';

const INSET_SPACING = 40;

Expand All @@ -32,7 +33,9 @@ export const ObjectViewerGroupingCell: FC<
event.stopPropagation();
};

const deletedRef = maybeGetDeletedRefValuePlaceholderFromRow(row);
const tooltipContent = row.path.toString();
const textContent = deletedRef ?? props.value;
const box = (
<CursorBox
$isClickable={isGroup || isExpandableRef}
Expand Down Expand Up @@ -109,8 +112,9 @@ export const ObjectViewerGroupingCell: FC<
textOverflow: 'ellipsis',
whiteSpace: 'nowrap',
flex: '1 1 auto',
textDecoration: deletedRef ? 'line-through' : 'none',
}}>
{props.value}
{textContent}
</Box>
}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export type EvaluationComparisonSummary = {

// Models are the Weave Objects used to define the model logic and properties.
models: {
[modelRef: string]: ModelObj;
Copy link
Member Author

Choose a reason for hiding this comment

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

models can now be deleted!

[modelRef: string]: ModelObj | null;
};

// ScoreMetrics define the metrics that are associated on each individual prediction
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {Box} from '@material-ui/core';
import {Box} from '@mui/material';
import React, {useMemo} from 'react';

import {
Expand All @@ -9,9 +9,10 @@ import {
import {hexToRGB} from '../../../../../../../../common/css/utils';
import {parseRef, WeaveObjectRef} from '../../../../../../../../react';
import {Icon, IconNames} from '../../../../../../../Icon';
import {SmallRef} from '../../../../../Browse2/SmallRef';
import {objectRefDisplayName, SmallRef} from '../../../../../Browse2/SmallRef';
import {CallLink, ObjectVersionLink} from '../../../common/Links';
import {useWFHooks} from '../../../wfReactInterface/context';
import {isObjDeleteError} from '../../../wfReactInterface/utilities';
import {ObjectVersionKey} from '../../../wfReactInterface/wfDataModelHooksInterface';
import {EvaluationComparisonState} from '../../ecpState';

Expand Down Expand Up @@ -43,11 +44,10 @@ export const EvaluationModelLink: React.FC<{
}> = props => {
const {useObjectVersion} = useWFHooks();
const evaluationCall = props.state.summary.evaluationCalls[props.callId];
const modelObj = props.state.summary.models[evaluationCall.modelRef];
const objRef = useMemo(
() => parseRef(modelObj.ref) as WeaveObjectRef,
[modelObj.ref]
);
const objRef = useMemo(() => {
Comment on lines 46 to -50
Copy link
Member Author

Choose a reason for hiding this comment

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

@tssweeney does this make sense? I think we actually never needed modelObj because we are only using it for its ref? Which I think is = to evaluationCall.modelRef but could be wrong...

Copy link
Collaborator

Choose a reason for hiding this comment

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

looks correct!

return parseRef(evaluationCall.modelRef) as WeaveObjectRef;
}, [evaluationCall.modelRef]);

const objVersionKey = useMemo(() => {
return {
scheme: 'weave',
Expand All @@ -69,10 +69,31 @@ export const EvaluationModelLink: React.FC<{
]);
const objectVersion = useObjectVersion(objVersionKey);

if (isObjDeleteError(objectVersion.error)) {
return (
<Box
sx={{
height: '22px',
flex: 1,
minWidth: 0,
overflow: 'hidden',
whiteSpace: 'nowrap',
textOverflow: 'ellipsis',
fontWeight: 500,
textDecoration: 'line-through',
}}>
<Box display="flex" alignItems="center">
<ModelIcon />
{objectRefDisplayName(objRef).label}
</Box>
</Box>
);
}

return (
<ObjectVersionLink
entityName={modelObj.entity}
projectName={modelObj.project}
entityName={objRef.entityName}
projectName={objRef.projectName}
objectName={objRef.artifactName}
version={objRef.artifactVersion}
versionIndex={objectVersion.result?.versionIndex ?? 0}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,11 +90,11 @@ export const ScorecardSection: React.FC<{
const propsRes: {[prop: string]: {[ref: string]: any}} = {};
modelRefs.forEach(ref => {
const model = props.state.summary.models[ref];
Object.keys(model.properties).forEach(prop => {
Object.keys(model?.properties ?? {}).forEach(prop => {
if (!propsRes[prop]) {
propsRes[prop] = {};
}
propsRes[prop][ref] = model.properties[prop];
propsRes[prop][ref] = model?.properties?.[prop];
});
});

Expand All @@ -104,7 +104,7 @@ export const ScorecardSection: React.FC<{
if (!propsRes.predict) {
propsRes.predict = {};
}
propsRes.predict[ref] = model.predictOpRef;
propsRes.predict[ref] = model?.predictOpRef;
});

return propsRes;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,21 @@
import Box from '@mui/material/Box';
import {Button} from '@wandb/weave/components/Button';
import {useObjectViewEvent} from '@wandb/weave/integrations/analytics/useViewEvents';
import React, {useMemo} from 'react';
import React, {useMemo, useState} from 'react';

import {maybePluralizeWord} from '../../../../../core/util/string';
import {Icon, IconName} from '../../../../Icon';
import {LoadingDots} from '../../../../LoadingDots';
import {Tailwind} from '../../../../Tailwind';
import {Tooltip} from '../../../../Tooltip';
import {useClosePeek} from '../context';
import {DatasetVersionPage} from '../datasets/DatasetVersionPage';
import {NotFoundPanel} from '../NotFoundPanel';
import {CustomWeaveTypeProjectContext} from '../typeViews/CustomWeaveTypeDispatcher';
import {WeaveCHTableSourceRefContext} from './CallPage/DataTableView';
import {ObjectViewerSection} from './CallPage/ObjectViewerSection';
import {WFHighLevelCallFilter} from './CallsPage/callsTableFilter';
import {DeleteModal, useShowDeleteButton} from './common/DeleteModal';
import {
CallLink,
CallsLink,
Expand All @@ -34,6 +37,7 @@ import {TabUsePrompt} from './TabUsePrompt';
import {KNOWN_BASE_OBJECT_CLASSES} from './wfReactInterface/constants';
import {useWFHooks} from './wfReactInterface/context';
import {
isObjDeleteError,
objectVersionKeyToRefUri,
refUriToOpVersionKey,
} from './wfReactInterface/utilities';
Expand Down Expand Up @@ -94,7 +98,10 @@ export const ObjectVersionPage: React.FC<{
path: props.filePath,
refExtra: props.refExtra,
});
if (objectVersion.loading) {
if (isObjDeleteError(objectVersion.error)) {
const deletedAtMessage = objectVersion.error?.message ?? 'Object deleted';
return <NotFoundPanel title={deletedAtMessage} />;
} else if (objectVersion.loading) {
return <CenteredAnimatedLoader />;
} else if (objectVersion.result == null) {
return <NotFoundPanel title="Object not found" />;
Expand Down Expand Up @@ -176,6 +183,8 @@ const ObjectVersionPageInner: React.FC<{
return data.result?.[0] ?? {};
}, [data.loading, data.result]);

const showDeleteButton = useShowDeleteButton();

const viewerDataAsObject = useMemo(() => {
const dataIsPrimitive =
typeof viewerData !== 'object' ||
Expand All @@ -199,7 +208,12 @@ const ObjectVersionPageInner: React.FC<{
}

if (isDataset) {
return <DatasetVersionPage objectVersion={objectVersion} />;
return (
<DatasetVersionPage
objectVersion={objectVersion}
showDeleteButton={showDeleteButton}
/>
);
}

return (
Expand All @@ -216,7 +230,7 @@ const ObjectVersionPageInner: React.FC<{
}
headerContent={
<Tailwind>
<div className="grid w-full auto-cols-max grid-flow-col gap-[16px] text-[14px]">
<div className="grid w-full grid-flow-col grid-cols-[auto_auto_1fr] gap-[16px] text-[14px]">
<div className="block">
<p className="text-moon-500">Name</p>
<div className="flex items-center">
Expand Down Expand Up @@ -257,6 +271,11 @@ const ObjectVersionPageInner: React.FC<{
<p>{refExtra}</p>
</div>
)}
{showDeleteButton && (
<div className="ml-auto">
<DeleteObjectButtonWithModal objVersionSchema={objectVersion} />
</div>
)}
</div>
</Tailwind>
}
Expand Down Expand Up @@ -623,3 +642,41 @@ const OpVersionCallsLink: React.FC<{
</>
);
};

export const DeleteObjectButtonWithModal: React.FC<{
objVersionSchema: ObjectVersionSchema;
overrideDisplayStr?: string;
}> = ({objVersionSchema, overrideDisplayStr}) => {
const {useObjectDeleteFunc} = useWFHooks();
const closePeek = useClosePeek();
const {objectVersionsDelete} = useObjectDeleteFunc();
const [deleteModalOpen, setDeleteModalOpen] = useState(false);

const deleteStr =
overrideDisplayStr ??
`${objVersionSchema.objectId}:v${objVersionSchema.versionIndex}`;

return (
<>
<Button
icon="delete"
variant="ghost"
onClick={() => setDeleteModalOpen(true)}
/>
<DeleteModal
open={deleteModalOpen}
onClose={() => setDeleteModalOpen(false)}
deleteTitleStr={deleteStr}
onDelete={() =>
objectVersionsDelete(
objVersionSchema.entity,
objVersionSchema.project,
objVersionSchema.objectId,
[objVersionSchema.versionHash]
)
}
onSuccess={closePeek}
/>
</>
);
};
Loading
Loading