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

Serving impovements #937

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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
14 changes: 14 additions & 0 deletions frontend/src/components/CPUField.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import * as React from 'react';
import ValueUnitField from './ValueUnitField';
import { CPU_UNITS } from '../utilities/valueUnits';

type CPUFieldProps = {
onChange: (newValue: string) => void;
value?: string;
};

const CPUField: React.FC<CPUFieldProps> = ({ onChange, value = '1' }) => {
return <ValueUnitField min={1} onChange={onChange} options={CPU_UNITS} value={value} />;
};

export default CPUField;
14 changes: 14 additions & 0 deletions frontend/src/components/MemoryField.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import * as React from 'react';
import ValueUnitField from './ValueUnitField';
import { MEMORY_UNITS } from '../utilities/valueUnits';

type MemoryFieldProps = {
onChange: (newValue: string) => void;
value?: string;
};

const MemoryField: React.FC<MemoryFieldProps> = ({ onChange, value = '1Gi' }) => {
return <ValueUnitField min={1} onChange={onChange} options={MEMORY_UNITS} value={value} />;
};

export default MemoryField;
27 changes: 27 additions & 0 deletions frontend/src/components/NumberInputWrapper.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import * as React from 'react';
import { NumberInput } from '@patternfly/react-core';

type NumberInputWrapperProps = {
onChange: (newValue: number) => void;
value: number;
} & Omit<React.ComponentProps<typeof NumberInput>, 'onChange' | 'value' | 'onPlus' | 'onMinus'>;
lucferbux marked this conversation as resolved.
Show resolved Hide resolved

const NumberInputWrapper: React.FC<NumberInputWrapperProps> = ({
onChange,
value,
...otherProps
}) => {
return (
<NumberInput
{...otherProps}
value={value}
onChange={(e) => {
onChange(parseInt(e.currentTarget.value));
}}
onPlus={() => onChange(value + 1)}
onMinus={() => onChange(value - 1)}
/>
);
};

export default NumberInputWrapper;
15 changes: 15 additions & 0 deletions frontend/src/components/ScrollViewOnMount.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import * as React from 'react';

const ScrollViewOnMount: React.FC = () => {
return (
<div
ref={(elm) => {
if (elm) {
elm.scrollIntoView();
}
}}
/>
);
};

export default ScrollViewOnMount;
63 changes: 63 additions & 0 deletions frontend/src/components/ValueUnitField.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import * as React from 'react';
import { Dropdown, DropdownItem, DropdownToggle, Split, SplitItem } from '@patternfly/react-core';
import NumberInputWrapper from './NumberInputWrapper';
import { splitValueUnit, UnitOption, ValueUnitString } from '../utilities/valueUnits';

type ValueUnitFieldProps = {
/** @defaults to unlimited pos/neg */
min?: number;
/** @defaults to unlimited pos/neg */
max?: number;
onChange: (newValue: string) => void;
options: UnitOption[];
value: ValueUnitString;
};

const ValueUnitField: React.FC<ValueUnitFieldProps> = ({
min,
max,
onChange,
options,
value: fullValue,
}) => {
const [open, setOpen] = React.useState(false);
const [currentValue, currentUnitOption] = splitValueUnit(fullValue, options);

return (
<Split hasGutter>
<SplitItem>
<NumberInputWrapper
min={min}
max={max}
value={currentValue}
onChange={(value) => {
onChange(`${value || min}${currentUnitOption.unit}`);
}}
/>
</SplitItem>
<SplitItem>
<Dropdown
toggle={
<DropdownToggle id="toggle-basic" onToggle={() => setOpen(!open)}>
{currentUnitOption.name}
</DropdownToggle>
}
isOpen={open}
dropdownItems={options.map((option) => (
<DropdownItem
key={option.unit}
onClick={() => {
onChange(`${currentValue}${option.unit}`);
setOpen(false);
}}
>
{option.name}
</DropdownItem>
))}
/>
</SplitItem>
</Split>
);
};

export default ValueUnitField;
11 changes: 9 additions & 2 deletions frontend/src/pages/modelServing/ModelServingContext.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as React from 'react';
import { ServingRuntimeKind, InferenceServiceKind } from '../../k8sTypes';
import { ServingRuntimeKind, InferenceServiceKind, ProjectKind } from '../../k8sTypes';
import { Outlet, useParams } from 'react-router-dom';
import {
Bullseye,
Expand All @@ -17,33 +17,39 @@ import useServingRuntimes from './useServingRuntimes';
import useInferenceServices from './useInferenceServices';
import { ContextResourceData } from '../../types';
import { useContextResourceData } from '../../utilities/useContextResourceData';
import useUserProjects from '../projects/screens/projects/useUserProjects';

type ModelServingContextType = {
refreshAllData: () => void;
servingRuntimes: ContextResourceData<ServingRuntimeKind>;
inferenceServices: ContextResourceData<InferenceServiceKind>;
projects: ContextResourceData<ProjectKind>;
};

export const ModelServingContext = React.createContext<ModelServingContextType>({
refreshAllData: () => undefined,
servingRuntimes: DEFAULT_CONTEXT_DATA,
inferenceServices: DEFAULT_CONTEXT_DATA,
projects: DEFAULT_CONTEXT_DATA,
});

const ModelServingContextProvider: React.FC = () => {
const navigate = useNavigate();
const { namespace } = useParams<{ namespace: string }>();
const projects = useContextResourceData<ProjectKind>(useUserProjects());
const servingRuntimes = useContextResourceData<ServingRuntimeKind>(useServingRuntimes(namespace));
const inferenceServices = useContextResourceData<InferenceServiceKind>(
useInferenceServices(namespace),
);

const projectRefresh = projects.refresh;
const servingRuntimeRefresh = servingRuntimes.refresh;
const inferenceServiceRefresh = inferenceServices.refresh;
const refreshAllData = React.useCallback(() => {
projectRefresh();
servingRuntimeRefresh();
inferenceServiceRefresh();
}, [servingRuntimeRefresh, inferenceServiceRefresh]);
}, [projectRefresh, servingRuntimeRefresh, inferenceServiceRefresh]);

if (servingRuntimes.error || inferenceServices.error) {
return (
Expand Down Expand Up @@ -75,6 +81,7 @@ const ModelServingContextProvider: React.FC = () => {
return (
<ModelServingContext.Provider
value={{
projects,
servingRuntimes,
inferenceServices,
refreshAllData,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { InferenceServiceKind, ServingRuntimeKind } from '../../../../k8sTypes';
import useTableColumnSort from '../../../../utilities/useTableColumnSort';
import { getInferenceServiceDisplayName } from './utils';
import ServeModelButton from './ServeModelButton';
import { inferenceServiceColumns } from './data';
import { getGlobalInferenceServiceColumns } from './data';
import SearchField, { SearchType } from '../../../projects/components/SearchField';
import InferenceServiceTable from './InferenceServiceTable';
import { ModelServingContext } from '../../ModelServingContext';
Expand All @@ -22,12 +22,16 @@ const InferenceServiceListView: React.FC<InferenceServiceListViewProps> = ({
}) => {
const {
inferenceServices: { refresh },
projects: { data: projects },
} = React.useContext(ModelServingContext);
const [searchType, setSearchType] = React.useState<SearchType>(SearchType.NAME);
const [search, setSearch] = React.useState('');
const [page, setPage] = React.useState(1);
const [pageSize, setPageSize] = React.useState(MIN_PAGE_SIZE);
const sortInferenceService = useTableColumnSort<InferenceServiceKind>(inferenceServiceColumns, 0);
const sortInferenceService = useTableColumnSort<InferenceServiceKind>(
getGlobalInferenceServiceColumns(projects),
0,
);
const filteredInferenceServices = sortInferenceService
.transformData(unfilteredInferenceServices)
.filter((project) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,29 @@ import * as React from 'react';
import { HelperText, HelperTextItem, Skeleton } from '@patternfly/react-core';
import { InferenceServiceKind } from '../../../../k8sTypes';
import { getProjectDisplayName } from '../../../projects/utils';
import useConnectedProject from './useConnectedProject';
import { ModelServingContext } from '../../ModelServingContext';

type InferenceServiceProjectProps = {
inferenceService: InferenceServiceKind;
};

const InferenceServiceProject: React.FC<InferenceServiceProjectProps> = ({ inferenceService }) => {
const [project, loaded, loadError] = useConnectedProject(inferenceService.metadata.namespace);
const {
projects: { data: projects, loaded, error },
} = React.useContext(ModelServingContext);
const project = projects.find(
({ metadata: { name } }) => name === inferenceService.metadata.namespace,
);

if (!loaded) {
return <Skeleton />;
}

if (loadError) {
if (error) {
return (
<HelperText>
<HelperTextItem variant="warning" hasIcon>
Failed to get project for this deployed model. {loadError.message}
Failed to get project for this deployed model. {error.message}
</HelperTextItem>
</HelperText>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@ import { TableComposable, Tbody, Td, Th, Thead, Tr } from '@patternfly/react-tab
import { Button } from '@patternfly/react-core';
import { GetColumnSort } from '../../../../utilities/useTableColumnSort';
import { InferenceServiceKind, ServingRuntimeKind } from '../../../../k8sTypes';
import { inferenceServiceColumns } from './data';
import { getGlobalInferenceServiceColumns, getProjectInferenceServiceColumns } from './data';
import InferenceServiceTableRow from './InferenceServiceTableRow';
import DeleteInferenceServiceModal from './DeleteInferenceServiceModal';
import ManageInferenceServiceModal from '../projects/InferenceServiceModal/ManageInferenceServiceModal';
import { ModelServingContext } from '../../ModelServingContext';

type InferenceServiceTableProps = {
clearFilters?: () => void;
Expand All @@ -23,13 +24,16 @@ const InferenceServiceTable: React.FC<InferenceServiceTableProps> = ({
getColumnSort,
refresh,
}) => {
const {
projects: { data: projects },
} = React.useContext(ModelServingContext);
const [deleteInferenceService, setDeleteInferenceService] =
React.useState<InferenceServiceKind>();
const [editInferenceService, setEditInferenceService] = React.useState<InferenceServiceKind>();
const isGlobal = !!clearFilters;
const mappedColumns = isGlobal
? inferenceServiceColumns
: inferenceServiceColumns.filter((column) => column.field !== 'project');
? getGlobalInferenceServiceColumns(projects)
: getProjectInferenceServiceColumns();
return (
<>
<TableComposable variant={isGlobal ? undefined : 'compact'}>
Expand All @@ -45,7 +49,7 @@ const InferenceServiceTable: React.FC<InferenceServiceTableProps> = ({
{isGlobal && inferenceServices.length === 0 && (
<Tbody>
<Tr>
<Td colSpan={inferenceServiceColumns.length} style={{ textAlign: 'center' }}>
<Td colSpan={mappedColumns.length} style={{ textAlign: 'center' }}>
No projects match your filters.{' '}
<Button variant="link" isInline onClick={clearFilters}>
Clear filters
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ const ModelServingGlobal: React.FC = () => {

return (
<ApplicationsPage
title="Deployed models"
title="Model serving"
description="Manage and view the health and performance of your deployed models."
loaded // already checked this in the context provider so loaded is always true here
empty={servingRuntimes.length === 0 || inferenceServices.length === 0}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ const ServeModelButton: React.FC = () => {
return (
<>
<Button variant="primary" onClick={() => setOpen(true)}>
Serve model
Deploy model
</Button>
<ManageInferenceServiceModal
isOpen={open}
Expand Down