import { useCallback } from 'react'
import {
BarChart, Bar, XAxis, YAxis, Tooltip, CartesianGrid,
ResponsiveContainer, Cell, PieChart, Pie, Legend,
} from 'recharts'
import { RefreshCw, Zap, Database, TrendingUp, Clock } from 'lucide-react'
import Card from '../components/ui/Card'
import Button from '../components/ui/Button'
import Spinner from '../components/ui/Spinner'
import { usePolling } from '../hooks/usePolling'
import { getForgeQueue, getForgeStats } from '../api/client'
const NEON = '#00d4ff'
const GREEN = '#00ff88'
const RED = '#ff3366'
const ORANGE = '#ff9900'
function CustomTooltip({ active, payload, label }) {
if (!active || !payload?.length) return null
return (
{label}
{payload.map(p => (
{p.name}: {p.value}
))}
)
}
const QUEUE_ZONES = [
{ key: 'learnable_count', label: 'Learnable', color: GREEN, desc: '0.20 ≤ pass@k ≤ 0.85' },
{ key: 'too_easy_count', label: 'Too Easy', color: NEON, desc: 'pass@k > 0.85' },
{ key: 'too_hard_count', label: 'Too Hard', color: RED, desc: 'pass@k < 0.20' },
{ key: 'pending_estimation_count', label: 'Pending Estimation', color: ORANGE, desc: 'awaiting pass@k estimate' },
]
export default function ForgeQueue() {
const qFetcher = useCallback(() => getForgeQueue(), [])
const sFetcher = useCallback(() => getForgeStats(), [])
const { data: queue, loading: qLoad, refresh: qRefresh } = usePolling(qFetcher, 4000)
const { data: stats, loading: sLoad } = usePolling(sFetcher, 4000)
const barData = QUEUE_ZONES.map(z => ({
name: z.label,
count: queue?.[z.key] ?? 0,
color: z.color,
}))
const pieData = QUEUE_ZONES
.map(z => ({ name: z.label, value: queue?.[z.key] ?? 0, color: z.color }))
.filter(d => d.value > 0)
const totalTasks = (queue?.seed_task_count ?? 0) + (queue?.generated_task_count ?? 0)
return (
Forge Queue
Curriculum scheduler - adaptive task difficulty management
{/* Zone cards */}
{QUEUE_ZONES.map(z => (
{z.label}
{queue?.replenishment_triggered && z.key === 'learnable_count' && (
replenishing
)}
{queue ? queue[z.key] ?? 0 : }
{z.desc}
))}
{/* Source cards */}
{[
{ icon: Database, label: 'Seed Tasks', key: 'seed_task_count', color: NEON },
{ icon: Zap, label: 'Generated Tasks', key: 'generated_task_count', color: GREEN },
{ icon: TrendingUp, label: 'Acceptance Rate', key: null, color: ORANGE },
{ icon: Clock, label: 'Total Episodes', key: null, color: '#7080aa' },
].map(({ icon: Icon, label, key, color }) => {
let value
if (key) value = queue?.[key] ?? (qLoad ? null : 0)
else if (label === 'Acceptance Rate') value = stats?.generator_acceptance_rate != null ? `${(stats.generator_acceptance_rate * 100).toFixed(1)}%` : (sLoad ? null : 'n/a')
else value = stats?.total_episodes ?? (sLoad ? null : 0)
return (
{label}
{value ?? }
)
})}
{/* Bar chart */}
Queue Counts by Zone
{barData.some(d => d.count > 0) ? (
} />
{barData.map((d, i) => (
|
))}
) : (
{qLoad ? : 'no data'}
)}
{/* Pie */}
Zone Distribution
{pieData.length > 0 ? (
<>
{pieData.map((d, i) => (
|
))}
} />
{pieData.map(d => (
{d.name}: {d.value}
))}
>
) : (
{qLoad ? : 'no data'}
)}
{/* Learnable threshold reference */}
Difficulty Thresholds
too-hard
learnable zone (0.20 – 0.85)
too-easy
{/* Markers */}
0.000.200.851.00
)
}