autonomy M2 phase2: add idle budget/block stats and source/status filters in task queue UI

This commit is contained in:
DBT
2026-02-28 06:42:37 +00:00
parent aca66ea9f4
commit 5adaf8b955
3 changed files with 47 additions and 5 deletions

View File

@@ -1475,7 +1475,19 @@ func (s *RegistryServer) handleWebUITaskQueue(w http.ResponseWriter, r *http.Req
}
}
_ = json.NewEncoder(w).Encode(map[string]interface{}{"ok": true, "running": running, "items": items})
stats := map[string]int{"total": len(items), "running": len(running), "idle_round_budget": 0, "active_user": 0, "manual_pause": 0}
for _, it := range items {
reason := fmt.Sprintf("%v", it["block_reason"])
switch reason {
case "idle_round_budget":
stats["idle_round_budget"]++
case "active_user":
stats["active_user"]++
case "manual_pause":
stats["manual_pause"]++
}
}
_ = json.NewEncoder(w).Encode(map[string]interface{}{"ok": true, "running": running, "items": items, "stats": stats})
}
func (s *RegistryServer) handleWebUIExecApprovals(w http.ResponseWriter, r *http.Request) {

View File

@@ -21,6 +21,8 @@ const resources = {
mediaSources: 'Media Sources',
lastPauseReason: 'Last Pause Reason',
lastPauseAt: 'Last Pause Time',
allSources: 'All Sources',
allStatus: 'All Status',
error: 'Error',
noTaskAudit: 'No task audit records',
selectTask: 'Select a task from the left list',
@@ -176,6 +178,8 @@ const resources = {
mediaSources: '媒体来源',
lastPauseReason: '最近暂停原因',
lastPauseAt: '最近暂停时间',
allSources: '全部来源',
allStatus: '全部状态',
error: '错误',
noTaskAudit: '暂无任务审计记录',
selectTask: '请从左侧选择任务',

View File

@@ -30,6 +30,8 @@ const TaskAudit: React.FC = () => {
const [items, setItems] = useState<TaskAuditItem[]>([]);
const [selected, setSelected] = useState<TaskAuditItem | null>(null);
const [loading, setLoading] = useState(false);
const [sourceFilter, setSourceFilter] = useState('all');
const [statusFilter, setStatusFilter] = useState('all');
const fetchData = async () => {
setLoading(true);
@@ -53,22 +55,46 @@ const TaskAudit: React.FC = () => {
useEffect(() => { fetchData(); }, [q]);
const filteredItems = useMemo(() => items.filter((it) => {
if (sourceFilter !== 'all' && String(it.source || '-') !== sourceFilter) return false;
if (statusFilter !== 'all' && String(it.status || '-') !== statusFilter) return false;
return true;
}), [items, sourceFilter, statusFilter]);
const selectedPretty = useMemo(() => selected ? JSON.stringify(selected, null, 2) : '', [selected]);
return (
<div className="h-full p-4 md:p-6 flex flex-col gap-4">
<div className="flex items-center justify-between">
<div className="flex items-center justify-between flex-wrap gap-3">
<h1 className="text-xl md:text-2xl font-semibold">{t('taskAudit')}</h1>
<button onClick={fetchData} className="px-3 py-1.5 rounded-lg bg-zinc-800 hover:bg-zinc-700 text-sm">{loading ? t('loading') : t('refresh')}</button>
<div className="flex items-center gap-2">
<select value={sourceFilter} onChange={(e)=>setSourceFilter(e.target.value)} className="bg-zinc-900 border border-zinc-700 rounded px-2 py-1 text-xs">
<option value="all">{t('allSources')}</option>
<option value="autonomy">autonomy</option>
<option value="direct">direct</option>
<option value="memory_todo">memory_todo</option>
<option value="-">-</option>
</select>
<select value={statusFilter} onChange={(e)=>setStatusFilter(e.target.value)} className="bg-zinc-900 border border-zinc-700 rounded px-2 py-1 text-xs">
<option value="all">{t('allStatus')}</option>
<option value="running">running</option>
<option value="waiting">waiting</option>
<option value="blocked">blocked</option>
<option value="success">success</option>
<option value="error">error</option>
<option value="suppressed">suppressed</option>
</select>
<button onClick={fetchData} className="px-3 py-1.5 rounded-lg bg-zinc-800 hover:bg-zinc-700 text-sm">{loading ? t('loading') : t('refresh')}</button>
</div>
</div>
<div className="flex-1 min-h-0 grid grid-cols-1 lg:grid-cols-[360px_1fr] gap-4">
<div className="border border-zinc-800 rounded-xl bg-zinc-900/40 overflow-hidden flex flex-col min-h-0">
<div className="px-3 py-2 border-b border-zinc-800 text-xs text-zinc-400 uppercase tracking-wider">{t('taskQueue')}</div>
<div className="overflow-y-auto min-h-0">
{items.length === 0 ? (
{filteredItems.length === 0 ? (
<div className="p-4 text-sm text-zinc-500">{t('noTaskAudit')}</div>
) : items.map((it, idx) => {
) : filteredItems.map((it, idx) => {
const active = selected?.task_id === it.task_id && selected?.time === it.time;
return (
<button