import { useGameStore } from '@/store'; import { formatNumber, formatPercent, type TrafficPriority, type OverflowBehavior, type RoutingStrategy, TRAFFIC_PRIORITIES, } from '@ai-tycoon/shared'; import { Activity, Shield, Clock, CheckCircle, XCircle, Layers, AlertTriangle, Zap, Server, ArrowRight, } from 'lucide-react'; const TIER_COLORS: Record = { 'enterprise': 'text-purple-400', 'api-paid': 'text-blue-400', 'consumer-paid': 'text-green-400', 'api-free': 'text-yellow-400', 'consumer-free': 'text-surface-400', }; const TIER_BG: Record = { 'enterprise': 'bg-purple-500/20', 'api-paid': 'bg-blue-500/20', 'consumer-paid': 'bg-green-500/20', 'api-free': 'bg-yellow-500/20', 'consumer-free': 'bg-surface-500/20', }; const TIER_LABELS: Record = { 'enterprise': 'Enterprise', 'api-paid': 'API Paid', 'consumer-paid': 'Consumer Paid', 'api-free': 'API Free', 'consumer-free': 'Consumer Free', }; const OVERFLOW_OPTIONS: { value: OverflowBehavior; label: string }[] = [ { value: 'queue', label: 'Queue' }, { value: 'reject', label: 'Reject' }, { value: 'degrade', label: 'Degrade' }, ]; const ROUTING_OPTIONS: { value: RoutingStrategy; label: string; desc: string }[] = [ { value: 'quality-first', label: 'Quality First', desc: 'Best model first — maximizes quality' }, { value: 'balanced', label: 'Balanced', desc: 'Adapts to load — quality when idle, speed when busy' }, { value: 'speed-first', label: 'Speed First', desc: 'Fastest model first — maximizes throughput' }, ]; function MetricCard({ icon: Icon, label, value, sub, color }: { icon: typeof Activity; label: string; value: string; sub?: string; color: string; }) { return (
{label}
{value}
{sub &&
{sub}
}
); } function PipelineFlow() { const sm = useGameStore(s => s.market.servingMetrics); const tiers = sm.tierMetrics; return (

Request Pipeline

{TRAFFIC_PRIORITIES.map(tier => { const m = tiers[tier]; if (!m || m.demandTokens === 0) return ( ); return ( ); })}
Tier Demand Served Queued Rejected Degraded Quality
{TIER_LABELS[tier]}
{TIER_LABELS[tier]} {formatNumber(m.demandTokens)} {formatNumber(m.servedTokens)} {m.queuedTokens > 0 ? formatNumber(m.queuedTokens) : '—'} {m.rejectedTokens > 0 ? formatNumber(m.rejectedTokens) : '—'} {m.degradedTokens > 0 ? formatNumber(m.degradedTokens) : '—'} {formatPercent(m.avgQualityDelivered)}
); } function ModelFleetPanel() { const utilization = useGameStore(s => s.market.servingMetrics.modelUtilization); if (utilization.length === 0) { return (

Model Fleet

No models deployed. Train and deploy models to start serving requests.

); } return (

Model Fleet

{utilization.map(m => (
{m.modelName} {m.quantization && ({m.quantization.toUpperCase()})}
0.9 ? 'bg-red-500' : m.utilization > 0.7 ? 'bg-yellow-500' : 'bg-green-500' }`} style={{ width: `${Math.min(100, m.utilization * 100)}%` }} />
{formatPercent(m.utilization)}
Q:{(m.qualityScore * 100).toFixed(0)}
{formatNumber(m.throughputCapacity)} t/s
))}
); } function PolicyControls() { const policy = useGameStore(s => s.market.overloadPolicy); const setPolicy = useGameStore(s => s.setOverloadPolicy); const completedResearch = useGameStore(s => s.research?.completedResearch ?? []); const hasRouting = completedResearch.includes('request-routing'); const hasPriorityQueues = completedResearch.includes('priority-queues'); const hasBatching = completedResearch.includes('request-batching'); const hasAutoScaling = completedResearch.includes('auto-scaling'); return (

Policy Controls

{/* Always available: Enterprise Reservation */}
setPolicy({ enterpriseReservation: Number(e.target.value) / 100 })} className="flex-1 accent-accent" /> {(policy.enterpriseReservation * 100).toFixed(0)}%

Reserve capacity for enterprise SLAs — protects contracts but limits other tiers

{/* Always available: Auto-Degradation toggle */}
{hasAutoScaling && policy.autoDegradation.enabled && (
setPolicy({ autoDegradation: { ...policy.autoDegradation, triggerThreshold: Number(e.target.value) / 100 }, })} className="flex-1 accent-accent" /> {(policy.autoDegradation.triggerThreshold * 100).toFixed(0)}%
setPolicy({ autoDegradation: { ...policy.autoDegradation, minQualityFloor: Number(e.target.value) / 100 }, })} className="flex-1 accent-accent" /> {(policy.autoDegradation.minQualityFloor * 100).toFixed(0)}%
)}
{/* Routing Strategy — requires research */} {hasRouting ? (
{ROUTING_OPTIONS.map(opt => ( ))}
) : (
Research "Intelligent Request Routing" to unlock routing strategies and per-tier rate limits
)} {/* Priority & Overflow — requires research */} {hasPriorityQueues ? (
{TRAFFIC_PRIORITIES.map(tier => (
{TIER_LABELS[tier]}
))}
setPolicy({ maxQueueDepth: Number(e.target.value) })} className="flex-1 accent-accent" /> {policy.maxQueueDepth}
) : !hasRouting ? null : (
Research "Priority Queue System" to unlock per-tier overflow behavior and queue controls
)} {/* Batch API — requires research */} {hasBatching ? (
{policy.batchApiEnabled && (
setPolicy({ batchApiDiscount: Number(e.target.value) / 100 })} className="flex-1 accent-accent" /> {(policy.batchApiDiscount * 100).toFixed(0)}%

Higher discount = more batch demand, lower per-token revenue

)}
) : hasRouting ? (
Research "Request Batching" to unlock the Batch API product line
) : null} {/* Rate limits — requires routing research */} {hasRouting && (
{TRAFFIC_PRIORITIES.map(tier => (
{TIER_LABELS[tier]} { const v = Number(e.target.value); if (v >= 10) { setPolicy({ rateLimitPerCustomer: { ...policy.rateLimitPerCustomer, [tier]: v, }, }); } }} className="w-28 bg-surface-800 border border-surface-600 rounded px-2 py-1 text-sm font-mono" min={10} step={100} />
))}
)}
); } function BatchApiPanel() { const batch = useGameStore(s => s.market.batchApi); const sm = useGameStore(s => s.market.servingMetrics); const policy = useGameStore(s => s.market.overloadPolicy); const completedResearch = useGameStore(s => s.research?.completedResearch ?? []); if (!completedResearch.includes('request-batching') || !policy.batchApiEnabled) return null; return (

Batch API

Pending Queue
{formatNumber(batch.pendingQueue)} tok
Served Last Tick
{formatNumber(batch.servedLastTick)} tok
Revenue
${sm.batchApiRevenue.toFixed(4)}/tick
); } export function ServingPage() { const sm = useGameStore(s => s.market.servingMetrics); const compute = useGameStore(s => s.compute); const totalDemand = sm.totalServed + sm.totalQueued + sm.totalRejected; const successRate = totalDemand > 0 ? sm.totalServed / totalDemand : 1; return (

Serving Pipeline

{/* Top metrics */}
0 ? `${formatNumber(sm.totalQueued)} queued` : 'No queuing'} color="text-yellow-400" /> 0 ? XCircle : CheckCircle} label="Success Rate" value={formatPercent(successRate)} sub={sm.totalRejected > 0 ? `${formatNumber(sm.totalRejected)} rejected` : 'All requests served'} color={sm.totalRejected > 0 ? 'text-red-400' : 'text-green-400'} />
{/* Pipeline flow table */} {/* Batch API metrics */} {/* Bottom row: controls + fleet */}
); }