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 getAgentStates = () => apiGet('/api/agent-office/states');
|
||||
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--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 {
|
||||
background: #f43f5e;
|
||||
color: #fff;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { getAgentTasks } from '../../../api';
|
||||
import { getAgentTasks, getAgentTokenUsage } from '../../../api';
|
||||
|
||||
const STATUS_BADGE = {
|
||||
pending: { label: '대기', bg: '#92400e' },
|
||||
@@ -26,6 +26,7 @@ const AgentColumn = ({ agentId, meta, agentState, notification, onCommand, onApp
|
||||
const [tasks, setTasks] = useState([]);
|
||||
const [input, setInput] = useState('');
|
||||
const [activeCommand, setActiveCommand] = useState(null);
|
||||
const [tokenUsage, setTokenUsage] = useState(null);
|
||||
|
||||
const state = agentState || { state: 'offline' };
|
||||
const commands = AGENT_COMMANDS[agentId] || [];
|
||||
@@ -45,6 +46,22 @@ const AgentColumn = ({ agentId, meta, agentState, notification, onCommand, onApp
|
||||
}
|
||||
}, [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) => {
|
||||
if (cmd.needsInput) {
|
||||
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-header" style={{ borderColor: meta.color }}>
|
||||
<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>
|
||||
{notification > 0 && <span className="ao-col-badge">{notification}</span>}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user