Last active
March 24, 2025 10:52
-
-
Save christianalfoni/5f7d05fa611a07d3d9f9e37368b7e7fb to your computer and use it in GitHub Desktop.
Refactor of component using constructor pattern and state machine
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { | |
Alert, | |
Button, | |
Card, | |
CardBanner, | |
CardBody, | |
CardHeader, | |
HStack, | |
VStack, | |
} from '@client/component-library' | |
import type { | |
UploadModelError, | |
UploadModelResponse, | |
} from '@shared/api-types/models' | |
import { uploadModelErrorSchema } from '@shared/api-types/models' | |
import { paths } from '@shared/utils/paths' | |
import { isAxiosError } from 'axios' | |
import { useState } from 'react' | |
import { InputFormField } from 'src/components/common/FormFields/InputFormField' | |
import { TextAreaFormField } from 'src/components/common/FormFields/TextAreaFormField' | |
import { CUSTOM_MODELS_LINK } from 'src/constants' | |
import { getUploadModelCodeSnippets } from 'src/utils/codeViewerSnippets' | |
import { APIReferenceSection } from './APIReferenceSection' | |
import { UploadJobProgress } from './UploadJobProgress' | |
import { useUploadModel } from './useUploadModel' | |
const HF_BASE_URL = 'https://huggingface.co/' | |
export const UploadModelPage = () => { | |
const [modelSource, setModelSource] = useState<string>() | |
const [modelName, setModelName] = useState<string>() | |
const [hasChangedModelNameManually, setHasChangedModelNameManually] = | |
useState(false) | |
const [modelDescription, setModelDescription] = useState<string>() | |
const [huggingfaceToken, setHuggingfaceToken] = useState<string>() | |
const [error, setError] = useState<UploadModelError | null>(null) | |
const [successPayload, setSuccessPayload] = | |
useState<UploadModelResponse | null>(null) | |
const [uploadJobId, setUploadJobId] = useState<string | null>(null) | |
const [uploadJobInProgress, setUploadJobInProgress] = useState(false) | |
const { mutate: uploadModel, isPending: isRequestingUpload } = | |
useUploadModel() | |
const isSourceHuggingface = modelSource?.startsWith(HF_BASE_URL) | |
const hasMissingFields = | |
!modelName || !modelSource || (isSourceHuggingface && !huggingfaceToken) | |
const handleUploadModel = (ev: React.FormEvent<HTMLFormElement>) => { | |
ev.preventDefault() | |
if (hasMissingFields) return | |
setError(null) | |
setSuccessPayload(null) | |
setUploadJobId(null) | |
setUploadJobInProgress(false) | |
uploadModel( | |
{ | |
model_name: modelName, | |
model_source: modelSource, | |
description: modelDescription, | |
...(isSourceHuggingface && { hf_token: huggingfaceToken }), | |
}, | |
{ | |
onSuccess(response) { | |
setSuccessPayload(response) | |
if (response.data?.job_id) { | |
setUploadJobInProgress(true) | |
setUploadJobId(response.data.job_id) | |
} else { | |
// Reset the form | |
setHasChangedModelNameManually(false) | |
setModelName('') | |
setModelSource('') | |
setHuggingfaceToken('') | |
setModelDescription('') | |
} | |
}, | |
onError(error) { | |
if (isAxiosError(error)) { | |
const customError = uploadModelErrorSchema.safeParse( | |
error.response?.data | |
) | |
if (customError.success) { | |
setError(customError.data) | |
return | |
} | |
} | |
setError({ | |
type: 'unknown_error', | |
message: | |
'Unexpected error. Please contact support if the problem persists.', | |
}) | |
}, | |
} | |
) | |
} | |
const showOpenModelButton = | |
successPayload?.model_name || error?.message === 'Model name already exists' | |
const resultModelName = successPayload?.model_name || modelName | |
return ( | |
<VStack gap={6}> | |
<Card> | |
<CardHeader headerText="Upload Custom Model" /> | |
{error && ( | |
<CardBanner | |
content={ | |
<Alert variant="error" squareCorners dismissible> | |
{error.message} | |
</Alert> | |
} | |
/> | |
)} | |
{successPayload && ( | |
<CardBanner | |
content={ | |
<Alert | |
variant={ | |
successPayload.message === | |
'Model uploaded and processed successfully' | |
? 'success' | |
: 'info' | |
} | |
squareCorners | |
dismissible | |
> | |
{successPayload.message} | |
</Alert> | |
} | |
/> | |
)} | |
<CardBody> | |
<HStack> | |
<VStack className="flex-1" gap={0}> | |
<form onSubmit={handleUploadModel} className="w-[508px]"> | |
<InputFormField | |
className="w-full" | |
label="Model Source" | |
description="Paste a huggingface URL, an S3 URL or a type in a model name in the format: 'organization/model'" | |
value={modelSource} | |
onChange={(ev) => { | |
const value = ev.target.value | |
setModelSource(value) | |
if (!hasChangedModelNameManually) { | |
// If the user did not touch the name, we automatically generate it | |
let orgAndName = value | |
if (value.startsWith(HF_BASE_URL)) { | |
orgAndName = value.replace(HF_BASE_URL, '') | |
} | |
setModelName(orgAndName.split('/').join('-')) | |
} | |
}} | |
placeholder={HF_BASE_URL + 'org/model'} | |
/> | |
<HStack fullWidth> | |
<InputFormField | |
label="Model Name" | |
className="w-full" | |
value={modelName} | |
onChange={(ev) => { | |
setModelName(ev.target.value) | |
setHasChangedModelNameManually(true) | |
}} | |
/> | |
{isSourceHuggingface && ( | |
<InputFormField | |
label="Hugging Face Token" | |
description="You hugging Face token is required to download the model." | |
value={huggingfaceToken} | |
onChange={(ev) => setHuggingfaceToken(ev.target.value)} | |
placeholder="hf_abc..." | |
/> | |
)} | |
</HStack> | |
<TextAreaFormField | |
label="Description" | |
value={modelDescription} | |
onChange={(ev) => setModelDescription(ev.target.value)} | |
/> | |
<HStack> | |
<Button | |
disabled={hasMissingFields} | |
busy={isRequestingUpload || uploadJobInProgress} | |
variant="filled" | |
type="submit" | |
> | |
Upload Model | |
</Button> | |
{showOpenModelButton && resultModelName && ( | |
<Button href={paths.model({ name: resultModelName })}> | |
Open Model | |
</Button> | |
)} | |
</HStack> | |
</form> | |
</VStack> | |
{uploadJobId && ( | |
<UploadJobProgress | |
jobId={uploadJobId} | |
onJobCompleted={(newModelName) => { | |
setUploadJobInProgress(false) | |
setSuccessPayload({ | |
message: 'Model uploaded and processed successfully', | |
model_name: newModelName, | |
}) | |
setError(null) | |
}} | |
onJobFailed={(errorMessage) => { | |
setUploadJobInProgress(false) | |
setError({ | |
type: 'upload_error', | |
message: `Model processing failed: ${errorMessage}`, | |
}) | |
setSuccessPayload(null) | |
}} | |
/> | |
)} | |
</HStack> | |
</CardBody> | |
</Card> | |
<APIReferenceSection | |
codeTemplates={getUploadModelCodeSnippets({ | |
modelName: modelName || '<model_name>', | |
modelSource: modelSource || '<model_source>', | |
description: modelDescription, | |
...(isSourceHuggingface && { | |
hfToken: huggingfaceToken || '<hf_token>', | |
}), | |
})} | |
docsLink={CUSTOM_MODELS_LINK} | |
/> | |
</VStack> | |
) | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { | |
Alert, | |
Button, | |
Card, | |
CardBanner, | |
CardBody, | |
CardHeader, | |
HStack, | |
VStack, | |
} from "@client/component-library"; | |
import type { | |
UploadModelError, | |
UploadModelResponse, | |
} from "@shared/api-types/models"; | |
import { uploadModelErrorSchema } from "@shared/api-types/models"; | |
import { paths } from "@shared/utils/paths"; | |
import { isAxiosError } from "axios"; | |
import { useState } from "react"; | |
import { InputFormField } from "src/components/common/FormFields/InputFormField"; | |
import { TextAreaFormField } from "src/components/common/FormFields/TextAreaFormField"; | |
import { CUSTOM_MODELS_LINK } from "src/constants"; | |
import { getUploadModelCodeSnippets } from "src/utils/codeViewerSnippets"; | |
import { APIReferenceSection } from "./APIReferenceSection"; | |
import { UploadJobProgress } from "./UploadJobProgress"; | |
import { useUploadModel } from "./useUploadModel"; | |
const HF_BASE_URL = "https://huggingface.co/"; | |
export const UploadModelPage = () => { | |
const [modelSource, setModelSource] = useState<string>(); | |
const [modelName, setModelName] = useState<string>(); | |
const [hasChangedModelNameManually, setHasChangedModelNameManually] = | |
useState(false); | |
const [modelDescription, setModelDescription] = useState<string>(); | |
const [huggingfaceToken, setHuggingfaceToken] = useState<string>(); | |
const [error, setError] = useState<UploadModelError | null>(null); | |
const [successPayload, setSuccessPayload] = | |
useState<UploadModelResponse | null>(null); | |
const [uploadJobId, setUploadJobId] = useState<string | null>(null); | |
const [uploadJobInProgress, setUploadJobInProgress] = useState(false); | |
const { mutate: uploadModel, isPending: isRequestingUpload } = | |
useUploadModel(); | |
const isSourceHuggingface = modelSource?.startsWith(HF_BASE_URL); | |
const hasMissingFields = | |
!modelName || !modelSource || (isSourceHuggingface && !huggingfaceToken); | |
const showOpenModelButton = | |
successPayload?.model_name || | |
error?.message === "Model name already exists"; | |
const resultModelName = successPayload?.model_name || modelName; | |
return ( | |
<VStack gap={6}> | |
<Card> | |
<CardHeader headerText="Upload Custom Model" /> | |
{error && ( | |
<CardBanner | |
content={ | |
<Alert variant="error" squareCorners dismissible> | |
{error.message} | |
</Alert> | |
} | |
/> | |
)} | |
{successPayload && ( | |
<CardBanner | |
content={ | |
<Alert | |
variant={ | |
successPayload.message === | |
"Model uploaded and processed successfully" | |
? "success" | |
: "info" | |
} | |
squareCorners | |
dismissible | |
> | |
{successPayload.message} | |
</Alert> | |
} | |
/> | |
)} | |
<CardBody> | |
<HStack> | |
<VStack className="flex-1" gap={0}> | |
<form onSubmit={handleUploadModel} className="w-[508px]"> | |
<InputFormField | |
className="w-full" | |
label="Model Source" | |
description="Paste a huggingface URL, an S3 URL or a type in a model name in the format: 'organization/model'" | |
value={modelSource} | |
onChange={handleSourceChange} | |
placeholder={HF_BASE_URL + "org/model"} | |
/> | |
<HStack fullWidth> | |
<InputFormField | |
label="Model Name" | |
className="w-full" | |
value={modelName} | |
onChange={handleNameChange} | |
/> | |
{isSourceHuggingface && ( | |
<InputFormField | |
label="Hugging Face Token" | |
description="You hugging Face token is required to download the model." | |
value={huggingfaceToken} | |
onChange={handleHuggingFaceTokenChange} | |
placeholder="hf_abc..." | |
/> | |
)} | |
</HStack> | |
<TextAreaFormField | |
label="Description" | |
value={modelDescription} | |
onChange={handleDescriptionChange} | |
/> | |
<HStack> | |
<Button | |
disabled={hasMissingFields} | |
busy={isRequestingUpload || uploadJobInProgress} | |
variant="filled" | |
type="submit" | |
> | |
Upload Model | |
</Button> | |
{showOpenModelButton && resultModelName && ( | |
<Button href={paths.model({ name: resultModelName })}> | |
Open Model | |
</Button> | |
)} | |
</HStack> | |
</form> | |
</VStack> | |
{uploadJobId && ( | |
<UploadJobProgress | |
jobId={uploadJobId} | |
onJobCompleted={handleJobCompleted} | |
onJobFailed={handleJobFailed} | |
/> | |
)} | |
</HStack> | |
</CardBody> | |
</Card> | |
<APIReferenceSection | |
codeTemplates={getUploadModelCodeSnippets({ | |
modelName: modelName || "<model_name>", | |
modelSource: modelSource || "<model_source>", | |
description: modelDescription, | |
...(isSourceHuggingface && { | |
hfToken: huggingfaceToken || "<hf_token>", | |
}), | |
})} | |
docsLink={CUSTOM_MODELS_LINK} | |
/> | |
</VStack> | |
); | |
function handleJobFailed(errorMessage: string) { | |
setUploadJobInProgress(false); | |
setError({ | |
type: "upload_error", | |
message: `Model processing failed: ${errorMessage}`, | |
}); | |
setSuccessPayload(null); | |
} | |
function handleJobCompleted(newModelName: string) { | |
setUploadJobInProgress(false); | |
setSuccessPayload({ | |
message: "Model uploaded and processed successfully", | |
model_name: newModelName, | |
}); | |
setError(null); | |
} | |
function handleDescriptionChange(ev: React.ChangeEvent<HTMLTextAreaElement>) { | |
setModelDescription(ev.target.value); | |
} | |
function handleHuggingFaceTokenChange( | |
ev: React.ChangeEvent<HTMLInputElement> | |
) { | |
setHuggingfaceToken(ev.target.value); | |
} | |
function handleNameChange(ev: React.ChangeEvent<HTMLInputElement>) { | |
setModelName(ev.target.value); | |
setHasChangedModelNameManually(true); | |
} | |
function handleSourceChange(ev: React.ChangeEvent<HTMLInputElement>) { | |
const value = ev.target.value; | |
setModelSource(value); | |
if (!hasChangedModelNameManually) { | |
// If the user did not touch the name, we automatically generate it | |
let orgAndName = value; | |
if (value.startsWith(HF_BASE_URL)) { | |
orgAndName = value.replace(HF_BASE_URL, ""); | |
} | |
setModelName(orgAndName.split("/").join("-")); | |
} | |
} | |
function handleUploadModel(ev: React.FormEvent<HTMLFormElement>) { | |
ev.preventDefault(); | |
if (hasMissingFields) return; | |
setError(null); | |
setSuccessPayload(null); | |
setUploadJobId(null); | |
setUploadJobInProgress(false); | |
uploadModel( | |
{ | |
model_name: modelName, | |
model_source: modelSource, | |
description: modelDescription, | |
...(isSourceHuggingface && { hf_token: huggingfaceToken }), | |
}, | |
{ | |
onSuccess(response) { | |
setSuccessPayload(response); | |
if (response.data?.job_id) { | |
setUploadJobInProgress(true); | |
setUploadJobId(response.data.job_id); | |
} else { | |
// Reset the form | |
setHasChangedModelNameManually(false); | |
setModelName(""); | |
setModelSource(""); | |
setHuggingfaceToken(""); | |
setModelDescription(""); | |
} | |
}, | |
onError(error) { | |
if (isAxiosError(error)) { | |
const customError = uploadModelErrorSchema.safeParse( | |
error.response?.data | |
); | |
if (customError.success) { | |
setError(customError.data); | |
return; | |
} | |
} | |
setError({ | |
type: "unknown_error", | |
message: | |
"Unexpected error. Please contact support if the problem persists.", | |
}); | |
}, | |
} | |
); | |
} | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { | |
Alert, | |
Button, | |
Card, | |
CardBanner, | |
CardBody, | |
CardHeader, | |
HStack, | |
VStack, | |
} from "@client/component-library"; | |
import type { | |
UploadModelError, | |
UploadModelResponse, | |
} from "@shared/api-types/models"; | |
import { uploadModelErrorSchema } from "@shared/api-types/models"; | |
import { paths } from "@shared/utils/paths"; | |
import { isAxiosError } from "axios"; | |
import { useState } from "react"; | |
import { InputFormField } from "src/components/common/FormFields/InputFormField"; | |
import { TextAreaFormField } from "src/components/common/FormFields/TextAreaFormField"; | |
import { CUSTOM_MODELS_LINK } from "src/constants"; | |
import { getUploadModelCodeSnippets } from "src/utils/codeViewerSnippets"; | |
import { APIReferenceSection } from "./APIReferenceSection"; | |
import { UploadJobProgress } from "./UploadJobProgress"; | |
import { useUploadModel } from "./useUploadModel"; | |
const HF_BASE_URL = "https://huggingface.co/"; | |
type Model = { | |
name: string; | |
source: string; | |
description: string; | |
huggingfaceToken: string; | |
hasChangedNameManually: boolean; | |
}; | |
type IDLE = { | |
current: "IDLE"; | |
model: Model; | |
updateSource(ev: React.ChangeEvent<HTMLInputElement>): void; | |
updateName(ev: React.ChangeEvent<HTMLInputElement>): void; | |
updateDescription(ev: React.ChangeEvent<HTMLTextAreaElement>): void; | |
updateHuggingfaceToken(ev: React.ChangeEvent<HTMLTextAreaElement>): void; | |
upload(): void; | |
}; | |
type UPLOADING = { | |
current: "UPLOADING"; | |
model: Model; | |
}; | |
type MODEL_EXISTS = { | |
current: "MODEL_EXISTS"; | |
model: Model; | |
}; | |
type JOB_REQUIRED = { | |
current: "JOB_REQUIRED"; | |
model: Model; | |
jobId: string; | |
jobCompleted(newModelName: string): void; | |
jobFailed(errorMessage: string): void; | |
}; | |
type JOB_COMPLETED = { | |
current: "JOB_COMPLETED"; | |
model: Model; | |
}; | |
type ERROR = { | |
current: "ERROR"; | |
model: Model; | |
type: string; | |
message: string; | |
}; | |
type State = | |
| IDLE | |
| UPLOADING | |
| MODEL_EXISTS | |
| JOB_REQUIRED | |
| JOB_COMPLETED | |
| ERROR; | |
export const UploadModelPage = () => { | |
const [state, setState] = useState<State>(IDLE()); | |
const { mutate: uploadModel, isPending: isRequestingUpload } = | |
useUploadModel(); | |
const showOpenModelButton = | |
state.current === "JOB_COMPLETED" || state.current === "MODEL_EXISTS"; | |
return ( | |
<VStack gap={6}> | |
<Card> | |
<CardHeader headerText="Upload Custom Model" /> | |
{state.current === "ERROR" && ( | |
<CardBanner | |
content={ | |
<Alert variant="error" squareCorners dismissible> | |
{error.message} | |
</Alert> | |
} | |
/> | |
)} | |
{state.current === "MODEL_EXISTS" && ( | |
<CardBanner | |
content={ | |
<Alert variant="info" squareCorners dismissible> | |
Model name already exists | |
</Alert> | |
} | |
/> | |
)} | |
{state.current === "JOB_COMPLETED" && ( | |
<CardBanner | |
content={ | |
<Alert variant="success" squareCorners dismissible> | |
Model uploaded and processed successfully | |
</Alert> | |
} | |
/> | |
)} | |
<CardBody> | |
<HStack> | |
<VStack className="flex-1" gap={0}> | |
<form | |
onSubmit={state.current === "IDLE" ? state.upload : undefined} | |
className="w-[508px]" | |
> | |
<InputFormField | |
className="w-full" | |
label="Model Source" | |
description="Paste a huggingface URL, an S3 URL or a type in a model name in the format: 'organization/model'" | |
value={state.model.source} | |
{...(state.current === "IDLE" | |
? { | |
onChange: state.updateSource, | |
placeholder: HF_BASE_URL + "org/model", | |
} | |
: { | |
disabled: true, | |
})} | |
/> | |
<HStack fullWidth> | |
<InputFormField | |
label="Model Name" | |
className="w-full" | |
value={modelName} | |
{...(state.current === "IDLE" | |
? { | |
onChange: state.updatedName, | |
} | |
: { | |
disabled: true, | |
})} | |
/> | |
{isSourceHuggingface && ( | |
<InputFormField | |
label="Hugging Face Token" | |
description="You hugging Face token is required to download the model." | |
value={huggingfaceToken} | |
{...(state.current === "IDLE" | |
? { | |
onChange: state.updateHuggingfaceToken, | |
placeholder: "hf_abc...", | |
} | |
: { | |
disabled: true, | |
})} | |
/> | |
)} | |
</HStack> | |
<TextAreaFormField | |
label="Description" | |
value={modelDescription} | |
{...(state.current === "IDLE" | |
? { | |
onChange: state.updateDescription, | |
} | |
: { | |
disabled: true, | |
})} | |
/> | |
<HStack> | |
<Button | |
disabled={ | |
state.current !== "IDLE" || | |
hasModelMissingFields(state.model) | |
} | |
busy={ | |
state.current === "UPLOADING" || | |
state.current === "JOB_REQUIRED" | |
} | |
variant="filled" | |
type="submit" | |
> | |
Upload Model | |
</Button> | |
{showOpenModelButton && ( | |
<Button href={paths.model({ name: state.model.name })}> | |
Open Model | |
</Button> | |
)} | |
</HStack> | |
</form> | |
</VStack> | |
{state.current === "JOB_REQUIRED" && ( | |
<UploadJobProgress | |
jobId={state.jobId} | |
onJobCompleted={state.jobCompleted} | |
onJobFailed={state.jobFailed} | |
/> | |
)} | |
</HStack> | |
</CardBody> | |
</Card> | |
<APIReferenceSection | |
codeTemplates={getUploadModelCodeSnippets({ | |
modelName: state.model.name || "<model_name>", | |
modelSource: state.model.source || "<model_source>", | |
description: state.model.description, | |
...(isSourceHuggingface(state.model) && { | |
hfToken: state.model.huggingfaceToken || "<hf_token>", | |
}), | |
})} | |
docsLink={CUSTOM_MODELS_LINK} | |
/> | |
</VStack> | |
); | |
function IDLE( | |
model: Model = { | |
name: "", | |
source: "", | |
description: "", | |
hunggingfaceToken: "", | |
} | |
): IDLE { | |
return { | |
current: "IDLE", | |
model, | |
updateSource(ev) { | |
const source = ev.target.value; | |
let name = model.name; | |
// If the user did not touch the name while changing the source, we generate the name | |
if (!model.hasChangedNameManually) { | |
if (source.startsWith(HF_BASE_URL)) { | |
name = value.replace(HF_BASE_URL, ""); | |
} | |
name = orgAndName.split("/").join("-"); | |
} | |
setState(IDLE({ ...model, source, name })); | |
}, | |
updateName(ev) { | |
setState( | |
IDLE({ | |
...model, | |
name: ev.target.value, | |
hasChangedNameManually: true, | |
}) | |
); | |
}, | |
updateDescription(ev) { | |
setState(IDLE({ ...model, description: ev.target.value })); | |
}, | |
updateHuggingfaceToken(ev) { | |
setState(IDLE({ ...model, huggingfaceToken: ev.target.value })); | |
}, | |
upload() { | |
if (hasModelMissingFields(model)) return; | |
setState(UPLOADING(model)); | |
}, | |
}; | |
} | |
function UPLOADING(model: Model): UPLOADING { | |
uploadModel( | |
{ | |
model_name: model.name, | |
model_source: model.source, | |
description: model.description, | |
...(isSourceHuggingface(model) && { hf_token: model.huggingfaceToken }), | |
}, | |
{ | |
onSuccess(response) { | |
const jobId = response.data?.job_id; | |
setState(jobId ? JOB_REQUIRED(model, jobId) : MODEL_EXISTS(model)); | |
}, | |
onError(error) { | |
if (isAxiosError(error)) { | |
const customError = uploadModelErrorSchema.safeParse( | |
error.response?.data | |
); | |
if (customError.success) { | |
setState(ERROR(model, customError.data)); | |
return; | |
} | |
} | |
setState( | |
ERROR(model, { | |
type: "unknown_error", | |
message: | |
"Unexpected error. Please contact support if the problem persists.", | |
}) | |
); | |
}, | |
} | |
); | |
return { | |
current: "UPLOADING", | |
model, | |
}; | |
} | |
function MODEL_EXISTS(model: Model): MODEL_EXISTS { | |
return { | |
current: "MODEL_EXISTS", | |
model, | |
}; | |
} | |
function JOB_REQUIRED(model: Model, jobId: string): JOB_REQUIRED { | |
return { | |
current: "JOB_REQUIRED", | |
model, | |
jobId, | |
jobCompleted(newModelName) { | |
setState(JOB_COMPLETED(model, newModelName)); | |
}, | |
jobFailed(errorMessage) { | |
setState( | |
ERROR(model, { | |
type: "upload_error", | |
message: `Model processing failed: ${errorMessage}`, | |
}) | |
); | |
}, | |
}; | |
} | |
function JOB_COMPLETED(model: Model, newModelName: string): JOB_COMPLETED { | |
return { | |
current: "JOB_COMPLETED", | |
model: { ...model, name: newModelName }, | |
}; | |
} | |
function ERROR( | |
model: Model, | |
error: { type: string; message: string } | |
): ERROR { | |
return { | |
current: "ERROR", | |
model, | |
type: error.type, | |
message: error.message, | |
}; | |
} | |
function isSourceHuggingFace(model: Model) { | |
return model.source.startsWith(HF_BASE_URL); | |
} | |
function hasModelMissingFields(model: Model) { | |
return ( | |
!model.name || | |
!model.source || | |
(isSourceHuggingface(model) && !model.huggingfaceToken) | |
); | |
} | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment