Skip to content

Commit 3507483

Browse files
authored
fix eval create split (#5570)
1 parent d771bc6 commit 3507483

File tree

1 file changed

+147
-0
lines changed
  • packages/service/core/app/evaluation

1 file changed

+147
-0
lines changed
Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
import { evaluationFileErrors } from '@fastgpt/global/core/app/evaluation/constants';
2+
import { getEvaluationFileHeader } from '@fastgpt/global/core/app/evaluation/utils';
3+
import type { VariableItemType } from '@fastgpt/global/core/app/type';
4+
import { addLog } from '../../../common/system/log';
5+
import { VariableInputEnum } from '@fastgpt/global/core/workflow/constants';
6+
import Papa from 'papaparse';
7+
8+
export const parseEvaluationCSV = (rawText: string) => {
9+
const parseResult = Papa.parse(rawText.trim(), {
10+
skipEmptyLines: true,
11+
header: false,
12+
transformHeader: (header: string) => header.trim()
13+
});
14+
15+
if (parseResult.errors.length > 0) {
16+
addLog.error('CSV parsing failed', parseResult.errors);
17+
throw new Error('CSV parsing failed');
18+
}
19+
20+
return parseResult.data as string[][];
21+
};
22+
23+
export const validateEvaluationFile = async (
24+
rawText: string,
25+
appVariables?: VariableItemType[]
26+
) => {
27+
// Parse CSV using Papa Parse
28+
const csvData = parseEvaluationCSV(rawText);
29+
const dataLength = csvData.length;
30+
31+
// Validate file header
32+
const expectedHeader = getEvaluationFileHeader(appVariables);
33+
const actualHeader = csvData[0]?.join(',') || '';
34+
if (actualHeader !== expectedHeader) {
35+
addLog.error(`Header mismatch. Expected: ${expectedHeader}, Got: ${actualHeader}`);
36+
return Promise.reject(evaluationFileErrors);
37+
}
38+
39+
// Validate data rows count
40+
if (dataLength <= 1) {
41+
addLog.error('No data rows found');
42+
return Promise.reject(evaluationFileErrors);
43+
}
44+
45+
const maxRows = 1000;
46+
if (dataLength - 1 > maxRows) {
47+
addLog.error(`Too many rows. Max: ${maxRows}, Got: ${dataLength - 1}`);
48+
return Promise.reject(evaluationFileErrors);
49+
}
50+
51+
const headers = csvData[0];
52+
53+
// Get required field indices
54+
const requiredFields = headers
55+
.map((header, index) => ({ header: header.trim(), index }))
56+
.filter(({ header }) => header.startsWith('*'));
57+
58+
const errors: string[] = [];
59+
60+
// Validate each data row
61+
for (let i = 1; i < csvData.length; i++) {
62+
const values = csvData[i];
63+
64+
// Check required fields
65+
requiredFields.forEach(({ header, index }) => {
66+
if (!values[index]?.trim()) {
67+
errors.push(`Row ${i + 1}: required field "${header}" is empty`);
68+
}
69+
});
70+
71+
// Validate app variables
72+
if (appVariables) {
73+
validateRowVariables({
74+
values,
75+
variables: appVariables,
76+
rowNum: i + 1,
77+
errors
78+
});
79+
}
80+
}
81+
82+
if (errors.length > 0) {
83+
addLog.error(`Validation failed: ${errors.join('; ')}`);
84+
return Promise.reject(evaluationFileErrors);
85+
}
86+
87+
return { csvData, dataLength };
88+
};
89+
90+
const validateRowVariables = ({
91+
values,
92+
variables,
93+
rowNum,
94+
errors
95+
}: {
96+
values: string[];
97+
variables: VariableItemType[];
98+
rowNum: number;
99+
errors: string[];
100+
}) => {
101+
variables.forEach((variable, index) => {
102+
const value = values[index]?.trim();
103+
104+
// Skip validation if value is empty and not required
105+
if (!value && !variable.required) return;
106+
107+
switch (variable.type) {
108+
case VariableInputEnum.input:
109+
// Validate string length
110+
if (variable.maxLength && value && value.length > variable.maxLength) {
111+
errors.push(
112+
`Row ${rowNum}: "${variable.label}" exceeds max length (${variable.maxLength})`
113+
);
114+
}
115+
break;
116+
117+
case VariableInputEnum.numberInput:
118+
// Validate number type and range
119+
if (value) {
120+
const numValue = Number(value);
121+
if (isNaN(numValue)) {
122+
errors.push(`Row ${rowNum}: "${variable.label}" must be a number`);
123+
} else {
124+
if (variable.min !== undefined && numValue < variable.min) {
125+
errors.push(`Row ${rowNum}: "${variable.label}" below minimum (${variable.min})`);
126+
}
127+
if (variable.max !== undefined && numValue > variable.max) {
128+
errors.push(`Row ${rowNum}: "${variable.label}" exceeds maximum (${variable.max})`);
129+
}
130+
}
131+
}
132+
break;
133+
134+
case VariableInputEnum.select:
135+
// Validate select options
136+
if (value && variable.enums?.length) {
137+
const validOptions = variable.enums.map((item) => item.value);
138+
if (!validOptions.includes(value)) {
139+
errors.push(
140+
`Row ${rowNum}: "${variable.label}" invalid option. Valid: [${validOptions.join(', ')}]`
141+
);
142+
}
143+
}
144+
break;
145+
}
146+
});
147+
};

0 commit comments

Comments
 (0)