Skip to content

Commit

Permalink
[feat] Show logged artifacts in Aim UI (#3114)
Browse files Browse the repository at this point in the history
* [feat] Add artifacts info in Run's info web API response

* [feat] Add ability to specify the icon in CopyToClipboard component

* [feat] Add artifacts card component

* [feat] Add artifacts card in Run detail page overview tab

* [doc] Update changelog
  • Loading branch information
alberttorosyan authored Mar 13, 2024
1 parent 285dec9 commit f83fe1b
Show file tree
Hide file tree
Showing 10 changed files with 140 additions and 2 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
### Enhancements:
- Replace grpc with http/ws as transport for aim tracking server (mihran113)
- Remove `aim storage upgrade 2to3` command (mihran113)
- Support artifacts logging and storage in AWS S3 (alberttorosyan)

### Fixes
- Allow the web UI to serve assets symlinked into the static files directory (martenlienen)
Expand Down
7 changes: 7 additions & 0 deletions aim/web/api/runs/pydantic_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,10 +75,17 @@ class MetricSearchRunView(BaseModel):
props: PropsView


class ArtifactInfo(BaseModel):
name: str
path: str
uri: str


class RunInfoOut(BaseModel):
params: dict
traces: Dict[str, List[TraceOverview]]
props: PropsView
artifacts: List[ArtifactInfo]


RunMetricSearchApiOut = Dict[str, MetricSearchRunView]
Expand Down
11 changes: 11 additions & 0 deletions aim/web/api/runs/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,17 @@ def get_run_props(run: Run):
}


def get_run_artifacts(run: Run):
artifacts_info = []
for artifact in run.artifacts.values():
artifacts_info.append({
'name': artifact.name,
'path': artifact.path,
'uri': artifact.uri,
})
return artifacts_info


def numpy_to_encodable(array: np.ndarray) -> Optional[dict]:
encoded_numpy = {
'type': 'numpy',
Expand Down
4 changes: 3 additions & 1 deletion aim/web/api/runs/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
get_run_or_404,
get_run_params,
get_run_props,
get_run_artifacts,
metric_search_result_streamer,
run_active_result_streamer,
run_search_result_streamer,
Expand Down Expand Up @@ -151,7 +152,8 @@ async def run_params_api(run_id: str,
response = {
'params': get_run_params(run, skip_system=skip_system),
'traces': run.collect_sequence_info(sequence, skip_last_value=True),
'props': get_run_props(run)
'props': get_run_props(run),
'artifacts': get_run_artifacts(run),
}
# Convert NaN and Inf to strings
response = convert_nan_and_inf_to_str(response)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ function CopyToClipboard({
className = '',
copyContent = null,
iconSize = 'medium',
isURL = false,
}: ICopyToClipBoardProps): React.FunctionComponentElement<ICopyToClipBoardProps> {
const { onCopy, copied, setCopied } = useCopy(copyContent ?? contentRef);

Expand All @@ -27,12 +28,13 @@ function CopyToClipboard({
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [copied]);

const iconName = isURL ? 'link' : 'copy';
return (
<ErrorBoundary>
<Tooltip title={copied ? 'Copied!' : 'Copy to clipboard'}>
<span className={className} onClick={onCopy}>
<Button withOnlyIcon color='secondary' size={iconSize}>
{copied ? <Icon name='check' /> : <Icon name='copy' />}
{copied ? <Icon name='check' /> : <Icon name={iconName} />}
</Button>
</span>
</Tooltip>
Expand Down
12 changes: 12 additions & 0 deletions aim/web/ui/src/pages/RunDetail/RunOverviewTab/RunOverviewTab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import useRunMetricsBatch from '../hooks/useRunMetricsBatch';

import GitInfoCard from './components/GitInfoCard';
import RunOverviewTabMetricsCard from './components/MetricsCard/RunOverviewTabMetricsCard';
import RunOverviewTabArtifactsCard from './components/ArtifactsCard/RunOverviewTabArtifactsCard';
import RunOverviewTabPackagesCard from './components/Packages/RunOverviewTabPackagesCard';
import RunOverviewTabParamsCard from './components/ParamsCard/RunOverviewTabParamsCard';
import RunOverviewSidebar from './components/RunOverviewSidebar/RunOverviewSidebar';
Expand Down Expand Up @@ -50,6 +51,9 @@ function RunOverviewTab({ runData, runHash }: IRunOverviewTabProps) {
if (!_.isEmpty(runData?.runSystemBatch)) {
data.runSystemBatch = runData.runSystemBatch;
}
if (!_.isEmpty(runData?.runArtifacts)) {
data.artifacts = runData.runArtifacts;
}
if (systemParams) {
if (!_.isEmpty(systemParams.arguments)) {
data.cliArguments = systemParams.arguments;
Expand Down Expand Up @@ -114,6 +118,14 @@ function RunOverviewTab({ runData, runHash }: IRunOverviewTabProps) {
/>
</ErrorBoundary>
)}
{_.isEmpty(cardsData.artifacts) ? null : (
<ErrorBoundary>
<RunOverviewTabArtifactsCard
artifacts={cardsData.artifacts}
isRunInfoLoading={runData?.isRunInfoLoading}
/>
</ErrorBoundary>
)}
{_.isEmpty(cardsData.cliArguments) ? null : (
<ErrorBoundary>
<RunOverviewTabCLIArgumentsCard
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export interface IRunOverviewTabArtifactsCardProps {
artifacts: { [key: string]: string };
isRunInfoLoading: boolean;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
import React from 'react';

import { IconEye } from '@tabler/icons-react';

import { Card, Text } from 'components/kit';
import ErrorBoundary from 'components/ErrorBoundary/ErrorBoundary';
import BusyLoaderWrapper from 'components/BusyLoaderWrapper/BusyLoaderWrapper';
import { ICardProps } from 'components/kit/Card/Card.d';
import CopyToClipBoard from 'components/CopyToClipBoard/CopyToClipBoard';

import { formatValue } from 'utils/formatValue';

import { IRunOverviewTabArtifactsCardProps } from './RunOverviewTabArtifactsCard.d';

function RunOverviewTabArtifactsCard({
artifacts,
isRunInfoLoading,
}: IRunOverviewTabArtifactsCardProps) {
const tableData = React.useMemo(() => artifacts, [artifacts]);

const dataListProps = React.useMemo(
(): ICardProps['dataListProps'] => ({
calcTableHeight: true,
tableColumns: [
{
dataKey: 'name',
key: 'name',
width: '20%',
title: (
<Text weight={600} size={14} tint={100}>
Name
<Text
weight={600}
size={14}
tint={50}
className='RunOverviewTab__cardBox__tableTitleCount'
>
({tableData.length})
</Text>
</Text>
),
cellRenderer: ({ cellData }: any) => (
<p title={cellData}>{cellData}</p>
),
},
{
dataKey: 'path',
key: 'path',
width: '40%',
title: 'Path',
cellRenderer: ({ cellData }: any) => (
<p title={cellData}>{cellData}</p>
),
},
{
dataKey: 'uri',
key: 'uri',
width: '40%',
title: 'URI',
cellRenderer: ({ cellData }: any) => (
<div>
<Text size={14}>{cellData}</Text>
<CopyToClipBoard
iconSize='xSmall'
isURL='true'
copyContent={cellData}
/>
</div>
),
},
],
tableData,
illustrationConfig: {
size: 'large',
title: 'No Results',
},
}),
[tableData],
);
return (
<ErrorBoundary>
<BusyLoaderWrapper isLoading={isRunInfoLoading} height='100%'>
<Card
title='Run Artifacts'
className='RunOverviewTab__cardBox'
dataListProps={dataListProps}
/>
</BusyLoaderWrapper>
</ErrorBoundary>
);
}

RunOverviewTabArtifactsCard.displayName = 'RunOverviewTabArtifactsCard';

export default React.memo<IRunOverviewTabArtifactsCardProps>(
RunOverviewTabArtifactsCard,
);
1 change: 1 addition & 0 deletions aim/web/ui/src/services/models/runs/runDetailAppModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ function getRunInfo(runHash: string): IApiRequest<void> {
runParams: data.params,
runTraces: data.traces,
runInfo: data.props,
runArtifacts: data.artifacts,
experimentId: data.props.experiment.id,
isRunInfoLoading: false,
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,5 @@ export interface ICopyToClipBoardProps {
className?: string;
copyContent?: string | null;
iconSize?: IButtonProps['size'];
isURL?: bool | null;
}

0 comments on commit f83fe1b

Please sign in to comment.