import { lazy, Suspense, useMemo, useState } from 'react'; import { Bot, ChevronRight, LockKeyhole, Pause, Play, RotateCcw, Save, ShieldCheck, SkipForward, } from 'lucide-react'; import { useShallow } from 'zustand/react/shallow'; import { Badge, Button, Dialog, Select } from '../components/ui'; import type { ParameterConstraint, RewardConfiguration } from '../training/types'; import { ACTIVE_SESSION_STATES, PARAMETER_DEFINITIONS, STATE_META, parameterValue } from './domain'; import { useTuningStore } from './tuningStore'; const RewardConfigDiffEditor = lazy(() => import('./RewardConfigDiffEditor').then((module) => ({ default: module.RewardConfigDiffEditor })), ); const FSM_PHASES = ['Agent 分析', 'PPO 训练', '固定评估', '人工审批'] as const; type DraftMode = 'free' | 'range' | 'fixed'; interface ConstraintDraft { mode: DraftMode; min: string; max: string; value: string; } function createDrafts( constraints: Record, current: RewardConfiguration | undefined, ): Record { return Object.fromEntries( PARAMETER_DEFINITIONS.map((definition) => { const constraint = constraints[definition.path]; const value = parameterValue(current, definition.path); if (constraint?.kind === 'range') return [ definition.path, { mode: 'range', min: String(constraint.min), max: String(constraint.max), value: String(value), }, ]; if (constraint?.kind === 'fixed') return [ definition.path, { mode: 'fixed', min: String(definition.minimum), max: String(definition.maximum), value: String(constraint.value), }, ]; return [ definition.path, { mode: 'free', min: String(definition.minimum), max: String(definition.maximum), value: String(value), }, ]; }), ); } function serializeConstraints( drafts: Record, ): Record { const constraints: Record = {}; for (const definition of PARAMETER_DEFINITIONS) { const draft = drafts[definition.path]; if (!draft || draft.mode === 'free') continue; if (draft.mode === 'fixed') { const value = Number(draft.value); if (!Number.isFinite(value) || value < definition.minimum || value > definition.maximum) throw new Error( `${definition.label}固定值必须在 ${definition.minimum}–${definition.maximum} 内`, ); if (!definition.allowZero && value === 0) throw new Error(`${definition.label}不允许固定为 0`); constraints[definition.path] = { kind: 'fixed', value }; continue; } const min = Number(draft.min); const max = Number(draft.max); if ( !Number.isFinite(min) || !Number.isFinite(max) || min < definition.minimum || max > definition.maximum || min > max ) throw new Error( `${definition.label}范围必须满足 ${definition.minimum} ≤ 下限 ≤ 上限 ≤ ${definition.maximum}`, ); if (!definition.allowZero && min <= 0 && max >= 0) throw new Error(`${definition.label}的范围不能包含 0`); constraints[definition.path] = { kind: 'range', min, max }; } return constraints; } function FsmStrip({ state }: { state: keyof typeof STATE_META }) { const meta = STATE_META[state]; const terminal = ['succeeded', 'failed', 'cancelled'].includes(state); return (
{FSM_PHASES.map((label, index) => { const reached = terminal ? state === 'succeeded' : index <= meta.phase; const active = !terminal && index === meta.phase && state !== 'paused'; return (
{label}
{index < FSM_PHASES.length - 1 && ( )}
); })}
); } function ParameterConstraintDialog({ open, close, current, }: { open: boolean; close(): void; current?: RewardConfiguration; }) { const constraints = useTuningStore((state) => state.parameterConstraints); const revision = useTuningStore((state) => state.constraintsRevision); const effectiveAfterCurrent = useTuningStore((state) => state.constraintsEffectiveAfterCurrent); const busy = useTuningStore((state) => state.busyOperations.includes('constraints')); const [drafts, setDrafts] = useState(() => createDrafts(constraints, current)); const [query, setQuery] = useState(''); const [problem, setProblem] = useState(); const filtered = useMemo(() => { const normalized = query.trim().toLowerCase(); if (!normalized) return PARAMETER_DEFINITIONS; return PARAMETER_DEFINITIONS.filter( (definition) => definition.label.toLowerCase().includes(normalized) || definition.path.toLowerCase().includes(normalized), ); }, [query]); const update = (path: string, patch: Partial) => setDrafts((value) => ({ ...value, [path]: { ...value[path], ...patch } })); const save = async () => { try { const value = serializeConstraints(drafts); setProblem(undefined); await useTuningStore.getState().saveParameterConstraints(value); if (!useTuningStore.getState().error) close(); } catch (error) { setProblem(error instanceof Error ? error.message : String(error)); } }; return (

Revision {revision} · 安全边界由训练服务再次校验,不依赖浏览器状态

} >
固定值禁止 Agent 修改;范围值越界将被服务端拒绝
setQuery(event.target.value)} />
{effectiveAfterCurrent && (

当前 Trial 已在执行;新护栏从下一次 Proposal 生效。

)} {problem && (

{problem}

)}
{filtered.map((definition) => { const draft = drafts[definition.path]; const currentValue = parameterValue(current, definition.path); return ( ); })}
参数 当前值 策略 工程下限 工程上限 / 固定值 系统边界

{definition.label}

{definition.path}
{currentValue} update(definition.path, { min: event.target.value })} /> update( definition.path, draft.mode === 'fixed' ? { value: event.target.value } : { max: event.target.value }, ) } /> [{definition.minimum}, {definition.maximum}]
); } export function TuningControlToolbar() { const [state, mode, message, bestTrialId, currentTrialId] = useTuningStore( useShallow( (value) => [ value.sessionState, value.sessionMode, value.sessionMessage, value.bestTrialId, value.currentTrialId, ] as const, ), ); const [busyOperations, constraints, dispatchTokens, runPolicy] = useTuningStore( useShallow( (value) => [ value.busyOperations, value.parameterConstraints, value.dispatchTokens, value.runPolicy, ] as const, ), ); const current = useTuningStore((value) => { const id = currentTrialId ?? value.selectedTrialId ?? value.trialIds.at(-1); return id ? value.trialsById[id] : undefined; }); const best = useTuningStore((value) => (bestTrialId ? value.trialsById[bestTrialId] : undefined)); const [constraintsOpen, setConstraintsOpen] = useState(false); const [rollbackOpen, setRollbackOpen] = useState(false); if (!state || !mode) return null; const meta = STATE_META[state]; const busy = busyOperations.length > 0; const resumable = state === 'paused' || state === 'interrupted'; const pausable = ['queued', 'running', 'evaluating', 'awaiting_approval'].includes(state); const stepAllowed = state === 'paused' || state === 'awaiting_approval'; const rollbackAllowed = Boolean(best) && (state === 'paused' || state === 'awaiting_approval'); const active = ACTIVE_SESSION_STATES.has(state); const modeSwitchable = [ 'queued', 'running', 'evaluating', 'awaiting_approval', 'paused', ].includes(state); return ( <>
Agent FSM {meta.label}

{message}

{constraintsOpen && ( setConstraintsOpen(false)} current={current?.rewardConfig} /> )} setRollbackOpen(false)} title="安全回滚到历史最优 Trial" className="!max-w-5xl" footer={

非破坏性操作:历史结果不变;后续 Proposal 以该不可变配置为基准

} >

当前 Trial

{current ? `T${current.number} · R${current.rung}` : '—'}

目标 Best Trial

{best ? `T${best.number} · R${best.rung}` : '—'}

Best Score

{best?.score?.toFixed(5) ?? '—'}

{current && best ? (
正在按需加载 Monaco Diff…
} > ) : (
尚无可回滚的最优配置
)}
); }