Add an import config button (#733)

This commit is contained in:
Rasmus Lerdorf 2026-03-23 17:41:27 -04:00 committed by GitHub
parent 7a0bbca5b1
commit 4ad14d211a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
1 changed files with 62 additions and 1 deletions

View File

@ -1,6 +1,6 @@
'use client';
import { useEffect, useState } from 'react';
import { useEffect, useRef, useState } from 'react';
import { useSearchParams, useRouter } from 'next/navigation';
import { defaultJobConfig, defaultDatasetConfig, migrateJobConfig } from './jobConfig';
import { jobTypeOptions } from './options';
@ -11,6 +11,7 @@ import { SelectInput } from '@/components/formInputs';
import useSettings from '@/hooks/useSettings';
import useGPUInfo from '@/hooks/useGPUInfo';
import useDatasetList from '@/hooks/useDatasetList';
import YAML from 'yaml';
import path from 'path';
import { TopBar, MainContent } from '@/components/layout';
import { Button } from '@headlessui/react';
@ -36,6 +37,49 @@ export default function TrainingForm() {
const [jobConfig, setJobConfig] = useNestedState<JobConfig>(objectCopy(defaultJobConfig));
const [status, setStatus] = useState<'idle' | 'saving' | 'success' | 'error'>('idle');
const fileInputRef = useRef<HTMLInputElement>(null);
const handleImportConfig = () => {
fileInputRef.current?.click();
};
const handleFileSelected = (e: React.ChangeEvent<HTMLInputElement>) => {
const file = e.target.files?.[0];
if (!file) return;
const reader = new FileReader();
reader.onload = () => {
try {
const text = reader.result as string;
let parsed: any;
if (file.name.endsWith('.json') || file.name.endsWith('.jsonc')) {
parsed = JSON.parse(text.replace(/\/\/.*$/gm, '').replace(/\/\*[\s\S]*?\*\//g, ''));
} else {
parsed = YAML.parse(text);
}
// Set required fields (same pattern as AdvancedJob.handleChange)
try {
parsed.config.process[0].sqlite_db_path = './aitk_db.db';
parsed.config.process[0].training_folder = settings.TRAINING_FOLDER;
parsed.config.process[0].device = 'cuda';
parsed.config.process[0].performance_log_every = 10;
} catch (err) {
console.warn('Could not set required fields on imported config:', err);
}
migrateJobConfig(parsed);
setJobConfig(parsed);
} catch (err) {
console.error('Failed to parse config file:', err);
alert('Failed to parse config file. Please check the file format.');
}
};
reader.readAsText(file);
// Reset so the same file can be re-imported
e.target.value = '';
};
useEffect(() => {
if (!isSettingsLoaded) return;
@ -161,6 +205,15 @@ export default function TrainingForm() {
/>
</div>
<div className="mx-4 bg-gray-200 dark:bg-gray-800 w-1 h-6"></div>
<div>
<Button
className="text-gray-200 bg-gray-800 px-3 py-1 rounded-md"
onClick={handleImportConfig}
>
Import Config
</Button>
</div>
<div className="mx-4 bg-gray-200 dark:bg-gray-800 w-1 h-6"></div>
</>
)}
{!showAdvancedView && (
@ -215,6 +268,14 @@ export default function TrainingForm() {
</div>
</TopBar>
<input
ref={fileInputRef}
type="file"
accept=".yaml,.yml,.json,.jsonc"
style={{ display: 'none' }}
onChange={handleFileSelected}
/>
{showAdvancedView ? (
<div className="pt-[48px] absolute top-0 left-0 w-full h-full overflow-auto">
<AdvancedJob