diff --git a/src/renderer/components/Settings/TransformerLabSettings.tsx b/src/renderer/components/Settings/TransformerLabSettings.tsx index e845ec3c9ade830810a748cf767f4a426f56c2b4..a00f2837aae344e6e01d007a0bc06e1bceb11838 100644 --- a/src/renderer/components/Settings/TransformerLabSettings.tsx +++ b/src/renderer/components/Settings/TransformerLabSettings.tsx @@ -15,6 +15,10 @@ import { Table, Typography, Alert, + Tabs, + TabList, + Tab, + TabPanel, } from '@mui/joy'; import * as chatAPI from 'renderer/lib/transformerlab-api-sdk'; @@ -35,7 +39,7 @@ export default function TransformerLabSettings() { mutate: hftokenmutate, } = useSWR( chatAPI.Endpoints.Config.Get('HuggingfaceUserAccessToken'), - fetcher + fetcher, ); const [showJobsOfType, setShowJobsOfType] = React.useState('NONE'); const [showProvidersPage, setShowProvidersPage] = React.useState(false); @@ -71,170 +75,188 @@ export default function TransformerLabSettings() { ); } - - return ( <> - <Typography level="h1" marginBottom={3}> + <Typography level="h1" marginBottom={1}> Transformer Lab Settings </Typography> <Sheet sx={{ width: '100%', overflowY: 'auto' }}> - {canLogInToHuggingFaceIsLoading && <CircularProgress />} - <Typography level="title-lg" marginBottom={2}> - Huggingface Credentials: - </Typography> - {canLogInToHuggingFace?.message === 'OK' ? ( - <Alert color="success">Login to Huggingface Successful</Alert> - ) : ( - <> - <Alert color="danger" sx={{ mb: 1 }}> - Login to Huggingface Failed. Please set credentials below. - </Alert> - <FormControl sx={{ maxWidth: '500px' }}> - <FormLabel>User Access Token</FormLabel> - {hftokenisloading ? ( - <CircularProgress /> - ) : ( - <Input - name="hftoken" - defaultValue={hftoken} - type="password" - endDecorator={ - <IconButton - onClick={() => { - const x = document.getElementsByName('hftoken')[0]; - x.type = x.type === 'text' ? 'password' : 'text'; - setShowPassword(!showPassword); - }} + <Tabs defaultValue={0}> + <TabList> + <Tab>Settings</Tab> + <Tab>View Jobs</Tab> + </TabList> + <TabPanel value={0}> + {canLogInToHuggingFaceIsLoading && <CircularProgress />} + <Typography level="title-lg" marginBottom={2}> + Huggingface Credentials: + </Typography> + {canLogInToHuggingFace?.message === 'OK' ? ( + <Alert color="success">Login to Huggingface Successful</Alert> + ) : ( + <> + <Alert color="danger" sx={{ mb: 1 }}> + Login to Huggingface Failed. Please set credentials below. + </Alert> + <FormControl sx={{ maxWidth: '500px' }}> + <FormLabel>User Access Token</FormLabel> + {hftokenisloading ? ( + <CircularProgress /> + ) : ( + <Input + name="hftoken" + defaultValue={hftoken} + type="password" + endDecorator={ + <IconButton + onClick={() => { + const x = document.getElementsByName('hftoken')[0]; + x.type = x.type === 'text' ? 'password' : 'text'; + setShowPassword(!showPassword); + }} + > + {showPassword ? <EyeOffIcon /> : <EyeIcon />} + </IconButton> + } + /> + )} + <Button + onClick={async () => { + const token = + document.getElementsByName('hftoken')[0].value; + await fetch( + chatAPI.Endpoints.Config.Set( + 'HuggingfaceUserAccessToken', + token, + ), + ); + // Now manually log in to Huggingface + await fetch(chatAPI.Endpoints.Models.HuggingFaceLogin()); + hftokenmutate(token); + canLogInToHuggingFaceMutate(); + }} + sx={{ marginTop: 1, width: '100px', alignSelf: 'flex-end' }} + > + Save + </Button> + <FormHelperText> + A Huggingface access token is required in order to access + certain models and datasets (those marked as "Gated"). + </FormHelperText> + <FormHelperText> + Documentation here:{' '} + <a + href="https://huggingface.co/docs/hub/security-tokens" + target="_blank" + rel="noreferrer" > - {showPassword ? <EyeOffIcon /> : <EyeIcon />} - </IconButton> - } - /> - )} - <Button - onClick={async () => { - const token = document.getElementsByName('hftoken')[0].value; - await fetch(chatAPI.Endpoints.Config.Set('HuggingfaceUserAccessToken', token)); - // Now manually log in to Huggingface - await fetch(chatAPI.Endpoints.Models.HuggingFaceLogin()); - hftokenmutate(token); - canLogInToHuggingFaceMutate(); - }} - sx={{ marginTop: 1, width: '100px', alignSelf: 'flex-end' }} - > - Save - </Button> - <FormHelperText> - A Huggingface access token is required in order to access certain - models and datasets (those marked as "Gated"). - </FormHelperText> - <FormHelperText> - Documentation here:{' '} - <a - href="https://huggingface.co/docs/hub/security-tokens" - target="_blank" - rel="noreferrer" + https://huggingface.co/docs/hub/security-tokens + </a> + </FormHelperText> + </FormControl> + </> + )} + {wandbLoginStatus?.message === 'OK' ? ( + <Alert color="success"> + Login to Weights & Biases Successful + </Alert> + ) : ( + <FormControl sx={{ maxWidth: '500px', mt: 2 }}> + <FormLabel>Weights & Biases API Key</FormLabel> + <Input name="wandbToken" type="password" /> + <Button + onClick={async () => { + const token = + document.getElementsByName('wandbToken')[0].value; + await fetch( + chatAPI.Endpoints.Config.Set('WANDB_API_KEY', token), + ); + await fetch(chatAPI.Endpoints.Models.wandbLogin()); + wandbLoginMutate(); + }} + sx={{ marginTop: 1, width: '100px', alignSelf: 'flex-end' }} > - https://huggingface.co/docs/hub/security-tokens - </a> - </FormHelperText> - </FormControl> - </> - )} - {wandbLoginStatus?.message === 'OK' ? ( - <Alert color="success">Login to Weights & Biases Successful</Alert> - ) : ( - <FormControl sx={{ maxWidth: '500px', mt: 2 }}> - <FormLabel>Weights & Biases API Key</FormLabel> - <Input name="wandbToken" type="password" /> + Save + </Button> + </FormControl> + )} + <Divider sx={{ mt: 2, mb: 2 }} /> + <Typography level="title-lg" marginBottom={2}> + AI Providers & Models: + </Typography> + {/* Clickable list option */} + <Button variant="soft" onClick={() => setShowProvidersPage(true)}> + Set API Keys for AI Providers + </Button> + + <Divider sx={{ mt: 2, mb: 2 }} /> + <Typography level="title-lg" marginBottom={2}> + Application: + </Typography> <Button - onClick={async () => { - const token = document.getElementsByName('wandbToken')[0].value; - await fetch(chatAPI.Endpoints.Config.Set('WANDB_API_KEY', token)); - await fetch(chatAPI.Endpoints.Models.wandbLogin()); - wandbLoginMutate(); + variant="soft" + onClick={() => { + // find and delete all items in local storage that begin with oneTimePopup: + for (const key in localStorage) { + if (key.startsWith('oneTimePopup')) { + localStorage.removeItem(key); + } + } }} - sx={{ marginTop: 1, width: '100px', alignSelf: 'flex-end' }} > - Save + Reset all Tutorial Popup Screens </Button> - </FormControl> - )} - <Divider sx={{ mt: 2, mb: 2 }} /> - <Typography level="title-lg" marginBottom={2}> - AI Providers & Models: - </Typography> - {/* Clickable list option */} - <Button variant="soft" onClick={() => setShowProvidersPage(true)}> - Set API Keys for AI Providers - </Button> - - <Divider sx={{ mt: 2, mb: 2 }} /> - <Typography level="title-lg" marginBottom={2}> - Application: - </Typography> - <Button - variant="soft" - onClick={() => { - // find and delete all items in local storage that begin with oneTimePopup: - for (const key in localStorage) { - if (key.startsWith('oneTimePopup')) { - localStorage.removeItem(key); - } - } - }} - > - Reset all Tutorial Popup Screens - </Button> - <Divider sx={{ mt: 2, mb: 2 }} /> - <Typography level="title-lg" marginBottom={2}> - View Jobs (debug):{' '} - <IconButton onClick={() => jobsMutate()}> - <RotateCcwIcon size="14px" /> - </IconButton> - </Typography> - <Select - sx={{ width: '400px' }} - value={showJobsOfType} - onChange={(e, newValue) => { - setShowJobsOfType(newValue); - }} - > - <Option value="NONE">None</Option> - <Option value="">All</Option> - <Option value="DOWNLOAD_MODEL">Download Model</Option> - <Option value="LOAD_MODEL">Load Model</Option> - <Option value="TRAIN">Train</Option> - <Option value="GENERATE">Generate</Option> - <Option value="EVAL">Evaluate</Option> - </Select> - {showJobsOfType !== 'NONE' && ( - <Table sx={{ tableLayout: 'auto', overflow: 'scroll' }}> - <thead> - <tr> - <td>Job ID</td> - <td>Job Type</td> - <td>Job Status</td> - <td>Job Progress</td> - <td>Job Data</td> - </tr> - </thead> - <tbody> - {jobs?.map((job) => ( - <tr key={job.id}> - <td>{job.id}</td> - <td>{job.type}</td> - <td>{job.status}</td> - <td>{job.progress}</td> - <td> - <pre>{JSON.stringify(job.job_data, null, 2)}</pre> - </td> - </tr> - ))} - </tbody> - </Table> - )} + </TabPanel> + <TabPanel value={1}> + <Typography level="title-lg" marginBottom={2}> + View Jobs (debug):{' '} + <IconButton onClick={() => jobsMutate()}> + <RotateCcwIcon size="14px" /> + </IconButton> + </Typography> + <Select + sx={{ width: '400px' }} + value={showJobsOfType} + onChange={(e, newValue) => { + setShowJobsOfType(newValue); + }} + > + <Option value="NONE">None</Option> + <Option value="">All</Option> + <Option value="DOWNLOAD_MODEL">Download Model</Option> + <Option value="LOAD_MODEL">Load Model</Option> + <Option value="TRAIN">Train</Option> + <Option value="GENERATE">Generate</Option> + <Option value="EVAL">Evaluate</Option> + </Select> + {showJobsOfType !== 'NONE' && ( + <Table sx={{ tableLayout: 'auto', overflow: 'scroll' }}> + <thead> + <tr> + <td>Job ID</td> + <td>Job Type</td> + <td>Job Status</td> + <td>Job Progress</td> + <td>Job Data</td> + </tr> + </thead> + <tbody> + {jobs?.map((job) => ( + <tr key={job.id}> + <td>{job.id}</td> + <td>{job.type}</td> + <td>{job.status}</td> + <td>{job.progress}</td> + <td> + <pre>{JSON.stringify(job.job_data, null, 2)}</pre> + </td> + </tr> + ))} + </tbody> + </Table> + )} + </TabPanel> + </Tabs> </Sheet> </> );