InferRoute / docs /index.html
Ypeng12's picture
feat: implement dynamic client-side sandbox playground for 9 academic routing models directly in static documentation page
20fb169
Raw
History Blame Contribute Delete
122 kB
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>InferRoute Academic & Technical Documentation Hub</title>
<!-- Outfit & Inter Fonts -->
<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@300;400;500;600;700&family=Outfit:wght@400;500;600;700;800&display=swap" rel="stylesheet">
<!-- MathJax for rendering LaTeX formulas -->
<script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js"></script>
<!-- Mermaid JS for sequence diagrams -->
<script src="https://cdn.jsdelivr.net/npm/mermaid/dist/mermaid.min.js"></script>
<script>
mermaid.initialize({
startOnLoad: true,
theme: 'default',
themeVariables: {
background: '#FFFFFF',
primaryColor: '#6D28D9',
primaryTextColor: '#0F172A',
lineColor: '#475569',
secondaryColor: '#047857',
tertiaryColor: '#D97706'
}
});
</script>
<style>
:root {
--bg-color: #F8FAFC;
--panel-bg: rgba(255, 255, 255, 0.9);
--border-color: rgba(0, 0, 0, 0.08);
--text-primary: #0F172A;
--text-secondary: #475569;
--accent-violet: #6D28D9;
--accent-violet-glow: rgba(109, 40, 217, 0.08);
--accent-emerald: #047857;
--accent-emerald-glow: rgba(4, 120, 87, 0.08);
--accent-amber: #F59E0B;
--accent-rose: #B91C1C;
--font-outfit: 'Outfit', sans-serif;
--font-inter: 'Inter', sans-serif;
}
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
background-color: var(--bg-color);
color: var(--text-primary);
font-family: var(--font-inter);
overflow-x: hidden;
line-height: 1.6;
}
/* ── Header ── */
header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 1.5rem 2rem;
background: rgba(255, 255, 255, 0.85);
backdrop-filter: blur(12px);
border-bottom: 1px solid var(--border-color);
position: sticky;
top: 0;
z-index: 100;
}
.logo-section {
display: flex;
align-items: center;
gap: 0.75rem;
}
.logo-text {
font-family: var(--font-outfit);
font-size: 1.6rem;
font-weight: 800;
background: linear-gradient(135deg, #0F172A 30%, var(--accent-violet) 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
.badge {
font-size: 0.75rem;
font-weight: 600;
padding: 0.25rem 0.75rem;
border-radius: 9999px;
background: var(--accent-violet-glow);
border: 1px solid rgba(109, 40, 217, 0.2);
color: var(--accent-violet);
letter-spacing: 0.5px;
}
.actions {
display: flex;
gap: 1rem;
}
.btn {
font-family: var(--font-inter);
font-size: 0.85rem;
font-weight: 500;
padding: 0.5rem 1rem;
border-radius: 8px;
cursor: pointer;
transition: all 0.3s;
text-decoration: none;
display: inline-flex;
align-items: center;
gap: 0.35rem;
}
.btn-outline {
background: transparent;
border: 1px solid var(--border-color);
color: var(--text-primary);
}
.btn-outline:hover {
background: rgba(0, 0, 0, 0.03);
border-color: rgba(0, 0, 0, 0.2);
}
.btn-primary {
background: var(--accent-violet);
border: 1px solid var(--accent-violet);
color: var(--text-primary);
box-shadow: 0 4px 12px rgba(139, 92, 246, 0.25);
}
.btn-primary:hover {
transform: translateY(-1px);
box-shadow: 0 6px 16px rgba(139, 92, 246, 0.35);
}
/* ── Container ── */
.container {
max-width: 1200px;
margin: 0 auto;
padding: 2.5rem 1.5rem;
}
/* ── Hero ── */
.hero {
text-align: center;
margin-bottom: 3.5rem;
}
.hero h1 {
font-family: var(--font-outfit);
font-size: 2.8rem;
font-weight: 800;
margin-bottom: 1rem;
letter-spacing: -1px;
line-height: 1.2;
background: linear-gradient(to right, #0F172A, var(--accent-violet));
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
.hero p {
font-size: 1.1rem;
color: var(--text-secondary);
max-width: 700px;
margin: 0 auto;
}
/* ── Navigation Tabs ── */
.tabs-nav {
display: grid;
grid-template-columns: repeat(5, 1fr);
gap: 0.75rem;
margin-bottom: 2.5rem;
background: rgba(0, 0, 0, 0.02);
border: 1px solid var(--border-color);
padding: 0.5rem;
border-radius: 12px;
}
.tab-btn {
font-family: var(--font-outfit);
background: transparent;
border: none;
color: var(--text-secondary);
padding: 0.75rem;
font-size: 0.95rem;
font-weight: 600;
border-radius: 8px;
cursor: pointer;
transition: all 0.3s;
display: flex;
justify-content: center;
align-items: center;
gap: 0.5rem;
}
.tab-btn:hover {
color: var(--text-primary);
background: rgba(0, 0, 0, 0.03);
}
.tab-btn.active {
color: var(--text-primary);
background: var(--accent-violet);
box-shadow: 0 4px 12px rgba(139, 92, 246, 0.2);
}
/* ── Content Layout ── */
.tab-content {
display: none;
animation: fadeIn 0.4s ease;
}
.tab-content.active {
display: block;
}
@keyframes fadeIn {
from { opacity: 0; transform: translateY(10px); }
to { opacity: 1; transform: translateY(0); }
}
/* ── Glass Cards ── */
.glass-card {
background: var(--panel-bg);
backdrop-filter: blur(12px);
border: 1px solid var(--border-color);
border-radius: 16px;
padding: 2rem;
margin-bottom: 2rem;
box-shadow: 0 10px 30px rgba(0,0,0,0.05);
}
.glass-card h2 {
font-family: var(--font-outfit);
font-size: 1.8rem;
font-weight: 700;
margin-bottom: 1.25rem;
display: flex;
align-items: center;
gap: 0.5rem;
color: var(--text-primary);
}
.card-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 1.5rem;
margin-top: 1.5rem;
}
.sub-card {
background: rgba(0, 0, 0, 0.02);
border: 1px solid var(--border-color);
border-radius: 12px;
padding: 1.5rem;
transition: all 0.3s;
}
.sub-card:hover {
border-color: rgba(109, 40, 217, 0.25);
background: rgba(109, 40, 217, 0.01);
}
.sub-card h3 {
font-family: var(--font-outfit);
font-size: 1.2rem;
font-weight: 600;
margin-bottom: 0.75rem;
color: var(--text-primary);
}
.formula-box {
background: rgba(0, 0, 0, 0.02);
border-radius: 8px;
padding: 1rem;
margin: 1rem 0;
border: 1px solid var(--border-color);
display: flex;
justify-content: center;
align-items: center;
}
/* ── Step Tracker Sequence ── */
.lifecycle-container {
display: flex;
flex-direction: column;
gap: 1rem;
margin-top: 1.5rem;
}
.lifecycle-step {
display: flex;
gap: 1.5rem;
padding: 1.25rem;
background: rgba(0, 0, 0, 0.02);
border: 1px solid var(--border-color);
border-radius: 12px;
align-items: flex-start;
position: relative;
transition: all 0.3s;
}
.lifecycle-step:hover {
transform: translateX(5px);
background: rgba(109, 40, 217, 0.02);
border-color: rgba(109, 40, 217, 0.15);
}
.step-num {
background: var(--accent-violet-glow);
border: 1px solid rgba(109, 40, 217, 0.3);
color: var(--accent-violet);
width: 32px;
height: 32px;
border-radius: 50%;
display: flex;
justify-content: center;
align-items: center;
font-weight: 700;
font-family: var(--font-outfit);
flex-shrink: 0;
}
.step-info h4 {
font-family: var(--font-outfit);
font-size: 1.1rem;
color: var(--text-primary);
margin-bottom: 0.25rem;
}
.step-info p {
font-size: 0.9rem;
color: var(--text-secondary);
}
/* ── Tables ── */
table {
width: 100%;
border-collapse: collapse;
margin-top: 1rem;
font-size: 0.9rem;
}
th, td {
padding: 0.85rem 1rem;
text-align: left;
border-bottom: 1px solid var(--border-color);
}
th {
font-family: var(--font-outfit);
font-weight: 600;
color: var(--text-primary);
background: rgba(0, 0, 0, 0.02);
}
tr:hover td {
background: rgba(255, 255, 255, 0.01);
}
.highlight-emerald {
color: var(--accent-emerald);
font-weight: 600;
}
.highlight-violet {
color: var(--accent-violet);
font-weight: 600;
}
/* ── Live Cascade Simulator ── */
.simulator-box {
background: rgba(0, 0, 0, 0.01);
border: 1px solid var(--border-color);
border-radius: 12px;
padding: 1.5rem;
margin-top: 1.5rem;
}
.sim-controls {
display: grid;
grid-template-columns: 2fr 1fr 1fr;
gap: 1rem;
margin-bottom: 1.5rem;
align-items: center;
}
.sim-slider-container {
display: flex;
flex-direction: column;
gap: 0.5rem;
}
.sim-slider-label {
font-size: 0.85rem;
color: var(--text-secondary);
display: flex;
justify-content: space-between;
}
input[type="range"] {
-webkit-appearance: none;
width: 100%;
height: 6px;
background: rgba(0, 0, 0, 0.08);
border-radius: 3px;
outline: none;
}
input[type="range"]::-webkit-slider-thumb {
-webkit-appearance: none;
width: 16px;
height: 16px;
background: var(--accent-violet);
border-radius: 50%;
cursor: pointer;
box-shadow: 0 0 8px var(--accent-violet);
transition: transform 0.2s;
}
input[type="range"]::-webkit-slider-thumb:hover {
transform: scale(1.2);
}
select {
background: rgba(0, 0, 0, 0.03);
border: 1px solid var(--border-color);
border-radius: 8px;
color: var(--text-primary);
padding: 0.6rem;
outline: none;
font-family: var(--font-inter);
cursor: pointer;
}
/* Simulator Stepper Steps */
.sim-steps-wrapper {
position: relative;
margin: 2rem 0;
display: flex;
flex-direction: column;
gap: 1.5rem;
}
.sim-step-node {
display: flex;
gap: 1.25rem;
padding: 1rem;
border-radius: 10px;
border: 1px dashed var(--border-color);
background: rgba(0,0,0,0.01);
align-items: center;
opacity: 0.5;
transition: all 0.4s;
}
.sim-step-node.active {
opacity: 1;
border-style: solid;
border-color: var(--accent-violet);
box-shadow: 0 0 15px rgba(109, 40, 217, 0.08);
background: rgba(109, 40, 217, 0.01);
}
.sim-step-node.accepted {
opacity: 1;
border-style: solid;
border-color: var(--accent-emerald);
box-shadow: 0 0 15px rgba(4, 120, 87, 0.08);
background: rgba(4, 120, 87, 0.01);
}
.sim-step-node.escalated {
opacity: 0.75;
border-style: solid;
border-color: var(--accent-rose);
background: rgba(220, 38, 38, 0.01);
}
.sim-icon {
width: 36px;
height: 36px;
border-radius: 50%;
display: flex;
justify-content: center;
align-items: center;
background: rgba(0,0,0,0.03);
font-size: 1.1rem;
}
.sim-step-node.active .sim-icon {
background: var(--accent-violet-glow);
color: var(--accent-violet);
border: 1px solid var(--accent-violet);
animation: pulse-violet-glow 1.5s infinite;
}
.sim-step-node.accepted .sim-icon {
background: var(--accent-emerald-glow);
color: var(--accent-emerald);
border: 1px solid var(--accent-emerald);
}
.sim-step-node.escalated .sim-icon {
background: rgba(239, 68, 68, 0.1);
color: var(--accent-rose);
border: 1px solid var(--accent-rose);
}
@keyframes pulse-violet-glow {
0% { box-shadow: 0 0 0 0 rgba(139, 92, 246, 0.4); }
70% { box-shadow: 0 0 0 6px rgba(139, 92, 246, 0); }
100% { box-shadow: 0 0 0 0 rgba(139, 92, 246, 0); }
}
.node-details {
flex-grow: 1;
}
.node-name {
font-family: var(--font-outfit);
font-weight: 700;
font-size: 1rem;
color: var(--text-primary);
display: flex;
justify-content: space-between;
}
.node-output {
font-size: 0.85rem;
color: var(--text-secondary);
margin-top: 0.2rem;
font-family: monospace;
}
.node-badge {
font-size: 0.75rem;
font-weight: 600;
padding: 0.1rem 0.5rem;
border-radius: 4px;
background: rgba(0,0,0,0.03);
}
.sim-step-node.accepted .node-badge {
background: var(--accent-emerald-glow);
color: var(--accent-emerald);
}
.sim-step-node.escalated .node-badge {
background: rgba(239, 68, 68, 0.1);
color: var(--accent-rose);
}
.sim-console {
background: rgba(0, 0, 0, 0.03);
font-family: monospace;
font-size: 0.85rem;
padding: 1rem;
border-radius: 8px;
border: 1px solid var(--border-color);
color: #0369A1;
max-height: 120px;
overflow-y: auto;
margin-top: 1rem;
}
/* ── Paper summary details ── */
details.paper-summary-details {
margin-top: 1rem;
padding-top: 0.75rem;
border-top: 1px dashed var(--border-color);
}
details.paper-summary-details summary {
font-family: var(--font-outfit);
font-size: 0.85rem;
font-weight: 600;
color: var(--accent-violet);
cursor: pointer;
outline: none;
user-select: none;
}
details.paper-summary-details[open] summary {
margin-bottom: 0.75rem;
}
.paper-summary-content {
font-size: 0.85rem;
color: var(--text-secondary);
line-height: 1.6;
}
.paper-summary-content h5 {
font-family: var(--font-outfit);
color: var(--text-primary);
margin-top: 0.85rem;
margin-bottom: 0.35rem;
font-size: 0.9rem;
font-weight: 700;
}
.paper-summary-content p {
margin-bottom: 0.5rem;
}
.paper-summary-content code {
background: rgba(0,0,0,0.03);
padding: 0.1rem 0.3rem;
border-radius: 4px;
font-family: monospace;
font-size: 0.8rem;
color: var(--accent-violet);
}
.paper-summary-content pre {
background: rgba(0,0,0,0.03);
padding: 0.5rem;
border-radius: 6px;
font-family: monospace;
font-size: 0.8rem;
overflow-x: auto;
margin: 0.5rem 0;
border: 1px solid var(--border-color);
}
/* ── Playground Styles ── */
.playground-grid {
display: grid;
grid-template-columns: 1.3fr 0.7fr;
gap: 1.5rem;
margin-top: 1.5rem;
}
.playground-main-panel {
background: rgba(15, 23, 42, 0.95);
border: 1px solid rgba(255, 255, 255, 0.1);
border-radius: 16px;
display: flex;
flex-direction: column;
height: 600px;
overflow: hidden;
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.3);
color: #E2E8F0;
}
.playground-header {
padding: 1rem 1.5rem;
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
display: flex;
justify-content: space-between;
align-items: center;
background: rgba(30, 41, 59, 0.5);
}
.playground-controls {
display: flex;
gap: 0.75rem;
align-items: center;
}
.playground-select {
background: rgba(255, 255, 255, 0.05);
border: 1px solid rgba(255, 255, 255, 0.1);
color: #F8FAFC;
padding: 0.4rem 0.75rem;
border-radius: 8px;
font-size: 0.8rem;
outline: none;
cursor: pointer;
}
.playground-select option {
background: #0F172A;
color: #F8FAFC;
}
.playground-chat-history {
flex: 1;
padding: 1.5rem;
overflow-y: auto;
display: flex;
flex-direction: column;
gap: 1rem;
background: rgba(15, 23, 42, 0.5);
}
.playground-bubble {
max-width: 80%;
padding: 0.85rem 1.1rem;
border-radius: 12px;
font-size: 0.9rem;
line-height: 1.5;
}
.playground-bubble.user {
background: #6D28D9;
color: #FFFFFF;
align-self: flex-end;
border-bottom-right-radius: 2px;
}
.playground-bubble.assistant {
background: rgba(255, 255, 255, 0.05);
border: 1px solid rgba(255, 255, 255, 0.08);
color: #E2E8F0;
align-self: flex-start;
border-bottom-left-radius: 2px;
}
.playground-input-row {
padding: 1rem 1.25rem;
border-top: 1px solid rgba(255, 255, 255, 0.1);
display: flex;
gap: 0.75rem;
background: rgba(30, 41, 59, 0.3);
}
.playground-textarea {
flex: 1;
background: rgba(255, 255, 255, 0.03);
border: 1px solid rgba(255, 255, 255, 0.1);
color: #F8FAFC;
padding: 0.75rem;
border-radius: 8px;
font-size: 0.9rem;
outline: none;
resize: none;
height: 44px;
}
.playground-send-btn {
background: #6D28D9;
color: white;
border: none;
padding: 0 1.25rem;
border-radius: 8px;
font-weight: 600;
cursor: pointer;
transition: all 0.3s;
}
.playground-send-btn:hover {
background: #7C3AED;
box-shadow: 0 0 10px rgba(124, 58, 237, 0.4);
}
.playground-sidebar {
display: flex;
flex-direction: column;
gap: 1.25rem;
}
.playground-wallet {
background: var(--panel-bg);
border: 1px solid var(--border-color);
border-radius: 12px;
padding: 1rem 1.25rem;
display: flex;
justify-content: space-between;
align-items: center;
}
.playground-wallet-recharge {
background: var(--accent-emerald-glow);
border: 1px solid var(--accent-emerald);
color: var(--accent-emerald);
padding: 0.25rem 0.5rem;
border-radius: 4px;
font-size: 0.75rem;
cursor: pointer;
font-weight: 600;
}
.playground-metrics-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 0.75rem;
}
.playground-metric-card {
background: var(--panel-bg);
border: 1px solid var(--border-color);
border-radius: 12px;
padding: 1rem;
display: flex;
flex-direction: column;
gap: 0.25rem;
}
.playground-metric-label {
font-size: 0.75rem;
color: var(--text-secondary);
font-weight: 500;
}
.playground-metric-val {
font-size: 1.3rem;
font-weight: 700;
color: var(--text-primary);
}
.playground-pipeline {
background: var(--panel-bg);
border: 1px solid var(--border-color);
border-radius: 12px;
padding: 1.25rem;
flex: 1;
display: flex;
flex-direction: column;
gap: 1rem;
}
.playground-pipeline-flow {
display: flex;
flex-direction: column;
gap: 0.85rem;
position: relative;
padding-left: 1.25rem;
border-left: 2px dashed var(--border-color);
margin-left: 6px;
}
.playground-pipeline-step {
font-size: 0.8rem;
opacity: 0.4;
transition: all 0.3s;
position: relative;
}
.playground-pipeline-step::before {
content: '';
width: 8px;
height: 8px;
background: var(--text-secondary);
border-radius: 50%;
position: absolute;
left: -19px;
top: 5px;
}
.playground-pipeline-step.active {
opacity: 1;
color: var(--accent-violet);
font-weight: 600;
}
.playground-pipeline-step.active::before {
background: var(--accent-violet);
box-shadow: 0 0 8px var(--accent-violet);
}
.playground-pipeline-step.success {
opacity: 1;
color: var(--accent-emerald);
}
.playground-pipeline-step.success::before {
background: var(--accent-emerald);
}
/* ── PDF / Print Styles ── */
@media print {
body {
background: #FFF;
color: #000;
}
header, .tabs-nav, .actions, .simulator-box, .btn {
display: none !important;
}
.container {
max-width: 100%;
padding: 0;
}
.tab-content {
display: block !important;
opacity: 1 !important;
page-break-after: always;
}
.glass-card {
background: none !important;
border: none !important;
box-shadow: none !important;
padding: 0;
margin-bottom: 3rem;
}
.glass-card h2, .sub-card h3, .step-info h4 {
color: #000 !important;
}
.sub-card {
background: none !important;
border: 1px solid #DDD !important;
}
th {
background: #EEE !important;
color: #000 !important;
}
td {
border-bottom: 1px solid #DDD !important;
}
}
</style>
</head>
<body>
<header>
<div class="logo-section">
<span class="logo-text">InferRoute</span>
<span class="badge">Technical Docs</span>
</div>
<div class="actions">
<a href="https://github.com/ypeng12/InferRoute" target="_blank" class="btn btn-outline">
<span>🐙</span> GitHub
</a>
<button class="btn btn-outline" onclick="window.print()">
<span>🖨️</span> Export PDF
</button>
<a href="/" class="btn btn-primary">
<span></span> Playground
</a>
</div>
</header>
<div class="container">
<!-- Hero Section -->
<div class="hero">
<h1>Academic Research & Mathematical Foundations</h1>
<p>InferRoute is built upon robust theoretical frameworks for cost-performance trade-offs and multi-tier cascading inference.</p>
</div>
<!-- Navigation Tabs -->
<div class="tabs-nav">
<button class="tab-btn active" onclick="switchTab(event, 'foundations')">
<span>🔍</span> Foundations
</button>
<button class="tab-btn" onclick="switchTab(event, 'architecture')">
<span>🏗️</span> Architecture
</button>
<button class="tab-btn" onclick="switchTab(event, 'benchmarks')">
<span>📊</span> Benchmarks
</button>
<button class="tab-btn" onclick="switchTab(event, 'self-healing')">
<span>🛡️</span> Self-Healing
</button>
<button class="tab-btn" onclick="switchTab(event, 'playground')">
<span>🎮</span> Playground Sandbox
</button>
</div>
<!-- Tab 1: Foundations -->
<div id="foundations" class="tab-content active">
<!-- FrugalGPT Card -->
<div class="glass-card">
<h2>🔄 FrugalGPT: LLM Cascades & Prompt Adaptation</h2>
<p>Derived from the paper <em>"FrugalGPT: How to Use Large Language Models While Reducing Cost and Improving Performance"</em> (Chen et al., Stanford University, 2023), InferRoute implements three key cost-saving mechanics:</p>
<div class="card-grid">
<div class="sub-card">
<h3>1. Prompt Adaptation</h3>
<p>Prunes long prompt histories or few-shot example prefixes down to at most 1 context example when querying cheap local models (Ollama/vLLM), restoring full complexity only when cascading to commercial endpoints.</p>
</div>
<div class="sub-card">
<h3>2. LLM Approximation (Redis Cache)</h3>
<p>Uses standard Redis completion caches. Matches queries in under 10ms, avoiding upstream model fees completely for exact duplicate concurrent prompts.</p>
</div>
</div>
<div class="card-grid" style="margin-top: 1.5rem;">
<div class="sub-card">
<h3>3. Sequential LLM Cascade</h3>
<p>Sequentially routes queries through a chain of backends (Ollama ➔ vLLM ➔ Gemini ➔ OpenAI). A Reliability Judge assesses output quality at each tier, escalating to the next tier if the quality score falls below \(\tau\).</p>
</div>
<div class="sub-card">
<h3>4. Streaming Cascade Buffer Heuristics</h3>
<p>Buffers SSE stream chunks server-side to detect infinite loops or gibberish outputs. Speculatively cancels degraded local streams and escalates to premium cloud nodes mid-stream to avoid client-facing disruptions.</p>
</div>
</div>
<!-- Academic Breakdown: FrugalGPT Cascading Core Logic -->
<div class="sub-card" style="margin-top: 1.5rem; background: rgba(109, 40, 217, 0.02); border-color: rgba(109, 40, 217, 0.15); display: flex; gap: 1rem; align-items: flex-start;">
<span style="font-size: 1.5rem;">📄</span>
<div>
<h4 style="font-family: var(--font-outfit); font-weight: 700; color: var(--accent-violet); margin-bottom: 0.25rem;">Theoretical Framework: Model Cascading & Optimization</h4>
<p style="font-size: 0.9rem; color: var(--text-secondary); line-height: 1.6;">
The FrugalGPT framework models cost-performance optimization as a decision sequence under cost bounds. By arranging models in ascending order of cost and capabilities (e.g., \(M_1, M_2, \dots, M_k\)), the system routes the query sequentially. For each model \(M_j\), the response is validated by a specialized quality assessor (Reliability Judge). If the response quality satisfies the threshold (\(Q(M_j, x) \ge \tau\)), the generation stops, avoiding subsequent cloud execution fees. Otherwise, the request escalates to the next model tier, guaranteeing response quality while keeping average costs minimal.
</p>
</div>
</div>
<!-- Live Cascade Simulator Box -->
<div class="simulator-box">
<h3>🎛️ Interactive Cascade Simulator</h3>
<p style="font-size: 0.85rem; color: var(--text-secondary); margin-bottom: 1rem;">Adjust the sliding acceptance threshold \(\tau\) and click Simulate to trace the sequential escalation path.</p>
<div class="sim-controls">
<div class="sim-slider-container">
<div class="sim-slider-label">
<span>Acceptance Threshold (\(\tau\)):</span>
<span id="simTauVal" style="font-weight:600; color:#C084FC;">0.60</span>
</div>
<input type="range" id="simTauSlider" min="0" max="1" step="0.05" value="0.60" oninput="updateSimTau(this.value)">
</div>
<select id="simQueryType">
<option value="math">Math Equation (5x - 15 = 20)</option>
<option value="code">Python Coding Prompt (def sort...)</option>
<option value="greeting">Simple Greeting (Hello!)</option>
</select>
<button class="btn btn-primary" onclick="runSimulation()" style="justify-content:center; padding: 0.65rem 1rem;">
Simulate Cascade
</button>
</div>
<div class="sim-steps-wrapper">
<!-- Step 1: Ollama -->
<div class="sim-step-node" id="simNode_ollama">
<div class="sim-icon">1</div>
<div class="node-details">
<div class="node-name">
<span>OLLAMA (Tier 1 - Cheap Local)</span>
<span class="node-badge" id="simBadge_ollama">Pending</span>
</div>
<div class="node-output" id="simOutput_ollama">Waiting to run...</div>
</div>
</div>
<!-- Step 2: vLLM -->
<div class="sim-step-node" id="simNode_vllm">
<div class="sim-icon">2</div>
<div class="node-details">
<div class="node-name">
<span>vLLM (Tier 2 - Mid Local)</span>
<span class="node-badge" id="simBadge_vllm">Pending</span>
</div>
<div class="node-output" id="simOutput_vllm">Waiting to run...</div>
</div>
</div>
<!-- Step 3: OpenAI -->
<div class="sim-step-node" id="simNode_openai">
<div class="sim-icon">3</div>
<div class="node-details">
<div class="node-name">
<span>OPENAI (Tier 3 - Premium Cloud)</span>
<span class="node-badge" id="simBadge_openai">Pending</span>
</div>
<div class="node-output" id="simOutput_openai">Waiting to run...</div>
</div>
</div>
</div>
<div class="sim-console" id="simConsole">
System ready. Click "Simulate Cascade" to start.
</div>
</div>
</div>
<!-- RouterBench Card -->
<div class="glass-card">
<h2>🧠 RouterBench: Mathematical Optimization</h2>
<p>Based on the paper <em>"RouterBench: A Benchmark for Multi-LLM Routing System"</em> (Li et al., Martian, 2024), InferRoute structures content-aware models using standard cost-quality constraints.</p>
<div class="sub-card" style="margin-bottom: 1.5rem;">
<h3>1. The Utility Score Formula</h3>
<p>The routing engine maximizes target utility for prompt \(x\) by selecting backend \(m\):</p>
<div class="formula-box">
\[\text{Score}(m, x) = \lambda \cdot \text{Quality}_{\text{pred}}(m, x) - \text{Cost}(m)\]
</div>
<p style="font-size: 0.85rem; color: var(--text-secondary);">
Here, \(\lambda\) is the cost-quality trade-off parameter (willingness-to-pay), \(\text{Quality}_{\text{pred}}\) is the predicted model quality score (0.0 to 1.0), and \(\text{Cost}(m)\) represents model API execution fees.
</p>
</div>
<div class="card-grid">
<div class="sub-card">
<h3>2. Routing Curve Metric (AIQ)</h3>
<p>To evaluate a routing policy globally across budgets, we calculate the **AIQ (Area under the cost-quality curve)** using the Trapezoidal Rule:</p>
<div class="formula-box" style="font-size: 0.85rem;">
\[\text{AIQ} = \int_{c_{\min}}^{c_{\max}} Q(c) \, dc \approx \sum_{i=0}^{n-1} \frac{q_i + q_{i+1}}{2} \cdot (c_{i+1} - c_i)\]
</div>
</div>
<div class="sub-card">
<h3>3. Supported Routing Policies</h3>
<p>InferRoute implements six distinct routing strategies matching the RouterBench & FrugalGPT frameworks:</p>
<div style="display: flex; flex-direction: column; gap: 0.65rem; margin-top: 0.75rem; font-size: 0.85rem; color: var(--text-secondary);">
<div><strong style="color: var(--text-primary);">🎲 Zero Router Baseline (zero):</strong> Non-content-aware routing. Randomly routes requests to Cloud vs. Local backends based on a target mixture ratio \(p \in [0, 1]\) to form the baseline cost-quality curve.</div>
<div><strong style="color: var(--text-primary);">📋 Rule-Based Router (rule):</strong> Content-aware heuristics. Evaluates prompt keywords (e.g., routing math tasks to GPT/Gemini, coding tasks to local vLLM, simple greetings to Ollama).</div>
<div><strong style="color: var(--text-primary);">🧠 KNN-Based Router (knn):</strong> Jaccard nearest-neighbor lookup on historical runs. Finds the \(K\) most similar prompts, averages their quality, and maximizes the score equation.</div>
<div><strong style="color: var(--text-primary);">🕸️ MLP-Based Router (mlp):</strong> A fast logistic regression classifier extracting features (length, code, math, JSON) to predict model success rates and select the highest-scoring backend.</div>
<div><strong style="color: var(--text-primary);">🔮 Oracle Router (oracle):</strong> Theoretical optimal offline reference that has perfect knowledge of outcomes and chooses the cheapest backend that achieves a quality score \(\ge 0.8\).</div>
<div><strong style="color: var(--text-primary);">🔄 Cascade Router (cascade):</strong> FrugalGPT-style sequential escalation. Triggers cascading hops across model tiers if the reliability judge output score falls below threshold \(\tau\).</div>
</div>
</div>
</div>
</div>
<!-- Academic Breakdown: RouterBench Optimization Framework -->
<div class="sub-card" style="margin-top: 1.5rem; background: rgba(109, 40, 217, 0.02); border-color: rgba(109, 40, 217, 0.15); display: flex; gap: 1rem; align-items: flex-start; margin-bottom: 2rem;">
<span style="font-size: 1.5rem;">📄</span>
<div>
<h4 style="font-family: var(--font-outfit); font-weight: 700; color: var(--accent-violet); margin-bottom: 0.25rem;">Theoretical Framework: Cost-Quality Optimization Frontier</h4>
<p style="font-size: 0.9rem; color: var(--text-secondary); line-height: 1.6;">
The RouterBench framework models LLM selection as a multi-objective optimization problem. By defining the parameter \(\lambda\) (cost-quality trade-off coefficient), the scoring equation evaluates the economic utility of selecting a model \(m\) for a prompt \(x\). The parameter \(\lambda\) represents a user's willingness-to-pay: setting a higher \(\lambda\) prioritizes response quality, while a lower \(\lambda\) emphasizes cost savings. The Area under the cost-quality curve (AIQ) measures the cumulative routing performance across all budget constraints, serving as a unified metric for evaluating routing efficiency.
</p>
</div>
</div>
<div class="glass-card" style="margin-top: 2rem;">
<h2>📚 Original Research Papers & Reference Hub</h2>
<p>Read and preview the full research publications associated with this routing engine directly in your browser:</p>
<div class="paper-grid" style="display: grid; grid-template-columns: repeat(auto-fill, minmax(350px, 1fr)); gap: 1.5rem; margin-top: 1.5rem;">
<!-- Paper 1 -->
<div class="sub-card" style="display: flex; flex-direction: column; padding: 1.5rem; background: rgba(0, 0, 0, 0.02); border: 1px solid var(--border-color); border-radius: 12px; color: var(--text-primary);">
<span style="font-size: 1.5rem; margin-bottom: 0.5rem;">📄</span>
<span style="font-family: var(--font-outfit); font-weight: 700; font-size: 1.1rem; color: var(--text-primary); margin-bottom: 0.25rem;">FrugalGPT Paper</span>
<span style="font-size: 0.8rem; color: var(--text-secondary); margin-bottom: 0.75rem;">How to Use Large Language Models While Reducing Cost and Improving Performance (Stanford, 2023)</span>
<a href="article/How_to_Use_Large_Language_Models.pdf" target="_blank" style="align-self: flex-start; background: rgba(139, 92, 246, 0.15); border: 1px solid rgba(109, 40, 217, 0.2); color: var(--accent-violet); font-size: 0.75rem; font-weight: 600; padding: 0.25rem 0.65rem; border-radius: 6px; text-decoration: none; display: inline-flex; align-items: center; gap: 0.25rem; margin-bottom: 0.75rem;">Read / Preview PDF</a>
<details class="paper-summary-details">
<summary>查看学术综述 (Paper Summary)</summary>
<div class="paper-summary-content">
<h5>🔍 研究背景</h5>
<p>商业大模型单次调用费用昂贵,而开源/小尺寸模型(如 Llama、GPT-3.5)极其便宜但准确率参差不齐。本论文提出通过调度低成本模型并搭配判定机制,以在保留高准确率的同时大幅削减总费用。</p>
<h5>📐 数学建模与公式</h5>
<p><strong>级联模型 (Cascade Decision):</strong> 设定模型序列 \((M_1, M_2, \dots, M_k)\) 以及质量评估器 \(J: \text{Response} \to [0, 1]\)。</p>
<p>对于请求 \(x\),系统依次生成 \(y_i = M_i(x)\),若 \(J(y_i) \ge \tau\)(接受度阈值),则立刻终止级联返回,否则 escalation 到下一级。</p>
<h5>📊 实验结论</h5>
<p>相比直接调用 GPT-4,FrugalGPT 可降低高达 90% 的总账单,并指出小模型无法有效吸收冗长上下文,提示词裁剪至关重要。</p>
<h5>⚙️ Codebase 集成落地</h5>
<p>级联选路运行在 <a href="file:///c:/Users/pengy/OneDrive/Desktop/InferRoute/inferroute/main.py">main.py</a> 的级联流中,裁剪在 <a href="file:///c:/Users/pengy/OneDrive/Desktop/InferRoute/inferroute/prompt_adapter.py">prompt_adapter.py</a>,评分判定运行在 <a href="file:///c:/Users/pengy/OneDrive/Desktop/InferRoute/inferroute/validator.py">validator.py</a></p>
</div>
</details>
</div>
<!-- Paper 2 -->
<div class="sub-card" style="display: flex; flex-direction: column; padding: 1.5rem; background: rgba(0, 0, 0, 0.02); border: 1px solid var(--border-color); border-radius: 12px; color: var(--text-primary);">
<span style="font-size: 1.5rem; margin-bottom: 0.5rem;">📄</span>
<span style="font-family: var(--font-outfit); font-weight: 700; font-size: 1.1rem; color: var(--text-primary); margin-bottom: 0.25rem;">RouterBench Paper</span>
<span style="font-size: 0.8rem; color: var(--text-secondary); margin-bottom: 0.75rem;">A Benchmark for Multi-LLM Routing System (Martian, 2024)</span>
<a href="article/A_Benchmark_for_Multi_LLM_Routing_System.pdf" target="_blank" style="align-self: flex-start; background: rgba(139, 92, 246, 0.15); border: 1px solid rgba(109, 40, 217, 0.2); color: var(--accent-violet); font-size: 0.75rem; font-weight: 600; padding: 0.25rem 0.65rem; border-radius: 6px; text-decoration: none; display: inline-flex; align-items: center; gap: 0.25rem; margin-bottom: 0.75rem;">Read / Preview PDF</a>
<details class="paper-summary-details">
<summary>查看学术综述 (Paper Summary)</summary>
<div class="paper-summary-content">
<h5>🔍 研究背景</h5>
<p>大模型路由逐步多样化,但缺乏标准化的评估基准和数学框架来对比不同路由器在性价比上的优劣。</p>
<h5>📐 数学建模与公式</h5>
<p><strong>效用评分公式:</strong> \(S(m, x) = \lambda \cdot Q_{\text{pred}}(m, x) - C(m)\),其中 \(\lambda\) 代表用户的支付意愿系数,\(Q\) 代表模型的质量预测,\(C\) 代表计费成本。</p>
<p><strong>AIQ 曲线下面积积分:</strong> \(\text{AIQ} = \int_{c_{\min}}^{c_{\max}} Q(c) \, dc\),衡量在各种预算曲线下的全局选路表现。</p>
<h5>📊 实验结论</h5>
<p>引入预测型 MLP 路由器相比静态概率分配(Zero Router)可提升整体 AIQ 达 15% 以上。Oracle 决策上限揭示了路由组合的潜能。</p>
<h5>⚙️ Codebase 集成落地</h5>
<p>效用评分与路由在 <a href="file:///c:/Users/pengy/OneDrive/Desktop/InferRoute/inferroute/router.py">router.py</a> 中的 KNN/MLP 选路策略中运行,帕累托分析和 AIQ 计算在 <a href="file:///c:/Users/pengy/OneDrive/Desktop/InferRoute/benchmarks/plot_results.py">plot_results.py</a></p>
</div>
</details>
</div>
<!-- Paper 3 -->
<div class="sub-card" style="display: flex; flex-direction: column; padding: 1.5rem; background: rgba(0, 0, 0, 0.02); border: 1px solid var(--border-color); border-radius: 12px; color: var(--text-primary);">
<span style="font-size: 1.5rem; margin-bottom: 0.5rem;">📄</span>
<span style="font-family: var(--font-outfit); font-weight: 700; font-size: 1.1rem; color: var(--text-primary); margin-bottom: 0.25rem;">Hybrid LLM Routing Paper</span>
<span style="font-size: 0.8rem; color: var(--text-secondary); margin-bottom: 0.75rem;">Hybrid LLM: Cost-Efficient and Quality-Aware Query Routing (IBM / Tsinghua, 2024)</span>
<a href="article/cost_efficiency.pdf" target="_blank" style="align-self: flex-start; background: rgba(139, 92, 246, 0.15); border: 1px solid rgba(109, 40, 217, 0.2); color: var(--accent-violet); font-size: 0.75rem; font-weight: 600; padding: 0.25rem 0.65rem; border-radius: 6px; text-decoration: none; display: inline-flex; align-items: center; gap: 0.25rem; margin-bottom: 0.75rem;">Read / Preview PDF</a>
<details class="paper-summary-details">
<summary>查看学术综述 (Paper Summary)</summary>
<div class="paper-summary-content">
<h5>🔍 研究背景</h5>
<p>解决企业在拥有高并发免费本地小模型群(Edge)与计费的云端强模型(Cloud)时,如何实现高可用混合选路,减少多级判定带来的 TTFT 耗时。</p>
<h5>📐 数学建模与公式</h5>
<p><strong>难度分类器 (Difficulty Estimator):</strong> \(D(x) = \text{Classifier}(x) \in \{0, 1\}\),直接判定请求难易度并直达目标模型,强调一击即中。</p>
<h5>📊 实验结论</h5>
<p>中等体量分类器能以 85% 以上精度区分复杂度。能够降低多达 40% 的平均网络往返延迟,节约超 60% 费用。</p>
<h5>⚙️ Codebase 集成落地</h5>
<p><a href="file:///c:/Users/pengy/OneDrive/Desktop/InferRoute/inferroute/learned_router.py">learned_router.py</a> 中实现了提取 prompt 任务特质(数学、代码等)的特征估计和直达策略分流。</p>
</div>
</details>
</div>
<!-- Paper 4 -->
<div class="sub-card" style="display: flex; flex-direction: column; padding: 1.5rem; background: rgba(0, 0, 0, 0.02); border: 1px solid var(--border-color); border-radius: 12px; color: var(--text-primary);">
<span style="font-size: 1.5rem; margin-bottom: 0.5rem;">📄</span>
<span style="font-family: var(--font-outfit); font-weight: 700; font-size: 1.1rem; color: var(--text-primary); margin-bottom: 0.25rem;">RouteLLM Paper</span>
<span style="font-size: 0.8rem; color: var(--text-secondary); margin-bottom: 0.75rem;">Learning to Route LLMs with Preference Data (LMSYS / Berkeley, ICLR 2025)</span>
<a href="article/RouteLLM_Preference_Data.pdf" target="_blank" style="align-self: flex-start; background: rgba(139, 92, 246, 0.15); border: 1px solid rgba(109, 40, 217, 0.2); color: var(--accent-violet); font-size: 0.75rem; font-weight: 600; padding: 0.25rem 0.65rem; border-radius: 6px; text-decoration: none; display: inline-flex; align-items: center; gap: 0.25rem; margin-bottom: 0.75rem;">Read / Preview PDF</a>
<details class="paper-summary-details">
<summary>查看学术综述 (Paper Summary)</summary>
<div class="paper-summary-content">
<h5>🔍 研究背景</h5>
<p>针对写作、创意、日常对话等缺乏唯一标准解的任务,探讨如何利用大模型竞技场(Chatbot Arena)产生的人类真实偏好对战数据训练二分类器。</p>
<h5>📐 数学建模与公式</h5>
<p><strong>偏好对战概率 (Bradley-Terry Extension):</strong> \(P(M_{\text{strong}} \succ M_{\text{cheap}} \mid x) = \sigma(f(x))\),通过交叉熵损失优化预测。概率大于阈值 \(\theta\) 时上报强模型,否则分流至便宜模型。</p>
<h5>📊 实验结论</h5>
<p>在 Arena 上能在维持 GPT-4 95% 满意度的同时,缩减 50% API 费用,并验证了轻量级分类网络的优越性。</p>
<h5>⚙️ Codebase 集成落地</h5>
<p>概率选路决策与阈值判定借鉴了该设计(<a href="file:///c:/Users/pengy/OneDrive/Desktop/InferRoute/inferroute/router.py">router.py</a>),拟在后续工作中引入专门的偏好二分类预测器 `preference_router.py`。</p>
</div>
</details>
</div>
<!-- Paper 5 -->
<div class="sub-card" style="display: flex; flex-direction: column; padding: 1.5rem; background: rgba(0, 0, 0, 0.02); border: 1px solid var(--border-color); border-radius: 12px; color: var(--text-primary);">
<span style="font-size: 1.5rem; margin-bottom: 0.5rem;">📄</span>
<span style="font-family: var(--font-outfit); font-weight: 700; font-size: 1.1rem; color: var(--text-primary); margin-bottom: 0.25rem;">EquiRouter Paper</span>
<span style="font-size: 0.8rem; color: var(--text-secondary); margin-bottom: 0.75rem;">When Routing Collapses: On Degenerate Convergence (Lai & Ye, 2026)</span>
<a href="article/When_Routing_Collapses.pdf" target="_blank" style="align-self: flex-start; background: rgba(139, 92, 246, 0.15); border: 1px solid rgba(109, 40, 217, 0.2); color: var(--accent-violet); font-size: 0.75rem; font-weight: 600; padding: 0.25rem 0.65rem; border-radius: 6px; text-decoration: none; display: inline-flex; align-items: center; gap: 0.25rem; margin-bottom: 0.75rem;">Read / Preview PDF</a>
<details class="paper-summary-details">
<summary>查看学术综述 (Paper Summary)</summary>
<div class="paper-summary-content">
<h5>🔍 研究背景</h5>
<p>指出当存在 3 个以上候选模型池时,传统的 MSE 回归训练机制会导致在高预算(大 \(\lambda\))时决策权坍缩,强制全选最昂贵模型。</p>
<h5>📐 数学建模与公式</h5>
<p><strong>决策感知排序损失 (Decision-Aware Ranking Loss):</strong></p>
<p>\(\mathcal{L}_{\text{rank}} = -\sum_{i \ne j} \log \sigma \Big( \big(\text{Utility}(M_i, x) - \text{Utility}(M_j, x)\big) \cdot \mathbb{I}(M_i \succ M_j) \Big)\),强调学习两模型效用之差,维持边界决策概率。</p>
<h5>📊 实验结论</h5>
<p>EquiRouter 成功解决回归多分类塌陷问题,在同等质量下,高预算区间多降低 17% 开销。</p>
<h5>⚙️ Codebase 集成落地</h5>
<p>网关对效用归一化进行了放塌陷微调。未来将在 `benchmarks/train_router.py` 中直接换用此排名损失函数进行优化。</p>
</div>
</details>
</div>
<!-- Paper 6 -->
<div class="sub-card" style="display: flex; flex-direction: column; padding: 1.5rem; background: rgba(0, 0, 0, 0.02); border: 1px solid var(--border-color); border-radius: 12px; color: var(--text-primary);">
<span style="font-size: 1.5rem; margin-bottom: 0.5rem;">📄</span>
<span style="font-family: var(--font-outfit); font-weight: 700; font-size: 1.1rem; color: var(--text-primary); margin-bottom: 0.25rem;">R2-Router Paper</span>
<span style="font-size: 0.8rem; color: var(--text-secondary); margin-bottom: 0.75rem;">R2-Router: A New Paradigm for LLM Routing with Reasoning (ICML 2026)</span>
<a href="article/R2_Router_Reasoning.pdf" target="_blank" style="align-self: flex-start; background: rgba(139, 92, 246, 0.15); border: 1px solid rgba(109, 40, 217, 0.2); color: var(--accent-violet); font-size: 0.75rem; font-weight: 600; padding: 0.25rem 0.65rem; border-radius: 6px; text-decoration: none; display: inline-flex; align-items: center; gap: 0.25rem; margin-bottom: 0.75rem;">Read / Preview PDF</a>
<details class="paper-summary-details">
<summary>查看学术综述 (Paper Summary)</summary>
<div class="paper-summary-content">
<h5>🔍 研究背景</h5>
<p>大模型调用费用大多由生成字数决定。若不设防生成字数,大模型输出的冗长答复会极大地蚕食路由的成本红利。</p>
<h5>📐 数学建模与公式</h5>
<p><strong>联合寻优公式:</strong> \(\max_{m, L} \left[ \text{Quality}(m, x, L) - \lambda \cdot \text{Cost}(m, L) \right]\),其中 \(L\) 代表限制最大输出 token 字数。</p>
<h5>📊 实验结论</h5>
<p>常识问答和提取任务在缩短字数后质量维持原样,这为输出开销带来了 4-5 倍的缩减,显著加快了端到端流式接收。</p>
<h5>⚙️ Codebase 集成落地</h5>
<p><a href="file:///c:/Users/pengy/OneDrive/Desktop/InferRoute/inferroute/prompt_adapter.py">prompt_adapter.py</a> 中实现了动态提示词注入与长度自适应,根据模型档次调整 payload `max_tokens` 参数。</p>
</div>
</details>
</div>
<!-- Paper 7 -->
<div class="sub-card" style="display: flex; flex-direction: column; padding: 1.5rem; background: rgba(0, 0, 0, 0.02); border: 1px solid var(--border-color); border-radius: 12px; color: var(--text-primary);">
<span style="font-size: 1.5rem; margin-bottom: 0.5rem;">📄</span>
<span style="font-family: var(--font-outfit); font-weight: 700; font-size: 1.1rem; color: var(--text-primary); margin-bottom: 0.25rem;">Router-R1 Paper</span>
<span style="font-size: 0.8rem; color: var(--text-secondary); margin-bottom: 0.75rem;">Multi-Round Routing and Aggregation via Reinforcement Learning (2025)</span>
<a href="article/Router_R1_Multi_Round.pdf" target="_blank" style="align-self: flex-start; background: rgba(139, 92, 246, 0.15); border: 1px solid rgba(109, 40, 217, 0.2); color: var(--accent-violet); font-size: 0.75rem; font-weight: 600; padding: 0.25rem 0.65rem; border-radius: 6px; text-decoration: none; display: inline-flex; align-items: center; gap: 0.25rem; margin-bottom: 0.75rem;">Read / Preview PDF</a>
<details class="paper-summary-details">
<summary>查看学术综述 (Paper Summary)</summary>
<div class="paper-summary-content">
<h5>🔍 研究背景</h5>
<p>单次单轮分类分流遇到超复杂的多步推理或代码排错任务基本失灵。复杂任务需要多轮拆解、反复求证与多次升级路由。</p>
<h5>📐 数学建模与公式</h5>
<p><strong>RL 奖励机制:</strong> \(\mathcal{R} = \mathcal{R}_{\text{accuracy}}(y) + \mathcal{R}_{\text{format}}(\text{think\_blocks}) - \beta \cdot \text{Cost}_{\text{inference}}\),用强化学习调教 local 选路 agent。Agent 生成带有 `&lt;think&gt;` 思维链的逻辑步骤,拆分调度子请求并汇总。</p>
<h5>📊 实验结论</h5>
<p>训练过的 8B 代理学会了在思考链中调度预算,仅耗费 GPT-4 35% 的成本就达到了等同程度的数学解答水准。</p>
<h5>⚙️ Codebase 集成落地</h5>
<p>对应了网关在 <a href="file:///c:/Users/pengy/OneDrive/Desktop/InferRoute/inferroute/main.py">main.py</a> 级联检验模块中配置的失败回退重试与条件流阻断。</p>
</div>
</details>
</div>
<!-- Paper 8 -->
<div class="sub-card" style="display: flex; flex-direction: column; padding: 1.5rem; background: rgba(0, 0, 0, 0.02); border: 1px solid var(--border-color); border-radius: 12px; color: var(--text-primary);">
<span style="font-size: 1.5rem; margin-bottom: 0.5rem;">📄</span>
<span style="font-family: var(--font-outfit); font-weight: 700; font-size: 1.1rem; color: var(--text-primary); margin-bottom: 0.25rem;">LLMRouterBench Paper</span>
<span style="font-size: 0.8rem; color: var(--text-secondary); margin-bottom: 0.75rem;">A Massive Benchmark and Unified Framework for LLM Routing (2026)</span>
<a href="article/LLMRouterBench_Massive.pdf" target="_blank" style="align-self: flex-start; background: rgba(139, 92, 246, 0.15); border: 1px solid rgba(109, 40, 217, 0.2); color: var(--accent-violet); font-size: 0.75rem; font-weight: 600; padding: 0.25rem 0.65rem; border-radius: 6px; text-decoration: none; display: inline-flex; align-items: center; gap: 0.25rem; margin-bottom: 0.75rem;">Read / Preview PDF</a>
<details class="paper-summary-details">
<summary>查看学术综述 (Paper Summary)</summary>
<div class="paper-summary-content">
<h5>🔍 研究背景</h5>
<p>解决路由领域实验设计混乱、微调偏好漂移等数据漂移(Data Drift)带来的评测不稳定性,亟需大规模科学对照评测基准。</p>
<h5>📊 实验结论</h5>
<p>构建了 400K 级多任务标准测试集,证实路由存在 Scaling Laws(缩放定律):决策模型并非越大越好,1B 以下的特征分类器往往性能/能耗性价比最高。</p>
<h5>⚙️ Codebase 集成落地</h5>
<p>网关所采用的 Reproducible Evaluation Harness 脚本提供了基础实验测试设计方法与任务配置格式(<a href="file:///c:/Users/pengy/OneDrive/Desktop/InferRoute/benchmarks/datasets/workload.json">workload.json</a>)。</p>
</div>
</details>
</div>
<!-- Paper 9 -->
<div class="sub-card" style="display: flex; flex-direction: column; padding: 1.5rem; background: rgba(0, 0, 0, 0.02); border: 1px solid var(--border-color); border-radius: 12px; color: var(--text-primary);">
<span style="font-size: 1.5rem; margin-bottom: 0.5rem;">📄</span>
<span style="font-family: var(--font-outfit); font-weight: 700; font-size: 1.1rem; color: var(--text-primary); margin-bottom: 0.25rem;">Routing Survey Paper</span>
<span style="font-size: 0.8rem; color: var(--text-secondary); margin-bottom: 0.75rem;">A Survey on Routing Strategies for Resource Optimisation (2025)</span>
<a href="article/Survey_Routing_Resource_Optimisation.pdf" target="_blank" style="align-self: flex-start; background: rgba(139, 92, 246, 0.15); border: 1px solid rgba(109, 40, 217, 0.2); color: var(--accent-violet); font-size: 0.75rem; font-weight: 600; padding: 0.25rem 0.65rem; border-radius: 6px; text-decoration: none; display: inline-flex; align-items: center; gap: 0.25rem; margin-bottom: 0.75rem;">Read / Preview PDF</a>
<details class="paper-summary-details">
<summary>查看学术综述 (Paper Summary)</summary>
<div class="paper-summary-content">
<h5>🔍 研究背景</h5>
<p>为大模型服务架构及硬件开销分摊在资源优化垂直领域的科学分类(Taxonomy)建立体系。</p>
<h5>📊 总结机制</h5>
<p>从路由特征空间(Embedding/Text/Agent)、选路时间节点(Pre-generation/In-generation/Post-generation)与基础设施成本(本地 GPU 折旧 vs 云 API 计费)对比了各种架构的吞吐量、响应延时等折中机制。</p>
<h5>⚙️ Codebase 集成落地</h5>
<p>确定了 InferRoute 数据平面与控制平面分离、网关多指标 Prometheus 监控的设计方针。</p>
</div>
</details>
</div>
</div>
</div>
<!-- Academic Bibliography & References -->
<div class="glass-card" style="margin-top: 2rem;">
<h2>📚 Academic Bibliography & References</h2>
<p>Formal scientific citations for the core research papers referenced during the design and optimization of the InferRoute gateway:</p>
<div style="display: flex; flex-direction: column; gap: 1.5rem; margin-top: 1.5rem;">
<div class="sub-card" style="background: rgba(0,0,0,0.01);">
<h4 style="font-family: var(--font-outfit); font-weight: 700; color: var(--text-primary); margin-bottom: 0.5rem;">1. FrugalGPT (Stanford University)</h4>
<p style="font-size: 0.9rem; color: var(--text-secondary); margin-bottom: 0.75rem; font-style: italic;">
Chen, L., Zaharia, M., & Zou, J. (2023). FrugalGPT: How to use large language models while reducing cost and improving performance. <em>arXiv preprint arXiv:2305.05196</em>.
</p>
<details style="cursor: pointer; font-size: 0.85rem;">
<summary style="color: var(--accent-violet); font-weight: 600; outline: none; margin-bottom: 0.5rem;">Show BibTeX Citation</summary>
<pre style="background: rgba(0,0,0,0.03); padding: 0.75rem; border-radius: 6px; border: 1px solid var(--border-color); overflow-x: auto; font-family: monospace; color: var(--text-secondary);">@article{chen2023frugalgpt,
title={FrugalGPT: How to use large language models while reducing cost and improving performance},
author={Chen, Lingjiao and Zaharia, Matei and Zou, James},
journal={arXiv preprint arXiv:2305.05196},
year={2023}
}</pre>
</details>
</div>
<div class="sub-card" style="background: rgba(0,0,0,0.01);">
<h4 style="font-family: var(--font-outfit); font-weight: 700; color: var(--text-primary); margin-bottom: 0.5rem;">2. RouterBench (Martian)</h4>
<p style="font-size: 0.9rem; color: var(--text-secondary); margin-bottom: 0.75rem; font-style: italic;">
Li, T., Martian Team, et al. (2024). RouterBench: A Benchmark for Multi-LLM Routing System. <em>arXiv preprint arXiv:2403.11164</em>.
</p>
<details style="cursor: pointer; font-size: 0.85rem;">
<summary style="color: var(--accent-violet); font-weight: 600; outline: none; margin-bottom: 0.5rem;">Show BibTeX Citation</summary>
<pre style="background: rgba(0,0,0,0.03); padding: 0.75rem; border-radius: 6px; border: 1px solid var(--border-color); overflow-x: auto; font-family: monospace; color: var(--text-secondary);">@article{li2024routerbench,
title={RouterBench: A Benchmark for Multi-LLM Routing System},
author={Li, Teh-Hsien and others},
journal={arXiv preprint arXiv:2403.11164},
year={2024}
}</pre>
</details>
</div>
<div class="sub-card" style="background: rgba(0,0,0,0.01);">
<h4 style="font-family: var(--font-outfit); font-weight: 700; color: var(--text-primary); margin-bottom: 0.5rem;">3. Hybrid LLM Routing (IBM / Tsinghua)</h4>
<p style="font-size: 0.9rem; color: var(--text-secondary); margin-bottom: 0.75rem; font-style: italic;">
Ding, J., et al. (2024). Hybrid LLM: Cost-Efficient and Quality-Aware Query Routing. <em>arXiv preprint arXiv:2404.14944</em>.
</p>
<details style="cursor: pointer; font-size: 0.85rem;">
<summary style="color: var(--accent-violet); font-weight: 600; outline: none; margin-bottom: 0.5rem;">Show BibTeX Citation</summary>
<pre style="background: rgba(0,0,0,0.03); padding: 0.75rem; border-radius: 6px; border: 1px solid var(--border-color); overflow-x: auto; font-family: monospace; color: var(--text-secondary);">@article{ding2024hybrid,
title={Hybrid LLM: Cost-Efficient and Quality-Aware Query Routing},
author={Ding, Jiayi and others},
journal={arXiv preprint arXiv:2404.14944},
year={2024}
}</pre>
</details>
</div>
<div class="sub-card" style="background: rgba(0,0,0,0.01);">
<h4 style="font-family: var(--font-outfit); font-weight: 700; color: var(--text-primary); margin-bottom: 0.5rem;">4. RouteLLM (LMSYS / UC Berkeley)</h4>
<p style="font-size: 0.9rem; color: var(--text-secondary); margin-bottom: 0.75rem; font-style: italic;">
Ong, I., Almahairi, A., Wu, V., Chiang, W. L., Wu, T., Gonzalez, J. E., Kadous, M. W., & Stoica, I. (2025). RouteLLM: Learning to Route LLMs with Preference Data. <em>Proceedings of the Thirteenth International Conference on Learning Representations (ICLR)</em>.
</p>
<details style="cursor: pointer; font-size: 0.85rem;">
<summary style="color: var(--accent-violet); font-weight: 600; outline: none; margin-bottom: 0.5rem;">Show BibTeX Citation</summary>
<pre style="background: rgba(0,0,0,0.03); padding: 0.75rem; border-radius: 6px; border: 1px solid var(--border-color); overflow-x: auto; font-family: monospace; color: var(--text-secondary);">@inproceedings{ong2025routellm,
title={RouteLLM: Learning to Route LLMs with Preference Data},
author={Ong, Isaac and Almahairi, Amjad and Wu, Vincent and Chiang, Wei-Lin and Wu, Tianhao and Gonzalez, Joseph E. and Kadous, M. Waleed and Stoica, Ion},
booktitle={The Thirteenth International Conference on Learning Representations},
year={2025}
}</pre>
</details>
</div>
<div class="sub-card" style="background: rgba(0,0,0,0.01);">
<h4 style="font-family: var(--font-outfit); font-weight: 700; color: var(--text-primary); margin-bottom: 0.5rem;">5. EquiRouter (Routing Collapse Mitigation)</h4>
<p style="font-size: 0.9rem; color: var(--text-secondary); margin-bottom: 0.75rem; font-style: italic;">
Lai, G., & Ye, H. J. (2026). When Routing Collapses: On the Degenerate Convergence of LLM Routers. <em>arXiv preprint arXiv:2602.03478</em>.
</p>
<details style="cursor: pointer; font-size: 0.85rem;">
<summary style="color: var(--accent-violet); font-weight: 600; outline: none; margin-bottom: 0.5rem;">Show BibTeX Citation</summary>
<pre style="background: rgba(0,0,0,0.03); padding: 0.75rem; border-radius: 6px; border: 1px solid var(--border-color); overflow-x: auto; font-family: monospace; color: var(--text-secondary);">@article{lai2026when,
title={When Routing Collapses: On the Degenerate Convergence of LLM Routers},
author={Lai, Guannan and Ye, Han-Jia},
journal={arXiv preprint arXiv:2602.03478},
year={2026}
}</pre>
</details>
</div>
<div class="sub-card" style="background: rgba(0,0,0,0.01);">
<h4 style="font-family: var(--font-outfit); font-weight: 700; color: var(--text-primary); margin-bottom: 0.5rem;">6. R2-Router (Output-Length-Constrained Routing)</h4>
<p style="font-size: 0.9rem; color: var(--text-secondary); margin-bottom: 0.75rem; font-style: italic;">
Anonymous (2026). R2-Router: A New Paradigm for LLM Routing with Reasoning. <em>arXiv preprint arXiv:2602.02823</em>.
</p>
<details style="cursor: pointer; font-size: 0.85rem;">
<summary style="color: var(--accent-violet); font-weight: 600; outline: none; margin-bottom: 0.5rem;">Show BibTeX Citation</summary>
<pre style="background: rgba(0,0,0,0.03); padding: 0.75rem; border-radius: 6px; border: 1px solid var(--border-color); overflow-x: auto; font-family: monospace; color: var(--text-secondary);">@article{r2router2026,
title={R2-Router: A New Paradigm for LLM Routing with Reasoning},
journal={arXiv preprint arXiv:2602.02823},
year={2026}
}</pre>
</details>
</div>
<div class="sub-card" style="background: rgba(0,0,0,0.01);">
<h4 style="font-family: var(--font-outfit); font-weight: 700; color: var(--text-primary); margin-bottom: 0.5rem;">7. Router-R1 (Reinforcement Learned Multi-Round Router)</h4>
<p style="font-size: 0.9rem; color: var(--text-secondary); margin-bottom: 0.75rem; font-style: italic;">
Anonymous (2025). Router-R1: Teaching LLMs Multi-Round Routing and Aggregation via Reinforcement Learning. <em>arXiv preprint arXiv:2506.09033</em>.
</p>
<details style="cursor: pointer; font-size: 0.85rem;">
<summary style="color: var(--accent-violet); font-weight: 600; outline: none; margin-bottom: 0.5rem;">Show BibTeX Citation</summary>
<pre style="background: rgba(0,0,0,0.03); padding: 0.75rem; border-radius: 6px; border: 1px solid var(--border-color); overflow-x: auto; font-family: monospace; color: var(--text-secondary);">@article{routerr12025,
title={Router-R1: Teaching LLMs Multi-Round Routing and Aggregation via Reinforcement Learning},
journal={arXiv preprint arXiv:2506.09033},
year={2025}
}</pre>
</details>
</div>
<div class="sub-card" style="background: rgba(0,0,0,0.01);">
<h4 style="font-family: var(--font-outfit); font-weight: 700; color: var(--text-primary); margin-bottom: 0.5rem;">8. LLMRouterBench (Large-Scale Benchmarking Framework)</h4>
<p style="font-size: 0.9rem; color: var(--text-secondary); margin-bottom: 0.75rem; font-style: italic;">
Anonymous (2026). LLMRouterBench: A Massive Benchmark and Unified Framework for LLM Routing. <em>arXiv preprint arXiv:2601.07206</em>.
</p>
<details style="cursor: pointer; font-size: 0.85rem;">
<summary style="color: var(--accent-violet); font-weight: 600; outline: none; margin-bottom: 0.5rem;">Show BibTeX Citation</summary>
<pre style="background: rgba(0,0,0,0.03); padding: 0.75rem; border-radius: 6px; border: 1px solid var(--border-color); overflow-x: auto; font-family: monospace; color: var(--text-secondary);">@article{llmrouterbench2026,
title={LLMRouterBench: A Massive Benchmark and Unified Framework for LLM Routing},
journal={arXiv preprint arXiv:2601.07206},
year={2026}
}</pre>
</details>
</div>
<div class="sub-card" style="background: rgba(0,0,0,0.01);">
<h4 style="font-family: var(--font-outfit); font-weight: 700; color: var(--text-primary); margin-bottom: 0.5rem;">9. Resource-Optimized LLM Routing Survey</h4>
<p style="font-size: 0.9rem; color: var(--text-secondary); margin-bottom: 0.75rem; font-style: italic;">
Anonymous (2025). Doing More with Less: A Survey on Routing Strategies for Resource Optimisation in Large Language Model-Based Systems. <em>arXiv preprint arXiv:2502.00409</em>.
</p>
<details style="cursor: pointer; font-size: 0.85rem;">
<summary style="color: var(--accent-violet); font-weight: 600; outline: none; margin-bottom: 0.5rem;">Show BibTeX Citation</summary>
<pre style="background: rgba(0,0,0,0.03); padding: 0.75rem; border-radius: 6px; border: 1px solid var(--border-color); overflow-x: auto; font-family: monospace; color: var(--text-secondary);">@article{resource_routing_survey_2025,
title={Doing More with Less: A Survey on Routing Strategies for Resource Optimisation in Large Language Model-Based Systems},
journal={arXiv preprint arXiv:2502.00409},
year={2025}
}</pre>
</details>
</div>
</div>
</div>
</div>
<!-- Tab 2: Architecture -->
<div id="architecture" class="tab-content">
<div class="glass-card">
<h2>🏗 InferRoute Gateway Request Lifecycle</h2>
<p>The sequence details how the gateway interceptor resolves client requests, manages cache, allocates concurrency slots, and executes cascades:</p>
<div class="mermaid-box" style="background: rgba(0,0,0,0.35); padding: 1.5rem; border-radius: 12px; border: 1px solid var(--border-color); display: flex; justify-content: center; overflow-x: auto; margin-top: 1rem; margin-bottom: 2.5rem; box-shadow: inset 0 2px 8px rgba(0,0,0,0.4);">
<div class="mermaid" style="width: 100%; min-width: 600px;">
sequenceDiagram
autonumber
actor Client as Client App / SDK
participant GW as InferRoute Gateway
participant Auth as Auth & Credit Gate
participant Cache as Cache Layer (Redis)
participant Limiter as Vegas Limiter
participant Router as Routing Engine
participant Model as LLM Upstream
participant Audit as DB Audit & Billing
Client->>GW: POST /v1/chat/completions (Stream)
GW->>Auth: verify_api_key & check_balance
alt Balance <= $0.00
Auth-->>Client: 402 Payment Required
else Balance OK
Auth-->>GW: Tenant ID Resolved
GW->>Cache: try_acquire_dedup_lock
alt Cache Hit
Cache-->>Client: Stream Cached chunks directly
else Cache Miss
GW->>Cache: match_longest_prefix
Cache-->>GW: Return Cache-Affinity Weight
GW->>Limiter: acquire_slot
alt Concurrency Exceeded
Limiter-->>Client: 429 Too Many Requests
else Slot Acquired
GW->>Router: choose_backend (Scoring weights)
Router-->>GW: Selected Backend (e.g. Ollama)
GW->>Model: Invoke Model Stream
Model-->>GW: Yield Stream Chunks
GW->>Client: Forward Stream Chunks
alt Loop/Repetitive Garbage Detected
GW->>Model: Cancel speculative stream
GW->>Router: Trigger Fallback Cascade
Router->>Model: Invoke Cloud Backend (OpenAI)
Model-->>Client: Stream Cloud response
end
GW->>Limiter: release_slot
GW->>Audit: db_log_request & debit wallet
end
end
end
</div>
</div>
<div class="lifecycle-container">
<div class="lifecycle-step">
<div class="step-num">1</div>
<div class="step-info">
<h4>Authentication & Credit check</h4>
<p>Resolves client headers to tenant ID and asserts balance balance \(> \$0.00\). Applies a resilient fail-open policy if the database is unreachable.</p>
</div>
</div>
<div class="lifecycle-step">
<div class="step-num">2</div>
<div class="step-info">
<h4>Exact & Prefix Cache Match</h4>
<p>Performs a Redis exact completion lookup. If missing, checks the Radix Trie prefix index to score warm KV-cache affinity on self-hosted model backends.</p>
</div>
</div>
<div class="lifecycle-step">
<div class="step-num">3</div>
<div class="step-info">
<h4>Vegas Concurrency Control</h4>
<p>Queries concurrency limits to dynamically protect local GPU memory allocations, rejecting or cascading requests to cloud buffers if limits are breached.</p>
</div>
</div>
<div class="lifecycle-step">
<div class="step-num">4</div>
<div class="step-info">
<h4>Model Selection & Cascade Stream</h4>
<p>Routes prompts to the chosen backend. For cascades, it buffers output stream tokens, runs heuristics, and transparently initiates speculative escalations upon validation failures.</p>
</div>
</div>
<div class="lifecycle-step">
<div class="step-num">5</div>
<div class="step-info">
<h4>Audit Ledger logging</h4>
<p>Logs latency telemetry and final aggregated token costs asynchronously to PostgreSQL database ledgers, decrementing tenant credit limits.</p>
</div>
</div>
</div>
</div>
<!-- Playground Control Center Card -->
<div class="glass-card" style="margin-top: 2rem;">
<h2>🎨 Observability Control Center & Interactive Playground</h2>
<p>InferRoute features an interactive client playground dashboard (served at the root <code>/</code> path) allowing developers to monitor and simulate gateway functions in real-time:</p>
<div class="card-grid" style="margin-top: 1.5rem;">
<div class="sub-card">
<h3>1. Live Telemetry Cost Dashboard</h3>
<p>Displays financial metrics including cumulative API dollars saved, tokens processed, Redis cache hit rates, average Time-to-First-Token (TTFT), and system uptime in real-time.</p>
</div>
<div class="sub-card">
<h3>2. Interceptor Pipeline Visualizer</h3>
<p>Renders a live vertical step visualizer tracking individual requests. Watch prompts flow through Cache lookup ➔ Concurrency limit verification ➔ Primary model execution ➔ speculative loops cancellation ➔ Cascade trigger.</p>
</div>
<div class="sub-card">
<h3>3. Wallet & Credit Controller</h3>
<p>Simulates tenant wallet balances and limits. Allows manual top-up adjustments (e.g., refilling $10.00 trial credits) to inspect rate-limiting triggers and HTTP 402 payment requirements.</p>
</div>
<div class="sub-card">
<h3>4. Chaos Engineering Panel</h3>
<p>Allows manual injection of failures (latency spikes, HTTP 500 crashes, network dropouts) into specific backend nodes to observe gateway self-healing, automatic failovers, and circuit-breaker status changes in real-time.</p>
</div>
</div>
</div>
</div>
<!-- Tab 3: Benchmarks -->
<div id="benchmarks" class="tab-content">
<div class="glass-card">
<h2>📊 RouterBench Policy Sweep Outcomes</h2>
<p>Below are evaluation statistics sweeping mixture ratios (\(p\)), trade-off factors (\(\lambda\)), and cascade thresholds (\(\tau\)) over workload dataset prompts:</p>
<table>
<thead>
<tr>
<th>Routing Strategy</th>
<th>Cost per Request ($ USD)</th>
<th>Avg Quality Score (0 - 1.0)</th>
<th>Avg Latency (ms)</th>
<th>SLO Compliance</th>
<th>Fallback Hops</th>
</tr>
</thead>
<tbody>
<tr>
<td class="highlight-violet">Oracle Router Optimal</td>
<td>$0.000022</td>
<td>0.78</td>
<td>258ms</td>
<td class="highlight-emerald">100.0%</td>
<td>0.0%</td>
</tr>
<tr>
<td>KNN Router (\(\lambda = 1.00\))</td>
<td>$0.000019</td>
<td>0.75</td>
<td>266ms</td>
<td class="highlight-emerald">100.0%</td>
<td>0.0%</td>
</tr>
<tr>
<td>MLP Router (\(\lambda = 0.50\))</td>
<td>$0.000023</td>
<td>0.75</td>
<td>258ms</td>
<td class="highlight-emerald">100.0%</td>
<td>0.0%</td>
</tr>
<tr>
<td>Cascade Router (\(\tau = 0.60\))</td>
<td>$0.000017</td>
<td>0.62</td>
<td>239ms</td>
<td class="highlight-emerald">100.0%</td>
<td>66.7%</td>
</tr>
<tr>
<td>Always OpenAI Cloud</td>
<td>$0.000044</td>
<td>0.75</td>
<td>250ms</td>
<td class="highlight-emerald">100.0%</td>
<td>0.0%</td>
</tr>
<tr>
<td>Always Ollama Local</td>
<td>$0.000000</td>
<td>0.31</td>
<td>190ms</td>
<td class="highlight-emerald">100.0%</td>
<td>8.3%</td>
</tr>
</tbody>
</table>
</div>
<div class="glass-card">
<h2>📈 Trade-off Visualization Curves</h2>
<p>These curves show the actual measured performance frontier across swept cost levels:</p>
<div class="card-grid" style="margin-top: 1.5rem;">
<div class="sub-card" style="text-align: center;">
<h3 style="margin-bottom: 0.75rem; text-align: left;">Cost-Quality Pareto Frontier</h3>
<img src="cost_quality_frontier.png" alt="Cost-Quality Frontier" style="max-width: 100%; border-radius: 8px; border: 1px solid var(--border-color); box-shadow: 0 4px 15px rgba(0,0,0,0.3); transition: all 0.3s;" onmouseover="this.style.transform='scale(1.02)';" onmouseout="this.style.transform='scale(1)';" />
<p style="font-size: 0.8rem; color: var(--text-secondary); margin-top: 0.75rem; text-align: left;">Pareto sweeps comparing KNN, MLP, FrugalGPT Cascades, and the Zero Router baseline. Note the efficient frontier pushed to the top-left by the learned routers.</p>
</div>
<div class="sub-card" style="text-align: center;">
<h3 style="margin-bottom: 0.75rem; text-align: left;">Latency Comparison</h3>
<img src="latency_comparison.png" alt="Latency Comparison" style="max-width: 100%; border-radius: 8px; border: 1px solid var(--border-color); box-shadow: 0 4px 15px rgba(0,0,0,0.3); transition: all 0.3s;" onmouseover="this.style.transform='scale(1.02)';" onmouseout="this.style.transform='scale(1)';" />
<p style="font-size: 0.8rem; color: var(--text-secondary); margin-top: 0.75rem; text-align: left;">Comparison of processing latency and time-to-first-token (TTFT) metrics across different routing scenarios.</p>
</div>
</div>
</div>
<div class="glass-card">
<h2>📈 Executive Experiment Summary</h2>
<div class="card-grid">
<div class="sub-card">
<h3>98% API Cost Saved</h3>
<p>Through exact stream deduplication via Redis Pub/Sub, multiple concurrent burst requests calling duplicate system prompts are coalesced into a single upstream model invocation.</p>
</div>
<div class="sub-card">
<h3>80% TTFT Reduction</h3>
<p>Prefix-affinity routing identifies Warm KV-caches on GPU nodes using a Radix Trie, routing prompts to nodes with active context caches to eliminate prefill latency.</p>
</div>
</div>
</div>
<!-- Reproducible Evaluation Sweep Harness -->
<div class="glass-card" style="margin-top: 2rem;">
<h2>📊 Reproducible Evaluation Sweep Harness</h2>
<p>InferRoute provides a built-in evaluation framework to verify the cost-quality trade-offs of all routing algorithms under realistic workload datasets. The system sweeps ratios and willingness-to-pay parameters to export Pareto curves:</p>
<div style="display: flex; flex-direction: column; gap: 1rem; margin-top: 1rem;">
<div class="sub-card" style="background: rgba(0, 0, 0, 0.01);">
<h3>1. Run the Evaluation Sweep Orchestrator</h3>
<p style="margin-bottom: 0.75rem; font-size: 0.9rem; color: var(--text-secondary);">This script iterates across dataset prompts, simulating requests against mock or real endpoints and logging cost, latency, quality, and routing outputs:</p>
<pre style="background: rgba(0,0,0,0.03); padding: 0.75rem; border-radius: 6px; border: 1px solid var(--border-color); font-family: monospace; color: var(--text-primary); font-size: 0.85rem;">python benchmarks/run_router_eval.py</pre>
</div>
<div class="sub-card" style="background: rgba(0, 0, 0, 0.01);">
<h3>2. Compile Metrics & Generate Pareto Curves</h3>
<p style="margin-bottom: 0.75rem; font-size: 0.9rem; color: var(--text-secondary);">This script reads the raw evaluation outcomes, fits the cost-quality points using the Trapezoidal Rule to calculate Area Under the Curve (AIQ), and exports standard PNG curves:</p>
<pre style="background: rgba(0,0,0,0.03); padding: 0.75rem; border-radius: 6px; border: 1px solid var(--border-color); font-family: monospace; color: var(--text-primary); font-size: 0.85rem;">python benchmarks/plot_results.py</pre>
</div>
<div class="sub-card" style="background: rgba(109, 40, 217, 0.02); border-color: rgba(109, 40, 217, 0.15);">
<h3 style="color: var(--accent-violet);">⚙️ Sweeping Parameters Summary</h3>
<p style="font-size: 0.85rem; color: var(--text-secondary);">The evaluation sweeps the target cloud mixture ratio \(p \in [0, 1]\) for the Zero Router baseline, and sweeping trade-off thresholds \(\lambda \in [0, 1]\) or \(\tau \in [0, 1]\) for KNN, MLP, and Cascade routing algorithms to systematically construct the Pareto frontier.</p>
</div>
</div>
</div>
</div>
<!-- Tab 4: Self-Healing -->
<div id="self-healing" class="tab-content">
<div class="glass-card">
<h2>🛡️ Vegas Adaptive Limiting & Circuit Breakers</h2>
<p>InferRoute maintains system resilience through autonomous closed-loop feedback controllers.</p>
<div class="card-grid">
<div class="sub-card">
<h3>1. Vegas Congestion Limiter</h3>
<p>Inspired by TCP Vegas congestion control, the gateway dynamically scales concurrent request slots based on measured latency queue sizes. It auto-throttles requests during model spikes to prevent local GPU OOMs.</p>
</div>
<div class="sub-card">
<h3>2. Self-Healing Circuit Breaker</h3>
<p>Monitors consecutive timeouts and error codes. Transitions from <strong>CLOSED</strong> to <strong>OPEN</strong> upon 5 consecutive failures, bypassing degraded local nodes to fallback cloud targets instantly, recovering automatically via <strong>HALF-OPEN</strong> testing.</p>
</div>
</div>
</div>
</div>
<!-- Tab 5: Playground -->
<div id="playground" class="tab-content">
<div class="glass-card">
<h2>🎮 Interactive Gateway Sandbox (Client-Side Simulator)</h2>
<p>Play with all 9 academic routing policies directly in your browser. This sandbox simulates prefix cache check, Vegas limiter slots, RouteLLM Bradley-Terry勝率 matching, R2-Router word restraints, and Router-R1 agentic draft correction.</p>
<div class="playground-grid">
<!-- Left: Interactive Chat -->
<div class="playground-main-panel">
<div class="playground-header">
<span style="font-weight:700; font-family:var(--font-outfit);">Sandbox Panel</span>
<div class="playground-controls">
<select id="pgPolicySelect" class="playground-select" onchange="onPgPolicyChange(this.value)">
<option value="frugalgpt">🔄 Stanford FrugalGPT Cascade</option>
<option value="routerbench">🎯 Martian RouterBench Utility</option>
<option value="hybrid_llm">⚡ IBM Hybrid LLM Difficulty</option>
<option value="routellm">🧠 LMSYS RouteLLM Preference</option>
<option value="equirouter">⚖️ EquiRouter Decision-Aware MLP</option>
<option value="r2_router">📏 R2-Router Length Constrained</option>
<option value="router_r1">🤖 Router-R1 Multi-Round Agentic</option>
<option value="routing_survey">📋 Unified Routing Survey</option>
<option value="zero">🎲 Zero Router Baseline</option>
</select>
</div>
</div>
<div class="playground-chat-history" id="pgChatHistory">
<div class="playground-bubble assistant">
System initialized. Select any of the 9 academic routing policies from the dropdown above and send a message. The gateway pipeline visualizer and metrics will update in real-time.
</div>
</div>
<div class="playground-input-row">
<textarea id="pgPromptInput" class="playground-textarea" placeholder="Type a message to route... (e.g. 'Solve for x: 5x - 15 = 20' or 'def is_prime(n):')"></textarea>
<button class="playground-send-btn" onclick="sendPgMessage()">Send</button>
</div>
</div>
<!-- Right: Dashboard & Visualizer -->
<div class="playground-sidebar">
<div class="playground-wallet">
<span style="font-size:0.85rem; color:var(--text-secondary); font-weight:500;">Trial Wallet Balance:</span>
<div style="display:flex; align-items:center; gap:0.5rem;">
<span id="pgWalletBalance" style="color:var(--accent-emerald); font-weight:700; font-size:1.1rem;">$5.00</span>
<button class="playground-wallet-recharge" onclick="rechargePgWallet()">+ $10</button>
</div>
</div>
<div class="playground-metrics-grid">
<div class="playground-metric-card">
<span class="playground-metric-label">Estimated Savings</span>
<span class="playground-metric-val" id="pgSavingsVal">$0.00</span>
</div>
<div class="playground-metric-card">
<span class="playground-metric-label">Tokens Saved</span>
<span class="playground-metric-val" id="pgTokensVal">0</span>
</div>
<div class="playground-metric-card">
<span class="playground-metric-label">Last TTFT</span>
<span class="playground-metric-val" id="pgTtftVal">0 ms</span>
</div>
<div class="playground-metric-card">
<span class="playground-metric-label">Cache Hit Rate</span>
<span class="playground-metric-val" id="pgCacheHitVal">0%</span>
</div>
</div>
<div class="playground-pipeline">
<h3 style="font-size:0.95rem; font-family:var(--font-outfit); display:flex; justify-content:space-between; align-items:center; margin-bottom:0.25rem;">
<span>🚀 Gateway Pipeline Visualizer</span>
<span id="pgPipelineStatus" style="font-size:0.75rem; font-weight:600; color:var(--text-secondary);">IDLE</span>
</h3>
<div class="playground-pipeline-flow">
<div class="playground-pipeline-step" id="pgStep_cache">
<div class="step-name">1. Exact & Prefix Cache Check</div>
<div class="step-desc" id="pgStepDesc_cache">Checking Redis caches (Radix Trie check)</div>
</div>
<div class="playground-pipeline-step" id="pgStep_limiter">
<div class="step-name">2. Vegas Concurrency Limiter</div>
<div class="step-desc" id="pgStepDesc_limiter">Validating slot queue depth</div>
</div>
<div class="playground-pipeline-step" id="pgStep_routing">
<div class="step-name">3. Routing Decision Engine</div>
<div class="step-desc" id="pgStepDesc_routing">Evaluating policy formula</div>
</div>
<div class="playground-pipeline-step" id="pgStep_exec">
<div class="step-name">4. Verification & Output Judge</div>
<div class="step-desc" id="pgStepDesc_exec">Running syntactic & loop validation</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<script>
function switchTab(event, tabId) {
// Hide all tabs
const tabContents = document.getElementsByClassName("tab-content");
for (let content of tabContents) {
content.classList.remove("active");
}
// Remove active style from buttons
const tabButtons = document.getElementsByClassName("tab-btn");
for (let btn of tabButtons) {
btn.classList.remove("active");
}
// Show target tab
document.getElementById(tabId).classList.add("active");
event.currentTarget.classList.add("active");
}
function updateSimTau(val) {
document.getElementById("simTauVal").innerText = parseFloat(val).toFixed(2);
}
// ── Interactive Playground Simulation Engine ──
const COSTS = { ollama: 0.0001, vllm: 0.0002, gemini: 0.0015, openai: 0.0030 };
let pgWallet = 5.00;
let pgSavings = 0.00;
let pgTokensSaved = 0;
let pgCacheHits = 0;
let pgTotalReqs = 0;
let pgSentPrompts = new Set();
let pgIsProcessing = false;
function rechargePgWallet() {
pgWallet += 10.00;
document.getElementById("pgWalletBalance").innerText = "$" + pgWallet.toFixed(2);
appendSystemMessage("Wallet recharged with $10.00. New balance: $" + pgWallet.toFixed(2));
}
function onPgPolicyChange(policy) {
appendSystemMessage(`Selected Routing Policy: ${policy.toUpperCase()}`);
}
function appendSystemMessage(text) {
const chatHistory = document.getElementById("pgChatHistory");
const bubble = document.createElement("div");
bubble.className = "playground-bubble assistant";
bubble.style.borderStyle = "dashed";
bubble.style.borderColor = "rgba(139, 92, 246, 0.3)";
bubble.innerText = `⚙️ [SYSTEM LOG] ${text}`;
chatHistory.appendChild(bubble);
chatHistory.scrollTop = chatHistory.scrollHeight;
}
function appendChatBubble(role, text) {
const chatHistory = document.getElementById("pgChatHistory");
const bubble = document.createElement("div");
bubble.className = `playground-bubble ${role}`;
bubble.innerText = text;
chatHistory.appendChild(bubble);
chatHistory.scrollTop = chatHistory.scrollHeight;
return bubble;
}
function resetPipelineSteps() {
const steps = ["cache", "limiter", "routing", "exec"];
steps.forEach(s => {
const el = document.getElementById(`pgStep_${s}`);
el.className = "playground-pipeline-step";
});
document.getElementById("pgStepDesc_cache").innerText = "Checking Redis caches (Radix Trie check)";
document.getElementById("pgStepDesc_limiter").innerText = "Validating slot queue depth";
document.getElementById("pgStepDesc_routing").innerText = "Evaluating policy formula";
document.getElementById("pgStepDesc_exec").innerText = "Running syntactic & loop validation";
}
function setPipelineStepState(step, state, descText) {
const el = document.getElementById(`pgStep_${step}`);
el.className = `playground-pipeline-step ${state}`;
if (descText) {
document.getElementById(`pgStepDesc_${step}`).innerText = descText;
}
}
function sendPgMessage() {
if (pgIsProcessing) return;
const promptInput = document.getElementById("pgPromptInput");
const promptText = promptInput.value.trim();
if (!promptText) return;
pgIsProcessing = true;
promptInput.value = "";
pgTotalReqs++;
// Append user bubble
appendChatBubble("user", promptText);
resetPipelineSteps();
document.getElementById("pgPipelineStatus").innerText = "PROCESSING";
document.getElementById("pgPipelineStatus").style.color = "var(--accent-violet)";
// Step 1: Cache check after 400ms
setTimeout(() => {
setPipelineStepState("cache", "active");
const isCacheHit = pgSentPrompts.has(promptText.toLowerCase());
pgSentPrompts.add(promptText.toLowerCase());
setTimeout(() => {
if (isCacheHit) {
setPipelineStepState("cache", "success", "Cache HIT (Exact match in Redis in 1ms)");
pgCacheHits++;
document.getElementById("pgCacheHitVal").innerText = Math.round((pgCacheHits / pgTotalReqs) * 100) + "%";
document.getElementById("pgTtftVal").innerText = "1 ms";
// Stream response immediately
const cachedResponse = `[CACHE HIT] The answer to your query: "${promptText}" is already cached in Redis memory.`;
const bubble = appendChatBubble("assistant", "");
streamTextIntoBubble(bubble, cachedResponse, () => {
finalizeRequest(0.00, 100, 1.0);
});
} else {
setPipelineStepState("cache", "success", "Cache MISS (Checking suffix tries... no match)");
// Step 2: Limiter check
setTimeout(() => {
setPipelineStepState("limiter", "active");
setTimeout(() => {
setPipelineStepState("limiter", "success", "Slot Acquired (Adaptive slots = 12/32)");
// Step 3: Routing
setTimeout(() => {
setPipelineStepState("routing", "active");
const policy = document.getElementById("pgPolicySelect").value;
const decision = makeRoutingDecision(promptText, policy);
setTimeout(() => {
setPipelineStepState("routing", "success", decision.reason);
// Step 4: Execution
setTimeout(() => {
setPipelineStepState("exec", "active");
executeModelQuery(promptText, decision);
}, 600);
}, 800);
}, 500);
}, 500);
}, 400);
}
}, 500);
}, 300);
}
function makeRoutingDecision(prompt, policy) {
const promptLower = prompt.toLowerCase();
const words = promptLower.split(" ");
const hasCode = promptLower.includes("def ") || promptLower.includes("function") || promptLower.includes("{") || promptLower.includes("code");
const hasMath = promptLower.includes("solve") || promptLower.includes("calculate") || promptLower.includes("math") || promptLower.includes("=") || promptLower.includes("+");
const hasJson = promptLower.includes("json") || promptLower.includes("schema") || promptLower.includes("extract");
let backend = "vllm";
let reason = "";
let r2Active = false;
let r1Active = false;
let cascadeActive = false;
if (policy === "frugalgpt" || policy === "cascade") {
cascadeActive = true;
backend = hasCode || hasMath ? "openai" : "vllm";
reason = `FrugalGPT sequential cascade resolved. Primary target: ${backend}`;
} else if (policy === "hybrid_llm") {
backend = (hasCode || hasMath || hasJson) ? "openai" : "vllm";
reason = `Hybrid LLM difficulty split: '${backend}' (Hard Task = ${hasCode || hasMath || hasJson})`;
} else if (policy === "routellm") {
const prob = hasMath ? 0.88 : (hasCode ? 0.76 : 0.32);
backend = prob >= 0.5 ? "openai" : "vllm";
reason = `RouteLLM Win rate = ${prob.toFixed(2)}. Selected: ${backend}`;
} else if (policy === "equirouter") {
backend = hasCode ? "vllm" : (hasMath ? "openai" : "gemini");
reason = `EquiRouter Decision-Aware MLP recommended: ${backend}`;
} else if (policy === "r2_router") {
r2Active = true;
backend = (hasCode || hasMath) ? "openai" : "gemini";
reason = `R2-Router selected '${backend}' with output brevity constraints`;
} else if (policy === "router_r1") {
r1Active = true;
backend = "vllm";
reason = `Router-R1 agentic drafting mode triggered on '${backend}'`;
} else if (policy === "routing_survey") {
backend = hasCode ? "vllm" : "openai";
reason = `Routing Survey pre-generation prediction: ${backend}`;
} else if (policy === "zero") {
backend = Math.random() < 0.5 ? "openai" : "ollama";
reason = `Zero Router mixture baseline selected: ${backend}`;
} else {
// routerbench
backend = hasCode ? "vllm" : (hasMath ? "openai" : "ollama");
reason = `RouterBench utility scoring recommended: ${backend}`;
}
return { backend, reason, r2Active, r1Active, cascadeActive };
}
function executeModelQuery(prompt, decision) {
const promptLower = prompt.toLowerCase();
const hasMath = promptLower.includes("solve") || promptLower.includes("calculate") || promptLower.includes("math") || promptLower.includes("=");
const hasCode = promptLower.includes("def ") || promptLower.includes("function") || promptLower.includes("{");
let ttft = 450;
let cost = COSTS[decision.backend] || 0.0002;
let completionText = "";
let tokens = 120;
if (decision.backend === "openai" || decision.backend === "gemini") {
ttft = decision.backend === "openai" ? 650 : 380;
tokens = 150;
cost = decision.backend === "openai" ? 0.0030 : 0.0015;
if (hasMath) {
completionText = "To solve 5x - 15 = 20, we isolate x step-by-step:\n1. Add 15 to both sides: 5x = 35\n2. Divide both sides by 5: x = 7.\nThe final value of x is 7.";
} else if (hasCode) {
completionText = "def is_prime(n):\n if n <= 1:\n return False\n for i in range(2, int(n**0.5) + 1):\n if n % i == 0:\n return False\n return True";
} else {
completionText = "Large language models can be routed dynamically to save API costs. I am running on a premium cloud model to provide maximum quality response.";
}
} else {
// Local vllm/ollama
ttft = decision.backend === "vllm" ? 180 : 90;
tokens = 85;
cost = decision.backend === "vllm" ? 0.0002 : 0.0001;
if (hasCode) {
completionText = "def is_prime(n):\n # Local model fast check\n return n > 1 and all(n % i for i in range(2, int(n**0.5) + 1))";
} else {
completionText = "Hello! I am a lightweight local model running on-device. Since this is an easy task, I was selected to save cost and reduce latency.";
}
}
// Adjust parameters if R2-Router is active
if (decision.r2Active) {
cost = cost * 0.4; // saves 60% completion tokens
tokens = Math.round(tokens * 0.4);
completionText = "Result: x = 7 (brevity constraint active).";
}
document.getElementById("pgTtftVal").innerText = ttft + " ms";
// If Router-R1 multi-round is active
if (decision.r1Active) {
// Simulates draft generation failure, then escalation correction!
setPipelineStepState("exec", "warning", "Draft failing validation score (0.42 < 0.8). Escalating to Cloud...");
setTimeout(() => {
const bubble = appendChatBubble("assistant", "");
let thinkingText = "<think>\nRound 1: Draft response by vllm failed code validator check.\nRound 2: Escalated correction payload to OpenAI GPT-4o-mini.\n</think>\n";
let correctionText = "def is_prime(n):\n if n <= 1:\n return False\n for i in range(2, int(n**0.5) + 1):\n if n % i == 0:\n return False\n return True";
streamTextIntoBubble(bubble, thinkingText + correctionText, () => {
setPipelineStepState("exec", "success", "Escalation corrected & validated (Pass)");
finalizeRequest(COSTS["vllm"] + COSTS["openai"], tokens + 50, ttft + 800);
});
}, 600);
} else {
// Normal streaming
const bubble = appendChatBubble("assistant", "");
streamTextIntoBubble(bubble, completionText, () => {
setPipelineStepState("exec", "success", "Response generated and validated (Pass)");
finalizeRequest(cost, tokens, ttft);
});
}
}
function streamTextIntoBubble(bubble, fullText, callback) {
let idx = 0;
const words = fullText.split(" ");
function streamNext() {
if (idx < words.length) {
bubble.innerText += (idx === 0 ? "" : " ") + words[idx];
idx++;
const chatHistory = document.getElementById("pgChatHistory");
chatHistory.scrollTop = chatHistory.scrollHeight;
setTimeout(streamNext, 40);
} else {
if (callback) callback();
}
}
streamNext();
}
function finalizeRequest(cost, tokens, ttft) {
// Deduct wallet
pgWallet = Math.max(0.00, pgWallet - cost);
document.getElementById("pgWalletBalance").innerText = "$" + pgWallet.toFixed(2);
// Compute savings vs OpenAI base ($0.0030)
const baseCost = 0.0030;
const saved = Math.max(0.0, baseCost - cost);
pgSavings += saved;
document.getElementById("pgSavingsVal").innerText = "$" + pgSavings.toFixed(4);
// Increment tokens saved if routed to local model or cache
if (cost < 0.0015) {
pgTokensSaved += tokens;
document.getElementById("pgTokensVal").innerText = pgTokensSaved;
}
document.getElementById("pgCacheHitVal").innerText = Math.round((pgCacheHits / pgTotalReqs) * 100) + "%";
// Idle pipeline status
document.getElementById("pgPipelineStatus").innerText = "COMPLETED";
document.getElementById("pgPipelineStatus").style.color = "var(--accent-emerald)";
pgIsProcessing = false;
}
// Live Stepper Simulation Code
const simulatedOutputs = {
math: [
{ backend: 'ollama', output: '5', score: 0.0 },
{ backend: 'vllm', output: '6', score: 0.0 },
{ backend: 'openai', output: '7', score: 1.0 }
],
code: [
{ backend: 'ollama', output: 'def quicksort(arr): return arr', score: 0.3 },
{ backend: 'vllm', output: 'def quicksort(arr):\n if len(arr) <= 1: return arr\n pivot = arr[0]...', score: 0.8 },
{ backend: 'openai', output: 'def quicksort(arr):\n if len(arr) <= 1: return arr\n pivot = arr[len(arr)//2]\n ...', score: 1.0 }
],
greeting: [
{ backend: 'ollama', output: 'Hello there!', score: 0.9 },
{ backend: 'vllm', output: 'Hello! How can I help you today?', score: 1.0 },
{ backend: 'openai', output: 'Greetings! I am here to assist you.', score: 1.0 }
]
};
let simInterval = null;
function runSimulation() {
if (simInterval) clearInterval(simInterval);
const tau = parseFloat(document.getElementById("simTauSlider").value);
const queryType = document.getElementById("simQueryType").value;
const steps = simulatedOutputs[queryType];
const consoleBox = document.getElementById("simConsole");
consoleBox.innerHTML = "Starting cascade routing simulation...<br>";
// Reset nodes
const backends = ['ollama', 'vllm', 'openai'];
backends.forEach(b => {
const node = document.getElementById(`simNode_${b}`);
const badge = document.getElementById(`simBadge_${b}`);
const outputDiv = document.getElementById(`simOutput_${b}`);
node.className = "sim-step-node";
badge.innerText = "Pending";
badge.className = "node-badge";
outputDiv.innerText = "Waiting to run...";
});
let stepIdx = 0;
function executeStep() {
if (stepIdx >= steps.length) {
consoleBox.innerHTML += "Cascade completed. All models evaluated.<br>";
return;
}
const step = steps[stepIdx];
const node = document.getElementById(`simNode_${step.backend}`);
const badge = document.getElementById(`simBadge_${step.backend}`);
const outputDiv = document.getElementById(`simOutput_${step.backend}`);
// Set node active
node.className = "sim-step-node active";
badge.innerText = "Running...";
consoleBox.innerHTML += `Querying backend: ${step.backend.toUpperCase()}...<br>`;
setTimeout(() => {
outputDiv.innerText = step.output;
const score = step.score;
const accepted = score >= tau;
consoleBox.innerHTML += `-> ${step.backend.toUpperCase()} output score: ${score.toFixed(2)} (Threshold: ${tau.toFixed(2)})<br>`;
if (accepted || stepIdx === steps.length - 1) {
node.className = "sim-step-node accepted";
badge.innerText = accepted ? "Accepted" : "Terminal Accept";
consoleBox.innerHTML += `✔ [ACCEPTED] Cascade stopped at tier: ${step.backend.toUpperCase()}<br>`;
} else {
node.className = "sim-step-node escalated";
badge.innerText = "Escalated";
consoleBox.innerHTML += `❌ [ESCALATED] Score ${score.toFixed(2)} < Threshold ${tau.toFixed(2)}. Escalating...<br>`;
stepIdx++;
executeStep();
}
}, 1000);
}
executeStep();
}
</script>
</body>
</html>