mirror of
https://github.com/ostris/ai-toolkit.git
synced 2026-04-28 02:01:29 +00:00
Cleanup and add hooks
This commit is contained in:
@@ -5,32 +5,12 @@ import Card from '@/components/Card';
|
|||||||
import { Modal } from '@/components/Modal';
|
import { Modal } from '@/components/Modal';
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
import { TextInput } from '@/components/formInputs';
|
import { TextInput } from '@/components/formInputs';
|
||||||
|
import useDatasetList from '@/hooks/useDatasetList';
|
||||||
|
|
||||||
export default function Datasets() {
|
export default function Datasets() {
|
||||||
const [datasets, setDatasets] = useState([]);
|
const { datasets, status, refreshDatasets } = useDatasetList();
|
||||||
const [newDatasetName, setNewDatasetName] = useState('');
|
const [newDatasetName, setNewDatasetName] = useState('');
|
||||||
const [isNewDatasetModalOpen, setIsNewDatasetModalOpen] = useState(false);
|
const [isNewDatasetModalOpen, setIsNewDatasetModalOpen] = useState(false);
|
||||||
const [status, setStatus] = useState<'idle' | 'loading' | 'success' | 'error'>('idle');
|
|
||||||
|
|
||||||
const refreshDatasets = () => {
|
|
||||||
setStatus('loading');
|
|
||||||
fetch('/api/datasets/list')
|
|
||||||
.then(res => res.json())
|
|
||||||
.then(data => {
|
|
||||||
console.log('Datasets:', data);
|
|
||||||
// sort
|
|
||||||
data.sort((a: string, b: string) => a.localeCompare(b));
|
|
||||||
setDatasets(data);
|
|
||||||
setStatus('success');
|
|
||||||
})
|
|
||||||
.catch(error => {
|
|
||||||
console.error('Error fetching datasets:', error);
|
|
||||||
setStatus('error');
|
|
||||||
});
|
|
||||||
};
|
|
||||||
useEffect(() => {
|
|
||||||
refreshDatasets();
|
|
||||||
}, []);
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="space-y-6">
|
<div className="space-y-6">
|
||||||
|
|||||||
@@ -1,29 +1,12 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
|
import useSettings from '@/hooks/useSettings';
|
||||||
|
|
||||||
export default function Settings() {
|
export default function Settings() {
|
||||||
const [settings, setSettings] = useState({
|
const { settings, setSettings } = useSettings();
|
||||||
HF_TOKEN: '',
|
|
||||||
TRAINING_FOLDER: '',
|
|
||||||
DATASETS_FOLDER: '',
|
|
||||||
});
|
|
||||||
const [status, setStatus] = useState<'idle' | 'saving' | 'success' | 'error'>('idle');
|
const [status, setStatus] = useState<'idle' | 'saving' | 'success' | 'error'>('idle');
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
// Fetch current settings
|
|
||||||
fetch('/api/settings')
|
|
||||||
.then(res => res.json())
|
|
||||||
.then(data => {
|
|
||||||
setSettings({
|
|
||||||
HF_TOKEN: data.HF_TOKEN || '',
|
|
||||||
TRAINING_FOLDER: data.TRAINING_FOLDER || '',
|
|
||||||
DATASETS_FOLDER: data.DATASETS_FOLDER || '',
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.catch(error => console.error('Error fetching settings:', error));
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const handleSubmit = async (e: React.FormEvent) => {
|
const handleSubmit = async (e: React.FormEvent) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
setStatus('saving');
|
setStatus('saving');
|
||||||
|
|||||||
@@ -1,28 +1,50 @@
|
|||||||
'use client';
|
'use client';
|
||||||
// todo update training folder from settings
|
// todo update training folder from settings
|
||||||
|
|
||||||
import { useEffect, useMemo, useState } from 'react';
|
import { use, useEffect, useMemo, useState } from 'react';
|
||||||
import { useSearchParams, useRouter } from 'next/navigation';
|
import { useSearchParams, useRouter } from 'next/navigation';
|
||||||
import { options } from './options';
|
import { options } from './options';
|
||||||
import { GPUApiResponse } from '@/types';
|
|
||||||
import { defaultJobConfig, defaultDatasetConfig } from './jobConfig';
|
import { defaultJobConfig, defaultDatasetConfig } from './jobConfig';
|
||||||
import { JobConfig } from '@/types';
|
import { JobConfig } from '@/types';
|
||||||
import { objectCopy } from '@/utils/basic';
|
import { objectCopy } from '@/utils/basic';
|
||||||
import { useNestedState } from '@/utils/hooks';
|
import { useNestedState } from '@/utils/hooks';
|
||||||
import { TextInput, SelectInput, Checkbox, FormGroup, NumberInput } from '@/components/formInputs';
|
import { TextInput, SelectInput, Checkbox, FormGroup, NumberInput } from '@/components/formInputs';
|
||||||
import Card from '@/components/Card';
|
import Card from '@/components/Card';
|
||||||
import { Trash, X } from 'lucide-react';
|
import { X } from 'lucide-react';
|
||||||
|
import useSettings from '@/hooks/useSettings';
|
||||||
|
import useGPUInfo from '@/hooks/useGPUInfo';
|
||||||
|
import useDatasetList from '@/hooks/useDatasetList';
|
||||||
|
import path from 'path';
|
||||||
|
|
||||||
export default function TrainingForm() {
|
export default function TrainingForm() {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const searchParams = useSearchParams();
|
const searchParams = useSearchParams();
|
||||||
const runId = searchParams.get('id');
|
const runId = searchParams.get('id');
|
||||||
const [gpuID, setGpuID] = useState<number | null>(null);
|
const [gpuID, setGpuID] = useState<number | null>(null);
|
||||||
const [gpuList, setGpuList] = useState<number[]>([]);
|
const { settings, isSettingsLoaded } = useSettings();
|
||||||
|
const { gpuList, isGPUInfoLoaded } = useGPUInfo();
|
||||||
|
const { datasets, status: datasetFetchStatus } = useDatasetList();
|
||||||
|
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');
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!isSettingsLoaded) return;
|
||||||
|
if (datasetFetchStatus !== 'success') return;
|
||||||
|
|
||||||
|
const datasetOptions = datasets.map(name => ({ value: path.join(settings.DATASETS_FOLDER, name), label: name }));
|
||||||
|
setDatasetOptions(datasetOptions);
|
||||||
|
const defaultDatasetPath = defaultDatasetConfig.folder_path;
|
||||||
|
|
||||||
|
for(let i = 0; i < jobConfig.config.process[0].datasets.length; i++) {
|
||||||
|
const dataset = jobConfig.config.process[0].datasets[i];
|
||||||
|
if (dataset.folder_path === defaultDatasetPath) {
|
||||||
|
setJobConfig(datasetOptions[0].value, `config.process[0].datasets[${i}].folder_path`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [datasets, settings, isSettingsLoaded, datasetFetchStatus]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (runId) {
|
if (runId) {
|
||||||
fetch(`/api/training?id=${runId}`)
|
fetch(`/api/training?id=${runId}`)
|
||||||
@@ -36,33 +58,18 @@ export default function TrainingForm() {
|
|||||||
}, [runId]);
|
}, [runId]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const fetchGpuInfo = async () => {
|
if (isGPUInfoLoaded) {
|
||||||
try {
|
if (gpuID === null && gpuList.length > 0) {
|
||||||
const response = await fetch('/api/gpu');
|
setGpuID(gpuList[0]);
|
||||||
|
|
||||||
if (!response.ok) {
|
|
||||||
throw new Error(`HTTP error! Status: ${response.status}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
const data: GPUApiResponse = await response.json();
|
|
||||||
setGpuList(data.gpus.map(gpu => gpu.index).sort());
|
|
||||||
} catch (err) {
|
|
||||||
console.log(`Failed to fetch GPU data: ${err instanceof Error ? err.message : String(err)}`);
|
|
||||||
} finally {
|
|
||||||
// setLoading(false);
|
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
}, [gpuList, isGPUInfoLoaded]);
|
||||||
|
|
||||||
fetch('/api/settings')
|
useEffect(() => {
|
||||||
.then(res => res.json())
|
if (isSettingsLoaded) {
|
||||||
.then(data => {
|
setJobConfig(settings.TRAINING_FOLDER, 'config.process[0].training_folder');
|
||||||
setJobConfig(data.TRAINING_FOLDER, 'config.process[0].training_folder');
|
}
|
||||||
})
|
}, [settings, isSettingsLoaded]);
|
||||||
.catch(error => console.error('Error fetching settings:', error));
|
|
||||||
|
|
||||||
// Fetch immediately on component mount
|
|
||||||
fetchGpuInfo();
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const handleSubmit = async (e: React.FormEvent) => {
|
const handleSubmit = async (e: React.FormEvent) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
@@ -296,14 +303,20 @@ export default function TrainingForm() {
|
|||||||
<h2 className="text-lg font-bold mb-4">Dataset {i + 1}</h2>
|
<h2 className="text-lg font-bold mb-4">Dataset {i + 1}</h2>
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
|
||||||
<div>
|
<div>
|
||||||
<TextInput
|
<SelectInput
|
||||||
|
label="Dataset"
|
||||||
|
value={dataset.folder_path}
|
||||||
|
onChange={value => setJobConfig(value, `config.process[0].datasets[${i}].folder_path`)}
|
||||||
|
options={datasetOptions}
|
||||||
|
/>
|
||||||
|
{/* <TextInput
|
||||||
label="Folder Path"
|
label="Folder Path"
|
||||||
value={dataset.folder_path}
|
value={dataset.folder_path}
|
||||||
onChange={value => setJobConfig(value, `config.process[0].datasets[${i}].folder_path`)}
|
onChange={value => setJobConfig(value, `config.process[0].datasets[${i}].folder_path`)}
|
||||||
placeholder="eg. /path/to/images/folder"
|
placeholder="eg. /path/to/images/folder"
|
||||||
required
|
required
|
||||||
/>
|
/> */}
|
||||||
<TextInput
|
{/* <TextInput
|
||||||
label="Mask Folder Path"
|
label="Mask Folder Path"
|
||||||
className="pt-2"
|
className="pt-2"
|
||||||
value={dataset.mask_path || ''}
|
value={dataset.mask_path || ''}
|
||||||
@@ -325,7 +338,7 @@ export default function TrainingForm() {
|
|||||||
min={0}
|
min={0}
|
||||||
max={1}
|
max={1}
|
||||||
required
|
required
|
||||||
/>
|
/> */}
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<TextInput
|
<TextInput
|
||||||
|
|||||||
30
ui/src/hooks/useDatasetList.tsx
Normal file
30
ui/src/hooks/useDatasetList.tsx
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import { useEffect, useState } from 'react';
|
||||||
|
|
||||||
|
export default function useDatasetList() {
|
||||||
|
const [datasets, setDatasets] = useState<string[]>([]);
|
||||||
|
const [status, setStatus] = useState<'idle' | 'loading' | 'success' | 'error'>('idle');
|
||||||
|
|
||||||
|
const refreshDatasets = () => {
|
||||||
|
setStatus('loading');
|
||||||
|
fetch('/api/datasets/list')
|
||||||
|
.then(res => res.json())
|
||||||
|
.then(data => {
|
||||||
|
console.log('Datasets:', data);
|
||||||
|
// sort
|
||||||
|
data.sort((a: string, b: string) => a.localeCompare(b));
|
||||||
|
setDatasets(data);
|
||||||
|
setStatus('success');
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.error('Error fetching datasets:', error);
|
||||||
|
setStatus('error');
|
||||||
|
});
|
||||||
|
};
|
||||||
|
useEffect(() => {
|
||||||
|
refreshDatasets();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return { datasets, setDatasets, status, refreshDatasets };
|
||||||
|
}
|
||||||
32
ui/src/hooks/useGPUInfo.tsx
Normal file
32
ui/src/hooks/useGPUInfo.tsx
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import { GPUApiResponse } from '@/types';
|
||||||
|
import { useEffect, useState } from 'react';
|
||||||
|
|
||||||
|
export default function useGPUInfo() {
|
||||||
|
const [gpuList, setGpuList] = useState<number[]>([]);
|
||||||
|
const [isGPUInfoLoaded, setIsLoaded] = useState(false);
|
||||||
|
useEffect(() => {
|
||||||
|
const fetchGpuInfo = async () => {
|
||||||
|
try {
|
||||||
|
const response = await fetch('/api/gpu');
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`HTTP error! Status: ${response.status}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const data: GPUApiResponse = await response.json();
|
||||||
|
setGpuList(data.gpus.map(gpu => gpu.index).sort());
|
||||||
|
} catch (err) {
|
||||||
|
console.log(`Failed to fetch GPU data: ${err instanceof Error ? err.message : String(err)}`);
|
||||||
|
} finally {
|
||||||
|
setIsLoaded(true);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Fetch immediately on component mount
|
||||||
|
fetchGpuInfo();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return { gpuList, setGpuList, isGPUInfoLoaded };
|
||||||
|
}
|
||||||
28
ui/src/hooks/useSettings.tsx
Normal file
28
ui/src/hooks/useSettings.tsx
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import { useEffect, useState } from 'react';
|
||||||
|
|
||||||
|
export default function useSettings() {
|
||||||
|
const [settings, setSettings] = useState({
|
||||||
|
HF_TOKEN: '',
|
||||||
|
TRAINING_FOLDER: '',
|
||||||
|
DATASETS_FOLDER: '',
|
||||||
|
});
|
||||||
|
const [isSettingsLoaded, setIsLoaded] = useState(false);
|
||||||
|
useEffect(() => {
|
||||||
|
// Fetch current settings
|
||||||
|
fetch('/api/settings')
|
||||||
|
.then(res => res.json())
|
||||||
|
.then(data => {
|
||||||
|
setSettings({
|
||||||
|
HF_TOKEN: data.HF_TOKEN || '',
|
||||||
|
TRAINING_FOLDER: data.TRAINING_FOLDER || '',
|
||||||
|
DATASETS_FOLDER: data.DATASETS_FOLDER || '',
|
||||||
|
});
|
||||||
|
setIsLoaded(true);
|
||||||
|
})
|
||||||
|
.catch(error => console.error('Error fetching settings:', error));
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return { settings, setSettings, isSettingsLoaded };
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user