first-update
This commit is contained in:
@@ -0,0 +1,94 @@
|
||||
'use client';
|
||||
|
||||
import { useState, useEffect, useCallback } from 'react';
|
||||
|
||||
/**
|
||||
* 评估任务详情 Hook
|
||||
*/
|
||||
export default function useEvalTaskDetail(projectId, taskId) {
|
||||
const [task, setTask] = useState(null);
|
||||
const [results, setResults] = useState([]);
|
||||
const [stats, setStats] = useState(null);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState('');
|
||||
|
||||
// 分页和筛选状态
|
||||
const [page, setPage] = useState(1);
|
||||
const [pageSize, setPageSize] = useState(10);
|
||||
const [filterType, setFilterType] = useState(null);
|
||||
const [filterCorrect, setFilterCorrect] = useState(null); // null: all, true: correct, false: incorrect
|
||||
const [total, setTotal] = useState(0);
|
||||
|
||||
// 加载任务详情
|
||||
const loadData = useCallback(async () => {
|
||||
if (!projectId || !taskId) return;
|
||||
|
||||
try {
|
||||
setLoading(true);
|
||||
setError('');
|
||||
|
||||
// 构建查询参数
|
||||
const params = new URLSearchParams({
|
||||
page: page.toString(),
|
||||
pageSize: pageSize.toString()
|
||||
});
|
||||
|
||||
if (filterType) {
|
||||
params.append('type', filterType);
|
||||
}
|
||||
|
||||
if (filterCorrect !== null) {
|
||||
params.append('isCorrect', filterCorrect.toString());
|
||||
}
|
||||
|
||||
const response = await fetch(`/api/projects/${projectId}/eval-tasks/${taskId}?${params.toString()}`);
|
||||
const result = await response.json();
|
||||
|
||||
if (result.code === 0) {
|
||||
setTask(result.data.task);
|
||||
setResults(result.data.results || []);
|
||||
setTotal(result.data.total || 0);
|
||||
setStats(result.data.stats);
|
||||
} else {
|
||||
setError(result.error || '加载失败');
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('加载任务详情失败:', err);
|
||||
setError('加载失败');
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}, [projectId, taskId, page, pageSize, filterType, filterCorrect]);
|
||||
|
||||
// 初始加载
|
||||
useEffect(() => {
|
||||
loadData();
|
||||
}, [loadData]);
|
||||
|
||||
// 自动刷新进行中的任务 (仅在第一页且无筛选时刷新,避免干扰用户查看历史记录)
|
||||
useEffect(() => {
|
||||
if (task?.status !== 0 || page !== 1 || filterType || filterCorrect !== null) return;
|
||||
|
||||
const interval = setInterval(loadData, 3000);
|
||||
return () => clearInterval(interval);
|
||||
}, [task?.status, page, filterType, filterCorrect, loadData]);
|
||||
|
||||
return {
|
||||
task,
|
||||
results,
|
||||
stats,
|
||||
total,
|
||||
page,
|
||||
setPage,
|
||||
pageSize,
|
||||
setPageSize,
|
||||
filterType,
|
||||
setFilterType,
|
||||
filterCorrect,
|
||||
setFilterCorrect,
|
||||
loading,
|
||||
error,
|
||||
setError,
|
||||
loadData
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,187 @@
|
||||
'use client';
|
||||
|
||||
import { useState, useEffect } from 'react';
|
||||
import { getDefaultScoreAnchors } from '@/lib/llm/prompts/llmJudge';
|
||||
|
||||
export function useEvalTaskForm(projectId, open) {
|
||||
const [models, setModels] = useState([]);
|
||||
const [selectedModels, setSelectedModels] = useState([]);
|
||||
const [judgeModel, setJudgeModel] = useState('');
|
||||
const [evalDatasets, setEvalDatasets] = useState([]);
|
||||
const [availableTags, setAvailableTags] = useState([]);
|
||||
|
||||
// 筛选条件
|
||||
const [questionTypes, setQuestionTypes] = useState([]);
|
||||
const [selectedTags, setSelectedTags] = useState([]);
|
||||
const [searchKeyword, setSearchKeyword] = useState('');
|
||||
const [questionCount, setQuestionCount] = useState(0);
|
||||
|
||||
// 后端统计 & 采样结果
|
||||
const [filteredTotal, setFilteredTotal] = useState(0);
|
||||
const [sampledIds, setSampledIds] = useState([]);
|
||||
const [hasSubjectiveQuestions, setHasSubjectiveQuestions] = useState(false);
|
||||
// 主观题类型统计(用于确定显示哪个评分规则表单)
|
||||
const [hasShortAnswer, setHasShortAnswer] = useState(false);
|
||||
const [hasOpenEnded, setHasOpenEnded] = useState(false);
|
||||
|
||||
// 自定义评分规则
|
||||
const [shortAnswerScoreAnchors, setShortAnswerScoreAnchors] = useState([]);
|
||||
const [openEndedScoreAnchors, setOpenEndedScoreAnchors] = useState([]);
|
||||
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [error, setError] = useState('');
|
||||
|
||||
// 加载数据
|
||||
useEffect(() => {
|
||||
if (open && projectId) {
|
||||
loadModels();
|
||||
loadEvalDatasets();
|
||||
}
|
||||
}, [open, projectId]);
|
||||
|
||||
// 当筛选条件变化时,调用后端统计数量
|
||||
useEffect(() => {
|
||||
if (!open || !projectId) return;
|
||||
|
||||
const controller = new AbortController();
|
||||
|
||||
const fetchCount = async () => {
|
||||
try {
|
||||
const params = new URLSearchParams();
|
||||
if (questionTypes.length > 0) {
|
||||
questionTypes.forEach(t => params.append('questionTypes', t));
|
||||
}
|
||||
if (searchKeyword.trim()) {
|
||||
params.append('keyword', searchKeyword.trim());
|
||||
}
|
||||
if (selectedTags.length > 0) {
|
||||
selectedTags.forEach(tag => params.append('tags', tag));
|
||||
}
|
||||
|
||||
const response = await fetch(`/api/projects/${projectId}/eval-datasets/count?${params.toString()}`, {
|
||||
signal: controller.signal
|
||||
});
|
||||
if (response.ok) {
|
||||
const result = await response.json();
|
||||
const total = result?.data?.total ?? 0;
|
||||
const hasSubjective = result?.data?.hasSubjective ?? false;
|
||||
const hasShort = result?.data?.hasShortAnswer ?? false;
|
||||
const hasOpen = result?.data?.hasOpenEnded ?? false;
|
||||
setFilteredTotal(total);
|
||||
setHasSubjectiveQuestions(hasSubjective);
|
||||
setHasShortAnswer(hasShort);
|
||||
setHasOpenEnded(hasOpen);
|
||||
}
|
||||
} catch (err) {
|
||||
if (err.name !== 'AbortError') {
|
||||
console.error('加载评估题目数量失败:', err);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
fetchCount();
|
||||
|
||||
return () => {
|
||||
controller.abort();
|
||||
};
|
||||
}, [open, projectId, questionTypes, selectedTags, searchKeyword]);
|
||||
|
||||
const loadModels = async () => {
|
||||
try {
|
||||
const response = await fetch(`/api/projects/${projectId}/model-config`);
|
||||
if (response.ok) {
|
||||
const result = await response.json();
|
||||
const modelList = result?.data || [];
|
||||
const availableModels = modelList.filter(m => m.apiKey && m.apiKey.trim() !== '' && m.status === 1);
|
||||
setModels(availableModels);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('加载模型列表失败:', err);
|
||||
setModels([]);
|
||||
}
|
||||
};
|
||||
|
||||
const loadEvalDatasets = async () => {
|
||||
try {
|
||||
setLoading(true);
|
||||
// 这里只需要拿到全部可用标签和题型分布,可以复用已有列表接口或标签接口
|
||||
const response = await fetch(`/api/projects/${projectId}/eval-datasets?includeStats=true&page=1&pageSize=20`);
|
||||
if (response.ok) {
|
||||
const data = await response.json();
|
||||
const stats = data.stats || {};
|
||||
const byTag = stats.byTag || {};
|
||||
const tags = Object.keys(byTag);
|
||||
setAvailableTags(tags.sort());
|
||||
|
||||
// 用部分数据来判断是否存在主观题(类型统计更准确)
|
||||
const byType = stats.byType || {};
|
||||
const mockDatasets = Object.entries(byType).map(([type]) => ({ questionType: type }));
|
||||
setEvalDatasets(mockDatasets);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('加载评估题目失败:', err);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const resetFilters = () => {
|
||||
setQuestionTypes([]);
|
||||
setSelectedTags([]);
|
||||
setSearchKeyword('');
|
||||
setQuestionCount(0);
|
||||
setFilteredTotal(0);
|
||||
setSampledIds([]);
|
||||
setHasShortAnswer(false);
|
||||
setHasOpenEnded(false);
|
||||
};
|
||||
|
||||
// 初始化评分规则(根据语言环境)
|
||||
const initScoreAnchors = (language = 'zh-CN') => {
|
||||
setShortAnswerScoreAnchors(getDefaultScoreAnchors('short_answer', language));
|
||||
setOpenEndedScoreAnchors(getDefaultScoreAnchors('open_ended', language));
|
||||
};
|
||||
|
||||
const resetForm = () => {
|
||||
setSelectedModels([]);
|
||||
setJudgeModel('');
|
||||
resetFilters();
|
||||
setError('');
|
||||
setShortAnswerScoreAnchors([]);
|
||||
setOpenEndedScoreAnchors([]);
|
||||
};
|
||||
|
||||
return {
|
||||
models,
|
||||
selectedModels,
|
||||
setSelectedModels,
|
||||
judgeModel,
|
||||
setJudgeModel,
|
||||
evalDatasets,
|
||||
availableTags,
|
||||
questionTypes,
|
||||
setQuestionTypes,
|
||||
selectedTags,
|
||||
setSelectedTags,
|
||||
searchKeyword,
|
||||
setSearchKeyword,
|
||||
questionCount,
|
||||
setQuestionCount,
|
||||
filteredTotal,
|
||||
sampledIds,
|
||||
hasSubjectiveQuestions,
|
||||
hasShortAnswer,
|
||||
hasOpenEnded,
|
||||
shortAnswerScoreAnchors,
|
||||
setShortAnswerScoreAnchors,
|
||||
openEndedScoreAnchors,
|
||||
setOpenEndedScoreAnchors,
|
||||
initScoreAnchors,
|
||||
loading,
|
||||
error,
|
||||
setError,
|
||||
setSampledIds,
|
||||
resetFilters,
|
||||
resetForm
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
'use client';
|
||||
|
||||
import { useState, useEffect, useCallback } from 'react';
|
||||
|
||||
/**
|
||||
* 评估任务列表 Hook
|
||||
*/
|
||||
export default function useEvalTasks(projectId) {
|
||||
const [tasks, setTasks] = useState([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState('');
|
||||
const [page, setPage] = useState(1);
|
||||
const [pageSize, setPageSize] = useState(12);
|
||||
const [total, setTotal] = useState(0);
|
||||
|
||||
// 加载任务列表
|
||||
const loadTasks = useCallback(
|
||||
async (isRefresh = false) => {
|
||||
if (!projectId) return;
|
||||
|
||||
try {
|
||||
if (!isRefresh) setLoading(true);
|
||||
setError('');
|
||||
const response = await fetch(`/api/projects/${projectId}/eval-tasks?page=${page}&pageSize=${pageSize}`);
|
||||
const result = await response.json();
|
||||
|
||||
if (result.code === 0) {
|
||||
setTasks(result.data.items || []);
|
||||
setTotal(result.data.total || 0);
|
||||
} else {
|
||||
setError(result.error || '加载失败');
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('加载评估任务失败:', err);
|
||||
setError('加载失败');
|
||||
} finally {
|
||||
if (!isRefresh) setLoading(false);
|
||||
}
|
||||
},
|
||||
[projectId, page, pageSize]
|
||||
);
|
||||
|
||||
// 初始加载和分页变化加载
|
||||
useEffect(() => {
|
||||
loadTasks();
|
||||
}, [loadTasks]);
|
||||
|
||||
// 自动刷新进行中的任务
|
||||
useEffect(() => {
|
||||
const hasProcessingTasks = tasks.some(t => t.status === 0);
|
||||
if (!hasProcessingTasks) return;
|
||||
|
||||
const interval = setInterval(() => loadTasks(true), 5000);
|
||||
return () => clearInterval(interval);
|
||||
}, [tasks, loadTasks]);
|
||||
|
||||
// 删除任务
|
||||
const deleteTask = useCallback(
|
||||
async taskId => {
|
||||
try {
|
||||
const response = await fetch(`/api/projects/${projectId}/eval-tasks/${taskId}`, {
|
||||
method: 'DELETE'
|
||||
});
|
||||
const result = await response.json();
|
||||
|
||||
if (result.code === 0) {
|
||||
loadTasks();
|
||||
return true;
|
||||
} else {
|
||||
setError(result.error || '删除失败');
|
||||
return false;
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('删除任务失败:', err);
|
||||
setError('删除失败');
|
||||
return false;
|
||||
}
|
||||
},
|
||||
[projectId]
|
||||
);
|
||||
|
||||
// 中断任务
|
||||
const interruptTask = useCallback(
|
||||
async taskId => {
|
||||
try {
|
||||
const response = await fetch(`/api/projects/${projectId}/eval-tasks/${taskId}`, {
|
||||
method: 'PUT',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ action: 'interrupt' })
|
||||
});
|
||||
const result = await response.json();
|
||||
|
||||
if (result.code === 0) {
|
||||
loadTasks();
|
||||
return true;
|
||||
} else {
|
||||
setError(result.error || '中断失败');
|
||||
return false;
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('中断任务失败:', err);
|
||||
setError('中断失败');
|
||||
return false;
|
||||
}
|
||||
},
|
||||
[projectId, loadTasks]
|
||||
);
|
||||
|
||||
// 创建任务
|
||||
const createTasks = useCallback(
|
||||
async data => {
|
||||
try {
|
||||
const response = await fetch(`/api/projects/${projectId}/eval-tasks`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(data)
|
||||
});
|
||||
const result = await response.json();
|
||||
|
||||
if (result.code === 0) {
|
||||
loadTasks();
|
||||
return { success: true, data: result.data };
|
||||
} else {
|
||||
return { success: false, error: result.error };
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('创建任务失败:', err);
|
||||
return { success: false, error: '创建失败' };
|
||||
}
|
||||
},
|
||||
[projectId, loadTasks]
|
||||
);
|
||||
|
||||
return {
|
||||
tasks,
|
||||
loading,
|
||||
error,
|
||||
setError,
|
||||
loadTasks,
|
||||
deleteTask,
|
||||
interruptTask,
|
||||
createTasks,
|
||||
page,
|
||||
setPage,
|
||||
pageSize,
|
||||
setPageSize,
|
||||
total
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user