feat(agent-office): AI 토큰 사용량 뱃지 표시
- api.js: getAgentTokenUsage 헬퍼 추가 - AgentColumn: 헤더에 오늘 토큰 사용량 뱃지 (🧮 N,NNN) - 30초 폴링 + state 변경 시 즉시 갱신 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -599,4 +599,5 @@ export const sendAgentCommand = (agent, action, params={}) => apiPost('/api/age
|
|||||||
export const approveAgentTask = (agent, task_id, approved, feedback='') => apiPost('/api/agent-office/approve', { agent, task_id, approved, feedback });
|
export const approveAgentTask = (agent, task_id, approved, feedback='') => apiPost('/api/agent-office/approve', { agent, task_id, approved, feedback });
|
||||||
export const getAgentStates = () => apiGet('/api/agent-office/states');
|
export const getAgentStates = () => apiGet('/api/agent-office/states');
|
||||||
export const getActivityFeed = (limit=50, offset=0) => apiGet(`/api/agent-office/activity?limit=${limit}&offset=${offset}`);
|
export const getActivityFeed = (limit=50, offset=0) => apiGet(`/api/agent-office/activity?limit=${limit}&offset=${offset}`);
|
||||||
|
export const getAgentTokenUsage = (id, days=1) => apiGet(`/api/agent-office/agents/${id}/token-usage?days=${days}`);
|
||||||
|
|
||||||
|
|||||||
@@ -89,6 +89,18 @@
|
|||||||
.ao-col-state--break { background: #4c1d95; color: #c4b5fd; }
|
.ao-col-state--break { background: #4c1d95; color: #c4b5fd; }
|
||||||
.ao-col-state--offline { background: #1f1f1f; color: #555; }
|
.ao-col-state--offline { background: #1f1f1f; color: #555; }
|
||||||
|
|
||||||
|
.ao-col-tokens {
|
||||||
|
font-size: 0.7rem;
|
||||||
|
color: #8b5cf6;
|
||||||
|
background: rgba(139, 92, 246, 0.12);
|
||||||
|
padding: 2px 8px;
|
||||||
|
border-radius: 8px;
|
||||||
|
margin-left: 6px;
|
||||||
|
cursor: help;
|
||||||
|
font-family: 'Courier New', monospace;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
.ao-col-badge {
|
.ao-col-badge {
|
||||||
background: #f43f5e;
|
background: #f43f5e;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import { getAgentTasks } from '../../../api';
|
import { getAgentTasks, getAgentTokenUsage } from '../../../api';
|
||||||
|
|
||||||
const STATUS_BADGE = {
|
const STATUS_BADGE = {
|
||||||
pending: { label: '대기', bg: '#92400e' },
|
pending: { label: '대기', bg: '#92400e' },
|
||||||
@@ -26,6 +26,7 @@ const AgentColumn = ({ agentId, meta, agentState, notification, onCommand, onApp
|
|||||||
const [tasks, setTasks] = useState([]);
|
const [tasks, setTasks] = useState([]);
|
||||||
const [input, setInput] = useState('');
|
const [input, setInput] = useState('');
|
||||||
const [activeCommand, setActiveCommand] = useState(null);
|
const [activeCommand, setActiveCommand] = useState(null);
|
||||||
|
const [tokenUsage, setTokenUsage] = useState(null);
|
||||||
|
|
||||||
const state = agentState || { state: 'offline' };
|
const state = agentState || { state: 'offline' };
|
||||||
const commands = AGENT_COMMANDS[agentId] || [];
|
const commands = AGENT_COMMANDS[agentId] || [];
|
||||||
@@ -45,6 +46,22 @@ const AgentColumn = ({ agentId, meta, agentState, notification, onCommand, onApp
|
|||||||
}
|
}
|
||||||
}, [agentId, state.state, state.detail]);
|
}, [agentId, state.state, state.detail]);
|
||||||
|
|
||||||
|
// 오늘자 AI 토큰 사용량 폴링 (30초 간격 + 작업 완료 시 즉시 갱신)
|
||||||
|
useEffect(() => {
|
||||||
|
let cancelled = false;
|
||||||
|
const fetchUsage = () => {
|
||||||
|
getAgentTokenUsage(agentId, 1)
|
||||||
|
.then(d => { if (!cancelled) setTokenUsage(d); })
|
||||||
|
.catch(() => {});
|
||||||
|
};
|
||||||
|
fetchUsage();
|
||||||
|
const interval = setInterval(fetchUsage, 30000);
|
||||||
|
return () => {
|
||||||
|
cancelled = true;
|
||||||
|
clearInterval(interval);
|
||||||
|
};
|
||||||
|
}, [agentId, state.state, state.detail]);
|
||||||
|
|
||||||
const handleQuickAction = (cmd) => {
|
const handleQuickAction = (cmd) => {
|
||||||
if (cmd.needsInput) {
|
if (cmd.needsInput) {
|
||||||
setActiveCommand(cmd.action);
|
setActiveCommand(cmd.action);
|
||||||
@@ -67,6 +84,14 @@ const AgentColumn = ({ agentId, meta, agentState, notification, onCommand, onApp
|
|||||||
<div className="ao-col" onClick={onClearNotification}>
|
<div className="ao-col" onClick={onClearNotification}>
|
||||||
<div className="ao-col-header" style={{ borderColor: meta.color }}>
|
<div className="ao-col-header" style={{ borderColor: meta.color }}>
|
||||||
<span className="ao-col-name" style={{ color: meta.color }}>{meta.name}</span>
|
<span className="ao-col-name" style={{ color: meta.color }}>{meta.name}</span>
|
||||||
|
{tokenUsage && tokenUsage.total_tokens > 0 && (
|
||||||
|
<span
|
||||||
|
className="ao-col-tokens"
|
||||||
|
title={`오늘 ${tokenUsage.task_count}건 작업 · ${tokenUsage.total_tokens.toLocaleString()} 토큰`}
|
||||||
|
>
|
||||||
|
🧮 {tokenUsage.total_tokens.toLocaleString()}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
<span className={`ao-col-state ao-col-state--${state.state}`}>{state.state}</span>
|
<span className={`ao-col-state ao-col-state--${state.state}`}>{state.state}</span>
|
||||||
{notification > 0 && <span className="ao-col-badge">{notification}</span>}
|
{notification > 0 && <span className="ao-col-badge">{notification}</span>}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user