Skip to content

Commit 69e255b

Browse files
authored
Merge pull request transformerlab#674 from transformerlab/fix/eval-editing-errors
Fix Editing, Saving on TRAIN, EVAL and GENERATE
2 parents ae2c0db + 8bf0234 commit 69e255b

File tree

8 files changed

+121
-28
lines changed

8 files changed

+121
-28
lines changed

src/renderer/components/Experiment/Eval/EvalModal.tsx

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import {
2222
import { generateFriendlyName } from 'renderer/lib/utils';
2323
import DynamicPluginForm from '../DynamicPluginForm';
2424
import TrainingModalDataTab from '../Train/TraningModalDataTab';
25+
import SafeJSONParse from 'renderer/components/Shared/SafeJSONParse';
2526

2627
const fetcher = (url) => fetch(url).then((res) => res.json());
2728

@@ -169,6 +170,10 @@ export default function EvalModal({
169170
} else {
170171
setNameInput('');
171172
setHasDatasetKey(false);
173+
setSelectedDataset(null);
174+
setDatasetDisplayMessage('');
175+
setConfig({});
176+
setCurrentTab(0);
172177
}
173178
}, [open]);
174179

@@ -180,7 +185,7 @@ export default function EvalModal({
180185
currentEvalId &&
181186
currentEvalId !== ''
182187
) {
183-
const evalConfig = JSON.parse(evalData.config);
188+
const evalConfig = SafeJSONParse(evalData.config, null);
184189
if (evalConfig) {
185190
setConfig(evalConfig);
186191
const datasetKeyExists = Object.keys(evalConfig).some(
@@ -197,11 +202,18 @@ export default function EvalModal({
197202
key.toLowerCase().includes('tasks'),
198203
);
199204
if (tasksKeyExists) {
200-
evalConfig.tasks = evalConfig.tasks.split(',');
201-
setConfig(evalConfig);
205+
// If tasks key exists and evalConfig.tasks is not an array,
206+
// split it into an array
207+
if (!Array.isArray(evalConfig.tasks)) {
208+
// Ensure tasks is an array
209+
if (typeof evalConfig.tasks === 'string') {
210+
// If tasks is a string, split it into an array
211+
evalConfig.tasks = evalConfig.tasks.split(',');
212+
}
213+
setConfig(evalConfig);
214+
}
202215
}
203-
204-
if (hasDatasetKey && evalConfig.dataset_name.length > 0) {
216+
if (datasetKeyExists && evalConfig.dataset_name.length > 0) {
205217
setSelectedDataset(evalConfig.dataset_name);
206218
}
207219
if (!nameInput && evalConfig?.run_name.length > 0) {

src/renderer/components/Experiment/Eval/EvalTasksTable.tsx

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,6 @@ function formatTemplateConfig(script_parameters): ReactElement {
7070
} catch (error) {
7171
// Not valid JSON, treat as comma-separated string
7272
}
73-
7473
// Handle as comma-separated string
7574
const taskString = script_parameters.tasks.trim();
7675
if (predefined_tasks && predefined_tasks !== '') {
@@ -79,7 +78,41 @@ function formatTemplateConfig(script_parameters): ReactElement {
7978
return taskString;
8079
}
8180

82-
// If tasks is not a string, fall back to original behavior
81+
// If tasks is an array, handle it properly
82+
if (Array.isArray(script_parameters.tasks)) {
83+
let taskNames: string;
84+
85+
// Check if it's an array of strings or objects with name field
86+
if (script_parameters.tasks.length > 0) {
87+
if (typeof script_parameters.tasks[0] === 'string') {
88+
// Array of strings
89+
taskNames = script_parameters.tasks.join(', ');
90+
} else if (script_parameters.tasks[0]?.name) {
91+
// Array of objects with name field
92+
taskNames = script_parameters.tasks
93+
.map((task: any) => task.name)
94+
.join(', ');
95+
} else {
96+
// Fallback for other object structures
97+
taskNames = script_parameters.tasks.join(', ');
98+
}
99+
} else {
100+
// Empty array
101+
taskNames = '';
102+
}
103+
104+
if (predefined_tasks && predefined_tasks !== '') {
105+
// If tasks array is empty, return only predefined tasks
106+
if (taskNames === '') {
107+
return predefined_tasks;
108+
}
109+
// Join with predefined tasks
110+
return taskNames + ',' + predefined_tasks;
111+
}
112+
return taskNames;
113+
}
114+
115+
// If tasks is not a string or array, fall back to original behavior
83116
return script_parameters.tasks + predefined_tasks;
84117
}
85118
return script_parameters.tasks + predefined_tasks;

src/renderer/components/Experiment/Generate/GenerateModal.tsx

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -217,8 +217,6 @@ export default function GenerateModal({
217217
currentGenerationId &&
218218
currentGenerationId != ''
219219
) {
220-
console.log(currentGenerationId);
221-
console.log(generationData);
222220
const generationConfig = JSON.parse(generationData.config);
223221
if (generationConfig) {
224222
setConfig(generationConfig.script_parameters);
@@ -253,7 +251,7 @@ export default function GenerateModal({
253251
}
254252

255253
if (
256-
hasDatasetKey &&
254+
datasetKeyExists &&
257255
generationConfig.script_parameters.dataset_name
258256
) {
259257
setSelectedDataset(generationConfig.script_parameters.dataset_name);

src/renderer/components/Experiment/Generate/GenerateTasksTable.tsx

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,9 +57,14 @@ function formatTemplateConfig(scriptParameters): ReactElement {
5757
const provider = rawModel?.provider;
5858
const useFallback = !rawModel || rawModel === 'N/A' || provider === 'local';
5959

60-
const generationModel = useFallback
61-
? scriptParameters.model_name || 'N/A'
62-
: rawModel;
60+
let generationModel;
61+
if (useFallback) {
62+
generationModel = scriptParameters.model_name || 'N/A';
63+
} else if (typeof rawModel === 'object' && rawModel?.provider) {
64+
generationModel = rawModel.provider;
65+
} else {
66+
generationModel = rawModel;
67+
}
6368

6469
return (
6570
<>

src/renderer/components/Experiment/Train/TrainingModalDataTemplatingTab.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,8 @@ function TrainingModalDataTemplatingTab({
122122
const [debouncedTemplate] = useDebounce(template, 3000);
123123
const [debouncedChatTemplate] = useDebounce(chatTemplate, 3000);
124124

125-
const parsedData = SafeJSONParse(data, null);
125+
const parsedData =
126+
data && data !== 'FILE NOT FOUND' ? SafeJSONParse(data, null) : null;
126127

127128
function PreviewSection() {
128129
if (applyChatTemplate && !chatColumn) {

src/renderer/components/Experiment/Train/TrainingModalLoRA.tsx

Lines changed: 42 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import AvailableFieldsImage from 'renderer/img/show-available-fields.png';
2828
import { generateFriendlyName } from 'renderer/lib/utils';
2929
import OneTimePopup from 'renderer/components/Shared/OneTimePopup';
3030
import TrainingModalDataTemplatingTab from './TrainingModalDataTemplatingTab';
31+
import SafeJSONParse from 'renderer/components/Shared/SafeJSONParse';
3132
const fetcher = (url) => fetch(url).then((res) => res.json());
3233

3334
function PluginIntroduction({ experimentInfo, pluginId }) {
@@ -95,18 +96,20 @@ export default function TrainingModalLoRA({
9596
if (
9697
trainingTypeData &&
9798
trainingTypeData !== 'undefined' &&
99+
trainingTypeData !== 'FILE NOT FOUND' &&
98100
trainingTypeData.length > 0
99101
) {
100-
trainingType = JSON.parse(trainingTypeData)?.train_type || 'LoRA';
102+
trainingType = SafeJSONParse(trainingTypeData)?.train_type || 'LoRA';
101103
}
102104

103105
let runSweeps = false;
104106
if (
105107
trainingTypeData &&
106108
trainingTypeData !== 'undefined' &&
109+
trainingTypeData !== 'FILE NOT FOUND' &&
107110
trainingTypeData.length > 0
108111
) {
109-
const parsedData = JSON.parse(trainingTypeData);
112+
const parsedData = SafeJSONParse(trainingTypeData, {});
110113
if (Array.isArray(parsedData?.supports)) {
111114
runSweeps = parsedData.supports.includes('sweeps');
112115
}
@@ -182,11 +185,39 @@ export default function TrainingModalLoRA({
182185
const result = await response.json();
183186
return result;
184187
}
188+
// Handle modal open/close state - reset form when modal closes
189+
useEffect(() => {
190+
if (open) {
191+
if (!task_id || task_id === '') {
192+
setNameInput(generateFriendlyName());
193+
} else {
194+
// If we have a task_id and modal is opening, refetch the template data
195+
templateMutate();
196+
}
197+
} else {
198+
// Reset all form state when modal closes
199+
setSelectedDataset(null);
200+
setConfig({});
201+
setNameInput('');
202+
setCurrentTab(0);
203+
setSweepConfig({});
204+
setIsRunSweeps(false);
205+
setApplyChatTemplate(false);
206+
setChatColumn('');
207+
setFormattingTemplate('');
208+
setFormattingChatTemplate('');
209+
}
210+
}, [open, task_id, templateMutate]);
211+
185212
// Whenever template data updates, we need to update state variables used in the form.
186213
useEffect(() => {
187-
if (templateData && typeof templateData.config === 'string') {
214+
if (
215+
templateData &&
216+
(typeof templateData.config === 'string' ||
217+
typeof templateData.config === 'object')
218+
) {
188219
// Should only parse data once after initial load
189-
templateData.config = JSON.parse(templateData.config);
220+
templateData.config = SafeJSONParse(templateData.config, null);
190221
}
191222
if (templateData && templateData.config) {
192223
setSelectedDataset(templateData.config.dataset_name);
@@ -208,7 +239,7 @@ export default function TrainingModalLoRA({
208239
}
209240

210241
if (templateData.config.sweep_config) {
211-
setSweepConfig(JSON.parse(templateData.config.sweep_config));
242+
setSweepConfig(SafeJSONParse(templateData.config.sweep_config, {}));
212243
} else {
213244
setSweepConfig({});
214245
}
@@ -352,7 +383,7 @@ export default function TrainingModalLoRA({
352383
let parameterTypes = {};
353384
try {
354385
if (trainingTypeData && trainingTypeData !== 'undefined') {
355-
const parsedData = JSON.parse(trainingTypeData);
386+
const parsedData = SafeJSONParse(trainingTypeData, {});
356387
// Extract parameter names and their types from the training type data
357388
if (parsedData?.parameters) {
358389
availableParameters = Object.keys(parsedData.parameters);
@@ -601,8 +632,11 @@ export default function TrainingModalLoRA({
601632
if (templateData && task_id) {
602633
//Only update if we are currently editing a template
603634
// For all keys in templateData.inputs that are in formJson, set the value from formJson
604-
const templateDataInputs = JSON.parse(templateData.inputs);
605-
const templateDataOutputs = JSON.parse(templateData.outputs);
635+
const templateDataInputs = SafeJSONParse(templateData.inputs, {});
636+
const templateDataOutputs = SafeJSONParse(
637+
templateData.outputs,
638+
{},
639+
);
606640
for (const key in templateDataInputs) {
607641
if (
608642
key in formJson &&
@@ -645,9 +679,6 @@ export default function TrainingModalLoRA({
645679
}),
646680
);
647681
}
648-
setNameInput(generateFriendlyName());
649-
setSweepConfig({});
650-
setIsRunSweeps(false);
651682
onClose();
652683
}}
653684
>

src/renderer/components/Experiment/Train/TraningModalDataTab.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ export default function TrainingModalDataTab({
4848

4949
let parsedData;
5050
try {
51-
parsedData = data ? JSON.parse(data) : null;
51+
parsedData = data && data !== 'FILE NOT FOUND' ? JSON.parse(data) : null;
5252
} catch (e) {
5353
console.error('Error parsing data', e);
5454
parsedData = '';

src/renderer/components/Plugins/PluginDetails.tsx

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -182,20 +182,33 @@ export default function PluginDetails() {
182182
const editorRef = useRef(null);
183183

184184
useEffect(() => {
185-
if (data !== null) {
185+
if (data !== null && data !== 'FILE NOT FOUND') {
186186
if (editorRef?.current && typeof data === 'string') {
187187
editorRef?.current?.setValue(data);
188188
}
189189
editorRef?.current?.updateOptions({
190190
readOnly: false,
191191
});
192192
editorRef?.current?.layout();
193+
} else if (data === 'FILE NOT FOUND') {
194+
// Handle case where file doesn't exist - set empty content
195+
if (editorRef?.current) {
196+
editorRef?.current?.setValue('');
197+
editorRef?.current?.updateOptions({
198+
readOnly: false,
199+
});
200+
editorRef?.current?.layout();
201+
}
193202
}
194203
}, [data]);
195204

196205
function handleEditorDidMount(editor, monaco) {
197206
editorRef.current = editor;
198-
if (editorRef?.current && typeof data === 'string') {
207+
if (
208+
editorRef?.current &&
209+
typeof data === 'string' &&
210+
data !== 'FILE NOT FOUND'
211+
) {
199212
editorRef?.current?.setValue(data);
200213
editorRef?.current?.updateOptions({
201214
readOnly: false,

0 commit comments

Comments
 (0)