Skip to content

Commit

Permalink
Merge pull request #637 from gchq/BAI-650-add-warning-message-for-max…
Browse files Browse the repository at this point in the history
…-model-upload-size

Bai 650 add warning message for max model upload size
  • Loading branch information
P20179 authored Jul 21, 2023
2 parents 304a4ab + 8587d35 commit c715059
Show file tree
Hide file tree
Showing 10 changed files with 118 additions and 45 deletions.
1 change: 1 addition & 0 deletions backend/config/default.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -212,5 +212,6 @@ module.exports = {
image: 'seldonio/seldon-core-s2i-python37:1.10.0',
},
],
maxModelSizeGB: 50
},
}
6 changes: 6 additions & 0 deletions backend/src/routes/v1/specification.ts
Original file line number Diff line number Diff line change
Expand Up @@ -507,6 +507,12 @@ function parseValue(value: unknown) {
example: value,
}
}
if (typeof value === 'number') {
return {
type: 'number',
example: value,
}
}
if (typeof value === 'boolean') {
return {
type: 'boolean',
Expand Down
1 change: 1 addition & 0 deletions backend/src/types/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ export interface UiConfig {
}

seldonVersions: Array<SeldonVersion>
maxModelSizeGB: number
}

export type SeldonVersion = {
Expand Down
2 changes: 2 additions & 0 deletions backend/src/utils/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,8 @@ export interface Config {
name: string
image: string
}>
//max model size is calculated in gigabytes
maxModelSizeGB: number
}
}

Expand Down
12 changes: 8 additions & 4 deletions frontend/pages/model/[uuid]/new-version.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import Paper from '@mui/material/Paper'
import axios from 'axios'
import { useGetUiConfig } from 'data/uiConfig'
import { useRouter } from 'next/router'
import React, { useEffect, useState } from 'react'
import Loading from 'src/common/Loading'
import MessageAlert from 'src/MessageAlert'

import { useGetModel, useGetModelVersions } from '../../../data/model'
Expand Down Expand Up @@ -47,6 +49,7 @@ function Upload() {
const { model, isModelLoading, isModelError, mutateModel } = useGetModel(modelUuid)
const { schema, isSchemaLoading, isSchemaError } = useGetSchema(model?.schemaRef)
const { versions } = useGetModelVersions(modelUuid)
const { uiConfig, isUiConfigError, isUiConfigLoading } = useGetUiConfig()

const cModel = useCacheVariable(model)
const cSchema = useCacheVariable(schema)
Expand Down Expand Up @@ -88,7 +91,7 @@ function Upload() {

render: RenderFileTab,
renderBasic: RenderBasicFileTab,
isComplete: fileTabComplete,
isComplete: (step) => fileTabComplete(step, uiConfig ? uiConfig.maxModelSizeGB : 0),
})
)

Expand All @@ -115,16 +118,17 @@ function Upload() {
}

setSplitSchema({ reference: cSchema.reference, steps })
}, [cModel, cSchema])
}, [cModel, cSchema, uiConfig])

const errorWrapper = MultipleErrorWrapper(`Unable to load edit page`, {
isModelError,
isSchemaError,
isUiConfigError,
})
if (errorWrapper) return errorWrapper

if (isModelLoading || isSchemaLoading) {
return null
if (isModelLoading || isSchemaLoading || isUiConfigLoading) {
return <Loading />
}

if (!model || !schema) {
Expand Down
20 changes: 10 additions & 10 deletions frontend/pages/upload.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@ import Box from '@mui/material/Box'
import Grid from '@mui/material/Grid'
import Paper from '@mui/material/Paper'
import axios from 'axios'
import { useGetUiConfig } from 'data/uiConfig'
import { useRouter } from 'next/router'
import React, { useEffect, useState } from 'react'
import Loading from 'src/common/Loading'

import { useGetDefaultSchema, useGetSchemas } from '../data/schema'
import { useGetCurrentUser } from '../data/user'
Expand Down Expand Up @@ -47,6 +49,7 @@ function Upload() {
const { defaultSchema, isDefaultSchemaError, isDefaultSchemaLoading } = useGetDefaultSchema('UPLOAD')
const { schemas, isSchemasLoading, isSchemasError } = useGetSchemas('UPLOAD')
const { currentUser, isCurrentUserLoading, isCurrentUserError } = useGetCurrentUser()
const { uiConfig, isUiConfigError, isUiConfigLoading } = useGetUiConfig()

const router = useRouter()

Expand Down Expand Up @@ -104,7 +107,7 @@ function Upload() {

render: RenderFileTab,
renderBasic: RenderBasicFileTab,
isComplete: fileTabComplete,
isComplete: (step) => fileTabComplete(step, uiConfig ? uiConfig.maxModelSizeGB : 0),
})
)

Expand All @@ -131,27 +134,24 @@ function Upload() {
}

setSplitSchema({ reference, steps })
}, [currentSchema, user])
}, [currentSchema, uiConfig, user])

const errorWrapper = MultipleErrorWrapper(
`Unable to load upload page`,
{
isDefaultSchemaError,
isSchemasError,
isCurrentUserError,
isUiConfigError,
},
MinimalErrorWrapper
)
if (errorWrapper) return errorWrapper

if (isDefaultSchemaLoading || isSchemasLoading || isCurrentUserLoading) {
return null
}
const Loading = <Wrapper title='Loading...' page='deployment' />

if (isDefaultSchemaLoading || !defaultSchema) return Loading
if (isSchemasLoading || !schemas) return Loading
if (isCurrentUserLoading || !currentUser) return Loading
if (isDefaultSchemaLoading || !defaultSchema) return <Loading />
if (isSchemasLoading || !schemas) return <Loading />
if (isCurrentUserLoading || !currentUser) return <Loading />
if (isUiConfigLoading || !uiConfig) return <Loading />

const onSubmit = async () => {
setError(undefined)
Expand Down
105 changes: 74 additions & 31 deletions frontend/src/Form/RenderFileTab.tsx
Original file line number Diff line number Diff line change
@@ -1,69 +1,112 @@
import { Paper } from '@mui/material'
import Box from '@mui/material/Box'
import Divider from '@mui/material/Divider'
import Grid from '@mui/material/Grid'
import Stack from '@mui/material/Stack'
import Typography from '@mui/material/Typography'
import React, { ChangeEvent, useMemo } from 'react'
import { useGetUiConfig } from 'data/uiConfig'
import { ChangeEvent, useEffect, useMemo, useState } from 'react'
import Loading from 'src/common/Loading'
import { convertGigabytesToBytes } from 'utils/byteUtils'

import { RenderInterface, Step } from '../../types/interfaces'
import { ModelUploadType } from '../../types/types'
import { ModelUploadType, UiConfig } from '../../types/types'
import { setStepState } from '../../utils/formUtils'
import FileInput from '../common/FileInput'
import SubmissionError from './SubmissionError'

export default function RenderFileTab({ step, splitSchema, setSplitSchema }: RenderInterface) {
const { state } = step
const { binary, code, docker } = state
const [error, setError] = useState('')
const [totalFileSize, setTotalFileSize] = useState(0)

const { uiConfig, isUiConfigError, isUiConfigLoading } = useGetUiConfig()

useEffect(() => {
const codeSize = state.code ? state.code.size : 0
const binarySize = state.binary ? state.binary.size : 0
const dockerSize = state.docker ? state.docker.size : 0
setTotalFileSize(codeSize + binarySize + dockerSize)
}, [state.binary, state.code, state.docker])

useEffect(() => {
if (isUiConfigError) setError(isUiConfigError.message)
else if (!uiConfig) setError('Failed to load UI config')
else if (totalFileSize > convertGigabytesToBytes(uiConfig.maxModelSizeGB))
setError('Model size exceeds maximum upload size of ' + uiConfig.maxModelSizeGB + 'GB')
else setError('')
}, [isUiConfigError, totalFileSize, uiConfig])

const buildOptionsStep = useMemo(
() => splitSchema.steps.find((buildOptionSchemaStep) => buildOptionSchemaStep.section === 'buildOptions'),
[splitSchema.steps]
)

const handleCodeChange = (event: ChangeEvent<HTMLInputElement>) => {
if (event.target.files) setStepState(splitSchema, setSplitSchema, step, { ...state, code: event.target.files[0] })
if (event.target.files) {
setStepState(splitSchema, setSplitSchema, step, { ...state, code: event.target.files[0] })
setTotalFileSize(totalFileSize + event.target.files[0].size)
}
}

const handleBinaryChange = (event: ChangeEvent<HTMLInputElement>) => {
if (event.target.files) setStepState(splitSchema, setSplitSchema, step, { ...state, binary: event.target.files[0] })
if (event.target.files) {
setStepState(splitSchema, setSplitSchema, step, { ...state, binary: event.target.files[0] })
setTotalFileSize(totalFileSize + event.target.files[0].size)
}
}

const handleDockerChange = (event: ChangeEvent<HTMLInputElement>) => {
if (event.target.files) setStepState(splitSchema, setSplitSchema, step, { ...state, docker: event.target.files[0] })
if (event.target.files) {
setStepState(splitSchema, setSplitSchema, step, { ...state, docker: event.target.files[0] })
setTotalFileSize(totalFileSize + event.target.files[0].size)
}
}

if (isUiConfigLoading) return <Loading />

return (
<Grid container justifyContent='center'>
{buildOptionsStep !== undefined && buildOptionsStep.state.uploadType === ModelUploadType.Zip && (
<Stack direction='row' spacing={3} sx={{ p: 3 }} alignItems='center'>
<Box sx={{ textAlign: 'center' }}>
<Typography variant='h5'>Upload a code file (.zip)</Typography>
<FileInput label='Select Code' onChange={handleCodeChange} file={code} accepts='.zip' />
</Box>
<Divider orientation='vertical' flexItem />
<Paper>
<Grid container justifyContent='center'>
{buildOptionsStep !== undefined && buildOptionsStep.state.uploadType === ModelUploadType.Zip && (
<Stack direction='row' spacing={3} sx={{ p: 3 }} alignItems='center'>
<Box sx={{ textAlign: 'center' }}>
<Typography variant='h5'>Upload a code file (.zip)</Typography>
<FileInput label='Select Code' onChange={handleCodeChange} file={code} accepts='.zip' />
</Box>
<Divider orientation='vertical' flexItem />
<Box sx={{ textAlign: 'center' }}>
<Typography variant='h5'>Upload a binary file (.zip)</Typography>
<FileInput label='Select Binary' onChange={handleBinaryChange} file={binary} accepts='.zip' />
</Box>
</Stack>
)}

{buildOptionsStep !== undefined && buildOptionsStep.state.uploadType === ModelUploadType.ModelCard && (
<Typography sx={{ p: 2 }}>Uploading a model card without any code or binary files</Typography>
)}
{buildOptionsStep !== undefined && buildOptionsStep.state.uploadType === ModelUploadType.Docker && (
<Box sx={{ textAlign: 'center' }}>
<Typography variant='h5'>Upload a binary file (.zip)</Typography>
<FileInput label='Select Binary' onChange={handleBinaryChange} file={binary} accepts='.zip' />
<Typography sx={{ p: 1 }} variant='h5'>
Upload a docker file (.tar)
</Typography>
<FileInput label='Select Docker Image' onChange={handleDockerChange} file={docker} accepts='.tar' />
</Box>
</Stack>
)}
{buildOptionsStep !== undefined && buildOptionsStep.state.uploadType === ModelUploadType.ModelCard && (
<Typography sx={{ p: 2 }}>Uploading a model card without any code or binary files</Typography>
)}
{buildOptionsStep !== undefined && buildOptionsStep.state.uploadType === ModelUploadType.Docker && (
<Box sx={{ textAlign: 'center' }}>
<Typography sx={{ p: 1 }} variant='h5'>
Upload a docker file (.tar)
</Typography>
<FileInput label='Select Docker Image' onChange={handleDockerChange} file={docker} accepts='.tar' />
</Box>
)}
</Grid>
)}
</Grid>
<SubmissionError error={error} />
</Paper>
)
}

export function fileTabComplete(step: Step) {
export function fileTabComplete(step: Step, maxModelSizeGB: UiConfig['maxModelSizeGB']) {
if (!step.steps) return false

const totalFileSize =
(step.state.binary ? step.state.binary.size : 0) +
(step.state.code ? step.state.code.size : 0) +
(step.state.docker ? step.state.docker.size : 0)
const buildOptionsStep = step.steps.find((buildOptionSchemaStep) => buildOptionSchemaStep.section === 'buildOptions')

const hasUploadType = !!buildOptionsStep?.state?.uploadType
Expand All @@ -76,7 +119,7 @@ export function fileTabComplete(step: Step) {
case ModelUploadType.ModelCard:
return true
case ModelUploadType.Zip:
return step.state.binary && step.state.code
return step.state.binary && step.state.code && totalFileSize <= convertGigabytesToBytes(maxModelSizeGB)
case ModelUploadType.Docker:
return !!step.state.docker
default:
Expand Down
10 changes: 10 additions & 0 deletions frontend/src/common/Loading.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { Box, CircularProgress } from '@mui/material'
import { ReactElement } from 'react'

export default function Loading(): ReactElement {
return (
<Box sx={{ display: 'flex', justifyContent: 'center' }}>
<CircularProgress />
</Box>
)
}
3 changes: 3 additions & 0 deletions frontend/types/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,9 @@ export interface UiConfig {
}

seldonVersions: Array<SeldonVersion>

//max model size is calculated in gigabytes
maxModelSizeGB: number
}

export type SeldonVersion = {
Expand Down
3 changes: 3 additions & 0 deletions frontend/utils/byteUtils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export function convertGigabytesToBytes(gigabytes: number) {
return gigabytes * 1024 * 1024 * 1024
}

0 comments on commit c715059

Please sign in to comment.