refactor(agent-office): extend useAgentManager with lotto agent and refresh triggers
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,81 +1,84 @@
|
|||||||
|
// src/pages/agent-office/hooks/useAgentManager.js
|
||||||
import { useState, useEffect, useRef, useCallback } from 'react';
|
import { useState, useEffect, useRef, useCallback } from 'react';
|
||||||
|
|
||||||
|
const WS_RECONNECT_DELAY = 3000;
|
||||||
|
|
||||||
export function useAgentManager() {
|
export function useAgentManager() {
|
||||||
const [agents, setAgents] = useState({});
|
const [agents, setAgents] = useState({}); // { agentId: { state, detail, task_id } }
|
||||||
const [pendingTasks, setPendingTasks] = useState([]);
|
const [pendingTasks, setPendingTasks] = useState([]); // [{id, agent_id, task_type, input_data}]
|
||||||
|
const [notifications, setNotifications] = useState({}); // { agentId: count }
|
||||||
const [connected, setConnected] = useState(false);
|
const [connected, setConnected] = useState(false);
|
||||||
const [notifications, setNotifications] = useState({});
|
const [refreshTrigger, setRefreshTrigger] = useState(0); // 탭 데이터 리프레시용
|
||||||
|
|
||||||
const wsRef = useRef(null);
|
const wsRef = useRef(null);
|
||||||
const reconnectTimer = useRef(null);
|
const reconnectRef = useRef(null);
|
||||||
|
|
||||||
const connect = useCallback(() => {
|
const connect = useCallback(() => {
|
||||||
const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
|
const protocol = window.location.protocol === 'https:' ? 'wss' : 'ws';
|
||||||
const wsUrl = `${protocol}//${window.location.host}/api/agent-office/ws`;
|
const ws = new WebSocket(`${protocol}://${window.location.host}/api/agent-office/ws`);
|
||||||
|
|
||||||
const ws = new WebSocket(wsUrl);
|
|
||||||
wsRef.current = ws;
|
wsRef.current = ws;
|
||||||
|
|
||||||
ws.onopen = () => {
|
ws.onopen = () => setConnected(true);
|
||||||
setConnected(true);
|
|
||||||
if (reconnectTimer.current) clearTimeout(reconnectTimer.current);
|
|
||||||
};
|
|
||||||
|
|
||||||
ws.onclose = () => {
|
ws.onmessage = (e) => {
|
||||||
setConnected(false);
|
const msg = JSON.parse(e.data);
|
||||||
reconnectTimer.current = setTimeout(connect, 3000);
|
|
||||||
};
|
|
||||||
|
|
||||||
ws.onerror = () => { ws.close(); };
|
|
||||||
|
|
||||||
ws.onmessage = (event) => {
|
|
||||||
const msg = JSON.parse(event.data);
|
|
||||||
|
|
||||||
switch (msg.type) {
|
switch (msg.type) {
|
||||||
case 'init': {
|
case 'init': {
|
||||||
|
// 에이전트 초기 상태 세팅
|
||||||
const agentMap = {};
|
const agentMap = {};
|
||||||
for (const a of msg.agents) {
|
for (const a of msg.agents) {
|
||||||
agentMap[a.agent_id] = { state: a.state, detail: a.detail };
|
agentMap[a.agent_id] = { state: a.state, detail: a.detail || '', task_id: a.task_id };
|
||||||
}
|
}
|
||||||
setAgents(agentMap);
|
setAgents(agentMap);
|
||||||
setPendingTasks(msg.pending || []);
|
setPendingTasks(msg.pending || []);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'agent_state':
|
case 'agent_state':
|
||||||
setAgents(prev => ({
|
setAgents(prev => ({
|
||||||
...prev,
|
...prev,
|
||||||
[msg.agent]: { state: msg.state, detail: msg.detail, taskId: msg.task_id },
|
[msg.agent]: { state: msg.state, detail: msg.detail || '', task_id: msg.task_id }
|
||||||
}));
|
}));
|
||||||
|
// idle 전환 시 데이터 리프레시
|
||||||
|
if (msg.state === 'idle') {
|
||||||
|
setRefreshTrigger(n => n + 1);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'task_complete':
|
case 'task_complete':
|
||||||
setAgents(prev => ({
|
setRefreshTrigger(n => n + 1);
|
||||||
...prev,
|
|
||||||
[msg.agent]: { ...prev[msg.agent], lastResult: msg.result },
|
|
||||||
}));
|
|
||||||
setPendingTasks(prev => prev.filter(id => id !== msg.task_id));
|
|
||||||
break;
|
|
||||||
case 'command_result':
|
|
||||||
setAgents(prev => ({
|
|
||||||
...prev,
|
|
||||||
[msg.agent]: { ...prev[msg.agent], lastCommand: msg.result },
|
|
||||||
}));
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'notification':
|
case 'notification':
|
||||||
setNotifications(prev => ({
|
setNotifications(prev => ({
|
||||||
...prev,
|
...prev,
|
||||||
[msg.agent]: (prev[msg.agent] || 0) + 1,
|
[msg.agent]: (prev[msg.agent] || 0) + 1
|
||||||
}));
|
}));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 'command_result':
|
||||||
|
// 사이드 패널에서 처리
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
ws.onclose = () => {
|
||||||
|
setConnected(false);
|
||||||
|
reconnectRef.current = setTimeout(connect, WS_RECONNECT_DELAY);
|
||||||
|
};
|
||||||
|
|
||||||
|
ws.onerror = () => ws.close();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
connect();
|
connect();
|
||||||
return () => {
|
return () => {
|
||||||
if (wsRef.current) wsRef.current.close();
|
if (wsRef.current) wsRef.current.close();
|
||||||
if (reconnectTimer.current) clearTimeout(reconnectTimer.current);
|
if (reconnectRef.current) clearTimeout(reconnectRef.current);
|
||||||
};
|
};
|
||||||
}, [connect]);
|
}, [connect]);
|
||||||
|
|
||||||
@@ -92,12 +95,17 @@ export function useAgentManager() {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const clearNotifications = useCallback((agentId) => {
|
const clearNotifications = useCallback((agentId) => {
|
||||||
setNotifications(prev => {
|
setNotifications(prev => ({ ...prev, [agentId]: 0 }));
|
||||||
const next = { ...prev };
|
|
||||||
delete next[agentId];
|
|
||||||
return next;
|
|
||||||
});
|
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return { agents, pendingTasks, connected, notifications, sendCommand, sendApproval, clearNotifications };
|
return {
|
||||||
|
agents,
|
||||||
|
pendingTasks,
|
||||||
|
notifications,
|
||||||
|
connected,
|
||||||
|
refreshTrigger,
|
||||||
|
sendCommand,
|
||||||
|
sendApproval,
|
||||||
|
clearNotifications
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user