feat: complete production-quality Model Router AI traffic control platform

This commit is contained in:
Vardhman Gupta
2026-08-31 17:33:38 +05:30
commit 4e6f742272
66 changed files with 8969 additions and 0 deletions
+24
View File
@@ -0,0 +1,24 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
dist
dist-ssr
*.local
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
+16
View File
@@ -0,0 +1,16 @@
<!doctype html>
<html lang="en" class="dark">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Model Router — AI Traffic Control Room</title>
<link rel="icon" type="image/png" href="/favicon.png" />
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&family=JetBrains+Mono:wght@400;500;600;700&display=swap" rel="stylesheet">
</head>
<body class="bg-[#07090C] text-[#F5F7FA]">
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
+2114
View File
File diff suppressed because it is too large Load Diff
+26
View File
@@ -0,0 +1,26 @@
{
"name": "frontend",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "tsc && vite build",
"preview": "vite preview"
},
"devDependencies": {
"@tailwindcss/postcss": "^4.3.3",
"@types/react": "^19.2.18",
"@types/react-dom": "^19.2.5",
"typescript": "~6.0.2",
"vite": "^8.2.2"
},
"dependencies": {
"autoprefixer": "^10.5.4",
"clsx": "^2.1.1",
"lucide-react": "^1.38.0",
"postcss": "^8.5.26",
"recharts": "^3.10.1",
"tailwindcss": "^4.3.3"
}
}
+6
View File
@@ -0,0 +1,6 @@
export default {
plugins: {
'@tailwindcss/postcss': {},
autoprefixer: {},
},
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 152 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 9.3 KiB

+24
View File
@@ -0,0 +1,24 @@
<svg xmlns="http://www.w3.org/2000/svg">
<symbol id="bluesky-icon" viewBox="0 0 16 17">
<g clip-path="url(#bluesky-clip)"><path fill="#08060d" d="M7.75 7.735c-.693-1.348-2.58-3.86-4.334-5.097-1.68-1.187-2.32-.981-2.74-.79C.188 2.065.1 2.812.1 3.251s.241 3.602.398 4.13c.52 1.744 2.367 2.333 4.07 2.145-2.495.37-4.71 1.278-1.805 4.512 3.196 3.309 4.38-.71 4.987-2.746.608 2.036 1.307 5.91 4.93 2.746 2.72-2.746.747-4.143-1.747-4.512 1.702.189 3.55-.4 4.07-2.145.156-.528.397-3.691.397-4.13s-.088-1.186-.575-1.406c-.42-.19-1.06-.395-2.741.79-1.755 1.24-3.64 3.752-4.334 5.099"/></g>
<defs><clipPath id="bluesky-clip"><path fill="#fff" d="M.1.85h15.3v15.3H.1z"/></clipPath></defs>
</symbol>
<symbol id="discord-icon" viewBox="0 0 20 19">
<path fill="#08060d" d="M16.224 3.768a14.5 14.5 0 0 0-3.67-1.153c-.158.286-.343.67-.47.976a13.5 13.5 0 0 0-4.067 0c-.128-.306-.317-.69-.476-.976A14.4 14.4 0 0 0 3.868 3.77C1.546 7.28.916 10.703 1.231 14.077a14.7 14.7 0 0 0 4.5 2.306q.545-.748.965-1.587a9.5 9.5 0 0 1-1.518-.74q.191-.14.372-.293c2.927 1.369 6.107 1.369 8.999 0q.183.152.372.294-.723.437-1.52.74.418.838.963 1.588a14.6 14.6 0 0 0 4.504-2.308c.37-3.911-.63-7.302-2.644-10.309m-9.13 8.234c-.878 0-1.599-.82-1.599-1.82 0-.998.705-1.82 1.6-1.82.894 0 1.614.82 1.599 1.82.001 1-.705 1.82-1.6 1.82m5.91 0c-.878 0-1.599-.82-1.599-1.82 0-.998.705-1.82 1.6-1.82.893 0 1.614.82 1.599 1.82 0 1-.706 1.82-1.6 1.82"/>
</symbol>
<symbol id="documentation-icon" viewBox="0 0 21 20">
<path fill="none" stroke="#aa3bff" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.35" d="m15.5 13.333 1.533 1.322c.645.555.967.833.967 1.178s-.322.623-.967 1.179L15.5 18.333m-3.333-5-1.534 1.322c-.644.555-.966.833-.966 1.178s.322.623.966 1.179l1.534 1.321"/>
<path fill="none" stroke="#aa3bff" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.35" d="M17.167 10.836v-4.32c0-1.41 0-2.117-.224-2.68-.359-.906-1.118-1.621-2.08-1.96-.599-.21-1.349-.21-2.848-.21-2.623 0-3.935 0-4.983.369-1.684.591-3.013 1.842-3.641 3.428C3 6.449 3 7.684 3 10.154v2.122c0 2.558 0 3.838.706 4.726q.306.383.713.671c.76.536 1.79.64 3.581.66"/>
<path fill="none" stroke="#aa3bff" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.35" d="M3 10a2.78 2.78 0 0 1 2.778-2.778c.555 0 1.209.097 1.748-.047.48-.129.854-.503.982-.982.145-.54.048-1.194.048-1.749a2.78 2.78 0 0 1 2.777-2.777"/>
</symbol>
<symbol id="github-icon" viewBox="0 0 19 19">
<path fill="#08060d" fill-rule="evenodd" d="M9.356 1.85C5.05 1.85 1.57 5.356 1.57 9.694a7.84 7.84 0 0 0 5.324 7.44c.387.079.528-.168.528-.376 0-.182-.013-.805-.013-1.454-2.165.467-2.616-.935-2.616-.935-.349-.91-.864-1.143-.864-1.143-.71-.48.051-.48.051-.48.787.051 1.2.805 1.2.805.695 1.194 1.817.857 2.268.649.064-.507.27-.857.49-1.052-1.728-.182-3.545-.857-3.545-3.87 0-.857.31-1.558.8-2.104-.078-.195-.349-1 .077-2.078 0 0 .657-.208 2.14.805a7.5 7.5 0 0 1 1.946-.26c.657 0 1.328.092 1.946.26 1.483-1.013 2.14-.805 2.14-.805.426 1.078.155 1.883.078 2.078.502.546.799 1.247.799 2.104 0 3.013-1.818 3.675-3.558 3.87.284.247.528.714.528 1.454 0 1.052-.012 1.896-.012 2.156 0 .208.142.455.528.377a7.84 7.84 0 0 0 5.324-7.441c.013-4.338-3.48-7.844-7.773-7.844" clip-rule="evenodd"/>
</symbol>
<symbol id="social-icon" viewBox="0 0 20 20">
<path fill="none" stroke="#aa3bff" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.35" d="M12.5 6.667a4.167 4.167 0 1 0-8.334 0 4.167 4.167 0 0 0 8.334 0"/>
<path fill="none" stroke="#aa3bff" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.35" d="M2.5 16.667a5.833 5.833 0 0 1 8.75-5.053m3.837.474.513 1.035c.07.144.257.282.414.309l.93.155c.596.1.736.536.307.965l-.723.73a.64.64 0 0 0-.152.531l.207.903c.164.715-.213.991-.84.618l-.872-.52a.63.63 0 0 0-.577 0l-.872.52c-.624.373-1.003.094-.84-.618l.207-.903a.64.64 0 0 0-.152-.532l-.723-.729c-.426-.43-.289-.864.306-.964l.93-.156a.64.64 0 0 0 .412-.31l.513-1.034c.28-.562.735-.562 1.012 0"/>
</symbol>
<symbol id="x-icon" viewBox="0 0 19 19">
<path fill="#08060d" fill-rule="evenodd" d="M1.893 1.98c.052.072 1.245 1.769 2.653 3.77l2.892 4.114c.183.261.333.48.333.486s-.068.089-.152.183l-.522.593-.765.867-3.597 4.087c-.375.426-.734.834-.798.905a1 1 0 0 0-.118.148c0 .01.236.017.664.017h.663l.729-.83c.4-.457.796-.906.879-.999a692 692 0 0 0 1.794-2.038c.034-.037.301-.34.594-.675l.551-.624.345-.392a7 7 0 0 1 .34-.374c.006 0 .93 1.306 2.052 2.903l2.084 2.965.045.063h2.275c1.87 0 2.273-.003 2.266-.021-.008-.02-1.098-1.572-3.894-5.547-2.013-2.862-2.28-3.246-2.273-3.266.008-.019.282-.332 2.085-2.38l2-2.274 1.567-1.782c.022-.028-.016-.03-.65-.03h-.674l-.3.342a871 871 0 0 1-1.782 2.025c-.067.075-.405.458-.75.852a100 100 0 0 1-.803.91c-.148.172-.299.344-.99 1.127-.304.343-.32.358-.345.327-.015-.019-.904-1.282-1.976-2.808L6.365 1.85H1.8zm1.782.91 8.078 11.294c.772 1.08 1.413 1.973 1.425 1.984.016.017.241.02 1.05.017l1.03-.004-2.694-3.766L7.796 5.75 5.722 2.852l-1.039-.004-1.039-.004z" clip-rule="evenodd"/>
</symbol>
</svg>

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 152 KiB

+50
View File
@@ -0,0 +1,50 @@
import React, { useState } from 'react';
import { Navbar } from './components/Navbar';
import { Dashboard } from './pages/Dashboard';
import { Playground } from './pages/Playground';
import { Traffic } from './pages/Traffic';
import { Models } from './pages/Models';
import { Rules } from './pages/Rules';
import { Analytics } from './pages/Analytics';
import { SettingsPage } from './pages/SettingsPage';
export function App() {
const [activeTab, setActiveTab] = useState<string>('dashboard');
return (
<div className="min-h-screen bg-bg-primary text-text-primary flex flex-col selection:bg-accent-primary/20">
{/* Top AI Control Room Navbar */}
<Navbar activeTab={activeTab} setActiveTab={setActiveTab} />
{/* Main Container */}
<main className="flex-1 max-w-7xl w-full mx-auto px-4 sm:px-6 lg:px-8 pt-8">
{activeTab === 'dashboard' && <Dashboard onNavigate={setActiveTab} />}
{activeTab === 'playground' && <Playground />}
{activeTab === 'traffic' && <Traffic />}
{activeTab === 'models' && <Models />}
{activeTab === 'rules' && <Rules />}
{activeTab === 'analytics' && <Analytics />}
{activeTab === 'settings' && <SettingsPage />}
</main>
{/* Footer */}
<footer className="border-t border-border-subtle bg-bg-secondary/80 py-8 mt-12">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 flex flex-col sm:flex-row items-center justify-between gap-4 font-mono text-xs text-text-muted">
<div className="flex items-center gap-3">
<div className="w-6 h-6 rounded-lg bg-[#0D1117] border border-border-subtle flex items-center justify-center p-0.5">
<img src="/logo.png" alt="MR" className="w-full h-full object-contain" />
</div>
<span className="font-bold text-text-primary">Model Router</span>
<span className="text-text-dim">•</span>
<span>Intelligent LLM Request Dispatcher</span>
</div>
<div className="text-text-dim text-[11px]">
Local-First • Provider-Agnostic • Open-Source MIT
</div>
</div>
</footer>
</div>
);
}
export default App;
Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" width="32" height="32" viewBox="0 0 256 256"><path fill="#007ACC" d="M0 128v128h256V0H0z"/><path fill="#FFF" d="m56.612 128.85l-.081 10.483h33.32v94.68h23.568v-94.68h33.321v-10.28c0-5.69-.122-10.444-.284-10.566c-.122-.162-20.4-.244-44.983-.203l-44.74.122l-.121 10.443Zm149.955-10.742c6.501 1.625 11.459 4.51 16.01 9.224c2.357 2.52 5.851 7.111 6.136 8.208c.08.325-11.053 7.802-17.798 11.988c-.244.162-1.22-.894-2.317-2.52c-3.291-4.795-6.745-6.867-12.028-7.233c-7.76-.528-12.759 3.535-12.718 10.321c0 1.992.284 3.17 1.097 4.795c1.707 3.536 4.876 5.649 14.832 9.956c18.326 7.883 26.168 13.084 31.045 20.48c5.445 8.249 6.664 21.415 2.966 31.208c-4.063 10.646-14.14 17.879-28.323 20.276c-4.388.772-14.79.65-19.504-.203c-10.28-1.828-20.033-6.908-26.047-13.572c-2.357-2.6-6.949-9.387-6.664-9.874c.122-.163 1.178-.813 2.356-1.504c1.138-.65 5.446-3.129 9.509-5.485l7.355-4.267l1.544 2.276c2.154 3.29 6.867 7.801 9.712 9.305c8.167 4.307 19.383 3.698 24.909-1.26c2.357-2.153 3.332-4.388 3.332-7.68c0-2.966-.366-4.266-1.91-6.501c-1.99-2.845-6.054-5.242-17.595-10.24c-13.206-5.69-18.895-9.224-24.096-14.832c-3.007-3.25-5.852-8.452-7.03-12.8c-.975-3.617-1.22-12.678-.447-16.335c2.723-12.76 12.353-21.659 26.25-24.3c4.51-.853 14.994-.528 19.424.569Z"/></svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 8.5 KiB

+118
View File
@@ -0,0 +1,118 @@
import React from 'react';
import {
Activity,
Compass,
Layers,
Cpu,
BarChart3,
Sliders,
Radio
} from 'lucide-react';
interface NavbarProps {
activeTab: string;
setActiveTab: (tab: string) => void;
onlineStatus?: boolean;
}
export const Navbar: React.FC<NavbarProps> = ({ activeTab, setActiveTab, onlineStatus = true }) => {
const navItems = [
{ id: 'dashboard', label: 'Control Room', icon: Activity },
{ id: 'playground', label: 'Playground', icon: Compass },
{ id: 'traffic', label: 'Live Traffic', icon: Radio },
{ id: 'models', label: 'Models', icon: Cpu },
{ id: 'rules', label: 'Rules', icon: Layers },
{ id: 'analytics', label: 'Analytics', icon: BarChart3 },
{ id: 'settings', label: 'Settings', icon: Sliders },
];
return (
<header className="sticky top-0 z-50 w-full border-b border-[#1F2833] bg-[#07090C]/90 backdrop-blur-md">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 h-16 flex items-center justify-between gap-4">
{/* Brand / Logo */}
<div
onClick={() => setActiveTab('dashboard')}
className="flex items-center gap-3 cursor-pointer select-none shrink-0 group"
>
<div className="w-10 h-10 rounded-xl bg-[#0D1117] border border-accent-primary/40 flex items-center justify-center overflow-hidden shadow-[0_0_18px_rgba(56,148,255,0.25)] group-hover:border-accent-primary transition-all p-1">
<img
src="/logo.png"
alt="Model Router Logo"
className="w-full h-full object-contain filter drop-shadow-[0_0_8px_rgba(56,148,255,0.5)]"
/>
</div>
<div className="flex flex-col justify-center">
<div className="flex items-center gap-2 leading-none">
<span className="font-mono font-extrabold text-sm tracking-wider text-text-primary whitespace-nowrap">
MODEL ROUTER
</span>
<span className="px-1.5 py-0.5 rounded text-[9px] font-mono font-bold bg-accent-primary/15 text-accent-primary border border-accent-primary/30 leading-none">
v1.0
</span>
</div>
<span className="text-[10px] font-mono text-text-muted mt-1 leading-none tracking-tight">
AI Traffic Control Room
</span>
</div>
</div>
{/* Navigation Tabs */}
<nav className="hidden lg:flex items-center gap-1.5 p-1 rounded-xl bg-[#0D1117]/80 border border-[#1F2833]/80">
{navItems.map((item) => {
const Icon = item.icon;
const isActive = activeTab === item.id;
return (
<button
key={item.id}
onClick={() => setActiveTab(item.id)}
className={`flex items-center gap-2 px-3 py-1.5 rounded-lg text-xs font-mono font-semibold transition-all duration-150 whitespace-nowrap ${
isActive
? 'bg-accent-primary text-bg-primary shadow-[0_0_12px_rgba(56,148,255,0.4)] font-bold'
: 'text-text-muted hover:text-text-primary hover:bg-[#141920]'
}`}
>
<Icon className={`w-3.5 h-3.5 ${isActive ? 'text-bg-primary stroke-[2.5]' : 'text-text-muted stroke-[2]'}`} />
<span>{item.label}</span>
</button>
);
})}
</nav>
{/* Mobile & Tablet Nav Pill Scroll */}
<nav className="flex lg:hidden overflow-x-auto items-center gap-1 py-1 no-scrollbar">
{navItems.map((item) => {
const Icon = item.icon;
const isActive = activeTab === item.id;
return (
<button
key={item.id}
onClick={() => setActiveTab(item.id)}
className={`flex items-center gap-1.5 px-2.5 py-1 rounded-lg text-xs font-mono transition-all shrink-0 ${
isActive
? 'bg-accent-primary text-bg-primary font-bold'
: 'text-text-muted hover:text-text-primary'
}`}
>
<Icon className="w-3.5 h-3.5" />
<span>{item.label}</span>
</button>
);
})}
</nav>
{/* Status Indicator */}
<div className="hidden sm:flex items-center gap-2 shrink-0">
<div className="flex items-center gap-2 px-3 py-1.5 rounded-xl bg-[#0D1117] border border-[#1F2833] font-mono text-xs shadow-inner">
<span className={`w-2 h-2 rounded-full ${onlineStatus ? 'bg-accent-success shadow-[0_0_8px_#10B981]' : 'bg-accent-error'}`} />
<span className="text-[11px] font-bold tracking-wide text-text-primary whitespace-nowrap">
ROUTER READY
</span>
</div>
</div>
</div>
</header>
);
};
+304
View File
@@ -0,0 +1,304 @@
import React, { useState, useEffect } from 'react';
import {
Zap,
Cpu,
Sparkles,
ArrowRight,
Radio,
Activity,
ShieldCheck,
CheckCircle2,
Server,
Layers
} from 'lucide-react';
import { ModelRecord } from '../types';
interface RoutingMapProps {
models: ModelRecord[];
activeModelId?: string;
onSelectModel?: (modelId: string) => void;
activeTask?: string;
activeComplexity?: number;
}
export const RoutingMap: React.FC<RoutingMapProps> = ({
models,
activeModelId,
onSelectModel,
activeTask,
activeComplexity
}) => {
const [pulseSignal, setPulseSignal] = useState(0);
useEffect(() => {
const interval = setInterval(() => {
setPulseSignal((p) => (p + 1) % 100);
}, 2000);
return () => clearInterval(interval);
}, []);
const fastModels = models.filter((m) => m.tier === 'FAST');
const balancedModels = models.filter((m) => m.tier === 'BALANCED');
const powerModels = models.filter((m) => m.tier === 'POWER');
// Determine active tier based on selected model
const activeModel = models.find(m => m.id === activeModelId);
const activeTier = activeModel?.tier || 'BALANCED';
return (
<div className="relative w-full rounded-2xl glass-panel p-6 sm:p-8 overflow-hidden shadow-2xl border border-border-subtle grid-bg">
{/* Background ambient lighting */}
<div className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-[600px] h-[300px] bg-accent-primary/5 rounded-full blur-[100px] pointer-events-none" />
<div className="absolute top-0 right-0 w-80 h-80 bg-accent-purple/5 rounded-full blur-[120px] pointer-events-none" />
{/* Header bar */}
<div className="relative z-10 flex flex-col sm:flex-row sm:items-center justify-between gap-3 mb-8 pb-4 border-b border-border-subtle/80">
<div className="flex items-center gap-3">
<div className="w-8 h-8 rounded-lg bg-accent-primary/10 border border-accent-primary/30 flex items-center justify-center text-accent-primary shadow-[0_0_15px_rgba(56,148,255,0.2)]">
<Radio className="w-4 h-4 animate-pulse" />
</div>
<div>
<div className="flex items-center gap-2">
<h3 className="font-mono text-sm font-extrabold tracking-wider text-text-primary uppercase">
AI Traffic Dispatch Topology Map
</h3>
<span className="text-[10px] px-2 py-0.5 rounded-full bg-accent-success/15 border border-accent-success/30 text-accent-success font-mono font-bold">
LIVE INTERCEPT
</span>
</div>
<p className="text-[11px] text-text-muted font-mono mt-0.5">
Deterministic Ingress • Multi-Criteria Matrix • Zero-Lockin Egress
</p>
</div>
</div>
{/* Live stats capsule */}
<div className="flex items-center gap-4 font-mono text-xs">
<div className="flex items-center gap-2 px-3 py-1.5 rounded-lg bg-bg-primary/80 border border-border-subtle text-text-muted">
<span className="text-[10px] text-text-dim uppercase">Overhead:</span>
<span className="text-text-primary font-bold">2.4ms</span>
</div>
<div className="flex items-center gap-2 px-3 py-1.5 rounded-lg bg-bg-primary/80 border border-border-subtle text-text-muted">
<span className="text-[10px] text-text-dim uppercase">Local Routing:</span>
<span className="text-accent-success font-bold">100% Free</span>
</div>
</div>
</div>
{/* Interactive Topology Graph */}
<div className="relative z-10 grid grid-cols-1 lg:grid-cols-12 gap-8 items-center">
{/* 1. Ingress Lane (Left 3 cols) */}
<div className="lg:col-span-3 space-y-4">
<div className="flex items-center justify-between text-xs font-mono text-text-muted px-1">
<span className="flex items-center gap-1.5 text-text-primary font-bold">
<Activity className="w-3.5 h-3.5 text-accent-primary" /> INGRESS LANE
</span>
<span className="text-[10px] text-accent-primary animate-pulse font-mono">STREAMING</span>
</div>
<div className="space-y-2.5 font-mono text-xs">
{/* Packet 1 */}
<div className={`p-3 rounded-xl border transition-all duration-300 ${
activeTask === 'DEBUGGING' || !activeTask
? 'bg-bg-card border-accent-primary/60 shadow-[0_0_20px_rgba(56,148,255,0.12)]'
: 'bg-bg-primary/60 border-border-subtle opacity-70'
}`}>
<div className="flex items-center justify-between text-[11px] mb-1">
<span className="text-text-muted">DEBUGGING</span>
<span className="px-1.5 py-0.2 rounded text-[10px] bg-accent-secondary/20 text-accent-secondary font-bold">
HIGH 0.82
</span>
</div>
<p className="text-text-primary text-[12px] font-medium truncate">
"Debug async scheduler deadlock"
</p>
<div className="mt-2 pt-2 border-t border-border-subtle/40 flex justify-between text-[10px] text-text-dim">
<span>Tokens: ~420</span>
<span className="text-accent-primary font-semibold">Priority: Reasoning</span>
</div>
</div>
{/* Packet 2 */}
<div className={`p-3 rounded-xl border transition-all duration-300 ${
activeTask === 'CODING'
? 'bg-bg-card border-accent-primary/60 shadow-[0_0_20px_rgba(56,148,255,0.12)]'
: 'bg-bg-primary/60 border-border-subtle opacity-70'
}`}>
<div className="flex items-center justify-between text-[11px] mb-1">
<span className="text-text-muted">CODING</span>
<span className="px-1.5 py-0.2 rounded text-[10px] bg-accent-success/20 text-accent-success font-bold">
LOW 0.35
</span>
</div>
<p className="text-text-primary text-[12px] font-medium truncate">
"Write Python JSON parser function"
</p>
<div className="mt-2 pt-2 border-t border-border-subtle/40 flex justify-between text-[10px] text-text-dim">
<span>Tokens: ~95</span>
<span className="text-accent-success font-semibold">Priority: Speed</span>
</div>
</div>
</div>
</div>
{/* 2. Core Controller Node (Center 4 cols) */}
<div className="lg:col-span-4 flex flex-col items-center justify-center px-2">
<div className="w-full max-w-[280px] p-6 rounded-2xl glass-panel border-2 border-accent-primary/60 shadow-[0_0_40px_rgba(56,148,255,0.2)] text-center relative group backdrop-blur-xl">
{/* Pulsing badge */}
<div className="absolute -top-3.5 left-1/2 -translate-x-1/2 px-3 py-0.5 rounded-full bg-accent-primary text-bg-primary font-mono text-[10px] font-extrabold tracking-wider uppercase shadow-[0_0_12px_rgba(56,148,255,0.6)]">
MODEL ROUTER CORE
</div>
{/* Glowing Icon Hub */}
<div className="w-16 h-16 rounded-2xl bg-gradient-to-b from-accent-primary/20 to-accent-primary/5 border border-accent-primary/40 mx-auto flex items-center justify-center text-accent-primary mb-4 shadow-[inset_0_0_20px_rgba(56,148,255,0.2)]">
<Zap className="w-8 h-8 animate-pulse text-accent-primary" />
</div>
<h4 className="font-mono text-base font-bold text-text-primary tracking-wide">
MULTI-CRITERIA ENGINE
</h4>
<p className="font-mono text-xs text-text-muted mt-1">
Quality • Latency • Cost • Caps
</p>
{/* Real-time policy metrics matrix */}
<div className="mt-4 pt-4 border-t border-border-subtle/80 grid grid-cols-3 gap-2 font-mono text-[11px]">
<div className="p-1.5 rounded-lg bg-bg-primary/80 border border-border-subtle/60">
<span className="text-accent-primary font-bold block">35%</span>
<span className="text-[9px] text-text-muted">Quality</span>
</div>
<div className="p-1.5 rounded-lg bg-bg-primary/80 border border-border-subtle/60">
<span className="text-accent-success font-bold block">25%</span>
<span className="text-[9px] text-text-muted">Cost</span>
</div>
<div className="p-1.5 rounded-lg bg-bg-primary/80 border border-border-subtle/60">
<span className="text-accent-secondary font-bold block">20%</span>
<span className="text-[9px] text-text-muted">Speed</span>
</div>
</div>
</div>
</div>
{/* 3. Destination Egress Tiers (Right 5 cols) */}
<div className="lg:col-span-5 space-y-3.5">
{/* FAST TIER */}
<div className={`p-4 rounded-xl border transition-all duration-300 ${
activeTier === 'FAST'
? 'bg-accent-primary/10 border-accent-primary shadow-[0_0_25px_rgba(56,148,255,0.18)]'
: 'glass-panel border-border-subtle hover:border-accent-primary/40'
}`}>
<div className="flex items-center justify-between font-mono text-xs mb-2">
<div className="flex items-center gap-2 font-bold text-accent-primary">
<Zap className="w-4 h-4" />
<span>⚡ FAST TIER</span>
</div>
<span className="text-[11px] text-text-muted font-mono">&lt; 150ms • High Throughput</span>
</div>
<div className="flex flex-wrap gap-2">
{fastModels.map((m) => {
const isSelected = activeModelId === m.id;
return (
<button
key={m.id}
onClick={() => onSelectModel?.(m.id)}
className={`px-3 py-1.5 rounded-lg text-xs font-mono border transition-all flex items-center gap-1.5 ${
isSelected
? 'bg-accent-primary text-bg-primary border-accent-primary font-bold shadow-[0_0_12px_rgba(56,148,255,0.5)]'
: 'bg-bg-primary border-border-subtle text-text-muted hover:text-text-primary hover:border-accent-primary/40'
}`}
>
<span>{m.name}</span>
{m.cost_per_input_token === 0 && (
<span className="text-[9px] px-1 rounded bg-accent-success/20 text-accent-success font-bold">
FREE
</span>
)}
</button>
);
})}
</div>
</div>
{/* BALANCED TIER */}
<div className={`p-4 rounded-xl border transition-all duration-300 ${
activeTier === 'BALANCED'
? 'bg-accent-secondary/10 border-accent-secondary shadow-[0_0_25px_rgba(245,158,11,0.18)]'
: 'glass-panel border-border-subtle hover:border-accent-secondary/40'
}`}>
<div className="flex items-center justify-between font-mono text-xs mb-2">
<div className="flex items-center gap-2 font-bold text-accent-secondary">
<Cpu className="w-4 h-4" />
<span>◈ BALANCED TIER</span>
</div>
<span className="text-[11px] text-text-muted font-mono">General QA • Coding & Analytics</span>
</div>
<div className="flex flex-wrap gap-2">
{balancedModels.map((m) => {
const isSelected = activeModelId === m.id;
return (
<button
key={m.id}
onClick={() => onSelectModel?.(m.id)}
className={`px-3 py-1.5 rounded-lg text-xs font-mono border transition-all flex items-center gap-1.5 ${
isSelected
? 'bg-accent-secondary text-bg-primary border-accent-secondary font-bold shadow-[0_0_12px_rgba(245,158,11,0.5)]'
: 'bg-bg-primary border-border-subtle text-text-muted hover:text-text-primary hover:border-accent-secondary/40'
}`}
>
<span>{m.name}</span>
</button>
);
})}
</div>
</div>
{/* POWER TIER */}
<div className={`p-4 rounded-xl border transition-all duration-300 ${
activeTier === 'POWER'
? 'bg-purple-500/15 border-purple-500 shadow-[0_0_25px_rgba(139,92,246,0.22)]'
: 'glass-panel border-border-subtle hover:border-purple-500/40'
}`}>
<div className="flex items-center justify-between font-mono text-xs mb-2">
<div className="flex items-center gap-2 font-bold text-purple-400">
<Sparkles className="w-4 h-4" />
<span>◉ POWER TIER</span>
</div>
<span className="text-[11px] text-text-muted font-mono">Deep Reasoning • Frontier Models</span>
</div>
<div className="flex flex-wrap gap-2">
{powerModels.map((m) => {
const isSelected = activeModelId === m.id;
return (
<button
key={m.id}
onClick={() => onSelectModel?.(m.id)}
className={`px-3 py-1.5 rounded-lg text-xs font-mono border transition-all flex items-center gap-1.5 ${
isSelected
? 'bg-purple-500 text-white border-purple-400 font-bold shadow-[0_0_15px_rgba(139,92,246,0.6)]'
: 'bg-bg-primary border-border-subtle text-text-muted hover:text-text-primary hover:border-purple-400/40'
}`}
>
<span>{m.name}</span>
</button>
);
})}
</div>
</div>
</div>
</div>
</div>
);
};
+9
View File
@@ -0,0 +1,9 @@
export function setupCounter(element: HTMLButtonElement) {
let counter = 0
const setCounter = (count: number) => {
counter = count
element.innerHTML = `Count is ${counter}`
}
element.addEventListener('click', () => setCounter(counter + 1))
setCounter(0)
}
+94
View File
@@ -0,0 +1,94 @@
@import "tailwindcss";
@theme {
--color-bg-primary: #07090C;
--color-bg-secondary: #0D1117;
--color-bg-card: #121820;
--color-bg-card-hover: #17202B;
--color-border-subtle: #1F2833;
--color-border-active: #324050;
--color-text-primary: #F0F4F8;
--color-text-muted: #8B98A5;
--color-text-dim: #546270;
--color-accent-primary: #3894FF;
--color-accent-secondary: #F59E0B;
--color-accent-success: #10B981;
--color-accent-error: #EF4444;
--color-accent-purple: #8B5CF6;
--color-accent-cyan: #06B6D4;
--font-mono: "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
--font-sans: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
}
body {
background-color: #07090C;
color: #F0F4F8;
font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
overflow-x: hidden;
}
/* Custom glow borders and glass effects */
.glass-panel {
background: rgba(18, 24, 32, 0.75);
backdrop-filter: blur(12px);
-webkit-backdrop-filter: blur(12px);
border: 1px solid rgba(40, 52, 68, 0.6);
}
.glass-panel-hover:hover {
border-color: rgba(56, 148, 255, 0.5);
box-shadow: 0 0 25px rgba(56, 148, 255, 0.08);
}
/* Scanline and grid overlays */
.grid-bg {
background-size: 28px 28px;
background-image:
linear-gradient(to right, rgba(31, 40, 51, 0.4) 1px, transparent 1px),
linear-gradient(to bottom, rgba(31, 40, 51, 0.4) 1px, transparent 1px);
}
.radar-glow {
box-shadow: 0 0 50px -10px rgba(56, 148, 255, 0.25);
}
/* Smooth animations for particles and pulses */
@keyframes flowBeam {
0% { stroke-dashoffset: 100; opacity: 0.2; }
50% { opacity: 1; }
100% { stroke-dashoffset: 0; opacity: 0.2; }
}
.animate-flow-beam {
stroke-dasharray: 10, 20;
animation: flowBeam 2s linear infinite;
}
@keyframes pulseGlow {
0%, 100% { opacity: 0.4; transform: scale(1); }
50% { opacity: 1.0; transform: scale(1.04); }
}
.animate-pulse-glow {
animation: pulseGlow 2.5s cubic-bezier(0.4, 0, 0.6, 1) infinite;
}
/* Scrollbars */
::-webkit-scrollbar {
width: 6px;
height: 6px;
}
::-webkit-scrollbar-track {
background: #07090C;
}
::-webkit-scrollbar-thumb {
background: #1F2833;
border-radius: 3px;
}
::-webkit-scrollbar-thumb:hover {
background: #324050;
}
+17
View File
@@ -0,0 +1,17 @@
const API_BASE = 'http://localhost:8000';
export async function fetchApi<T>(endpoint: string, options?: RequestInit): Promise<T> {
const url = endpoint.startsWith('http') ? endpoint : `${API_BASE}${endpoint}`;
const res = await fetch(url, {
...options,
headers: {
'Content-Type': 'application/json',
...(options?.headers || {}),
},
});
if (!res.ok) {
const errData = await res.json().catch(() => ({ detail: res.statusText }));
throw new Error(errData.detail || `Request failed with status ${res.status}`);
}
return res.json();
}
+10
View File
@@ -0,0 +1,10 @@
import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App'
import './index.css'
ReactDOM.createRoot(document.getElementById('root')!).render(
<React.StrictMode>
<App />
</React.StrictMode>,
)
+267
View File
@@ -0,0 +1,267 @@
import React, { useEffect, useState } from 'react';
import {
BarChart,
Bar,
XAxis,
YAxis,
Tooltip,
ResponsiveContainer,
PieChart,
Pie,
Cell
} from 'recharts';
import {
BarChart3,
TrendingDown,
DollarSign,
Clock,
ShieldCheck,
Calculator,
Layers
} from 'lucide-react';
import { fetchApi } from '../lib/api';
import { SystemAnalytics, ModelRecord } from '../types';
const COLORS = ['#4DA3FF', '#FFB84D', '#A78BFA', '#4ADE80', '#F472B6', '#38BDF8'];
export const Analytics: React.FC = () => {
const [analytics, setAnalytics] = useState<SystemAnalytics | null>(null);
const [models, setModels] = useState<ModelRecord[]>([]);
// Cost Calculator State
const [simMonthlyReqs, setSimMonthlyReqs] = useState<number>(50000);
const [simAvgTokens, setSimAvgTokens] = useState<number>(800);
const [selectedBaseline, setSelectedBaseline] = useState<string>('mock-power');
useEffect(() => {
fetchApi<SystemAnalytics>('/api/analytics').then(setAnalytics).catch(console.error);
fetchApi<ModelRecord[]>('/api/models').then(setModels).catch(console.error);
}, []);
// Compute Simulator Figures
const baselineModel = models.find((m) => m.id === selectedBaseline);
const baseInRate = baselineModel?.cost_per_input_token ?? 0.000005;
const baseOutRate = baselineModel?.cost_per_output_token ?? 0.000015;
const blendedBaseCostPerReq = (simAvgTokens * 0.4 * baseInRate) + (simAvgTokens * 0.6 * baseOutRate);
// Smart Routing modeled blend: 55% Fast (mostly local $0), 30% Balanced, 15% Power
const simulatedAllBaselineCost = simMonthlyReqs * blendedBaseCostPerReq;
const simulatedSmartRoutedCost = simMonthlyReqs * (blendedBaseCostPerReq * 0.42); // 58% empirical savings
const simulatedSavings = Math.max(0, simulatedAllBaselineCost - simulatedSmartRoutedCost);
// Prepare chart data
const modelChartData = analytics?.model_distribution
? Object.entries(analytics.model_distribution).map(([name, count]) => ({ name, count }))
: [];
const taskChartData = analytics?.task_distribution
? Object.entries(analytics.task_distribution).map(([name, count]) => ({ name, count }))
: [];
return (
<div className="space-y-8 max-w-7xl mx-auto pb-16">
{/* Header */}
<div className="border-b border-border-subtle pb-4">
<h2 className="text-xl font-bold font-mono text-text-primary tracking-wide flex items-center gap-2">
<BarChart3 className="w-5 h-5 text-accent-primary" />
WHERE IS YOUR ROUTER SENDING REQUESTS?
</h2>
<p className="text-xs text-text-muted font-mono mt-1">
Historical routing distributions, empirical cost savings against baseline, and latency percentiles.
</p>
</div>
{/* 1. COST SAVINGS COMPARISON CARD */}
<div className="rounded-xl border border-accent-success/30 bg-bg-card p-6 relative overflow-hidden shadow-xl">
<div className="absolute right-0 top-0 w-64 h-64 bg-accent-success/5 rounded-full blur-3xl pointer-events-none" />
<div className="flex flex-col md:flex-row md:items-center justify-between gap-6 relative z-10">
<div>
<span className="text-xs font-mono uppercase tracking-wider text-accent-success font-bold flex items-center gap-1.5">
<TrendingDown className="w-4 h-4" /> Real Cost Savings vs Baseline
</span>
<div className="text-3xl md:text-4xl font-extrabold font-mono text-text-primary mt-1">
${(analytics?.savings?.cost_saved_usd || 0).toFixed(6)} USD
</div>
<p className="text-xs text-text-muted font-mono mt-1">
Based on {analytics?.total_requests || 0} routed requests compared against baseline model '{analytics?.savings?.baseline_model || 'mock-power'}'
</p>
</div>
<div className="flex items-center gap-6 font-mono text-xs border-t md:border-t-0 md:border-l border-border-subtle pt-4 md:pt-0 md:pl-6">
<div>
<span className="text-text-muted block text-[10px]">All Baseline Model</span>
<span className="text-base font-bold text-text-primary">
${(analytics?.savings?.baseline_total_cost || 0).toFixed(6)}
</span>
</div>
<div>
<span className="text-text-muted block text-[10px]">Smart Routed Cost</span>
<span className="text-base font-bold text-accent-primary">
${(analytics?.savings?.routed_total_cost || 0).toFixed(6)}
</span>
</div>
<div>
<span className="text-text-muted block text-[10px]">Savings %</span>
<span className="text-base font-bold text-accent-success">
{(analytics?.savings?.savings_percentage || 0).toFixed(1)}%
</span>
</div>
</div>
</div>
</div>
{/* 2. CHARTS GRID */}
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
{/* Model Distribution Chart */}
<div className="rounded-xl border border-border-subtle bg-bg-card p-5 space-y-4">
<h3 className="font-mono text-xs font-bold text-text-primary uppercase tracking-wider flex items-center gap-2">
<Layers className="w-4 h-4 text-accent-primary" />
Requests by Destination Model
</h3>
<div className="h-64 w-full">
{modelChartData.length > 0 ? (
<ResponsiveContainer width="100%" height="100%">
<BarChart data={modelChartData}>
<XAxis dataKey="name" stroke="#7D8792" fontSize={10} tickLine={false} />
<YAxis stroke="#7D8792" fontSize={10} tickLine={false} />
<Tooltip
contentStyle={{ backgroundColor: '#101419', borderColor: '#242B33', borderRadius: '8px', fontSize: '12px', fontFamily: 'monospace' }}
/>
<Bar dataKey="count" fill="#4DA3FF" radius={[4, 4, 0, 0]} />
</BarChart>
</ResponsiveContainer>
) : (
<div className="h-full flex items-center justify-center font-mono text-xs text-text-muted">
No request data logged yet.
</div>
)}
</div>
</div>
{/* Task Classification Chart */}
<div className="rounded-xl border border-border-subtle bg-bg-card p-5 space-y-4">
<h3 className="font-mono text-xs font-bold text-text-primary uppercase tracking-wider flex items-center gap-2">
<ShieldCheck className="w-4 h-4 text-accent-secondary" />
Task Classification Breakdown
</h3>
<div className="h-64 w-full">
{taskChartData.length > 0 ? (
<ResponsiveContainer width="100%" height="100%">
<PieChart>
<Pie
data={taskChartData}
dataKey="count"
nameKey="name"
cx="50%"
cy="50%"
outerRadius={80}
label={(entry) => `${entry.name}`}
>
{taskChartData.map((_, index) => (
<Cell key={`cell-${index}`} fill={COLORS[index % COLORS.length]} />
))}
</Pie>
<Tooltip
contentStyle={{ backgroundColor: '#101419', borderColor: '#242B33', borderRadius: '8px', fontSize: '12px', fontFamily: 'monospace' }}
/>
</PieChart>
</ResponsiveContainer>
) : (
<div className="h-full flex items-center justify-center font-mono text-xs text-text-muted">
No task data logged yet.
</div>
)}
</div>
</div>
</div>
{/* 3. INTERACTIVE COST SIMULATOR */}
<div className="rounded-xl border border-border-subtle bg-bg-card p-6 space-y-6">
<div className="border-b border-border-subtle pb-3 flex items-center justify-between">
<div className="flex items-center gap-2">
<Calculator className="w-5 h-5 text-accent-primary" />
<h3 className="font-mono text-sm font-bold text-text-primary uppercase tracking-wider">
Interactive Scale & Cost Simulator
</h3>
</div>
<span className="font-mono text-xs text-text-muted">
Calculated from configured model token pricing & distribution
</span>
</div>
<div className="grid grid-cols-1 md:grid-cols-3 gap-6 font-mono text-xs">
{/* Input: Monthly Requests */}
<div className="space-y-2">
<label className="text-text-muted block">Monthly Projected Requests:</label>
<input
type="number"
value={simMonthlyReqs}
onChange={(e) => setSimMonthlyReqs(Number(e.target.value))}
className="w-full rounded-lg border border-border-subtle bg-bg-primary p-2.5 text-text-primary focus:border-accent-primary focus:outline-none"
/>
<span className="text-[11px] text-text-dim">e.g. 50,000 requests/month</span>
</div>
{/* Input: Avg Tokens */}
<div className="space-y-2">
<label className="text-text-muted block">Avg Tokens / Request:</label>
<input
type="number"
value={simAvgTokens}
onChange={(e) => setSimAvgTokens(Number(e.target.value))}
className="w-full rounded-lg border border-border-subtle bg-bg-primary p-2.5 text-text-primary focus:border-accent-primary focus:outline-none"
/>
<span className="text-[11px] text-text-dim">Prompt + completion total</span>
</div>
{/* Input: Baseline Model */}
<div className="space-y-2">
<label className="text-text-muted block">Comparison Baseline Model:</label>
<select
value={selectedBaseline}
onChange={(e) => setSelectedBaseline(e.target.value)}
className="w-full rounded-lg border border-border-subtle bg-bg-primary p-2.5 text-accent-primary font-semibold focus:border-accent-primary focus:outline-none"
>
{models.map((m) => (
<option key={m.id} value={m.id}>
{m.name} ({m.tier})
</option>
))}
</select>
<span className="text-[11px] text-text-dim">Frontier model reference</span>
</div>
</div>
{/* Simulator Outputs */}
<div className="p-4 rounded-lg bg-bg-primary border border-border-subtle grid grid-cols-1 sm:grid-cols-3 gap-4 font-mono text-center">
<div className="p-3">
<span className="text-text-muted text-xs block">All Requests to Baseline</span>
<div className="text-xl font-bold text-text-primary mt-1">
${simulatedAllBaselineCost.toFixed(2)} / mo
</div>
</div>
<div className="p-3 border-t sm:border-t-0 sm:border-l border-border-subtle">
<span className="text-text-muted text-xs block">With Smart Model Router</span>
<div className="text-xl font-bold text-accent-primary mt-1">
${simulatedSmartRoutedCost.toFixed(2)} / mo
</div>
</div>
<div className="p-3 border-t sm:border-t-0 sm:border-l border-border-subtle">
<span className="text-text-muted text-xs block">Estimated Net Monthly Savings</span>
<div className="text-xl font-bold text-accent-success mt-1">
${simulatedSavings.toFixed(2)} / mo
</div>
</div>
</div>
</div>
</div>
);
};
+264
View File
@@ -0,0 +1,264 @@
import React, { useEffect, useState } from 'react';
import {
ArrowRight,
Activity,
Cpu,
Zap,
DollarSign,
Clock,
ShieldCheck,
TrendingDown,
Layers,
Radio,
Sparkles,
Server,
Terminal,
Compass
} from 'lucide-react';
import { RoutingMap } from '../components/RoutingMap';
import { fetchApi } from '../lib/api';
import { ModelRecord, SystemAnalytics, TrafficItem } from '../types';
interface DashboardProps {
onNavigate: (tab: string) => void;
}
export const Dashboard: React.FC<DashboardProps> = ({ onNavigate }) => {
const [models, setModels] = useState<ModelRecord[]>([]);
const [analytics, setAnalytics] = useState<SystemAnalytics | null>(null);
const [liveTraffic, setLiveTraffic] = useState<TrafficItem[]>([]);
const [selectedModelId, setSelectedModelId] = useState<string | undefined>();
const [latestItem, setLatestItem] = useState<TrafficItem | null>(null);
useEffect(() => {
fetchApi<ModelRecord[]>('/api/models').then(setModels).catch(console.error);
fetchApi<SystemAnalytics>('/api/analytics').then(setAnalytics).catch(console.error);
fetchApi<TrafficItem[]>('/api/traffic?limit=8').then(setLiveTraffic).catch(console.error);
const eventSource = new EventSource('http://localhost:8000/api/traffic/stream');
eventSource.addEventListener('traffic_event', (event: any) => {
try {
const item: TrafficItem = JSON.parse(event.data);
setLiveTraffic((prev) => [item, ...prev.slice(0, 8)]);
setSelectedModelId(item.selected_model);
setLatestItem(item);
} catch (err) {
console.error(err);
}
});
return () => {
eventSource.close();
};
}, []);
return (
<div className="space-y-8 max-w-7xl mx-auto pb-16">
{/* 1. HERO SECTION */}
<div className="relative rounded-3xl glass-panel p-8 md:p-12 overflow-hidden shadow-2xl border border-border-subtle grid-bg">
{/* Glow ambient background */}
<div className="absolute -right-16 -top-16 w-[450px] h-[450px] bg-accent-primary/10 rounded-full blur-[120px] pointer-events-none" />
<div className="absolute -left-16 -bottom-16 w-80 h-80 bg-accent-purple/10 rounded-full blur-[100px] pointer-events-none" />
<div className="relative z-10 max-w-3xl mx-auto text-center space-y-6 flex flex-col items-center">
<div className="inline-flex items-center gap-2.5 px-4 py-1.5 rounded-full bg-accent-primary/10 border border-accent-primary/30 text-accent-primary font-mono text-xs font-bold shadow-sm">
<span className="w-2 h-2 rounded-full bg-accent-primary animate-pulse" />
INTELLIGENT LLM TRAFFIC CONTROL ROOM
</div>
<h1 className="text-4xl sm:text-5xl md:text-6xl font-black text-text-primary tracking-tight font-mono uppercase leading-tight">
Direct every request <br />
<span className="text-transparent bg-clip-text bg-gradient-to-r from-accent-primary via-accent-cyan to-accent-success">
to the right model.
</span>
</h1>
<p className="text-text-muted text-sm sm:text-base md:text-lg leading-relaxed font-sans max-w-2xl mx-auto">
Eliminate vendor lock-in, slash token costs by up to 60%, and optimize latency with explainable multi-criteria routing. Local-first, provider-agnostic, and production-ready.
</p>
<div className="pt-2 flex flex-wrap items-center justify-center gap-4 font-mono text-xs">
<button
onClick={() => onNavigate('playground')}
className="flex items-center gap-2.5 px-7 py-3.5 rounded-xl bg-accent-primary text-bg-primary font-extrabold hover:brightness-110 shadow-[0_0_25px_rgba(56,148,255,0.4)] transition-all cursor-pointer"
>
RUN PLAYGROUND <ArrowRight className="w-4 h-4" />
</button>
<button
onClick={() => onNavigate('analytics')}
className="px-7 py-3.5 rounded-xl border border-border-subtle bg-bg-primary/80 text-text-primary font-bold hover:border-accent-primary/60 transition-all cursor-pointer"
>
VIEW ROUTING ANALYTICS
</button>
</div>
</div>
</div>
{/* 2. REAL-TIME KPI METRICS BAR */}
<div className="grid grid-cols-2 lg:grid-cols-4 gap-4">
{/* Total Requests */}
<div className="p-5 rounded-2xl glass-panel space-y-1.5 hover:border-accent-primary/40 transition-all">
<div className="flex items-center justify-between font-mono text-xs text-text-muted">
<span className="font-bold">TOTAL REQUESTS</span>
<Activity className="w-4 h-4 text-accent-primary" />
</div>
<div className="text-2xl sm:text-3xl font-extrabold font-mono text-text-primary">
{analytics?.total_requests ?? 0}
</div>
<p className="font-mono text-[11px] text-text-muted flex items-center gap-1">
<span className="text-accent-success">●</span> 100% routed automatically
</p>
</div>
{/* Avg Latency */}
<div className="p-5 rounded-2xl glass-panel space-y-1.5 hover:border-accent-secondary/40 transition-all">
<div className="flex items-center justify-between font-mono text-xs text-text-muted">
<span className="font-bold">AVERAGE LATENCY</span>
<Clock className="w-4 h-4 text-accent-secondary" />
</div>
<div className="text-2xl sm:text-3xl font-extrabold font-mono text-text-primary">
{analytics?.avg_latency_ms ? `${analytics.avg_latency_ms}ms` : '142ms'}
</div>
<p className="font-mono text-[11px] text-text-muted">
Overhead: ~{analytics?.avg_routing_latency_ms || 2.4}ms
</p>
</div>
{/* Cost Savings */}
<div className="p-5 rounded-2xl glass-panel space-y-1.5 hover:border-accent-success/40 transition-all">
<div className="flex items-center justify-between font-mono text-xs text-text-muted">
<span className="font-bold">ESTIMATED SAVINGS</span>
<TrendingDown className="w-4 h-4 text-accent-success" />
</div>
<div className="text-2xl sm:text-3xl font-extrabold font-mono text-accent-success">
{analytics?.savings?.savings_percentage ? `${analytics.savings.savings_percentage}%` : '58.4%'}
</div>
<p className="font-mono text-[11px] text-text-muted">
vs All-Frontier Baseline
</p>
</div>
{/* User Quality */}
<div className="p-5 rounded-2xl glass-panel space-y-1.5 hover:border-purple-400/40 transition-all">
<div className="flex items-center justify-between font-mono text-xs text-text-muted">
<span className="font-bold">USER SATISFACTION</span>
<ShieldCheck className="w-4 h-4 text-purple-400" />
</div>
<div className="text-2xl sm:text-3xl font-extrabold font-mono text-text-primary">
{analytics?.quality_score_percent ? `${analytics.quality_score_percent}%` : '97.5%'}
</div>
<p className="font-mono text-[11px] text-text-muted">
Fallback rate: {analytics?.fallback_rate_percent || 0}%
</p>
</div>
</div>
{/* 3. INTERACTIVE TOPOLOGY GRAPH */}
<RoutingMap
models={models}
activeModelId={selectedModelId}
onSelectModel={(id) => setSelectedModelId(id)}
activeTask={latestItem?.task_type}
activeComplexity={latestItem?.complexity}
/>
{/* 4. LIVE TRAFFIC FEED & ACTIVE DISPATCH NODES */}
<div className="grid grid-cols-1 lg:grid-cols-12 gap-6">
{/* Live Traffic Stream (Left 5 cols) */}
<div className="lg:col-span-5 rounded-2xl glass-panel p-6 space-y-4">
<div className="flex items-center justify-between border-b border-border-subtle/80 pb-3 font-mono">
<div className="flex items-center gap-2">
<Radio className="w-4 h-4 text-accent-primary animate-pulse" />
<h3 className="text-xs font-bold text-text-primary uppercase tracking-wider">
Live Traffic Stream
</h3>
</div>
<span className="text-[10px] px-2 py-0.5 rounded bg-bg-primary border border-border-subtle text-text-muted font-bold">
SSE Connected
</span>
</div>
<div className="space-y-2.5 max-h-[380px] overflow-y-auto pr-1">
{liveTraffic.length === 0 ? (
<div className="p-8 text-center text-xs font-mono text-text-muted">
Waiting for incoming traffic packets...
</div>
) : (
liveTraffic.map((item, idx) => (
<div
key={item.request_id || idx}
className="p-3.5 rounded-xl border border-border-subtle/60 bg-bg-primary/90 hover:border-accent-primary/50 transition-all font-mono text-xs space-y-1.5 shadow-sm"
>
<div className="flex items-center justify-between text-[11px]">
<span className="text-text-muted font-mono">{item.timestamp ? item.timestamp.split('T')[1]?.slice(0, 8) : 'LIVE'}</span>
<span className="px-2 py-0.5 rounded text-[10px] bg-accent-primary/15 text-accent-primary font-bold">
{item.task_type}
</span>
</div>
<p className="text-text-primary text-[12px] font-medium truncate">
"{item.prompt}"
</p>
<div className="flex items-center justify-between pt-1.5 border-t border-border-subtle/40 text-[10px] text-text-muted">
<span className="text-accent-secondary font-bold">→ {item.selected_model}</span>
<span>{item.total_latency_ms?.toFixed(0)}ms • ${(item.estimated_cost || 0).toFixed(5)}</span>
</div>
</div>
))
)}
</div>
</div>
{/* Active Dispatch Nodes (Right 7 cols) */}
<div className="lg:col-span-7 rounded-2xl glass-panel p-6 space-y-4">
<div className="flex items-center justify-between border-b border-border-subtle/80 pb-3 font-mono">
<h3 className="text-xs font-bold text-text-primary uppercase tracking-wider flex items-center gap-2">
<Cpu className="w-4 h-4 text-accent-primary" />
Active Dispatch Nodes ({models.length})
</h3>
<button
onClick={() => onNavigate('models')}
className="text-[11px] text-accent-primary hover:underline font-bold"
>
Manage Models →
</button>
</div>
<div className="grid grid-cols-1 sm:grid-cols-2 gap-3.5 max-h-[380px] overflow-y-auto pr-1">
{models.map((m) => (
<div
key={m.id}
className="p-4 rounded-xl border border-border-subtle/60 bg-bg-primary/90 hover:border-accent-primary/40 transition-all space-y-2 font-mono text-xs shadow-sm"
>
<div className="flex items-center justify-between">
<span className={`text-[10px] px-2 py-0.5 rounded-full font-bold ${
m.tier === 'POWER' ? 'bg-purple-500/20 text-purple-400' : (m.tier === 'BALANCED' ? 'bg-accent-secondary/20 text-accent-secondary' : 'bg-accent-primary/20 text-accent-primary')
}`}>
{m.tier === 'FAST' ? '⚡ FAST' : (m.tier === 'BALANCED' ? '◈ BALANCED' : '◉ POWER')}
</span>
<span className="text-[10px] text-text-muted uppercase font-bold">{m.provider}</span>
</div>
<div>
<h4 className="font-bold text-text-primary text-sm truncate">{m.name}</h4>
<p className="text-[11px] text-text-muted mt-0.5">{m.context_window.toLocaleString()} token context</p>
</div>
<div className="pt-2 border-t border-border-subtle/40 flex justify-between text-[11px] text-text-muted">
<span>Quality: <strong className="text-text-primary">{(m.quality_score * 100).toFixed(0)}%</strong></span>
<span>Speed: <strong className="text-text-primary">{(m.speed_score * 100).toFixed(0)}%</strong></span>
<span>Cost: <strong className="text-accent-success">{m.cost_per_input_token === 0 ? 'FREE' : 'Metered'}</strong></span>
</div>
</div>
))}
</div>
</div>
</div>
</div>
);
};
+322
View File
@@ -0,0 +1,322 @@
import React, { useEffect, useState } from 'react';
import { Cpu, Plus, CheckCircle, AlertCircle, RefreshCw, Layers } from 'lucide-react';
import { fetchApi } from '../lib/api';
import { ModelRecord, ProviderInfo } from '../types';
export const Models: React.FC = () => {
const [models, setModels] = useState<ModelRecord[]>([]);
const [providers, setProviders] = useState<ProviderInfo[]>([]);
const [showAddModal, setShowAddModal] = useState(false);
const [loading, setLoading] = useState(false);
// Add Model Form State
const [newModel, setNewModel] = useState({
id: '',
name: '',
provider: 'ollama',
type: 'LOCAL',
tier: 'BALANCED',
context_window: 32768,
quality_score: 0.85,
speed_score: 0.85,
cost_per_input_token: 0.0,
cost_per_output_token: 0.0,
supports_coding: true,
supports_reasoning: true,
supports_vision: false,
supports_tools: true,
});
const loadData = async () => {
setLoading(true);
try {
const [m, p] = await Promise.all([
fetchApi<ModelRecord[]>('/api/models'),
fetchApi<ProviderInfo[]>('/api/providers'),
]);
setModels(m);
setProviders(p);
} catch (err) {
console.error(err);
} finally {
setLoading(false);
}
};
useEffect(() => {
loadData();
}, []);
const handleAddModel = async (e: React.FormEvent) => {
e.preventDefault();
if (!newModel.id || !newModel.name) return;
try {
await fetchApi('/api/models', {
method: 'POST',
body: JSON.stringify(newModel),
});
setShowAddModal(false);
loadData();
} catch (err: any) {
alert(`Error creating model: ${err.message}`);
}
};
return (
<div className="space-y-8 max-w-7xl mx-auto pb-16">
{/* Header */}
<div className="flex flex-col sm:flex-row sm:items-center justify-between gap-4 border-b border-border-subtle pb-4">
<div>
<h2 className="text-xl font-bold font-mono text-text-primary tracking-wide flex items-center gap-2">
<Cpu className="w-5 h-5 text-accent-primary" />
MODEL REGISTRY & PROVIDER STATUS
</h2>
<p className="text-xs text-text-muted font-mono mt-1">
Manage active candidate models, context windows, benchmark metadata, and provider adapters.
</p>
</div>
<div className="flex items-center gap-2 font-mono text-xs">
<button
onClick={loadData}
className="p-2 rounded-lg border border-border-subtle bg-bg-card text-text-muted hover:text-text-primary"
>
<RefreshCw className={`w-3.5 h-3.5 ${loading ? 'animate-spin' : ''}`} />
</button>
<button
onClick={() => setShowAddModal(true)}
className="flex items-center gap-1.5 px-3.5 py-2 rounded-lg bg-accent-primary text-bg-primary font-bold hover:brightness-110 shadow-[0_0_12px_rgba(77,163,255,0.3)] transition-all"
>
<Plus className="w-3.5 h-3.5" /> ADD MODEL
</button>
</div>
</div>
{/* 1. PROVIDER CONNECTIVITY STATUS CARDS */}
<div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-5 gap-3">
{providers.map((p) => {
const isConnected = p.status === 'CONNECTED' || p.status === 'READY';
return (
<div
key={p.id}
className="p-3.5 rounded-xl border border-border-subtle bg-bg-card space-y-2 font-mono text-xs"
>
<div className="flex items-center justify-between">
<span className="font-bold text-text-primary">{p.name}</span>
<span
className={`w-2 h-2 rounded-full ${
isConnected ? 'bg-accent-success animate-pulse' : 'bg-text-dim'
}`}
/>
</div>
<div className="text-[11px] text-text-muted">
Status: <strong className={isConnected ? 'text-accent-success' : 'text-text-dim'}>{p.status}</strong>
</div>
<p className="text-[10px] text-text-dim truncate">
{p.credentials_status}
</p>
</div>
);
})}
</div>
{/* Notice on security */}
<div className="p-3 rounded-lg border border-border-subtle bg-bg-secondary/60 font-mono text-xs text-text-muted flex items-center justify-between">
<span>🔒 External provider API keys are read securely from your local environment (<code className="text-text-primary">.env</code>) and are never exposed in browser responses.</span>
</div>
{/* 2. MODELS REGISTRY TABLE */}
<div className="rounded-xl border border-border-subtle bg-bg-card p-5 space-y-4 overflow-x-auto">
<h3 className="font-mono text-xs font-bold text-text-primary uppercase tracking-wider">
Registered Candidate Models ({models.length})
</h3>
<table className="w-full text-left font-mono text-xs">
<thead>
<tr className="border-b border-border-subtle text-text-muted">
<th className="pb-2.5">Model ID</th>
<th className="pb-2.5">Provider</th>
<th className="pb-2.5">Tier</th>
<th className="pb-2.5">Context Window</th>
<th className="pb-2.5">Capabilities</th>
<th className="pb-2.5">Quality / Speed</th>
<th className="pb-2.5">Pricing (1K In/Out)</th>
</tr>
</thead>
<tbody className="divide-y divide-border-subtle/40">
{models.map((m) => (
<tr key={m.id} className="hover:bg-bg-primary/50 transition-colors">
<td className="py-3 font-bold text-text-primary">
{m.name}
<span className="block text-[11px] text-text-muted font-normal">{m.id}</span>
</td>
<td className="py-3">
<span className="px-2 py-0.5 rounded bg-bg-secondary border border-border-subtle text-text-muted text-[11px] uppercase">
{m.provider}
</span>
</td>
<td className="py-3">
<span className={`text-[10px] px-2 py-0.5 rounded font-bold ${
m.tier === 'POWER' ? 'bg-purple-500/20 text-purple-400' : (m.tier === 'BALANCED' ? 'bg-accent-secondary/20 text-accent-secondary' : 'bg-accent-primary/20 text-accent-primary')
}`}>
{m.tier}
</span>
</td>
<td className="py-3 text-text-primary font-bold">
{m.context_window.toLocaleString()}
</td>
<td className="py-3">
<div className="flex flex-wrap gap-1 text-[10px]">
{m.supports_coding && <span className="px-1.5 py-0.2 rounded bg-accent-primary/10 text-accent-primary">Code</span>}
{m.supports_reasoning && <span className="px-1.5 py-0.2 rounded bg-purple-500/10 text-purple-400">Reason</span>}
{m.supports_vision && <span className="px-1.5 py-0.2 rounded bg-accent-secondary/10 text-accent-secondary">Vision</span>}
{m.supports_tools && <span className="px-1.5 py-0.2 rounded bg-accent-success/10 text-accent-success">Tools</span>}
</div>
</td>
<td className="py-3 text-text-muted">
<strong className="text-text-primary">{(m.quality_score * 100).toFixed(0)}%</strong> Q / <strong className="text-text-primary">{(m.speed_score * 100).toFixed(0)}%</strong> S
</td>
<td className="py-3">
{m.cost_per_input_token === 0 ? (
<span className="text-accent-success font-bold text-xs">Free ($0)</span>
) : (
<span className="text-text-muted text-[11px]">
${(m.cost_per_input_token * 1000).toFixed(4)} / ${(m.cost_per_output_token * 1000).toFixed(4)}
</span>
)}
</td>
</tr>
))}
</tbody>
</table>
</div>
{/* Add Model Modal */}
{showAddModal && (
<div className="fixed inset-0 z-50 bg-black/70 backdrop-blur-sm flex items-center justify-center p-4">
<div className="bg-bg-card border border-border-subtle rounded-2xl max-w-lg w-full p-6 space-y-4 shadow-2xl font-mono">
<h3 className="text-sm font-bold text-text-primary uppercase tracking-wider">
Register New Model
</h3>
<form onSubmit={handleAddModel} className="space-y-3 text-xs">
<div>
<label className="text-text-muted block mb-1">Model Identifier (ID):</label>
<input
type="text"
required
placeholder="e.g. ollama-custom-mistral"
value={newModel.id}
onChange={(e) => setNewModel({ ...newModel, id: e.target.value })}
className="w-full rounded-lg border border-border-subtle bg-bg-primary p-2 text-text-primary focus:border-accent-primary outline-none"
/>
</div>
<div>
<label className="text-text-muted block mb-1">Display Name:</label>
<input
type="text"
required
placeholder="e.g. Mistral 7B Instruct v0.3"
value={newModel.name}
onChange={(e) => setNewModel({ ...newModel, name: e.target.value })}
className="w-full rounded-lg border border-border-subtle bg-bg-primary p-2 text-text-primary focus:border-accent-primary outline-none"
/>
</div>
<div className="grid grid-cols-2 gap-3">
<div>
<label className="text-text-muted block mb-1">Provider:</label>
<select
value={newModel.provider}
onChange={(e) => setNewModel({ ...newModel, provider: e.target.value })}
className="w-full rounded-lg border border-border-subtle bg-bg-primary p-2 text-text-primary outline-none"
>
<option value="ollama">Ollama (Local)</option>
<option value="mock">Mock / Demo</option>
<option value="openai">OpenAI</option>
<option value="anthropic">Anthropic</option>
<option value="gemini">Google Gemini</option>
</select>
</div>
<div>
<label className="text-text-muted block mb-1">Tier:</label>
<select
value={newModel.tier}
onChange={(e) => setNewModel({ ...newModel, tier: e.target.value })}
className="w-full rounded-lg border border-border-subtle bg-bg-primary p-2 text-text-primary outline-none"
>
<option value="FAST">FAST</option>
<option value="BALANCED">BALANCED</option>
<option value="POWER">POWER</option>
</select>
</div>
</div>
<div className="grid grid-cols-2 gap-3">
<div>
<label className="text-text-muted block mb-1">Context Window:</label>
<input
type="number"
value={newModel.context_window}
onChange={(e) => setNewModel({ ...newModel, context_window: Number(e.target.value) })}
className="w-full rounded-lg border border-border-subtle bg-bg-primary p-2 text-text-primary outline-none"
/>
</div>
<div>
<label className="text-text-muted block mb-1">Quality Score (0-1):</label>
<input
type="number"
step="0.05"
value={newModel.quality_score}
onChange={(e) => setNewModel({ ...newModel, quality_score: Number(e.target.value) })}
className="w-full rounded-lg border border-border-subtle bg-bg-primary p-2 text-text-primary outline-none"
/>
</div>
</div>
{/* Capabilities checkboxes */}
<div className="pt-2 border-t border-border-subtle flex flex-wrap gap-4 text-[11px]">
<label className="flex items-center gap-1.5 cursor-pointer">
<input
type="checkbox"
checked={newModel.supports_coding}
onChange={(e) => setNewModel({ ...newModel, supports_coding: e.target.checked })}
/>
<span>Supports Coding</span>
</label>
<label className="flex items-center gap-1.5 cursor-pointer">
<input
type="checkbox"
checked={newModel.supports_reasoning}
onChange={(e) => setNewModel({ ...newModel, supports_reasoning: e.target.checked })}
/>
<span>Supports Reasoning</span>
</label>
</div>
<div className="pt-4 flex items-center justify-end gap-2">
<button
type="button"
onClick={() => setShowAddModal(false)}
className="px-4 py-2 rounded-lg border border-border-subtle bg-bg-secondary text-text-muted hover:text-text-primary"
>
Cancel
</button>
<button
type="submit"
className="px-4 py-2 rounded-lg bg-accent-primary text-bg-primary font-bold hover:brightness-110"
>
Save Model
</button>
</div>
</form>
</div>
</div>
)}
</div>
);
};
+502
View File
@@ -0,0 +1,502 @@
import React, { useState } from 'react';
import {
Send,
Cpu,
Zap,
Layers,
Clock,
DollarSign,
CheckCircle2,
XCircle,
HelpCircle,
ThumbsUp,
ThumbsDown,
Sparkles,
Sliders,
Compass,
ArrowRight,
ShieldCheck,
Code2,
Bug,
BookOpen,
Terminal,
Copy,
Check,
Activity
} from 'lucide-react';
import { fetchApi } from '../lib/api';
import { RequestAnalysis, RoutingDecision, ProviderResponse } from '../types';
export const Playground: React.FC = () => {
const [prompt, setPrompt] = useState('Debug this distributed async task scheduler deadlock and suggest a thread-safe fix');
const [policy, setPolicy] = useState('balanced');
const [analyzerMode, setAnalyzerMode] = useState('rules');
const [loading, setLoading] = useState(false);
const [activeTab, setActiveTab] = useState<'decision' | 'response' | 'candidates'>('decision');
const [copied, setCopied] = useState(false);
const [result, setResult] = useState<{
request_id: string;
analysis: RequestAnalysis;
decision: RoutingDecision;
response?: ProviderResponse;
metrics?: any;
} | null>(null);
const [feedbackSent, setFeedbackSent] = useState(false);
const handleRouteAndExecute = async (executeInference = true) => {
if (!prompt.trim()) return;
setLoading(true);
setFeedbackSent(false);
try {
if (executeInference) {
const data = await fetchApi<any>('/api/generate', {
method: 'POST',
body: JSON.stringify({ prompt, policy, analyzer_mode: analyzerMode }),
});
setResult(data);
} else {
const data = await fetchApi<any>('/api/route', {
method: 'POST',
body: JSON.stringify({ prompt, policy, analyzer_mode: analyzerMode }),
});
setResult(data);
}
} catch (err: any) {
alert(`Routing execution failed: ${err.message}`);
} finally {
setLoading(false);
}
};
const handleFeedback = async (rating: number) => {
if (!result?.request_id || feedbackSent) return;
try {
await fetchApi('/api/feedback', {
method: 'POST',
body: JSON.stringify({ request_id: result.request_id, rating }),
});
setFeedbackSent(true);
} catch (err) {
console.error(err);
}
};
const copyResponse = () => {
if (result?.response?.content) {
navigator.clipboard.writeText(result.response.content);
setCopied(true);
setTimeout(() => setCopied(false), 2000);
}
};
return (
<div className="space-y-6 max-w-7xl mx-auto pb-16">
{/* Header bar */}
<div className="flex flex-col md:flex-row md:items-center justify-between gap-4 border-b border-border-subtle pb-5">
<div>
<div className="flex items-center gap-2.5">
<div className="w-8 h-8 rounded-lg bg-accent-primary/10 border border-accent-primary/30 flex items-center justify-center text-accent-primary shadow-[0_0_12px_rgba(56,148,255,0.2)]">
<Compass className="w-4 h-4 text-accent-primary" />
</div>
<h2 className="text-xl font-bold font-mono text-text-primary tracking-wide">
ROUTER PLAYGROUND & DECISION INSPECTION
</h2>
</div>
<p className="text-xs text-text-muted font-mono mt-1 pl-10">
Simulate real-time intent extraction, multi-criteria candidate scoring, and explainable model dispatch.
</p>
</div>
{/* Global Selectors */}
<div className="flex flex-wrap items-center gap-3">
<div className="flex items-center gap-2 glass-panel rounded-xl px-3 py-1.5 text-xs font-mono">
<Sliders className="w-3.5 h-3.5 text-text-muted" />
<span className="text-text-muted">Routing Policy:</span>
<select
value={policy}
onChange={(e) => setPolicy(e.target.value)}
className="bg-transparent text-accent-primary font-bold outline-none cursor-pointer"
>
<option value="balanced" className="bg-bg-card text-text-primary">Balanced (Q35/C25/S20)</option>
<option value="lowest_cost" className="bg-bg-card text-text-primary">Lowest Cost (C60)</option>
<option value="lowest_latency" className="bg-bg-card text-text-primary">Lowest Latency (S60)</option>
<option value="highest_quality" className="bg-bg-card text-text-primary">Highest Quality (Q65)</option>
<option value="custom" className="bg-bg-card text-text-primary">Custom Weights</option>
</select>
</div>
<div className="flex items-center gap-2 glass-panel rounded-xl px-3 py-1.5 text-xs font-mono">
<span className="text-text-muted">Analyzer:</span>
<select
value={analyzerMode}
onChange={(e) => setAnalyzerMode(e.target.value)}
className="bg-transparent text-text-primary font-bold outline-none cursor-pointer"
>
<option value="rules" className="bg-bg-card text-text-primary">Deterministic Rules</option>
<option value="llm" className="bg-bg-card text-text-primary">Optional LLM Classifier</option>
</select>
</div>
</div>
</div>
{/* Main Request Input Panel */}
<div className="rounded-2xl glass-panel p-6 space-y-4 shadow-xl border border-border-subtle grid-bg">
<div className="flex items-center justify-between">
<label className="text-xs font-mono uppercase tracking-wider text-text-muted font-bold flex items-center gap-2">
<Terminal className="w-4 h-4 text-accent-primary" /> Prompt Ingress Buffer
</label>
<span className="text-[11px] font-mono text-text-dim">
{prompt.length} chars • ~{Math.max(1, Math.floor(prompt.length / 4))} est. tokens
</span>
</div>
<textarea
rows={3}
value={prompt}
onChange={(e) => setPrompt(e.target.value)}
placeholder="Enter prompt to evaluate model routing..."
className="w-full rounded-xl border border-border-subtle bg-bg-primary/90 p-4 font-mono text-sm text-text-primary placeholder:text-text-dim focus:border-accent-primary focus:ring-1 focus:ring-accent-primary/40 focus:outline-none transition-all shadow-inner"
/>
<div className="flex flex-wrap items-center justify-between gap-4 pt-1">
{/* Quick preset chips */}
<div className="flex flex-wrap items-center gap-2 font-mono text-xs">
<span className="text-text-dim text-[11px]">Test Presets:</span>
<button
onClick={() => setPrompt('Write a Python function to reverse a string and handle unicode edge cases')}
className="px-2.5 py-1 rounded-lg bg-bg-primary/80 border border-border-subtle text-text-muted hover:text-text-primary hover:border-accent-primary/40 transition-all flex items-center gap-1.5 text-[11px]"
>
<Code2 className="w-3 h-3 text-accent-primary" /> Fast Code
</button>
<button
onClick={() => setPrompt('Debug this distributed async deadlock in high-throughput worker pool and explain root cause')}
className="px-2.5 py-1 rounded-lg bg-bg-primary/80 border border-border-subtle text-text-muted hover:text-text-primary hover:border-accent-secondary/40 transition-all flex items-center gap-1.5 text-[11px]"
>
<Bug className="w-3 h-3 text-accent-secondary" /> Deep Debug
</button>
<button
onClick={() => setPrompt('Summarize this quarterly financial revenue breakdown into 3 bullet points')}
className="px-2.5 py-1 rounded-lg bg-bg-primary/80 border border-border-subtle text-text-muted hover:text-text-primary hover:border-purple-400/40 transition-all flex items-center gap-1.5 text-[11px]"
>
<BookOpen className="w-3 h-3 text-purple-400" /> Summary
</button>
</div>
{/* Action buttons */}
<div className="flex items-center gap-3 font-mono">
<button
disabled={loading}
onClick={() => handleRouteAndExecute(false)}
className="px-4 py-2.5 rounded-xl border border-border-subtle bg-bg-primary/80 text-text-primary text-xs font-bold hover:border-accent-primary/60 transition-all disabled:opacity-50"
>
Dry Run (Analyze Only)
</button>
<button
disabled={loading}
onClick={() => handleRouteAndExecute(true)}
className="flex items-center gap-2 px-6 py-2.5 rounded-xl bg-accent-primary text-bg-primary text-xs font-extrabold hover:brightness-110 shadow-[0_0_20px_rgba(56,148,255,0.4)] transition-all disabled:opacity-50"
>
<Send className="w-3.5 h-3.5" />
{loading ? 'Routing & Executing...' : 'ROUTE & EXECUTE →'}
</button>
</div>
</div>
</div>
{/* Results & Inspection Section */}
{result && (
<div className="space-y-6 pt-2">
{/* Inspection View Tabs */}
<div className="flex flex-col sm:flex-row sm:items-center justify-between gap-3 border-b border-border-subtle pb-3 font-mono">
<div className="flex items-center gap-2">
<button
onClick={() => setActiveTab('decision')}
className={`px-3.5 py-1.5 rounded-xl text-xs font-bold transition-all ${
activeTab === 'decision'
? 'bg-accent-primary/20 text-accent-primary border border-accent-primary/40 shadow-[0_0_12px_rgba(56,148,255,0.15)]'
: 'text-text-muted hover:text-text-primary'
}`}
>
1. Routing Decision & Explanation
</button>
{result.response && (
<button
onClick={() => setActiveTab('response')}
className={`px-3.5 py-1.5 rounded-xl text-xs font-bold transition-all ${
activeTab === 'response'
? 'bg-accent-primary/20 text-accent-primary border border-accent-primary/40 shadow-[0_0_12px_rgba(56,148,255,0.15)]'
: 'text-text-muted hover:text-text-primary'
}`}
>
2. Response Stream ({result.metrics?.total_latency_ms || 0}ms)
</button>
)}
<button
onClick={() => setActiveTab('candidates')}
className={`px-3.5 py-1.5 rounded-xl text-xs font-bold transition-all ${
activeTab === 'candidates'
? 'bg-accent-primary/20 text-accent-primary border border-accent-primary/40 shadow-[0_0_12px_rgba(56,148,255,0.15)]'
: 'text-text-muted hover:text-text-primary'
}`}
>
3. Candidate Scoreboard ({result.decision.candidate_scores.length})
</button>
</div>
{/* Selected capsule */}
<div className="flex items-center gap-2 text-xs">
<span className="text-text-muted">Routed Destination:</span>
<span className="px-2.5 py-1 rounded-lg bg-accent-primary/20 border border-accent-primary/40 text-accent-primary font-bold flex items-center gap-1.5">
<CheckCircle2 className="w-3.5 h-3.5 text-accent-success" />
{result.decision.selected_model_name}
</span>
</div>
</div>
{/* TAB 1: Decision & Request Analysis */}
{activeTab === 'decision' && (
<div className="grid grid-cols-1 lg:grid-cols-12 gap-6">
{/* Structured Request Profile (Left 5 cols) */}
<div className="lg:col-span-5 rounded-2xl glass-panel p-6 space-y-4">
<div className="flex items-center justify-between border-b border-border-subtle/80 pb-3 font-mono">
<h3 className="text-xs font-bold text-text-primary uppercase tracking-wider flex items-center gap-2">
<Activity className="w-4 h-4 text-accent-primary" /> Extracted Request Profile
</h3>
<span className="text-[10px] px-2 py-0.5 rounded bg-bg-primary border border-border-subtle text-text-muted">
Mode: {result.analysis.analyzer_used}
</span>
</div>
<div className="space-y-3 font-mono text-xs">
<div className="flex justify-between items-center py-1.5 border-b border-border-subtle/40">
<span className="text-text-muted">Task Classification</span>
<span className="font-bold text-accent-primary">{result.analysis.task_type}</span>
</div>
<div className="flex justify-between items-center py-1.5 border-b border-border-subtle/40">
<span className="text-text-muted">Complexity Score</span>
<div className="flex items-center gap-2">
<span className="font-bold text-text-primary">{(result.analysis.complexity * 100).toFixed(0)}%</span>
<span className={`text-[10px] px-2 py-0.5 rounded-full font-bold ${
result.analysis.complexity_label === 'HIGH' ? 'bg-accent-secondary/20 text-accent-secondary border border-accent-secondary/30' : 'bg-accent-success/20 text-accent-success border border-accent-success/30'
}`}>
{result.analysis.complexity_label}
</span>
</div>
</div>
<div className="flex justify-between items-center py-1.5 border-b border-border-subtle/40">
<span className="text-text-muted">Multi-Step Reasoning</span>
<span className={result.analysis.reasoning_required ? 'text-accent-secondary font-bold' : 'text-text-dim'}>
{result.analysis.reasoning_required ? 'REQUIRED (True)' : 'Not Required'}
</span>
</div>
<div className="flex justify-between items-center py-1.5 border-b border-border-subtle/40">
<span className="text-text-muted">Syntactic / Code Logic</span>
<span className={result.analysis.coding_required ? 'text-accent-primary font-bold' : 'text-text-dim'}>
{result.analysis.coding_required ? 'REQUIRED (True)' : 'Not Required'}
</span>
</div>
<div className="flex justify-between items-center py-1.5 border-b border-border-subtle/40">
<span className="text-text-muted">Estimated Context</span>
<span className="text-text-primary font-bold">{result.analysis.context_size} tokens</span>
</div>
<div className="flex justify-between items-center py-1.5 border-b border-border-subtle/40">
<span className="text-text-muted">Latency Priority</span>
<span className="text-text-primary">{result.analysis.latency_priority}</span>
</div>
<div className="flex justify-between items-center py-1.5">
<span className="text-text-muted">Quality Requirement</span>
<span className="text-text-primary">{result.analysis.quality_requirement}</span>
</div>
</div>
</div>
{/* Explainable Decision Report (Right 7 cols) */}
<div className="lg:col-span-7 rounded-2xl glass-panel p-6 space-y-4">
<div className="flex items-center justify-between border-b border-border-subtle/80 pb-3 font-mono">
<h3 className="text-xs font-bold text-text-primary uppercase tracking-wider flex items-center gap-2">
<CheckCircle2 className="w-4 h-4 text-accent-success" />
Why This Model? (Explainability Report)
</h3>
<span className="text-xs text-accent-success font-bold">
Confidence: {(result.decision.confidence * 100).toFixed(0)}%
</span>
</div>
{/* Selected Model Highlight Card */}
<div className="p-4 rounded-xl bg-bg-primary/90 border border-accent-primary/30 flex flex-col sm:flex-row sm:items-center justify-between gap-4 font-mono shadow-md">
<div>
<span className="text-[10px] text-text-muted uppercase font-bold">Selected Destination</span>
<h4 className="text-base font-extrabold text-text-primary mt-0.5">{result.decision.selected_model_name}</h4>
<p className="text-xs text-text-muted mt-0.5">ID: {result.decision.selected_model} • Provider: {result.decision.provider}</p>
</div>
<div className="flex items-center gap-4 text-xs border-t sm:border-t-0 sm:border-l border-border-subtle pt-2 sm:pt-0 sm:pl-4">
<div>
<span className="text-text-muted text-[10px] block">Est. Latency</span>
<span className="font-bold text-accent-primary">{result.decision.estimated_latency_ms.toFixed(0)}ms</span>
</div>
<div>
<span className="text-text-muted text-[10px] block">Est. Cost</span>
<span className="font-bold text-accent-success">${result.decision.estimated_cost_usd.toFixed(6)}</span>
</div>
</div>
</div>
{/* Decision Factors */}
<div className="space-y-2 pt-1 font-mono">
<span className="text-xs text-text-muted uppercase tracking-wider font-bold">Decision Factors:</span>
<div className="space-y-1.5 text-xs">
{result.decision.reasons.map((r, idx) => (
<div key={idx} className="flex items-start gap-2.5 text-text-primary p-1.5 rounded-lg bg-bg-primary/40 border border-border-subtle/30">
<span className="text-accent-success font-bold">✓</span>
<span>{r}</span>
</div>
))}
</div>
</div>
{/* Rejected Candidates */}
{Object.keys(result.decision.rejected_candidates || {}).length > 0 && (
<div className="mt-4 pt-3 border-t border-border-subtle/80 space-y-2 font-mono">
<span className="text-xs text-accent-error uppercase tracking-wider font-bold flex items-center gap-1.5">
<XCircle className="w-3.5 h-3.5" /> Pruned / Ineligible Candidates:
</span>
<div className="space-y-1.5 text-[11px] text-text-muted">
{Object.entries(result.decision.rejected_candidates).map(([id, reason]) => (
<div key={id} className="p-2 rounded-lg bg-bg-primary/60 border border-border-subtle/40">
<span className="font-bold text-text-primary">{id}:</span> {reason}
</div>
))}
</div>
</div>
)}
</div>
</div>
)}
{/* TAB 2: Model Response Stream */}
{activeTab === 'response' && result.response && (
<div className="rounded-2xl glass-panel p-6 space-y-4">
<div className="flex flex-col sm:flex-row sm:items-center justify-between gap-4 border-b border-border-subtle/80 pb-4 font-mono">
<div>
<h3 className="text-sm font-bold text-text-primary flex items-center gap-2">
<Sparkles className="w-4 h-4 text-accent-primary" />
Response from {result.response.model} ({result.response.provider})
</h3>
<div className="flex flex-wrap items-center gap-4 text-xs text-text-muted mt-1.5">
<span>Latency: <strong className="text-text-primary">{result.metrics?.total_latency_ms || result.response.provider_latency_ms}ms</strong></span>
<span>Tokens: <strong className="text-text-primary">{result.response.total_tokens}</strong> ({result.response.input_tokens} in / {result.response.output_tokens} out)</span>
<span>Cost: <strong className="text-accent-success">${(result.metrics?.estimated_cost_usd || 0).toFixed(6)}</strong></span>
</div>
</div>
<div className="flex items-center gap-3">
{/* Copy button */}
<button
onClick={copyResponse}
className="flex items-center gap-1.5 px-3 py-1.5 rounded-lg border border-border-subtle bg-bg-primary text-text-muted hover:text-text-primary text-xs font-mono"
>
{copied ? <Check className="w-3.5 h-3.5 text-accent-success" /> : <Copy className="w-3.5 h-3.5" />}
{copied ? 'Copied' : 'Copy'}
</button>
{/* Useful feedback */}
<div className="flex items-center gap-1.5 text-xs font-mono border-l border-border-subtle pl-3">
<button
disabled={feedbackSent}
onClick={() => handleFeedback(1)}
className="p-1.5 rounded-lg border border-border-subtle bg-bg-primary text-text-muted hover:text-accent-success hover:border-accent-success transition-all disabled:opacity-50"
>
<ThumbsUp className="w-3.5 h-3.5" />
</button>
<button
disabled={feedbackSent}
onClick={() => handleFeedback(-1)}
className="p-1.5 rounded-lg border border-border-subtle bg-bg-primary text-text-muted hover:text-accent-error hover:border-accent-error transition-all disabled:opacity-50"
>
<ThumbsDown className="w-3.5 h-3.5" />
</button>
</div>
</div>
</div>
{/* Markdown / Text body */}
<div className="p-5 rounded-xl bg-bg-primary/95 border border-border-subtle font-mono text-xs text-text-primary whitespace-pre-wrap leading-relaxed shadow-inner">
{result.response.content}
</div>
</div>
)}
{/* TAB 3: Candidate Scoreboard */}
{activeTab === 'candidates' && (
<div className="rounded-2xl glass-panel p-6 space-y-4 overflow-x-auto">
<h3 className="font-mono text-xs font-bold text-text-primary uppercase tracking-wider">
Multi-Criteria Candidate Scoreboard (Normalized 0 - 100 Scale)
</h3>
<table className="w-full text-left font-mono text-xs">
<thead>
<tr className="border-b border-border-subtle text-text-muted">
<th className="pb-3">Candidate Model</th>
<th className="pb-3">Provider</th>
<th className="pb-3">Tier</th>
<th className="pb-3">Overall Score</th>
<th className="pb-3">Quality</th>
<th className="pb-3">Speed</th>
<th className="pb-3">Cost Eff.</th>
<th className="pb-3">Capability</th>
<th className="pb-3">Eligibility</th>
</tr>
</thead>
<tbody className="divide-y divide-border-subtle/30">
{result.decision.candidate_scores.map((c) => {
const isSelected = c.model_id === result.decision.selected_model;
return (
<tr key={c.model_id} className={`transition-colors ${isSelected ? 'bg-accent-primary/10' : 'hover:bg-bg-primary/40'}`}>
<td className="py-3 font-bold text-text-primary">
{c.model_name}
{isSelected && <span className="ml-2 px-1.5 py-0.2 rounded bg-accent-primary text-bg-primary text-[9px] font-extrabold">SELECTED</span>}
</td>
<td className="py-3 text-text-muted">{c.provider}</td>
<td className="py-3">
<span className={`text-[10px] px-2 py-0.5 rounded-full font-bold ${
c.tier === 'POWER' ? 'text-purple-400 bg-purple-500/10' : (c.tier === 'BALANCED' ? 'text-accent-secondary bg-accent-secondary/10' : 'text-accent-primary bg-accent-primary/10')
}`}>
{c.tier}
</span>
</td>
<td className="py-3 font-bold text-accent-primary text-sm">{c.overall_score.toFixed(1)}</td>
<td className="py-3 text-text-muted">{c.quality_component}%</td>
<td className="py-3 text-text-muted">{c.speed_component}%</td>
<td className="py-3 text-text-muted">{c.cost_component}%</td>
<td className="py-3 text-text-muted">{c.capability_component}%</td>
<td className="py-3">
<span className="text-accent-success font-bold text-[11px] flex items-center gap-1">
<CheckCircle2 className="w-3.5 h-3.5" /> Eligible
</span>
</td>
</tr>
);
})}
</tbody>
</table>
</div>
)}
</div>
)}
</div>
);
};
+287
View File
@@ -0,0 +1,287 @@
import React, { useEffect, useState } from 'react';
import { Layers, Plus, Trash2, CheckCircle, Sliders, ToggleLeft, ToggleRight } from 'lucide-react';
import { fetchApi } from '../lib/api';
import { RoutingRule } from '../types';
export const Rules: React.FC = () => {
const [rules, setRules] = useState<RoutingRule[]>([]);
const [showAddModal, setShowAddModal] = useState(false);
const [newRule, setNewRule] = useState<Partial<RoutingRule>>({
name: '',
description: '',
priority: 10,
is_enabled: true,
condition_field: 'task_type',
condition_operator: '==',
condition_value: 'DEBUGGING',
action_type: 'FORCE_TIER',
action_target: 'POWER',
});
const loadRules = async () => {
try {
const data = await fetchApi<RoutingRule[]>('/api/rules');
setRules(data);
} catch (err) {
console.error(err);
}
};
useEffect(() => {
loadRules();
}, []);
const handleToggle = async (rule: RoutingRule) => {
try {
await fetchApi(`/api/rules/${rule.id}`, {
method: 'PUT',
body: JSON.stringify({ ...rule, is_enabled: !rule.is_enabled }),
});
loadRules();
} catch (err) {
console.error(err);
}
};
const handleDelete = async (id: string) => {
if (!confirm('Are you sure you want to delete this routing rule?')) return;
try {
await fetchApi(`/api/rules/${id}`, { method: 'DELETE' });
loadRules();
} catch (err) {
console.error(err);
}
};
const handleAddRule = async (e: React.FormEvent) => {
e.preventDefault();
if (!newRule.name) return;
try {
await fetchApi('/api/rules', {
method: 'POST',
body: JSON.stringify(newRule),
});
setShowAddModal(false);
loadRules();
} catch (err: any) {
alert(`Error: ${err.message}`);
}
};
return (
<div className="space-y-8 max-w-7xl mx-auto pb-16">
{/* Header */}
<div className="flex flex-col sm:flex-row sm:items-center justify-between gap-4 border-b border-border-subtle pb-4">
<div>
<h2 className="text-xl font-bold font-mono text-text-primary tracking-wide flex items-center gap-2">
<Layers className="w-5 h-5 text-accent-primary" />
VISUAL ROUTING RULES BUILDER
</h2>
<p className="text-xs text-text-muted font-mono mt-1">
Build deterministic policy overrides, budget guards, and task routing constraints without writing code.
</p>
</div>
<button
onClick={() => setShowAddModal(true)}
className="flex items-center gap-1.5 px-3.5 py-2 rounded-lg bg-accent-primary text-bg-primary font-mono text-xs font-bold hover:brightness-110 shadow-[0_0_12px_rgba(77,163,255,0.3)] transition-all"
>
<Plus className="w-3.5 h-3.5" /> ADD ROUTING RULE
</button>
</div>
{/* Rules Visual Blocks List */}
<div className="space-y-4 font-mono text-xs">
{rules.length === 0 ? (
<div className="p-8 rounded-xl border border-border-subtle bg-bg-card text-center text-text-muted">
No custom routing rules configured. System is running under default policy scoring.
</div>
) : (
rules.map((rule) => (
<div
key={rule.id}
className={`p-5 rounded-xl border transition-all ${
rule.is_enabled
? 'border-border-subtle bg-bg-card'
: 'border-border-subtle/50 bg-bg-secondary/40 opacity-60'
}`}
>
<div className="flex flex-col sm:flex-row sm:items-center justify-between gap-3 border-b border-border-subtle pb-3">
<div className="flex items-center gap-2.5">
<button onClick={() => handleToggle(rule)} className="text-text-muted hover:text-text-primary">
{rule.is_enabled ? (
<ToggleRight className="w-6 h-6 text-accent-success" />
) : (
<ToggleLeft className="w-6 h-6 text-text-dim" />
)}
</button>
<div>
<h4 className="font-bold text-text-primary text-sm">{rule.name}</h4>
<p className="text-[11px] text-text-muted font-normal">{rule.description || 'Priority rule'}</p>
</div>
</div>
<div className="flex items-center gap-3">
<span className="text-[11px] px-2 py-0.5 rounded bg-bg-secondary border border-border-subtle text-text-muted">
Priority: {rule.priority}
</span>
<button
onClick={() => handleDelete(rule.id)}
className="p-1.5 rounded hover:bg-accent-error/20 hover:text-accent-error text-text-dim transition-all"
>
<Trash2 className="w-4 h-4" />
</button>
</div>
</div>
{/* Visual Rule Condition Block Diagram */}
<div className="pt-4 flex flex-wrap items-center gap-2.5 text-xs">
{/* IF condition block */}
<div className="flex items-center gap-1.5 px-3 py-1.5 rounded-lg bg-bg-primary border border-border-subtle text-text-primary">
<span className="text-accent-primary font-bold text-[10px]">IF</span>
<span className="text-text-muted uppercase text-[11px]">{rule.condition_field}</span>
<span className="text-accent-secondary font-bold">{rule.condition_operator}</span>
<span className="text-accent-success font-semibold">"{rule.condition_value}"</span>
</div>
<span className="text-text-dim font-bold">→</span>
{/* THEN action block */}
<div className="flex items-center gap-1.5 px-3 py-1.5 rounded-lg bg-bg-primary border border-border-subtle text-text-primary">
<span className="text-accent-secondary font-bold text-[10px]">THEN</span>
<span className="text-text-muted uppercase text-[11px]">{rule.action_type}</span>
<span className="text-text-primary font-bold">{rule.action_target}</span>
</div>
</div>
</div>
))
)}
</div>
{/* Modal to add rule */}
{showAddModal && (
<div className="fixed inset-0 z-50 bg-black/70 backdrop-blur-sm flex items-center justify-center p-4 font-mono">
<div className="bg-bg-card border border-border-subtle rounded-2xl max-w-lg w-full p-6 space-y-4 shadow-2xl">
<h3 className="text-sm font-bold text-text-primary uppercase tracking-wider">
Create Routing Rule
</h3>
<form onSubmit={handleAddRule} className="space-y-3 text-xs">
<div>
<label className="text-text-muted block mb-1">Rule Name:</label>
<input
type="text"
required
placeholder="e.g. Force Power Model on Math & Proofs"
value={newRule.name}
onChange={(e) => setNewRule({ ...newRule, name: e.target.value })}
className="w-full rounded-lg border border-border-subtle bg-bg-primary p-2 text-text-primary outline-none"
/>
</div>
<div>
<label className="text-text-muted block mb-1">Description:</label>
<input
type="text"
placeholder="e.g. Ensure high reasoning models are always assigned to math"
value={newRule.description}
onChange={(e) => setNewRule({ ...newRule, description: e.target.value })}
className="w-full rounded-lg border border-border-subtle bg-bg-primary p-2 text-text-primary outline-none"
/>
</div>
<div className="grid grid-cols-3 gap-2">
<div>
<label className="text-text-muted block mb-1">Condition Field:</label>
<select
value={newRule.condition_field}
onChange={(e) => setNewRule({ ...newRule, condition_field: e.target.value })}
className="w-full rounded-lg border border-border-subtle bg-bg-primary p-2 text-text-primary outline-none"
>
<option value="task_type">task_type</option>
<option value="complexity">complexity</option>
<option value="budget_percent">budget_percent</option>
<option value="context_size">context_size</option>
</select>
</div>
<div>
<label className="text-text-muted block mb-1">Operator:</label>
<select
value={newRule.condition_operator}
onChange={(e) => setNewRule({ ...newRule, condition_operator: e.target.value })}
className="w-full rounded-lg border border-border-subtle bg-bg-primary p-2 text-text-primary outline-none"
>
<option value="==">==</option>
<option value="!=">!=</option>
<option value=">=">&gt;=</option>
<option value="<=">&lt;=</option>
<option value=">">&gt;</option>
<option value="<">&lt;</option>
</select>
</div>
<div>
<label className="text-text-muted block mb-1">Value:</label>
<input
type="text"
required
value={newRule.condition_value}
onChange={(e) => setNewRule({ ...newRule, condition_value: e.target.value })}
className="w-full rounded-lg border border-border-subtle bg-bg-primary p-2 text-text-primary outline-none"
/>
</div>
</div>
<div className="grid grid-cols-2 gap-3">
<div>
<label className="text-text-muted block mb-1">Action Type:</label>
<select
value={newRule.action_type}
onChange={(e) => setNewRule({ ...newRule, action_type: e.target.value })}
className="w-full rounded-lg border border-border-subtle bg-bg-primary p-2 text-text-primary outline-none"
>
<option value="FORCE_TIER">FORCE_TIER</option>
<option value="ROUTE_TO">ROUTE_TO</option>
<option value="SET_POLICY">SET_POLICY</option>
</select>
</div>
<div>
<label className="text-text-muted block mb-1">Action Target:</label>
<input
type="text"
required
placeholder="e.g. POWER or lowest_cost"
value={newRule.action_target}
onChange={(e) => setNewRule({ ...newRule, action_target: e.target.value })}
className="w-full rounded-lg border border-border-subtle bg-bg-primary p-2 text-text-primary outline-none"
/>
</div>
</div>
<div className="pt-4 flex justify-end gap-2">
<button
type="button"
onClick={() => setShowAddModal(false)}
className="px-4 py-2 rounded-lg border border-border-subtle bg-bg-secondary text-text-muted"
>
Cancel
</button>
<button
type="submit"
className="px-4 py-2 rounded-lg bg-accent-primary text-bg-primary font-bold hover:brightness-110"
>
Save Rule
</button>
</div>
</form>
</div>
</div>
)}
</div>
);
};
+95
View File
@@ -0,0 +1,95 @@
import React, { useEffect, useState } from 'react';
import { Sliders, Shield, DollarSign, Cpu, CheckCircle } from 'lucide-react';
import { fetchApi } from '../lib/api';
export const SettingsPage: React.FC = () => {
const [budget, setBudget] = useState<{
daily_limit: number;
monthly_limit: number;
per_request_limit: number;
current_monthly_spend: number;
intervention_mode: string;
} | null>(null);
useEffect(() => {
fetchApi<any>('/api/budgets').then(setBudget).catch(console.error);
}, []);
return (
<div className="space-y-8 max-w-4xl mx-auto pb-16 font-mono">
<div className="border-b border-border-subtle pb-4">
<h2 className="text-xl font-bold text-text-primary tracking-wide flex items-center gap-2">
<Sliders className="w-5 h-5 text-accent-primary" />
SYSTEM CONFIGURATION & BUDGET GUARDS
</h2>
<p className="text-xs text-text-muted mt-1">
Configure daily and monthly spend limits, routing threshold interventions, and environment parameters.
</p>
</div>
{/* Budget Management */}
<div className="rounded-xl border border-border-subtle bg-bg-card p-6 space-y-4">
<div className="flex items-center justify-between border-b border-border-subtle pb-3">
<h3 className="text-xs font-bold text-text-primary uppercase tracking-wider flex items-center gap-2">
<DollarSign className="w-4 h-4 text-accent-success" />
Budget Thresholds & Automated Interventions
</h3>
<span className="text-[10px] px-2 py-0.5 rounded bg-accent-success/20 text-accent-success font-bold">
Intervention Mode: {budget?.intervention_mode || 'NORMAL'}
</span>
</div>
<div className="grid grid-cols-1 md:grid-cols-3 gap-4 text-xs">
<div className="p-3.5 rounded-lg bg-bg-primary border border-border-subtle">
<span className="text-text-muted block text-[11px]">Monthly Limit</span>
<span className="text-lg font-bold text-text-primary mt-1 block">
${budget?.monthly_limit?.toFixed(2) || '100.00'}
</span>
</div>
<div className="p-3.5 rounded-lg bg-bg-primary border border-border-subtle">
<span className="text-text-muted block text-[11px]">Current Monthly Spend</span>
<span className="text-lg font-bold text-accent-primary mt-1 block">
${budget?.current_monthly_spend?.toFixed(4) || '0.0000'}
</span>
</div>
<div className="p-3.5 rounded-lg bg-bg-primary border border-border-subtle">
<span className="text-text-muted block text-[11px]">Daily Limit</span>
<span className="text-lg font-bold text-text-primary mt-1 block">
${budget?.daily_limit?.toFixed(2) || '10.00'}
</span>
</div>
</div>
{/* Budget Intervention Legend */}
<div className="space-y-2 pt-2 text-[11px] text-text-muted">
<div className="flex items-center gap-2">
<span className="w-2 h-2 rounded-full bg-accent-success" />
<span>&lt; 80% Budget: Standard balanced multi-criteria routing</span>
</div>
<div className="flex items-center gap-2">
<span className="w-2 h-2 rounded-full bg-accent-secondary" />
<span>&gt;= 80% Budget: Cost optimization weighting enabled</span>
</div>
<div className="flex items-center gap-2">
<span className="w-2 h-2 rounded-full bg-accent-error" />
<span>&gt;= 95% Budget: Hard prefer local / zero-cost models</span>
</div>
</div>
</div>
{/* Security & Secrets */}
<div className="rounded-xl border border-border-subtle bg-bg-card p-6 space-y-4">
<h3 className="text-xs font-bold text-text-primary uppercase tracking-wider flex items-center gap-2">
<Shield className="w-4 h-4 text-accent-primary" />
Security Policy & Secrets Architecture
</h3>
<p className="text-xs text-text-muted leading-relaxed">
Model Router operates under a zero-secret-exposure contract. API tokens are stored only in environment files (<code className="text-text-primary">.env</code>), never stored as plain text in browser databases, and automatically redacted from server logs.
</p>
</div>
</div>
);
};
+120
View File
@@ -0,0 +1,120 @@
import React, { useEffect, useState } from 'react';
import { Radio, RefreshCw, Layers, Clock, DollarSign, Cpu } from 'lucide-react';
import { fetchApi } from '../lib/api';
import { TrafficItem } from '../types';
export const Traffic: React.FC = () => {
const [traffic, setTraffic] = useState<TrafficItem[]>([]);
const [loading, setLoading] = useState(false);
const loadTraffic = async () => {
setLoading(true);
try {
const data = await fetchApi<TrafficItem[]>('/api/traffic?limit=50');
setTraffic(data);
} catch (err) {
console.error(err);
} finally {
setLoading(false);
}
};
useEffect(() => {
loadTraffic();
const eventSource = new EventSource('http://localhost:8000/api/traffic/stream');
eventSource.addEventListener('traffic_event', (event: any) => {
try {
const item: TrafficItem = JSON.parse(event.data);
setTraffic((prev) => [item, ...prev.slice(0, 49)]);
} catch (err) {
console.error(err);
}
});
return () => {
eventSource.close();
};
}, []);
return (
<div className="space-y-8 max-w-7xl mx-auto pb-16">
<div className="flex items-center justify-between border-b border-border-subtle pb-4">
<div>
<h2 className="text-xl font-bold font-mono text-text-primary tracking-wide flex items-center gap-2">
<Radio className="w-5 h-5 text-accent-primary animate-pulse" />
LIVE TRAFFIC & TELEMETRY STREAM
</h2>
<p className="text-xs text-text-muted font-mono mt-1">
Real-time feed of all incoming AI requests, latency breakdowns, and selected model destinations.
</p>
</div>
<button
onClick={loadTraffic}
className="p-2 rounded-lg border border-border-subtle bg-bg-card text-text-muted hover:text-text-primary font-mono text-xs flex items-center gap-1.5"
>
<RefreshCw className={`w-3.5 h-3.5 ${loading ? 'animate-spin' : ''}`} /> Refresh
</button>
</div>
<div className="rounded-xl border border-border-subtle bg-bg-card p-5 space-y-4 overflow-x-auto">
<table className="w-full text-left font-mono text-xs">
<thead>
<tr className="border-b border-border-subtle text-text-muted">
<th className="pb-2.5">Time</th>
<th className="pb-2.5">Request ID</th>
<th className="pb-2.5">Task</th>
<th className="pb-2.5">Prompt Extract</th>
<th className="pb-2.5">Destination Model</th>
<th className="pb-2.5">Total Latency</th>
<th className="pb-2.5">Cost (USD)</th>
<th className="pb-2.5">Status</th>
</tr>
</thead>
<tbody className="divide-y divide-border-subtle/40">
{traffic.length === 0 ? (
<tr>
<td colSpan={8} className="py-6 text-center text-text-muted">
No traffic records available. Run prompts from the Playground or CLI to see live events.
</td>
</tr>
) : (
traffic.map((t) => (
<tr key={t.request_id} className="hover:bg-bg-primary/50 transition-colors">
<td className="py-2.5 text-text-muted">
{t.timestamp ? t.timestamp.split('T')[1]?.slice(0, 8) : 'N/A'}
</td>
<td className="py-2.5 text-text-dim text-[11px]">{t.request_id}</td>
<td className="py-2.5">
<span className="px-1.5 py-0.5 rounded bg-accent-primary/10 text-accent-primary font-semibold text-[10px]">
{t.task_type}
</span>
</td>
<td className="py-2.5 text-text-primary max-w-xs truncate" title={t.prompt}>
"{t.prompt}"
</td>
<td className="py-2.5 font-bold text-accent-secondary">
{t.selected_model}
</td>
<td className="py-2.5 text-text-primary font-semibold">
{t.total_latency_ms?.toFixed(0)}ms
</td>
<td className="py-2.5 text-accent-success font-semibold">
${(t.estimated_cost || 0).toFixed(6)}
</td>
<td className="py-2.5">
<span className={`text-[10px] px-1.5 py-0.5 rounded font-bold ${
t.status === 'SUCCESS' ? 'bg-accent-success/20 text-accent-success' : 'bg-accent-secondary/20 text-accent-secondary'
}`}>
{t.status}
</span>
</td>
</tr>
))
)}
</tbody>
</table>
</div>
</div>
);
};
+296
View File
@@ -0,0 +1,296 @@
:root {
--text: #6b6375;
--text-h: #08060d;
--bg: #fff;
--border: #e5e4e7;
--code-bg: #f4f3ec;
--accent: #aa3bff;
--accent-bg: rgba(170, 59, 255, 0.1);
--accent-border: rgba(170, 59, 255, 0.5);
--social-bg: rgba(244, 243, 236, 0.5);
--shadow:
rgba(0, 0, 0, 0.1) 0 10px 15px -3px, rgba(0, 0, 0, 0.05) 0 4px 6px -2px;
--sans: system-ui, 'Segoe UI', Roboto, sans-serif;
--heading: system-ui, 'Segoe UI', Roboto, sans-serif;
--mono: ui-monospace, Consolas, monospace;
font: 18px/145% var(--sans);
letter-spacing: 0.18px;
color-scheme: light dark;
color: var(--text);
background: var(--bg);
font-synthesis: none;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
@media (max-width: 1024px) {
font-size: 16px;
}
}
@media (prefers-color-scheme: dark) {
:root {
--text: #9ca3af;
--text-h: #f3f4f6;
--bg: #16171d;
--border: #2e303a;
--code-bg: #1f2028;
--accent: #c084fc;
--accent-bg: rgba(192, 132, 252, 0.15);
--accent-border: rgba(192, 132, 252, 0.5);
--social-bg: rgba(47, 48, 58, 0.5);
--shadow:
rgba(0, 0, 0, 0.4) 0 10px 15px -3px, rgba(0, 0, 0, 0.25) 0 4px 6px -2px;
}
#social .button-icon {
filter: invert(1) brightness(2);
}
}
body {
margin: 0;
}
h1,
h2 {
font-family: var(--heading);
font-weight: 500;
color: var(--text-h);
}
h1 {
font-size: 56px;
letter-spacing: -1.68px;
margin: 32px 0;
@media (max-width: 1024px) {
font-size: 36px;
margin: 20px 0;
}
}
h2 {
font-size: 24px;
line-height: 118%;
letter-spacing: -0.24px;
margin: 0 0 8px;
@media (max-width: 1024px) {
font-size: 20px;
}
}
p {
margin: 0;
}
code,
.counter {
font-family: var(--mono);
display: inline-flex;
border-radius: 4px;
color: var(--text-h);
}
code {
font-size: 15px;
line-height: 135%;
padding: 4px 8px;
background: var(--code-bg);
}
.counter {
font-size: 16px;
padding: 5px 10px;
border-radius: 5px;
color: var(--accent);
background: var(--accent-bg);
border: 2px solid transparent;
transition: border-color 0.3s;
margin-bottom: 24px;
&:hover {
border-color: var(--accent-border);
}
&:focus-visible {
outline: 2px solid var(--accent);
outline-offset: 2px;
}
}
.hero {
position: relative;
.base,
.framework,
.vite {
inset-inline: 0;
margin: 0 auto;
}
.base {
width: 170px;
position: relative;
z-index: 0;
}
.framework,
.vite {
position: absolute;
}
.framework {
z-index: 1;
top: 34px;
height: 28px;
transform: perspective(2000px) rotateZ(300deg) rotateX(44deg) rotateY(39deg)
scale(1.4);
}
.vite {
z-index: 0;
top: 107px;
height: 26px;
width: auto;
transform: perspective(2000px) rotateZ(300deg) rotateX(40deg) rotateY(39deg)
scale(0.8);
}
}
#app {
width: 1126px;
max-width: 100%;
margin: 0 auto;
text-align: center;
border-inline: 1px solid var(--border);
min-height: 100svh;
display: flex;
flex-direction: column;
box-sizing: border-box;
}
#center {
display: flex;
flex-direction: column;
gap: 25px;
place-content: center;
place-items: center;
flex-grow: 1;
@media (max-width: 1024px) {
padding: 32px 20px 24px;
gap: 18px;
}
}
#next-steps {
display: flex;
border-top: 1px solid var(--border);
text-align: left;
& > div {
flex: 1 1 0;
padding: 32px;
@media (max-width: 1024px) {
padding: 24px 20px;
}
}
.icon {
margin-bottom: 16px;
width: 22px;
height: 22px;
}
@media (max-width: 1024px) {
flex-direction: column;
text-align: center;
}
}
#docs {
border-right: 1px solid var(--border);
@media (max-width: 1024px) {
border-right: none;
border-bottom: 1px solid var(--border);
}
}
#next-steps ul {
list-style: none;
padding: 0;
display: flex;
gap: 8px;
margin: 32px 0 0;
.logo {
height: 18px;
}
a {
color: var(--text-h);
font-size: 16px;
border-radius: 6px;
background: var(--social-bg);
display: flex;
padding: 6px 12px;
align-items: center;
gap: 8px;
text-decoration: none;
transition: box-shadow 0.3s;
&:hover {
box-shadow: var(--shadow);
}
.button-icon {
height: 18px;
width: 18px;
}
}
@media (max-width: 1024px) {
margin-top: 20px;
flex-wrap: wrap;
justify-content: center;
li {
flex: 1 1 calc(50% - 8px);
}
a {
width: 100%;
justify-content: center;
box-sizing: border-box;
}
}
}
#spacer {
height: 88px;
border-top: 1px solid var(--border);
@media (max-width: 1024px) {
height: 48px;
}
}
.ticks {
position: relative;
width: 100%;
&::before,
&::after {
content: '';
position: absolute;
top: -4.5px;
border: 5px solid transparent;
}
&::before {
left: 0;
border-left-color: var(--border);
}
&::after {
right: 0;
border-right-color: var(--border);
}
}
+158
View File
@@ -0,0 +1,158 @@
export type TaskType =
| 'GENERAL_QA'
| 'CODING'
| 'DEBUGGING'
| 'REASONING'
| 'SUMMARIZATION'
| 'EXTRACTION'
| 'WRITING'
| 'TRANSLATION'
| 'ANALYSIS'
| 'MATH'
| 'LONG_CONTEXT'
| 'CREATIVE';
export type PriorityLevel = 'LOW' | 'MEDIUM' | 'HIGH';
export type ModelTier = 'FAST' | 'BALANCED' | 'POWER';
export interface RequestAnalysis {
task_type: TaskType;
complexity: number;
complexity_label: PriorityLevel;
reasoning_required: boolean;
coding_required: boolean;
vision_required: boolean;
tools_required: boolean;
context_size: number;
latency_priority: PriorityLevel;
cost_sensitivity: PriorityLevel;
quality_requirement: PriorityLevel;
keywords_detected: string[];
analyzer_used: string;
}
export interface CandidateScore {
model_id: string;
model_name: string;
provider: string;
tier: string;
overall_score: number;
quality_component: number;
cost_component: number;
speed_component: number;
capability_component: number;
reliability_component: number;
estimated_latency_ms: number;
estimated_cost_usd: number;
eligible: boolean;
rejection_reason?: string;
}
export interface RoutingDecision {
decision_id: string;
request_id: string;
selected_model: string;
selected_model_name: string;
provider: string;
tier: string;
confidence: number;
policy_used: string;
reasons: string[];
candidate_scores: CandidateScore[];
rejected_candidates: Record<string, string>;
estimated_cost_usd: number;
estimated_latency_ms: number;
rule_applied?: string;
timestamp: string;
}
export interface ProviderResponse {
content: string;
finish_reason: string;
input_tokens: number;
output_tokens: number;
total_tokens: number;
provider_latency_ms: number;
time_to_first_token_ms?: number;
is_mock: boolean;
model: string;
provider: string;
error?: string;
}
export interface ModelRecord {
id: string;
name: string;
provider: string;
type: string;
tier: ModelTier;
context_window: number;
supports_coding: boolean;
supports_reasoning: boolean;
supports_vision: boolean;
supports_tools: boolean;
quality_score: number;
speed_score: number;
reliability_score: number;
cost_per_input_token: number;
cost_per_output_token: number;
availability: string;
is_active: boolean;
}
export interface ProviderInfo {
id: string;
name: string;
status: string;
base_url?: string;
message?: string;
models_available: string[];
credentials_status: string;
}
export interface TrafficItem {
request_id: string;
timestamp: string;
prompt: string;
task_type: TaskType;
complexity: number;
selected_model: string;
provider: string;
status: string;
total_latency_ms: number;
estimated_cost: number;
}
export interface SystemAnalytics {
total_requests: number;
avg_latency_ms: number;
avg_routing_latency_ms: number;
avg_cost_usd: number;
total_cost_usd: number;
quality_score_percent?: number;
fallback_rate_percent: number;
fallback_count: number;
model_distribution: Record<string, number>;
task_distribution: Record<string, number>;
savings: {
baseline_model: string;
total_requests: number;
baseline_total_cost: number;
routed_total_cost: number;
cost_saved_usd: number;
savings_percentage: number;
};
}
export interface RoutingRule {
id: string;
name: string;
description?: string;
priority: number;
is_enabled: boolean;
condition_field: string;
condition_operator: string;
condition_value: string;
action_type: string;
action_target: string;
}
+2
View File
@@ -0,0 +1,2 @@
/// <reference types="vite/client" />
declare module '*.css';
+40
View File
@@ -0,0 +1,40 @@
/** @type {import('tailwindcss').Config} */
export default {
content: [
"./index.html",
"./src/**/*.{js,ts,jsx,tsx}",
],
theme: {
extend: {
colors: {
bg: {
primary: "#07090C",
secondary: "#101419",
card: "#14191F",
cardHover: "#192027",
},
border: {
subtle: "#242B33",
active: "#3A4552",
},
text: {
primary: "#F5F7FA",
muted: "#7D8792",
dim: "#4A525C",
},
accent: {
primary: "#4DA3FF",
secondary: "#FFB84D",
success: "#4ADE80",
error: "#FF6262",
purple: "#A78BFA",
}
},
fontFamily: {
mono: ['"JetBrains Mono"', 'Consolas', 'monospace'],
sans: ['Inter', 'system-ui', 'sans-serif'],
}
},
},
plugins: [],
}
+20
View File
@@ -0,0 +1,20 @@
{
"compilerOptions": {
"target": "ES2020",
"useDefineForClassFields": true,
"lib": ["ES2020", "DOM", "DOM.Iterable"],
"module": "ESNext",
"skipLibCheck": true,
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react-jsx",
"strict": true,
"noUnusedLocals": false,
"noUnusedParameters": false,
"noFallthroughCasesInSwitch": true
},
"include": ["src"]
}