import { useState } from 'react' import { useNavigate } from 'react-router-dom' import { Button, Drawer, Empty, Form, Input, InputNumber, Popconfirm, Progress, Select, Space, Spin, Table, Tabs, Tag, Tooltip, message, } from 'antd' import type { TabsProps } from 'antd' import type { ColumnsType } from 'antd/es/table' import { DeleteOutlined, PlusOutlined, ReloadOutlined, StopOutlined, UnorderedListOutlined, } from '@ant-design/icons' import FormDrawer from '../components/FormDrawer' import PageWrapper from '../components/PageWrapper' import EvalOverview from '../components/intelligent_eval/EvalOverview' import ExecutionProcess from '../components/intelligent_eval/ExecutionProcess' import DecisionProcess from '../components/intelligent_eval/DecisionProcess' import ConfigSnapshots from '../components/intelligent_eval/ConfigSnapshots' import EvalReport from '../components/intelligent_eval/EvalReport' import { EVAL_STATUS, TERMINAL_STATUSES } from '../components/intelligent_eval/status' import { useResource } from '../hooks/useResource' import { intelligentEvalsApi, targetsApi, type CreateIntelligentEvalPayload, type IntelligentEval, type Target, } from '../api' import { useTabStore, type TabItem } from '../stores/tabStore' import { colors, statusColors } from '../tokens' import { formatDateTime } from '../utils/date' import { useIntelligentEvalRead } from '../read/useIntelligentEvalRead' interface CreateFormValues { name: string target_id: string goal: string intent?: string role_description?: string seeds_json?: string time_window_hours: number } type DetailTab = 'overview' | 'execution' | 'decision' | 'history' | 'report' const TASKS_TAB: TabItem = { key: '/intelligent-evals/tasks', title: '任务队列', icon: , closable: true, } // 状态统计条:全部 + 各状态(含 cancelled,保证 全部 = 各状态之和;draft 为 0 不单列) // 标签从 EVAL_STATUS 单一出口派生,不与 status.ts 平行维护 const STAT_CHIPS: { key: string; label: string; color?: string }[] = [ { key: 'all', label: '全部评估' }, ...(['planning', 'pending_approval', 'executing', 'completed', 'cancelled', 'failed'] as const).map((s) => ({ key: s, label: EVAL_STATUS[s].label, color: s === 'completed' ? statusColors.completed : s === 'failed' ? statusColors.failed : undefined, })), ] export default function IntelligentEvalsPage() { const navigate = useNavigate() const openTab = useTabStore((s) => s.openTab) const [selectedId, setSelectedId] = useState(null) const [detailTab, setDetailTab] = useState('overview') const [createOpen, setCreateOpen] = useState(false) const [statusFilter, setStatusFilter] = useState('all') const [page, setPage] = useState(1) const [pageSize, setPageSize] = useState(10) const [submitting, setSubmitting] = useState(false) const [form] = Form.useForm() const { list, detail, reloadList, reloadDetail } = useIntelligentEvalRead( selectedId, undefined, page, pageSize, statusFilter === 'all' ? undefined : statusFilter, ) const evals = list.value const loading = list.phase === 'loading' const stats = list.stats const { data: targets } = useResource( () => targetsApi.list().then((r) => r.data), { tabPath: '/intelligent-evals' }, ) const selected = selectedId != null && detail.value?.id === selectedId ? detail.value : null const targetName = (id: string) => targets?.find((t) => t.id === id)?.name ?? id.slice(0, 8) const openDetail = (id: string, tab: DetailTab = 'overview') => { setSelectedId(id) setDetailTab(tab) } const closeDrawer = () => { setSelectedId(null) void reloadList() } const openTasks = () => { openTab(TASKS_TAB) navigate('/intelligent-evals/tasks') } const setFilter = (key: string) => { setStatusFilter(key) setPage(1) } const submitCreate = async () => { const values = await form.validateFields() let seeds: Record = {} const raw = (values.seeds_json ?? '').trim() if (raw) { try { const parsed = JSON.parse(raw) as unknown if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) { message.error('种子集必须是 JSON 对象') return } seeds = parsed as Record } catch { message.error('种子集不是合法 JSON') return } } const payload: CreateIntelligentEvalPayload = { name: values.name, target_id: values.target_id, goal: values.goal, seeds, intent: values.intent ?? '', role_description: values.role_description ?? '', time_window_hours: values.time_window_hours, } setSubmitting(true) try { await intelligentEvalsApi.create(payload) message.success('已创建,OpenClaw 开始规划') setCreateOpen(false) form.resetFields() void reloadList() } finally { setSubmitting(false) } } const handleDelete = async (id: string) => { await intelligentEvalsApi.remove(id) message.success('已删除') void reloadList() } const columns: ColumnsType = [ { title: '名称', dataIndex: 'name', key: 'name', width: 300, ellipsis: true, render: (name: string) => {name}, }, { title: '评测对象', dataIndex: 'target_id', key: 'target', width: 180, ellipsis: true, render: (id: string) => {targetName(id)}, }, { title: '状态', dataIndex: 'status', key: 'status', width: 96, render: (status: IntelligentEval['status']) => { const meta = EVAL_STATUS[status] ?? { label: status, color: 'default' } return {meta.label} }, }, { title: '会话进度', key: 'progress', width: 200, render: (_, ev) => { const estimated = ev.plan?.estimated_sessions ?? ev.session_count const running = (ev.sessions ?? []).filter((s) => s.status === 'running').length const pct = estimated ? Math.round((ev.completed_sessions / estimated) * 100) : 0 const tooltipText = `已完成 ${ev.completed_sessions} 个会话 / 预估 ${estimated} 个会话${running > 0 ? `(${running} 个进行中)` : ''}` return ( {ev.completed_sessions}/{estimated} {running > 0 && ({running} 进行中)} ) }, }, { title: '创建时间', dataIndex: 'created_at', key: 'created_at', width: 160, render: (v: string | null) => (v ? formatDateTime(v) : '—'), }, { title: '操作', key: 'action', width: 130, fixed: 'right' as const, render: (_, ev) => ( {TERMINAL_STATUSES.has(ev.status) && ( handleDelete(ev.id)} okText="删除" cancelText="取消" > } >
{STAT_CHIPS.map((c) => { const n = c.key === 'all' ? (stats ? Object.values(stats).reduce((a, b) => a + b, 0) : list.total) : (stats?.[c.key] ?? 0) return (
setFilter(c.key)}>
{n}
{c.label}
) })}
({ onClick: () => openDetail(record.id), style: { cursor: 'pointer' } })} pagination={{ current: page, pageSize, total: list.total, showSizeChanger: true, pageSizeOptions: [10, 20, 50], showTotal: (t) => `共 ${t} 个`, onChange: (p, ps) => { setPage(p) setPageSize(ps) }, }} locale={{ emptyText: }} /> {EVAL_STATUS[selected.status]?.label ?? selected.status} {selected.status === 'pending_approval' && ( )} {(selected.status === 'pending_approval' || selected.status === 'executing') && ( void intelligentEvalsApi.cancel(selected.id).then(() => { message.success('已取消'); void reloadDetail() })} > )} )} > {selected == null ? (
) : (
setDetailTab(k as DetailTab)} items={drawerItems} />
)}
setCreateOpen(false)} onSubmit={submitCreate} submitting={submitting} okText="创建并开始规划" >