Skip to content
Snippets Groups Projects
Unverified Commit 15d2884e authored by ali asaria's avatar ali asaria Committed by GitHub
Browse files

Merge pull request #303 from transformerlab/fix/improve-settings

Improve the Settings Screen with a separate tab for jobs
parents 97f44221 51062440
No related branches found
No related tags found
No related merge requests found
......@@ -15,6 +15,10 @@ import {
Table,
Typography,
Alert,
Tabs,
TabList,
Tab,
TabPanel,
} from '@mui/joy';
import * as chatAPI from 'renderer/lib/transformerlab-api-sdk';
......@@ -23,6 +27,8 @@ import { EyeIcon, EyeOffIcon, RotateCcwIcon } from 'lucide-react';
// Import the AIProvidersSettings component.
import AIProvidersSettings from './AIProvidersSettings';
import ViewJobsTab from './ViewJobsTab';
import { alignBox } from '@nivo/core';
const fetcher = (url) => fetch(url).then((res) => res.json());
......@@ -35,7 +41,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,171 +77,174 @@ export default function TransformerLabSettings() {
);
}
return (
<>
<Typography level="h1" marginBottom={3}>
<Sheet
sx={{
width: '100%',
height: '100%',
overflowY: 'hidden',
display: 'flex',
flexDirection: 'column',
}}
>
<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);
}}
<Sheet
sx={{
height: '100%',
overflowY: 'hidden',
display: 'flex',
flexDirection: 'column',
}}
>
<Tabs
defaultValue={0}
sx={{
height: '100%',
display: 'flex',
flexDirection: 'column',
overflow: 'hidden',
}}
>
<TabList>
<Tab>Settings</Tab>
<Tab>View Jobs</Tab>
</TabList>
<TabPanel value={0} style={{ overflow: '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);
}}
>
{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 &amp; Biases Successful
</Alert>
) : (
<FormControl sx={{ maxWidth: '500px', mt: 2 }}>
<FormLabel>Weights &amp; 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 &amp; Biases Successful</Alert>
) : (
<FormControl sx={{ maxWidth: '500px', mt: 2 }}>
<FormLabel>Weights &amp; 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}
sx={{
overflowY: 'hidden',
overflowX: 'hidden',
display: 'flex',
flexDirection: 'column',
}}
>
<ViewJobsTab />
</TabPanel>
</Tabs>
</Sheet>
</>
</Sheet>
);
}
import * as React from 'react';
import {
Typography,
IconButton,
Select,
Option,
Table,
Box,
Stack,
} from '@mui/joy';
import { RotateCcwIcon } from 'lucide-react';
import useSWR from 'swr';
import * as chatAPI from 'renderer/lib/transformerlab-api-sdk';
const fetcher = (url) => fetch(url).then((res) => res.json());
const jobTypes = [
{ value: 'NONE', label: 'None' },
{ value: '', label: 'All' },
{ value: 'DOWNLOAD_MODEL', label: 'Download Model' },
{ value: 'LOAD_MODEL', label: 'Load Model' },
{ value: 'TRAIN', label: 'Train' },
{ value: 'GENERATE', label: 'Generate' },
{ value: 'EVAL', label: 'Evaluate' },
];
export default function ViewJobsTab() {
const [type, setType] = React.useState('');
const {
data: jobs,
error: jobsError,
isLoading: jobsIsLoading,
mutate: jobsMutate,
} = useSWR(chatAPI.Endpoints.Jobs.GetJobsOfType(type, ''), fetcher);
return (
<>
<Stack flexDirection="row" alignItems="center" marginBottom={2}>
<Typography level="title-lg">View Jobs: &nbsp;</Typography>
<Select
sx={{ width: '200px' }}
value={type}
onChange={(e, newValue) => {
setType(newValue);
}}
>
{jobTypes.map((jobType) => (
<Option key={jobType.value} value={jobType.value}>
{jobType.label}
</Option>
))}
</Select>
<IconButton onClick={() => jobsMutate()}>
<RotateCcwIcon size="14px" />
</IconButton>
</Stack>
<Box
style={{ display: 'flex', flexDirection: 'column', overflow: 'auto' }}
>
{type !== 'NONE' && (
<Table
stickyHeader
sx={{ width: '100%', tableLayout: 'auto', overflow: 'scroll' }}
>
<thead>
<tr>
<th>Job ID</th>
<th>Job Type</th>
<th>Job Status</th>
<th>Job Progress</th>
<th style={{ overflow: 'hidden' }}>Job Data</th>
</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
style={{
maxHeight: '100px',
width: '500px',
overflowY: 'auto',
overflowX: 'hidden',
scrollbarWidth: 'thin', // For Firefox
msOverflowStyle: 'none', // For Internet Explorer and Edge
}}
>
{JSON.stringify(job.job_data, null, 2)}
</pre>
<style>
{`
pre::-webkit-scrollbar {
width: 4px; /* For Chrome, Safari, and Opera */
}`}
</style>
</td>
</tr>
))}
</tbody>
</Table>
)}
</Box>
</>
);
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment