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
35 changes: 18 additions & 17 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,20 +18,21 @@ repos:
hooks:
- id: end-of-file-fixer

- repo: https://github.com/pre-commit/mirrors-eslint
rev: ''
hooks:
- id: eslint
name: ESLint
entry: eslint --fix --config frontend/.eslintrc --ignore-path frontend/.eslintignore
language: node
pass_filenames: false
cwd: "frontend/"
additional_dependencies:
- eslint@8.31.0
- eslint-config-prettier@8.10.0
- eslint-plugin-i18n@2.4.0
- eslint-plugin-prettier@4.2.1
- eslint-plugin-simple-import-sort@10.0.0
- '@typescript-eslint/eslint-plugin@5.48.1'
- '@typescript-eslint/parser@5.48.1'
# TODO(oleg): fix ESLint warnings
# - repo: https://github.com/pre-commit/mirrors-eslint
# rev: 'v8.31.0'
# hooks:
# - id: eslint
# name: ESLint
# entry: eslint --fix --config frontend/.eslintrc --ignore-path frontend/.eslintignore
# language: node
# pass_filenames: false
# cwd: "frontend/"
# additional_dependencies:
# - eslint@8.31.0
# - eslint-config-prettier@8.10.0
# - eslint-plugin-i18n@2.4.0
# - eslint-plugin-prettier@4.2.1
# - eslint-plugin-simple-import-sort@10.0.0
# - '@typescript-eslint/eslint-plugin@5.48.1'
# - '@typescript-eslint/parser@5.48.1'
2 changes: 2 additions & 0 deletions frontend/src/libs/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,3 +108,5 @@ export const isValidUrl = (urlString: string) => {
export const includeSubString = (value: string, query: string) => {
return value.toLowerCase().includes(query.trim().toLowerCase());
};

export const capitalize = (str: string): string => str.charAt(0).toUpperCase() + str.slice(1);
41 changes: 40 additions & 1 deletion frontend/src/libs/run.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,17 @@
import { get as _get } from 'lodash';
import { StatusIndicatorProps } from '@cloudscape-design/components';

import { capitalize } from 'libs';

import { IModelExtended } from '../pages/Models/List/types';

export const getStatusIconType = (status: IRun['status']): StatusIndicatorProps['type'] => {
export const getStatusIconType = (
status: IRun['status'] | TJobStatus,
terminationReason: string | null | undefined,
): StatusIndicatorProps['type'] => {
if (terminationReason === 'interrupted_by_no_capacity') {
return 'stopped';
}
switch (status) {
case 'failed':
return 'error';
Expand All @@ -25,6 +33,37 @@ export const getStatusIconType = (status: IRun['status']): StatusIndicatorProps[
}
};

export const getStatusIconColor = (
status: IRun['status'] | TJobStatus,
terminationReason: string | null | undefined,
): StatusIndicatorProps.Color | undefined => {
if (terminationReason === 'failed_to_start_due_to_no_capacity' || terminationReason === 'interrupted_by_no_capacity') {
return 'yellow';
}

switch (status) {
case 'pulling':
return 'green';
case 'aborted':
return 'yellow';
default:
return undefined;
}
};

export const getRunStatusMessage = (run: IRun): string => {
if (run.latest_job_submission?.status_message) {
return capitalize(run.latest_job_submission.status_message);
} else {
return capitalize(run.status);
}
};

export const getRunError = (run: IRun): string | null => {
const error = run.error ?? run.latest_job_submission?.error ?? null;
return error ? capitalize(error) : null;
};

export const getExtendedModelFromRun = (run: IRun): IModelExtended | null => {
if (!run?.service?.model) return null;

Expand Down
35 changes: 28 additions & 7 deletions frontend/src/pages/Project/Backends/Table/constants.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,26 @@ export const BACKENDS_HELP_SKY = {
header: <h2>Backends</h2>,
body: (
<>
<p>To use <code>dstack</code> with cloud providers, you have to configure backends.</p>
<p>
To use <code>dstack</code> with cloud providers, you have to configure backends.
</p>
<h4>Marketplace</h4>
<p>By default, <code>dstack Sky</code> includes a preset of backends that let you access compute from the {' '}
<code>dstack</code> marketplace and pay through your <code>dstack Sky</code> user billing.</p>
<p>
By default, <code>dstack Sky</code> includes a preset of backends that let you access compute from the{' '}
<code>dstack</code> marketplace and pay through your <code>dstack Sky</code> user billing.
</p>
<h4>Your own cloud accounts</h4>
<p>You can also configure custom backends to use your own cloud providers, either instead of or in addition to the default ones.</p>
<p>See the <a href={'https://dstack.ai/docs/concepts/backends'} target='_blank'>documentation</a> for the list of supported backends.</p>
<p>
You can also configure custom backends to use your own cloud providers, either instead of or in addition to the
default ones.
</p>
<p>
See the{' '}
<a href={'https://dstack.ai/docs/concepts/backends'} target="_blank">
documentation
</a>{' '}
for the list of supported backends.
</p>
</>
),
};
Expand All @@ -19,8 +32,16 @@ export const BACKENDS_HELP_ENTERPRISE = {
header: <h2>Backends</h2>,
body: (
<>
<p>To use <code>dstack</code> with cloud providers, you have to configure backends.</p>
<p>See the <a href={'https://dstack.ai/docs/concepts/backends'} target='_blank'>documentation</a> for the list of supported backends.</p>
<p>
To use <code>dstack</code> with cloud providers, you have to configure backends.
</p>
<p>
See the{' '}
<a href={'https://dstack.ai/docs/concepts/backends'} target="_blank">
documentation
</a>{' '}
for the list of supported backends.
</p>
</>
),
};
5 changes: 4 additions & 1 deletion frontend/src/pages/Project/Backends/YAMLForm/constants.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,10 @@ export const CONFIG_YAML_HELP_ENTERPRISE = {
</p>
<p>
Each backend type may support different properties. See the{' '}
<a href={'https://dstack.ai/docs/concepts/backends'} target='_blank'>documentaiton</a> for more examples.
<a href={'https://dstack.ai/docs/concepts/backends'} target="_blank">
documentaiton
</a>{' '}
for more examples.
</p>
</>
),
Expand Down
11 changes: 8 additions & 3 deletions frontend/src/pages/Project/Details/Settings/constants.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,17 @@ export const CLI_INFO = {
<>
<p>
To use this project with your CLI, add it using the
<a href={'https://dstack.ai/docs/reference/cli/dstack/project/'} target='_blank'>
<code>dstack project add</code></a> command.
<a href={'https://dstack.ai/docs/reference/cli/dstack/project/'} target="_blank">
<code>dstack project add</code>
</a>{' '}
command.
</p>
<p>
To learn how to install the CLI, refer to the{' '}
<a href={'https://dstack.ai/docs/cli/installation'} target='_blank'>installation</a> guide.
<a href={'https://dstack.ai/docs/cli/installation'} target="_blank">
installation
</a>{' '}
guide.
</p>
</>
),
Expand Down
8 changes: 7 additions & 1 deletion frontend/src/pages/Project/Gateways/Table/constants.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,13 @@ export const GATEWAYS_INFO = {
body: (
<>
<p>Gateways manage the ingress traffic for running services.</p>
<p>To learn more about gateways, see the <a href={'https://dstack.ai/docs/concepts/gateways'} target='_blank'>documentation</a>.</p>
<p>
To learn more about gateways, see the{' '}
<a href={'https://dstack.ai/docs/concepts/gateways'} target="_blank">
documentation
</a>
.
</p>
</>
),
};
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,15 @@ import { useGetRunQuery } from 'services/run';

import { Logs } from '../../../Logs';
import {
getJobError,
getJobListItemBackend,
getJobListItemInstance,
getJobListItemPrice,
getJobListItemRegion,
getJobListItemResources,
getJobListItemSpot,
getJobStatus,
getJobStatusMessage,
getJobSubmittedAt,
getJobTerminationReason,
} from '../../List/helpers';
Expand Down Expand Up @@ -67,15 +69,15 @@ export const JobDetails = () => {
<div>
<Box variant="awsui-key-label">{t('projects.run.status')}</Box>
<div>
<StatusIndicator type={getStatusIconType(getJobStatus(jobData))}>
{t(`projects.run.statuses.${getJobStatus(jobData)}`)}
<StatusIndicator type={getStatusIconType(getJobStatus(jobData), getJobTerminationReason(jobData))}>
{getJobStatusMessage(jobData)}
</StatusIndicator>
</div>
</div>

<div>
<Box variant="awsui-key-label">{t('projects.run.termination_reason')}</Box>
<div>{getJobTerminationReason(jobData)}</div>
<Box variant="awsui-key-label">{t('projects.run.error')}</Box>
<div>{getJobError(jobData)}</div>
</div>

<div>
Expand Down
14 changes: 14 additions & 0 deletions frontend/src/pages/Runs/Details/Jobs/List/helpers.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { format } from 'date-fns';

import { DATE_TIME_FORMAT } from 'consts';
import { capitalize } from 'libs';

export const getJobListItemResources = (job: IJob) => {
return job.job_submissions?.[job.job_submissions.length - 1]?.job_provisioning_data?.instance_type?.resources?.description;
Expand Down Expand Up @@ -45,3 +46,16 @@ export const getJobStatus = (job: IJob) => {
export const getJobTerminationReason = (job: IJob) => {
return job.job_submissions?.[job.job_submissions.length - 1].termination_reason ?? '-';
};

export const getJobStatusMessage = (job: IJob): string | null => {
const latest_submission = job.job_submissions?.[job.job_submissions.length - 1];
if (latest_submission?.status_message) {
return capitalize(latest_submission.status_message);
} else {
return capitalize(latest_submission.status);
}
};

export const getJobError = (job: IJob): string | null => {
return job.job_submissions?.[job.job_submissions.length - 1]?.error ?? null;
};
14 changes: 11 additions & 3 deletions frontend/src/pages/Runs/Details/Jobs/List/hooks.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,17 @@ import { getStatusIconType } from 'libs/run';
import { ROUTES } from 'routes';

import {
getJobError,
getJobListItemBackend,
getJobListItemInstance,
getJobListItemPrice,
getJobListItemRegion,
getJobListItemResources,
getJobListItemSpot,
getJobStatus,
getJobStatusMessage,
getJobSubmittedAt,
getJobTerminationReason,
} from './helpers';

export const useColumnsDefinitions = ({ projectName, runId }: { projectName: string; runId: string }) => {
Expand Down Expand Up @@ -43,13 +46,18 @@ export const useColumnsDefinitions = ({ projectName, runId }: { projectName: str
cell: (item: IJob) => {
const status = getJobStatus(item);

if (!status) return '';

return (
<StatusIndicator type={getStatusIconType(status)}>{t(`projects.run.statuses.${status}`)}</StatusIndicator>
<StatusIndicator type={getStatusIconType(status, getJobTerminationReason(item))}>
{getJobStatusMessage(item)}
</StatusIndicator>
);
},
},
{
id: 'error',
header: t('projects.run.error'),
cell: (item: IJob) => getJobError(item),
},
{
id: 'resources',
header: `${t('projects.run.resources')}`,
Expand Down
17 changes: 14 additions & 3 deletions frontend/src/pages/Runs/Details/RunDetails/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { format } from 'date-fns';
import { Box, ColumnLayout, Container, Header, Loader, StatusIndicator } from 'components';

import { DATE_TIME_FORMAT } from 'consts';
import { getStatusIconType } from 'libs/run';
import { getRunError, getRunStatusMessage, getStatusIconColor, getStatusIconType } from 'libs/run';
import { useGetRunQuery } from 'services/run';

import {
Expand Down Expand Up @@ -47,6 +47,9 @@ export const RunDetails = () => {

if (!runData) return null;

const status = runData.latest_job_submission?.status ?? runData.status;
const terminationReason = runData.latest_job_submission?.termination_reason;

return (
<>
<Container header={<Header variant="h2">{t('common.general')}</Header>}>
Expand Down Expand Up @@ -82,12 +85,20 @@ export const RunDetails = () => {
<div>
<Box variant="awsui-key-label">{t('projects.run.status')}</Box>
<div>
<StatusIndicator type={getStatusIconType(runData.status)}>
{t(`projects.run.statuses.${runData.status}`)}
<StatusIndicator
type={getStatusIconType(status, terminationReason)}
colorOverride={getStatusIconColor(status, terminationReason)}
>
{getRunStatusMessage(runData)}
</StatusIndicator>
</div>
</div>

<div>
<Box variant="awsui-key-label">{t('projects.run.error')}</Box>
<div>{getRunError(runData)}</div>
</div>

{getRunListItemBackend(runData) && (
<div>
<Box variant="awsui-key-label">{t('projects.run.backend')}</Box>
Expand Down
22 changes: 15 additions & 7 deletions frontend/src/pages/Runs/List/hooks/useColumnsDefinitions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { format } from 'date-fns';
import { NavigateLink, StatusIndicator } from 'components';

import { DATE_TIME_FORMAT } from 'consts';
import { getRepoNameFromRun, getStatusIconType } from 'libs/run';
import { getRepoNameFromRun, getRunError, getRunStatusMessage, getStatusIconColor, getStatusIconType } from 'libs/run';
import { ROUTES } from 'routes';

import {
Expand Down Expand Up @@ -64,16 +64,24 @@ export const useColumnsDefinitions = () => {
{
id: 'status',
header: t('projects.run.status'),
cell: (item: IRun) => (
<StatusIndicator type={getStatusIconType(item.status)}>
{t(`projects.run.statuses.${item.status}`)}
</StatusIndicator>
),
cell: (item: IRun) => {
const status = item.latest_job_submission?.status ?? item.status;
const terminationReason = item.latest_job_submission?.termination_reason;

return (
<StatusIndicator
type={getStatusIconType(status, terminationReason)}
colorOverride={getStatusIconColor(status, terminationReason)}
>
{getRunStatusMessage(item)}
</StatusIndicator>
);
},
},
{
id: 'error',
header: t('projects.run.error'),
cell: (item: IRun) => item.error ?? '-',
cell: (item: IRun) => getRunError(item),
},
{
id: 'cost',
Expand Down
Loading
Loading