mirror of
https://github.com/labring/FastGPT.git
synced 2025-10-15 07:31:19 +00:00

* refactor: remove ModelProviderIdType and update related types (#5549) * perf: model provider * fix eval create split (#5570) * git rebase --continuedoc * add more variable types (#5540) * variable types * password * time picker * internal var * file * fix-test * time select default value & range * password & type render * fix * fix build * fix * move method * split date select * icon * perf: variable code * prompt editor add markdown plugin (#5556) * editor markdown * fix build * pnpm lock * add props * update code * fix list * editor ui * fix variable reset (#5586) * perf: variables type code * customize lexical indent (#5588) * perf: multiple selector * perf: tab plugin * doc * refactor: update workflow constants to use ToolTypeEnum (#5491) * refactor: replace FlowNodeTemplateTypeEnum with string literals in workflow templates * perf: tool type --------- Co-authored-by: archer <545436317@qq.com> * update doc * fix: make table's row more natural while dragging it (#5596) * feat: add APIGetTemplate function and refactor template fetching logic (#5498) * feat: add APIGetTemplate function and refactor template fetching logic * chore: adjust the code * chore: update sdk --------- Co-authored-by: FinleyGe <m13203533462@163.com> * perf init system * doc * remove log * remove i18n * perf: variables render --------- Co-authored-by: Ctrlz <143257420+ctrlz526@users.noreply.github.com> Co-authored-by: heheer <heheer@sealos.io> Co-authored-by: 伍闲犬 <whoeverimf5@gmail.com> Co-authored-by: FinleyGe <m13203533462@163.com>
148 lines
4.3 KiB
TypeScript
148 lines
4.3 KiB
TypeScript
import { evaluationFileErrors } from '@fastgpt/global/core/app/evaluation/constants';
|
|
import { getEvaluationFileHeader } from '@fastgpt/global/core/app/evaluation/utils';
|
|
import type { VariableItemType } from '@fastgpt/global/core/app/type';
|
|
import { addLog } from '../../../common/system/log';
|
|
import { VariableInputEnum } from '@fastgpt/global/core/workflow/constants';
|
|
import Papa from 'papaparse';
|
|
|
|
export const parseEvaluationCSV = (rawText: string) => {
|
|
const parseResult = Papa.parse(rawText.trim(), {
|
|
skipEmptyLines: true,
|
|
header: false,
|
|
transformHeader: (header: string) => header.trim()
|
|
});
|
|
|
|
if (parseResult.errors.length > 0) {
|
|
addLog.error('CSV parsing failed', parseResult.errors);
|
|
throw new Error('CSV parsing failed');
|
|
}
|
|
|
|
return parseResult.data as string[][];
|
|
};
|
|
|
|
export const validateEvaluationFile = async (
|
|
rawText: string,
|
|
appVariables?: VariableItemType[]
|
|
) => {
|
|
// Parse CSV using Papa Parse
|
|
const csvData = parseEvaluationCSV(rawText);
|
|
const dataLength = csvData.length;
|
|
|
|
// Validate file header
|
|
const expectedHeader = getEvaluationFileHeader(appVariables);
|
|
const actualHeader = csvData[0]?.join(',') || '';
|
|
if (actualHeader !== expectedHeader) {
|
|
addLog.error(`Header mismatch. Expected: ${expectedHeader}, Got: ${actualHeader}`);
|
|
return Promise.reject(evaluationFileErrors);
|
|
}
|
|
|
|
// Validate data rows count
|
|
if (dataLength <= 1) {
|
|
addLog.error('No data rows found');
|
|
return Promise.reject(evaluationFileErrors);
|
|
}
|
|
|
|
const maxRows = 1000;
|
|
if (dataLength - 1 > maxRows) {
|
|
addLog.error(`Too many rows. Max: ${maxRows}, Got: ${dataLength - 1}`);
|
|
return Promise.reject(evaluationFileErrors);
|
|
}
|
|
|
|
const headers = csvData[0];
|
|
|
|
// Get required field indices
|
|
const requiredFields = headers
|
|
.map((header, index) => ({ header: header.trim(), index }))
|
|
.filter(({ header }) => header.startsWith('*'));
|
|
|
|
const errors: string[] = [];
|
|
|
|
// Validate each data row
|
|
for (let i = 1; i < csvData.length; i++) {
|
|
const values = csvData[i];
|
|
|
|
// Check required fields
|
|
requiredFields.forEach(({ header, index }) => {
|
|
if (!values[index]?.trim()) {
|
|
errors.push(`Row ${i + 1}: required field "${header}" is empty`);
|
|
}
|
|
});
|
|
|
|
// Validate app variables
|
|
if (appVariables) {
|
|
validateRowVariables({
|
|
values,
|
|
variables: appVariables,
|
|
rowNum: i + 1,
|
|
errors
|
|
});
|
|
}
|
|
}
|
|
|
|
if (errors.length > 0) {
|
|
addLog.error(`Validation failed: ${errors.join('; ')}`);
|
|
return Promise.reject(evaluationFileErrors);
|
|
}
|
|
|
|
return { csvData, dataLength };
|
|
};
|
|
|
|
const validateRowVariables = ({
|
|
values,
|
|
variables,
|
|
rowNum,
|
|
errors
|
|
}: {
|
|
values: string[];
|
|
variables: VariableItemType[];
|
|
rowNum: number;
|
|
errors: string[];
|
|
}) => {
|
|
variables.forEach((variable, index) => {
|
|
const value = values[index]?.trim();
|
|
|
|
// Skip validation if value is empty and not required
|
|
if (!value && !variable.required) return;
|
|
|
|
switch (variable.type) {
|
|
case VariableInputEnum.input:
|
|
// Validate string length
|
|
if (variable.maxLength && value && value.length > variable.maxLength) {
|
|
errors.push(
|
|
`Row ${rowNum}: "${variable.label}" exceeds max length (${variable.maxLength})`
|
|
);
|
|
}
|
|
break;
|
|
|
|
case VariableInputEnum.numberInput:
|
|
// Validate number type and range
|
|
if (value) {
|
|
const numValue = Number(value);
|
|
if (isNaN(numValue)) {
|
|
errors.push(`Row ${rowNum}: "${variable.label}" must be a number`);
|
|
} else {
|
|
if (variable.min !== undefined && numValue < variable.min) {
|
|
errors.push(`Row ${rowNum}: "${variable.label}" below minimum (${variable.min})`);
|
|
}
|
|
if (variable.max !== undefined && numValue > variable.max) {
|
|
errors.push(`Row ${rowNum}: "${variable.label}" exceeds maximum (${variable.max})`);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case VariableInputEnum.select:
|
|
// Validate select options
|
|
if (value && variable.enums?.length) {
|
|
const validOptions = variable.enums.map((item) => item.value);
|
|
if (!validOptions.includes(value)) {
|
|
errors.push(
|
|
`Row ${rowNum}: "${variable.label}" invalid option. Valid: [${validOptions.join(', ')}]`
|
|
);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
});
|
|
};
|