Fixed some bugs with ui and lock job name to prevent issues with continuing training.

This commit is contained in:
Jaret Burkett
2025-02-22 11:49:36 -07:00
parent 5f094fb17a
commit f60698d0ee
3 changed files with 27 additions and 12 deletions

View File

@@ -28,6 +28,7 @@ export default function TrainingForm() {
const { datasets, status: datasetFetchStatus } = useDatasetList(); const { datasets, status: datasetFetchStatus } = useDatasetList();
const [datasetOptions, setDatasetOptions] = useState<{ value: string; label: string }[]>([]); const [datasetOptions, setDatasetOptions] = useState<{ value: string; label: string }[]>([]);
const [jobConfig, setJobConfig] = useNestedState<JobConfig>(objectCopy(defaultJobConfig)); const [jobConfig, setJobConfig] = useNestedState<JobConfig>(objectCopy(defaultJobConfig));
const [status, setStatus] = useState<'idle' | 'saving' | 'success' | 'error'>('idle'); const [status, setStatus] = useState<'idle' | 'saving' | 'success' | 'error'>('idle');
@@ -54,6 +55,7 @@ export default function TrainingForm() {
.then(data => { .then(data => {
setGpuIDs(data.gpu_ids); setGpuIDs(data.gpu_ids);
setJobConfig(JSON.parse(data.job_config)); setJobConfig(JSON.parse(data.job_config));
// setJobConfig(data.name, 'config.name');
}) })
.catch(error => console.error('Error fetching training:', error)); .catch(error => console.error('Error fetching training:', error));
} }
@@ -111,6 +113,8 @@ export default function TrainingForm() {
saveJob(); saveJob();
}; };
console.log('jobConfig.config.process[0].network.linear', jobConfig?.config?.process[0].network?.linear);
return ( return (
<> <>
<TopBar> <TopBar>
@@ -142,6 +146,7 @@ export default function TrainingForm() {
value={jobConfig.config.name} value={jobConfig.config.name}
onChange={value => setJobConfig(value, 'config.name')} onChange={value => setJobConfig(value, 'config.name')}
placeholder="Enter training name" placeholder="Enter training name"
disabled={runId !== null}
required required
/> />
<SelectInput <SelectInput
@@ -199,17 +204,18 @@ export default function TrainingForm() {
/> />
</FormGroup> </FormGroup>
</Card> </Card>
{jobConfig.config.process[0].network?.linear && ( {jobConfig.config.process[0].network?.type && (
<Card title="LoRA Configuration"> <Card title="LoRA Configuration">
<NumberInput <NumberInput
label="Linear Rank" label="Linear Rank"
value={jobConfig.config.process[0].network.linear} value={jobConfig.config.process[0].network.linear}
onChange={value => { onChange={value => {
console.log('onChange', value);
setJobConfig(value, 'config.process[0].network.linear'); setJobConfig(value, 'config.process[0].network.linear');
setJobConfig(value, 'config.process[0].network.linear_alpha'); setJobConfig(value, 'config.process[0].network.linear_alpha');
}} }}
placeholder="eg. 16" placeholder="eg. 16"
min={1} min={0}
max={1024} max={1024}
required required
/> />

View File

@@ -16,20 +16,25 @@ export interface TextInputProps extends InputProps {
value: string; value: string;
onChange: (value: string) => void; onChange: (value: string) => void;
type?: 'text' | 'password'; type?: 'text' | 'password';
disabled?: boolean;
} }
export const TextInput = (props: TextInputProps) => { export const TextInput = (props: TextInputProps) => {
const { label, value, onChange, placeholder, required } = props; const { label, value, onChange, placeholder, required, disabled } = props;
return ( return (
<div className={classNames(props.className)}> <div className={classNames(props.className)}>
{label && <label className={labelClasses}>{label}</label>} {label && <label className={labelClasses}>{label}</label>}
<input <input
type={props.type || 'text'} type={props.type || 'text'}
value={value} value={value}
onChange={e => onChange(e.target.value)} onChange={e => {
className={inputClasses} if (disabled) return;
onChange(e.target.value);
}}
className={`${inputClasses} ${disabled && 'opacity-30 cursor-not-allowed'}`}
placeholder={placeholder} placeholder={placeholder}
required={required} required={required}
disabled={disabled}
/> />
</div> </div>
); );
@@ -50,10 +55,10 @@ export const NumberInput = (props: NumberInputProps) => {
<input <input
type="number" type="number"
value={value} value={value}
onChange={(e) => { onChange={e => {
// Use parseFloat instead of Number to properly handle decimal values // Use parseFloat instead of Number to properly handle decimal values
const rawValue = e.target.value; const rawValue = e.target.value;
// Special handling for empty or partial inputs // Special handling for empty or partial inputs
if (rawValue === '' || rawValue === '-' || rawValue === '.') { if (rawValue === '' || rawValue === '-' || rawValue === '.') {
// For empty or partial inputs (like just a minus sign or decimal point), // For empty or partial inputs (like just a minus sign or decimal point),
@@ -62,18 +67,18 @@ export const NumberInput = (props: NumberInputProps) => {
onChange(0); onChange(0);
return; return;
} }
let value = parseFloat(rawValue); let value = Number(rawValue);
// Handle NaN cases // Handle NaN cases
if (isNaN(value)) { if (isNaN(value)) {
value = 0; value = 0;
} }
// Apply min/max constraints only for valid numbers // Apply min/max constraints only for valid numbers
if (min !== undefined && value < min) value = min; if (min !== undefined && value < min) value = min;
if (max !== undefined && value > max) value = max; if (max !== undefined && value > max) value = max;
onChange(value); onChange(value);
}} }}
className={inputClasses} className={inputClasses}

View File

@@ -77,6 +77,10 @@ export function useNestedState<T>(initialState: T): [T, (value: any, path?: stri
const [state, setState] = React.useState<T>(initialState); const [state, setState] = React.useState<T>(initialState);
const setValue = React.useCallback((value: any, path?: string) => { const setValue = React.useCallback((value: any, path?: string) => {
if (path === undefined) {
setState(value);
return
}
setState(prevState => setNestedValue(prevState, value, path)); setState(prevState => setNestedValue(prevState, value, path));
}, []); }, []);