Spaces:
Sleeping
Sleeping
Fix Render deployment dependencies for Quant.ai and InferRoute
Browse files- Dockerfile +2 -0
- app.py +13 -0
- assets/index-C6qvxrC6.js +0 -0
- assets → assets/index-DCSOhC_L.css +0 -0
- external/Quant.ai/README.md +2 -1
- external/Quant.ai/backend/main_api.py +7 -0
- external/Quant.ai/frontend/src/config.ts +6 -5
- favicon.svg +1 -0
- icons.svg +24 -0
- index.html +8 -8
- inferroute/main.py +20 -2
- platform/index.html +614 -0
- quant/index.html +14 -0
- requirements.txt +3 -0
- scratch/deploy_inferroute_space.py +6 -0
Dockerfile
CHANGED
|
@@ -30,6 +30,8 @@ COPY inferroute/ ./inferroute/
|
|
| 30 |
COPY docs/ ./docs/
|
| 31 |
COPY benchmarks/ ./benchmarks/
|
| 32 |
COPY external/ ./external/
|
|
|
|
|
|
|
| 33 |
|
| 34 |
# Expose default port (7860 for Hugging Face Spaces)
|
| 35 |
EXPOSE 7860
|
|
|
|
| 30 |
COPY docs/ ./docs/
|
| 31 |
COPY benchmarks/ ./benchmarks/
|
| 32 |
COPY external/ ./external/
|
| 33 |
+
COPY *.html ./
|
| 34 |
+
COPY assets/ ./assets/
|
| 35 |
|
| 36 |
# Expose default port (7860 for Hugging Face Spaces)
|
| 37 |
EXPOSE 7860
|
app.py
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import sys
|
| 2 |
+
import os
|
| 3 |
+
import uvicorn
|
| 4 |
+
|
| 5 |
+
# Ensure project root is in sys.path
|
| 6 |
+
root_dir = os.path.dirname(os.path.abspath(__file__))
|
| 7 |
+
if root_dir not in sys.path:
|
| 8 |
+
sys.path.insert(0, root_dir)
|
| 9 |
+
|
| 10 |
+
from inferroute.main import app
|
| 11 |
+
|
| 12 |
+
if __name__ == "__main__":
|
| 13 |
+
uvicorn.run("inferroute.main:app", host="0.0.0.0", port=7860, reload=False)
|
assets/index-C6qvxrC6.js
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
assets → assets/index-DCSOhC_L.css
RENAMED
|
File without changes
|
external/Quant.ai/README.md
CHANGED
|
@@ -3,7 +3,8 @@ title: Quant AI - Advanced Quant Trading Engine & Backtest Simulator
|
|
| 3 |
emoji: 📈
|
| 4 |
colorFrom: blue
|
| 5 |
colorTo: indigo
|
| 6 |
-
sdk:
|
|
|
|
| 7 |
pinned: false
|
| 8 |
---
|
| 9 |
|
|
|
|
| 3 |
emoji: 📈
|
| 4 |
colorFrom: blue
|
| 5 |
colorTo: indigo
|
| 6 |
+
sdk: docker
|
| 7 |
+
app_port: 7860
|
| 8 |
pinned: false
|
| 9 |
---
|
| 10 |
|
external/Quant.ai/backend/main_api.py
CHANGED
|
@@ -1,4 +1,11 @@
|
|
| 1 |
import os
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2 |
from fastapi import FastAPI, Query
|
| 3 |
from fastapi.middleware.cors import CORSMiddleware
|
| 4 |
from fastapi.staticfiles import StaticFiles
|
|
|
|
| 1 |
import os
|
| 2 |
+
import sys
|
| 3 |
+
|
| 4 |
+
# Ensure backend directory is in sys.path for importing 'app' modules
|
| 5 |
+
backend_dir = os.path.dirname(os.path.abspath(__file__))
|
| 6 |
+
if backend_dir not in sys.path:
|
| 7 |
+
sys.path.insert(0, backend_dir)
|
| 8 |
+
|
| 9 |
from fastapi import FastAPI, Query
|
| 10 |
from fastapi.middleware.cors import CORSMiddleware
|
| 11 |
from fastapi.staticfiles import StaticFiles
|
external/Quant.ai/frontend/src/config.ts
CHANGED
|
@@ -1,15 +1,16 @@
|
|
| 1 |
// frontend/src/config.ts
|
| 2 |
// Automatically resolves backend API URL for local dev, Docker container, or deployed HF Space
|
| 3 |
|
| 4 |
-
let defaultApiBase = '
|
| 5 |
|
| 6 |
if (typeof window !== 'undefined') {
|
| 7 |
-
if (window.location.port === '5173' || window.location.port === '3000') {
|
| 8 |
defaultApiBase = 'http://127.0.0.1:8000';
|
| 9 |
} else {
|
| 10 |
-
// When
|
| 11 |
-
defaultApiBase =
|
| 12 |
}
|
| 13 |
}
|
| 14 |
|
| 15 |
-
export const API_BASE = (import.meta.env?.VITE_API_BASE as string) || defaultApiBase;
|
|
|
|
|
|
| 1 |
// frontend/src/config.ts
|
| 2 |
// Automatically resolves backend API URL for local dev, Docker container, or deployed HF Space
|
| 3 |
|
| 4 |
+
let defaultApiBase = 'https://inferroute-977n.onrender.com';
|
| 5 |
|
| 6 |
if (typeof window !== 'undefined') {
|
| 7 |
+
if (window.location.port === '5173' || window.location.port === '3000' || window.location.hostname === 'localhost' || window.location.hostname === '127.0.0.1') {
|
| 8 |
defaultApiBase = 'http://127.0.0.1:8000';
|
| 9 |
} else {
|
| 10 |
+
// When hosted on Static HF Space or served via cloud FastAPI
|
| 11 |
+
defaultApiBase = 'https://inferroute-977n.onrender.com';
|
| 12 |
}
|
| 13 |
}
|
| 14 |
|
| 15 |
+
export const API_BASE = (import.meta.env?.VITE_API_BASE as string) || (typeof window !== 'undefined' && localStorage.getItem('API_BASE')) || defaultApiBase;
|
| 16 |
+
|
favicon.svg
ADDED
|
|
icons.svg
ADDED
|
|
index.html
CHANGED
|
@@ -76,16 +76,16 @@
|
|
| 76 |
<span>🚀 InferRoute & Quant.ai Hub</span>
|
| 77 |
</div>
|
| 78 |
<div class="tabs">
|
| 79 |
-
<button class="tab-btn active" onclick="switchTab('
|
| 80 |
-
<span>📈</span> Quant.ai Trading Terminal
|
| 81 |
-
</button>
|
| 82 |
-
<button class="tab-btn" onclick="switchTab('platform')">
|
| 83 |
<span>🌐</span> InferRoute LLM Gateway
|
| 84 |
</button>
|
|
|
|
|
|
|
|
|
|
| 85 |
</div>
|
| 86 |
</div>
|
| 87 |
|
| 88 |
-
<iframe id="mainFrame" class="content-frame" src="
|
| 89 |
|
| 90 |
<script>
|
| 91 |
function switchTab(tab) {
|
|
@@ -94,11 +94,11 @@
|
|
| 94 |
|
| 95 |
btns.forEach(btn => btn.classList.remove('active'));
|
| 96 |
|
| 97 |
-
if (tab === '
|
| 98 |
-
frame.src = '
|
| 99 |
btns[0].classList.add('active');
|
| 100 |
} else {
|
| 101 |
-
frame.src = '
|
| 102 |
btns[1].classList.add('active');
|
| 103 |
}
|
| 104 |
}
|
|
|
|
| 76 |
<span>🚀 InferRoute & Quant.ai Hub</span>
|
| 77 |
</div>
|
| 78 |
<div class="tabs">
|
| 79 |
+
<button class="tab-btn active" onclick="switchTab('platform')">
|
|
|
|
|
|
|
|
|
|
| 80 |
<span>🌐</span> InferRoute LLM Gateway
|
| 81 |
</button>
|
| 82 |
+
<button class="tab-btn" onclick="switchTab('quant')">
|
| 83 |
+
<span>📈</span> Quant.ai Trading Terminal
|
| 84 |
+
</button>
|
| 85 |
</div>
|
| 86 |
</div>
|
| 87 |
|
| 88 |
+
<iframe id="mainFrame" class="content-frame" src="platform.html"></iframe>
|
| 89 |
|
| 90 |
<script>
|
| 91 |
function switchTab(tab) {
|
|
|
|
| 94 |
|
| 95 |
btns.forEach(btn => btn.classList.remove('active'));
|
| 96 |
|
| 97 |
+
if (tab === 'platform') {
|
| 98 |
+
frame.src = 'platform.html';
|
| 99 |
btns[0].classList.add('active');
|
| 100 |
} else {
|
| 101 |
+
frame.src = 'quant.html';
|
| 102 |
btns[1].classList.add('active');
|
| 103 |
}
|
| 104 |
}
|
inferroute/main.py
CHANGED
|
@@ -150,8 +150,22 @@ if os.path.exists(quant_dist_dir):
|
|
| 150 |
|
| 151 |
|
| 152 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 153 |
@app.get("/platform", response_class=HTMLResponse)
|
|
|
|
| 154 |
async def get_platform_page():
|
|
|
|
|
|
|
|
|
|
|
|
|
| 155 |
template_path = os.path.join(os.path.dirname(__file__), "templates", "platform.html")
|
| 156 |
if os.path.exists(template_path):
|
| 157 |
with open(template_path, "r", encoding="utf-8") as f:
|
|
@@ -413,9 +427,13 @@ async def list_providers():
|
|
| 413 |
|
| 414 |
|
| 415 |
@app.get("/", response_class=HTMLResponse, tags=["ui"])
|
|
|
|
| 416 |
async def get_playground():
|
| 417 |
-
"""Serves the interactive playground UI."""
|
| 418 |
-
|
|
|
|
|
|
|
|
|
|
| 419 |
template_path = os.path.join(os.path.dirname(__file__), "templates", "playground.html")
|
| 420 |
if not os.path.exists(template_path):
|
| 421 |
raise HTTPException(status_code=404, detail="Playground UI template not found")
|
|
|
|
| 150 |
|
| 151 |
|
| 152 |
|
| 153 |
+
@app.get("/quant.html", response_class=HTMLResponse)
|
| 154 |
+
async def get_quant_html_page():
|
| 155 |
+
root_quant = os.path.join(os.path.dirname(os.path.dirname(__file__)), "quant.html")
|
| 156 |
+
if os.path.exists(root_quant):
|
| 157 |
+
with open(root_quant, "r", encoding="utf-8") as f:
|
| 158 |
+
return f.read()
|
| 159 |
+
return await get_quant_page("")
|
| 160 |
+
|
| 161 |
+
|
| 162 |
@app.get("/platform", response_class=HTMLResponse)
|
| 163 |
+
@app.get("/platform.html", response_class=HTMLResponse)
|
| 164 |
async def get_platform_page():
|
| 165 |
+
root_platform = os.path.join(os.path.dirname(os.path.dirname(__file__)), "platform.html")
|
| 166 |
+
if os.path.exists(root_platform):
|
| 167 |
+
with open(root_platform, "r", encoding="utf-8") as f:
|
| 168 |
+
return f.read()
|
| 169 |
template_path = os.path.join(os.path.dirname(__file__), "templates", "platform.html")
|
| 170 |
if os.path.exists(template_path):
|
| 171 |
with open(template_path, "r", encoding="utf-8") as f:
|
|
|
|
| 427 |
|
| 428 |
|
| 429 |
@app.get("/", response_class=HTMLResponse, tags=["ui"])
|
| 430 |
+
@app.get("/index.html", response_class=HTMLResponse, tags=["ui"])
|
| 431 |
async def get_playground():
|
| 432 |
+
"""Serves the interactive gateway hub / playground UI."""
|
| 433 |
+
root_index = os.path.join(os.path.dirname(os.path.dirname(__file__)), "index.html")
|
| 434 |
+
if os.path.exists(root_index):
|
| 435 |
+
with open(root_index, "r", encoding="utf-8") as f:
|
| 436 |
+
return f.read()
|
| 437 |
template_path = os.path.join(os.path.dirname(__file__), "templates", "playground.html")
|
| 438 |
if not os.path.exists(template_path):
|
| 439 |
raise HTTPException(status_code=404, detail="Playground UI template not found")
|
platform/index.html
ADDED
|
@@ -0,0 +1,614 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<!DOCTYPE html>
|
| 2 |
+
<html lang="en">
|
| 3 |
+
<head>
|
| 4 |
+
<meta charset="UTF-8">
|
| 5 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
| 6 |
+
<title>InferRoute - Multi-LLM API Gateway & Cost Optimization Engine</title>
|
| 7 |
+
<link href="https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;500;600;700;800&family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet">
|
| 8 |
+
<style>
|
| 9 |
+
:root {
|
| 10 |
+
--bg-light: #f8fafc;
|
| 11 |
+
--card-bg: #ffffff;
|
| 12 |
+
--card-border: #e2e8f0;
|
| 13 |
+
--card-shadow: 0 4px 20px -2px rgba(0, 0, 0, 0.05), 0 2px 6px -1px rgba(0, 0, 0, 0.02);
|
| 14 |
+
--accent-blue: #2563eb;
|
| 15 |
+
--accent-blue-light: #eff6ff;
|
| 16 |
+
--accent-cyan: #0891b2;
|
| 17 |
+
--accent-green: #16a34a;
|
| 18 |
+
--accent-purple: #7c3aed;
|
| 19 |
+
--accent-amber: #d97706;
|
| 20 |
+
--accent-red: #dc2626;
|
| 21 |
+
--text-main: #0f172a;
|
| 22 |
+
--text-muted: #64748b;
|
| 23 |
+
}
|
| 24 |
+
|
| 25 |
+
* { box-sizing: border-box; margin: 0; padding: 0; font-family: 'Outfit', -apple-system, sans-serif; }
|
| 26 |
+
body { background: var(--bg-light); color: var(--text-main); min-height: 100vh; display: flex; flex-direction: column; overflow-x: hidden; }
|
| 27 |
+
|
| 28 |
+
header {
|
| 29 |
+
position: sticky; top: 0; z-index: 50; backdrop-filter: blur(12px);
|
| 30 |
+
background: rgba(255, 255, 255, 0.92); border-bottom: 1px solid var(--card-border);
|
| 31 |
+
padding: 0.85rem 2rem; display: flex; justify-content: space-between; align-items: center;
|
| 32 |
+
box-shadow: 0 1px 3px rgba(0,0,0,0.03);
|
| 33 |
+
}
|
| 34 |
+
.brand { display: flex; align-items: center; gap: 0.75rem; font-weight: 800; font-size: 1.35rem; color: var(--text-main); cursor: pointer; }
|
| 35 |
+
.brand-badge { background: var(--accent-blue-light); color: var(--accent-blue); font-size: 0.75rem; padding: 0.2rem 0.65rem; border-radius: 20px; font-weight: 700; border: 1px solid rgba(37, 99, 235, 0.2); }
|
| 36 |
+
|
| 37 |
+
.nav-links { display: flex; gap: 1.5rem; align-items: center; }
|
| 38 |
+
.nav-link { color: var(--text-muted); text-decoration: none; font-weight: 600; font-size: 0.95rem; transition: color 0.2s; cursor: pointer; }
|
| 39 |
+
.nav-link:hover, .nav-link.active { color: var(--accent-blue); }
|
| 40 |
+
|
| 41 |
+
.header-controls { display: flex; gap: 1rem; align-items: center; }
|
| 42 |
+
.stat-pill { background: #fff; border: 1px solid var(--card-border); padding: 0.4rem 0.85rem; border-radius: 8px; font-size: 0.85rem; display: flex; align-items: center; gap: 0.5rem; }
|
| 43 |
+
.stat-val { font-weight: 700; color: var(--accent-green); }
|
| 44 |
+
|
| 45 |
+
.lang-toggle {
|
| 46 |
+
background: #ffffff; border: 1px solid var(--card-border); padding: 0.4rem 0.85rem;
|
| 47 |
+
border-radius: 8px; font-weight: 700; font-size: 0.85rem; cursor: pointer; color: var(--accent-blue);
|
| 48 |
+
transition: all 0.2s; display: flex; align-items: center; gap: 0.4rem;
|
| 49 |
+
}
|
| 50 |
+
.lang-toggle:hover { background: var(--accent-blue-light); }
|
| 51 |
+
|
| 52 |
+
main { max-width: 1200px; width: 100%; margin: 0 auto; padding: 2rem 1.5rem; display: flex; flex-direction: column; gap: 2rem; }
|
| 53 |
+
|
| 54 |
+
.spa-view { display: none; flex-direction: column; gap: 2rem; animation: fadeIn 0.3s ease-in-out; }
|
| 55 |
+
.spa-view.active { display: flex; }
|
| 56 |
+
|
| 57 |
+
@keyframes fadeIn { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: translateY(0); } }
|
| 58 |
+
|
| 59 |
+
.hero {
|
| 60 |
+
background: linear-gradient(135deg, #ffffff 0%, #eff6ff 100%);
|
| 61 |
+
border: 1px solid #bfdbfe; border-radius: 16px; padding: 2.5rem 2rem;
|
| 62 |
+
box-shadow: var(--card-shadow); text-align: center; display: flex; flex-direction: column; align-items: center; gap: 1rem;
|
| 63 |
+
}
|
| 64 |
+
.hero h1 { font-size: 2.3rem; font-weight: 800; color: var(--text-main); line-height: 1.25; }
|
| 65 |
+
.hero p { font-size: 1.05rem; color: var(--text-muted); max-width: 860px; line-height: 1.6; }
|
| 66 |
+
|
| 67 |
+
.tagline-banner {
|
| 68 |
+
display: inline-flex; gap: 0.6rem; align-items: center; background: #ffffff; border: 1px solid var(--card-border);
|
| 69 |
+
padding: 0.5rem 1.2rem; border-radius: 30px; font-size: 0.88rem; font-weight: 700; color: var(--accent-blue);
|
| 70 |
+
}
|
| 71 |
+
|
| 72 |
+
.card {
|
| 73 |
+
background: var(--card-bg); border: 1px solid var(--card-border); border-radius: 16px;
|
| 74 |
+
padding: 1.75rem; box-shadow: var(--card-shadow);
|
| 75 |
+
}
|
| 76 |
+
.card-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 1.2rem; }
|
| 77 |
+
.card-title { font-size: 1.25rem; font-weight: 800; color: var(--text-main); display: flex; align-items: center; gap: 0.6rem; }
|
| 78 |
+
|
| 79 |
+
.grid-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 1.5rem; }
|
| 80 |
+
.grid-3 { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1.5rem; }
|
| 81 |
+
.grid-4 { display: grid; grid-template-columns: repeat(4, 1fr); gap: 1.25rem; }
|
| 82 |
+
|
| 83 |
+
.output-box {
|
| 84 |
+
background: #0f172a; border: 1px solid var(--card-border); border-radius: 10px;
|
| 85 |
+
padding: 1.25rem; color: #34d399; font-size: 0.9rem; max-height: 380px; overflow-y: auto; white-space: pre-wrap; line-height: 1.6;
|
| 86 |
+
}
|
| 87 |
+
|
| 88 |
+
.code-box {
|
| 89 |
+
background: #0f172a; border: 1px solid var(--card-border); border-radius: 10px;
|
| 90 |
+
padding: 1.2rem; color: #e2e8f0; font-size: 0.85rem; overflow-x: auto; line-height: 1.6; font-family: 'JetBrains Mono', monospace;
|
| 91 |
+
}
|
| 92 |
+
|
| 93 |
+
.btn {
|
| 94 |
+
background: var(--accent-blue); color: #fff; text-decoration: none;
|
| 95 |
+
padding: 0.75rem 1.5rem; border: none; border-radius: 8px; font-weight: 700; cursor: pointer;
|
| 96 |
+
display: inline-flex; align-items: center; gap: 0.5rem; transition: all 0.2s ease; font-size: 0.95rem;
|
| 97 |
+
}
|
| 98 |
+
.btn:hover { background: #1d4ed8; transform: translateY(-1px); }
|
| 99 |
+
.btn-outline { background: transparent; border: 1px solid var(--card-border); color: var(--text-main); }
|
| 100 |
+
.btn-outline:hover { background: #f1f5f9; }
|
| 101 |
+
|
| 102 |
+
.metric-card {
|
| 103 |
+
background: #ffffff; border: 1px solid var(--card-border); border-radius: 12px;
|
| 104 |
+
padding: 1.25rem; text-align: center; display: flex; flex-direction: column; gap: 0.4rem; box-shadow: var(--card-shadow);
|
| 105 |
+
}
|
| 106 |
+
.metric-num { font-size: 1.8rem; font-weight: 800; color: var(--text-main); }
|
| 107 |
+
.metric-sub { font-size: 0.75rem; color: var(--text-muted); }
|
| 108 |
+
|
| 109 |
+
.status-dot { width: 10px; height: 10px; border-radius: 50%; display: inline-block; background: var(--accent-green); }
|
| 110 |
+
.tag-badge { background: #e0f2fe; color: #0369a1; font-size: 0.75rem; padding: 0.2rem 0.6rem; border-radius: 4px; font-weight: 600; }
|
| 111 |
+
|
| 112 |
+
pre, code { font-family: 'JetBrains Mono', monospace; }
|
| 113 |
+
</style>
|
| 114 |
+
</head>
|
| 115 |
+
<body>
|
| 116 |
+
|
| 117 |
+
<!-- Header Navigation -->
|
| 118 |
+
<header>
|
| 119 |
+
<div class="brand" onclick="navigateTo('api')">
|
| 120 |
+
🚀 InferRoute <span class="brand-badge">Multi-LLM Gateway</span>
|
| 121 |
+
</div>
|
| 122 |
+
<div class="nav-links">
|
| 123 |
+
<span class="nav-link active" onclick="navigateTo('api')" id="navApi">API Integration</span>
|
| 124 |
+
<span class="nav-link" onclick="navigateTo('apps')" id="navApps">App Showcase</span>
|
| 125 |
+
<span class="nav-link" onclick="navigateTo('proof')" id="navProof">Empirical Proof</span>
|
| 126 |
+
<span class="nav-link" onclick="navigateTo('cost')" id="navCost">Cost Savings</span>
|
| 127 |
+
<span class="nav-link" onclick="navigateTo('analytics')" id="navAnalytics">Live Dashboard</span>
|
| 128 |
+
</div>
|
| 129 |
+
<div class="header-controls">
|
| 130 |
+
<button class="lang-toggle" onclick="toggleLanguage()">
|
| 131 |
+
🌐 <span id="currentLangLabel">Language: EN</span>
|
| 132 |
+
</button>
|
| 133 |
+
<div class="stat-pill">Status: <span class="status-dot"></span> <span style="font-weight:700;">ONLINE</span></div>
|
| 134 |
+
</div>
|
| 135 |
+
</header>
|
| 136 |
+
|
| 137 |
+
<main>
|
| 138 |
+
|
| 139 |
+
<!-- VIEW 1: API INTEGRATION QUICKSTART (HOW TO USE IN YOUR PROJECTS) -->
|
| 140 |
+
<div class="spa-view active" id="viewApi">
|
| 141 |
+
<section class="hero">
|
| 142 |
+
<div class="tagline-banner" id="tagline">⚡ 100% Compatible with OpenAI SDK & LangChain</div>
|
| 143 |
+
<h1 id="heroTitle">InferRoute: 1-Minute API Integration Guide</h1>
|
| 144 |
+
<p id="heroDesc">
|
| 145 |
+
Plug InferRoute into any existing AI application in 1 minute. Redirect <code>base_url</code> to gain 35.8% KV-cache prefill acceleration, multi-provider failover, and up to 87.7% cost reduction.
|
| 146 |
+
</p>
|
| 147 |
+
</section>
|
| 148 |
+
|
| 149 |
+
<!-- SECTION 1: CODE INTEGRATION SNIPPETS -->
|
| 150 |
+
<section class="card">
|
| 151 |
+
<div class="card-header">
|
| 152 |
+
<div class="card-title">🔑 <span id="sec1Title">1-Min SDK Integration Code Snippets</span></div>
|
| 153 |
+
</div>
|
| 154 |
+
|
| 155 |
+
<div class="grid-2">
|
| 156 |
+
<div>
|
| 157 |
+
<label style="font-weight:700; font-size:0.85rem; display:block; margin-bottom:0.5rem;" id="lblPySdk">Python OpenAI SDK (1-Line Base URL Change)</label>
|
| 158 |
+
<div class="code-box"><span style="color:#60a5fa;">from</span> openai <span style="color:#60a5fa;">import</span> OpenAI
|
| 159 |
+
|
| 160 |
+
<span style="color:#94a3b8;"># Simply set base_url to InferRoute Gateway</span>
|
| 161 |
+
client = OpenAI(
|
| 162 |
+
api_key=<span style="color:#34d399;">"sk-inferroute-demo"</span>,
|
| 163 |
+
base_url=<span style="color:#34d399;">"http://localhost:8080/v1"</span> <span style="color:#94a3b8;"># Or public Space URL</span>
|
| 164 |
+
)
|
| 165 |
+
|
| 166 |
+
response = client.chat.completions.create(
|
| 167 |
+
model=<span style="color:#34d399;">"edge/auto"</span>, <span style="color:#94a3b8;"># Auto-routes across OpenAI/Gemini/vLLM</span>
|
| 168 |
+
messages=[{<span style="color:#34d399;">"role"</span>: <span style="color:#34d399;">"user"</span>, <span style="color:#34d399;">"content"</span>: <span style="color:#34d399;">"Analyze technical indicators"</span>}]
|
| 169 |
+
)
|
| 170 |
+
<span style="color:#f59e0b;">print</span>(response.choices[0].message.content)</div>
|
| 171 |
+
</div>
|
| 172 |
+
|
| 173 |
+
<div>
|
| 174 |
+
<label style="font-weight:700; font-size:0.85rem; display:block; margin-bottom:0.5rem;" id="lblCurl">cURL HTTP Endpoint Snippet</label>
|
| 175 |
+
<div class="code-box">curl http://localhost:8080/v1/chat/completions \
|
| 176 |
+
-H <span style="color:#34d399;">"Authorization: Bearer sk-inferroute-demo"</span> \
|
| 177 |
+
-H <span style="color:#34d399;">"Content-Type: application/json"</span> \
|
| 178 |
+
-d <span style="color:#34d399;">'{
|
| 179 |
+
"model": "edge/auto",
|
| 180 |
+
"messages": [{"role": "user", "content": "Hello InferRoute"}]
|
| 181 |
+
}'</span></div>
|
| 182 |
+
</div>
|
| 183 |
+
</div>
|
| 184 |
+
</section>
|
| 185 |
+
|
| 186 |
+
<!-- SECTION 2: HOW IT WORKS ARCHITECTURE -->
|
| 187 |
+
<section class="card">
|
| 188 |
+
<div class="card-header">
|
| 189 |
+
<div class="card-title">⚡ <span id="sec2Title">Gateway Execution & Failover Architecture</span></div>
|
| 190 |
+
</div>
|
| 191 |
+
|
| 192 |
+
<div class="grid-3">
|
| 193 |
+
<div style="background:#f8fafc; border:1px solid var(--card-border); padding:1.25rem; border-radius:12px;">
|
| 194 |
+
<h4 style="font-size:1rem; font-weight:800; margin-bottom:0.5rem; color:var(--accent-blue);" id="step1Title">1. Radix Trie Cache Matching</h4>
|
| 195 |
+
<p style="font-size:0.88rem; color:var(--text-muted); line-height:1.5;" id="step1Desc">
|
| 196 |
+
Matches prompt prefix in Radix Trie cache. Cuts prefill latency by 35.8% by reusing KV-cache states across requests.
|
| 197 |
+
</p>
|
| 198 |
+
</div>
|
| 199 |
+
|
| 200 |
+
<div style="background:#f8fafc; border:1px solid var(--card-border); padding:1.25rem; border-radius:12px;">
|
| 201 |
+
<h4 style="font-size:1rem; font-weight:800; margin-bottom:0.5rem; color:var(--accent-purple);" id="step2Title">2. SLO & Complexity Router</h4>
|
| 202 |
+
<p style="font-size:0.88rem; color:var(--text-muted); line-height:1.5;" id="step2Desc">
|
| 203 |
+
Evaluates prompt difficulty. Dispatches lightweight prompts to Gemini 1.5 / vLLM, and complex code/reasoning to GPT-4o.
|
| 204 |
+
</p>
|
| 205 |
+
</div>
|
| 206 |
+
|
| 207 |
+
<div style="background:#f8fafc; border:1px solid var(--card-border); padding:1.25rem; border-radius:12px;">
|
| 208 |
+
<h4 style="font-size:1rem; font-weight:800; margin-bottom:0.5rem; color:var(--accent-green);" id="step3Title">3. Circuit Breaker & Deduplication</h4>
|
| 209 |
+
<p style="font-size:0.88rem; color:var(--text-muted); line-height:1.5;" id="step3Desc">
|
| 210 |
+
Monitors provider health. Fails over in 10ms if any upstream API drops, and coalesces duplicate concurrent queries via Redis Pub/Sub.
|
| 211 |
+
</p>
|
| 212 |
+
</div>
|
| 213 |
+
</div>
|
| 214 |
+
</section>
|
| 215 |
+
</div>
|
| 216 |
+
|
| 217 |
+
<!-- VIEW: APP SHOWCASE & ECOSYSTEM (#apps) -->
|
| 218 |
+
<div class="spa-view" id="viewApps">
|
| 219 |
+
<div class="card-header" style="margin-bottom:0;">
|
| 220 |
+
<div class="page-title">
|
| 221 |
+
🌐 <span id="appsTitle">Ecosystem App Showcase (Hugging Face Spaces)</span>
|
| 222 |
+
<span class="tag-badge" style="background:#e0f2fe; color:#0369a1;">Plug & Play Ecosystem</span>
|
| 223 |
+
</div>
|
| 224 |
+
</div>
|
| 225 |
+
|
| 226 |
+
<section class="card">
|
| 227 |
+
<div class="card-header">
|
| 228 |
+
<div class="card-title">🧩 <span id="appsSubTitle">Projects Powered by InferRoute API Gateway</span></div>
|
| 229 |
+
</div>
|
| 230 |
+
|
| 231 |
+
<p style="font-size:0.95rem; color:var(--text-muted); line-height:1.6; margin-bottom:1.5rem;" id="appsDesc">
|
| 232 |
+
All applications hosted on Hugging Face Spaces or external servers route their LLM requests through InferRoute. The gateway automatically tracks token consumption, latency, and cost savings per application.
|
| 233 |
+
</p>
|
| 234 |
+
|
| 235 |
+
<div class="grid-3" style="margin-bottom:1.5rem;">
|
| 236 |
+
<!-- App 1: Quant AI -->
|
| 237 |
+
<div style="background:#ffffff; border:1px solid var(--card-border); border-radius:14px; padding:1.5rem; display:flex; flex-direction:column; gap:0.8rem; box-shadow:var(--card-shadow);">
|
| 238 |
+
<div style="display:flex; justify-content:space-between; align-items:center;">
|
| 239 |
+
<span style="font-size:1.6rem;">📈</span>
|
| 240 |
+
<span class="tag-badge" style="background:#dcfce7; color:#15803d;">Active Routing</span>
|
| 241 |
+
</div>
|
| 242 |
+
<h3 style="font-size:1.15rem; font-weight:800; color:var(--text-main);" id="app1Name">Quant-AI Financial Agent</h3>
|
| 243 |
+
<p style="font-size:0.85rem; color:var(--text-muted); line-height:1.5;" id="app1Desc">
|
| 244 |
+
Stock analysis, financial report extraction, and quantitative code generation. Uses Cascade routing to guarantee high-reasoning accuracy.
|
| 245 |
+
</p>
|
| 246 |
+
<div style="background:#f8fafc; border:1px solid var(--card-border); padding:0.6rem 0.8rem; border-radius:6px; font-size:0.78rem; font-family:'JetBrains Mono', monospace; color:var(--accent-blue);">
|
| 247 |
+
Tenant ID: quant-app<br>
|
| 248 |
+
Savings: ~78.4% Cost Reduction
|
| 249 |
+
</div>
|
| 250 |
+
</div>
|
| 251 |
+
|
| 252 |
+
<!-- App 2: Face AI -->
|
| 253 |
+
<div style="background:#ffffff; border:1px solid var(--card-border); border-radius:14px; padding:1.5rem; display:flex; flex-direction:column; gap:0.8rem; box-shadow:var(--card-shadow);">
|
| 254 |
+
<div style="display:flex; justify-content:space-between; align-items:center;">
|
| 255 |
+
<span style="font-size:1.6rem;">👤</span>
|
| 256 |
+
<span class="tag-badge" style="background:#fef3c7; color:#b45309;">Ready for Integration</span>
|
| 257 |
+
</div>
|
| 258 |
+
<h3 style="font-size:1.15rem; font-weight:800; color:var(--text-main);" id="app2Name">Face & Vision Feature AI</h3>
|
| 259 |
+
<p style="font-size:0.85rem; color:var(--text-muted); line-height:1.5;" id="app2Desc">
|
| 260 |
+
Facial attribute analysis and multimodal visual description. Automatically routes visual queries to Gemini Flash / Vision nodes.
|
| 261 |
+
</p>
|
| 262 |
+
<div style="background:#f8fafc; border:1px solid var(--card-border); padding:0.6rem 0.8rem; border-radius:6px; font-size:0.78rem; font-family:'JetBrains Mono', monospace; color:var(--accent-purple);">
|
| 263 |
+
Tenant ID: vision-app<br>
|
| 264 |
+
Savings: Prefill Speedup +35%
|
| 265 |
+
</div>
|
| 266 |
+
</div>
|
| 267 |
+
|
| 268 |
+
<!-- App 3: Agent AI -->
|
| 269 |
+
<div style="background:#ffffff; border:1px solid var(--card-border); border-radius:14px; padding:1.5rem; display:flex; flex-direction:column; gap:0.8rem; box-shadow:var(--card-shadow);">
|
| 270 |
+
<div style="display:flex; justify-content:space-between; align-items:center;">
|
| 271 |
+
<span style="font-size:1.6rem;">🤖</span>
|
| 272 |
+
<span class="tag-badge" style="background:#fef3c7; color:#b45309;">Ready for Integration</span>
|
| 273 |
+
</div>
|
| 274 |
+
<h3 style="font-size:1.15rem; font-weight:800; color:var(--text-main);" id="app3Name">Multi-Agent Framework</h3>
|
| 275 |
+
<p style="font-size:0.85rem; color:var(--text-muted); line-height:1.5;" id="app3Desc">
|
| 276 |
+
Autonomous multi-agent orchestration. Uses Deduplication & Radix Trie caching to avoid duplicate fees during high-frequency loop calls.
|
| 277 |
+
</p>
|
| 278 |
+
<div style="background:#f8fafc; border:1px solid var(--card-border); padding:0.6rem 0.8rem; border-radius:6px; font-size:0.78rem; font-family:'JetBrains Mono', monospace; color:var(--accent-cyan);">
|
| 279 |
+
Tenant ID: agent-app<br>
|
| 280 |
+
Savings: ~65.0% Cost Reduction
|
| 281 |
+
</div>
|
| 282 |
+
</div>
|
| 283 |
+
</div>
|
| 284 |
+
|
| 285 |
+
<!-- Integration snippet for HuggingFace Spaces -->
|
| 286 |
+
<div style="background:#f8fafc; border:1px solid var(--card-border); padding:1.25rem; border-radius:12px;">
|
| 287 |
+
<h4 style="font-size:0.95rem; font-weight:800; color:var(--text-main); margin-bottom:0.6rem;" id="hfGuideTitle">💡 How HF Space Projects Connect to InferRoute API</h4>
|
| 288 |
+
<div class="code-box"><span style="color:#60a5fa;">import</span> os
|
| 289 |
+
<span style="color:#60a5fa;">import</span> openai
|
| 290 |
+
|
| 291 |
+
<span style="color:#94a3b8;"># In your HuggingFace Space (Gradio / Streamlit app.py):</span>
|
| 292 |
+
client = openai.OpenAI(
|
| 293 |
+
base_url=os.getenv(<span style="color:#34d399;">"INFERROUTE_BASE_URL"</span>, <span style="color:#34d399;">"http://your-inferroute-gateway/v1"</span>),
|
| 294 |
+
api_key=os.getenv(<span style="color:#34d399;">"INFERROUTE_API_KEY"</span>, <span style="color:#34d399;">"sk-inferroute-demo"</span>)
|
| 295 |
+
)
|
| 296 |
+
<span style="color:#94a3b8;"># Zero code changes required for completion calls!</span></div>
|
| 297 |
+
</div>
|
| 298 |
+
</section>
|
| 299 |
+
</div>
|
| 300 |
+
|
| 301 |
+
<!-- VIEW 2: EMPIRICAL PROOF & BENCHMARK EVIDENCE (#proof) -->
|
| 302 |
+
<div class="spa-view" id="viewProof">
|
| 303 |
+
<div class="card-header" style="margin-bottom:0;">
|
| 304 |
+
<div class="page-title">
|
| 305 |
+
📊 <span id="proofTitle">Empirical Benchmark Evidence & Code Verification</span>
|
| 306 |
+
<span class="tag-badge">4,682 Evaluation Runs</span>
|
| 307 |
+
</div>
|
| 308 |
+
</div>
|
| 309 |
+
|
| 310 |
+
<section class="card">
|
| 311 |
+
<div class="card-header">
|
| 312 |
+
<div class="card-title">📈 <span id="proofSubTitle">Benchmark Proof (RouterBench Standard Evaluation)</span></div>
|
| 313 |
+
</div>
|
| 314 |
+
|
| 315 |
+
<div class="grid-4" style="margin-bottom:1.5rem;">
|
| 316 |
+
<div class="metric-card">
|
| 317 |
+
<div class="metric-num" style="color:var(--accent-blue);">4,682</div>
|
| 318 |
+
<div id="p1Title">Evaluation Requests</div>
|
| 319 |
+
<div class="metric-sub" id="p1Sub">RouterBench Standard Test</div>
|
| 320 |
+
</div>
|
| 321 |
+
<div class="metric-card">
|
| 322 |
+
<div class="metric-num" style="color:var(--accent-green);">-35.8%</div>
|
| 323 |
+
<div id="p2Title">Prefill Latency</div>
|
| 324 |
+
<div class="metric-sub" id="p2Sub">Radix Trie KV-Cache Boost</div>
|
| 325 |
+
</div>
|
| 326 |
+
<div class="metric-card">
|
| 327 |
+
<div class="metric-num" style="color:var(--accent-purple);">99.2%</div>
|
| 328 |
+
<div id="p3Title">Accuracy Retention</div>
|
| 329 |
+
<div class="metric-sub" id="p3Sub">vs Pure GPT-4 Oracle</div>
|
| 330 |
+
</div>
|
| 331 |
+
<div class="metric-card">
|
| 332 |
+
<div class="metric-num" style="color:var(--accent-amber);">99.99%</div>
|
| 333 |
+
<div id="p4Title">Availability SLA</div>
|
| 334 |
+
<div class="metric-sub" id="p4Sub">10ms Circuit Breaker Failover</div>
|
| 335 |
+
</div>
|
| 336 |
+
</div>
|
| 337 |
+
|
| 338 |
+
<p style="font-size:0.92rem; color:var(--text-muted); line-height:1.6;" id="proofDesc">
|
| 339 |
+
Our empirical evaluation on 4,682 benchmark queries proves that InferRoute preserves 99.2% of GPT-4's problem-solving accuracy while reducing prefill latencies by 35.8% and cutting API costs by 87.7%.
|
| 340 |
+
</p>
|
| 341 |
+
</section>
|
| 342 |
+
</div>
|
| 343 |
+
|
| 344 |
+
<!-- VIEW 3: COST SAVINGS CALCULATOR & BREAKDOWN (#cost) -->
|
| 345 |
+
<div class="spa-view" id="viewCost">
|
| 346 |
+
<div class="card-header" style="margin-bottom:0;">
|
| 347 |
+
<div class="page-title">
|
| 348 |
+
💰 <span id="costTitle">Real-Time Cost Savings & Baseline Comparison</span>
|
| 349 |
+
<span class="tag-badge" style="background:#dcfce7; color:#166534;">87.7% Savings</span>
|
| 350 |
+
</div>
|
| 351 |
+
</div>
|
| 352 |
+
|
| 353 |
+
<section class="card">
|
| 354 |
+
<div class="card-header">
|
| 355 |
+
<div class="card-title">🧮 <span id="calcTitle">Cost Savings Calculation Formula</span></div>
|
| 356 |
+
</div>
|
| 357 |
+
|
| 358 |
+
<p style="font-size:0.95rem; color:var(--text-muted); line-height:1.6; margin-bottom:1.2rem;" id="calcDesc">
|
| 359 |
+
For every single request routed through InferRoute, the database logs the exact baseline cost if GPT-4 were used vs the actual cost of the routed model:
|
| 360 |
+
</p>
|
| 361 |
+
|
| 362 |
+
<div class="code-box" style="margin-bottom:1.5rem;">
|
| 363 |
+
Saved Cost = Baseline_Cost (GPT-4) - Actual_Cost (Routed Model)
|
| 364 |
+
</div>
|
| 365 |
+
|
| 366 |
+
<div class="grid-2">
|
| 367 |
+
<div style="background:#f8fafc; border:1px solid var(--card-border); padding:1.5rem; border-radius:12px;">
|
| 368 |
+
<h4 style="font-size:1.1rem; font-weight:800; color:var(--accent-red); margin-bottom:0.5rem;" id="directTitle">Direct GPT-4 (Without Gateway)</h4>
|
| 369 |
+
<div style="font-size:1.8rem; font-weight:800; margin-bottom:0.5rem;">$1,500.00</div>
|
| 370 |
+
<p style="font-size:0.85rem; color:var(--text-muted);" id="directSub">100,000 requests @ $0.015 / 1k tokens</p>
|
| 371 |
+
</div>
|
| 372 |
+
|
| 373 |
+
<div style="background:#f0fdf4; border:1px solid #bbf7d0; padding:1.5rem; border-radius:12px;">
|
| 374 |
+
<h4 style="font-size:1.1rem; font-weight:800; color:var(--accent-green); margin-bottom:0.5rem;" id="routedTitle">Routed via InferRoute Engine</h4>
|
| 375 |
+
<div style="font-size:1.8rem; font-weight:800; color:var(--accent-green); margin-bottom:0.5rem;">$184.20</div>
|
| 376 |
+
<p style="font-size:0.85rem; color:var(--accent-green);" id="routedSub">Net Savings: $1,315.80 (87.7% Cost Reduction)</p>
|
| 377 |
+
</div>
|
| 378 |
+
</div>
|
| 379 |
+
</section>
|
| 380 |
+
</div>
|
| 381 |
+
|
| 382 |
+
<!-- VIEW 4: UNIFIED ANALYTICS DASHBOARD (#analytics) -->
|
| 383 |
+
<div class="spa-view" id="viewAnalytics">
|
| 384 |
+
<div class="card-header" style="margin-bottom:0;">
|
| 385 |
+
<div class="page-title">
|
| 386 |
+
📊 <span id="analyticsTitle">Unified Analytics & Real-Time Monitor</span>
|
| 387 |
+
<span class="tag-badge">Live Log Calculation</span>
|
| 388 |
+
</div>
|
| 389 |
+
</div>
|
| 390 |
+
|
| 391 |
+
<section class="card">
|
| 392 |
+
<div class="grid-4" style="margin-bottom:1.5rem;">
|
| 393 |
+
<div class="metric-card">
|
| 394 |
+
<div class="metric-num" id="valTotalReqs">0</div>
|
| 395 |
+
<div id="m1Title">Total Requests</div>
|
| 396 |
+
<div class="metric-sub" id="m1Sub">All Client Aggregation</div>
|
| 397 |
+
</div>
|
| 398 |
+
<div class="metric-card">
|
| 399 |
+
<div style="color:var(--accent-green);" class="metric-num" id="valTotalSaved">$0.00</div>
|
| 400 |
+
<div id="m2Title">Total Cost Saved ($)</div>
|
| 401 |
+
<div class="metric-sub" id="m2Sub">vs Direct GPT-4</div>
|
| 402 |
+
</div>
|
| 403 |
+
<div class="metric-card">
|
| 404 |
+
<div style="color:var(--accent-purple);" class="metric-num" id="valAntigravityReqs">0</div>
|
| 405 |
+
<div id="m3Title">OpenAI / Agent Requests</div>
|
| 406 |
+
<div class="metric-sub" id="m3Sub">High-Reasoning Cluster</div>
|
| 407 |
+
</div>
|
| 408 |
+
<div class="metric-card">
|
| 409 |
+
<div style="color:var(--accent-cyan);" class="metric-num" id="valQuantReqs">0</div>
|
| 410 |
+
<div id="m4Title">Gemini / Quant Requests</div>
|
| 411 |
+
<div class="metric-sub" id="m4Sub">Fast Trading Cluster</div>
|
| 412 |
+
</div>
|
| 413 |
+
</div>
|
| 414 |
+
|
| 415 |
+
<div class="card-header">
|
| 416 |
+
<div class="card-title">📜 eval_results.json Benchmark Log Output</div>
|
| 417 |
+
<button class="btn btn-outline" onclick="fetchAnalyticsSummary()" id="btnRefresh">🔄 Refresh Real-Time Metrics</button>
|
| 418 |
+
</div>
|
| 419 |
+
<div class="output-box" id="analyticsOutput">Loading evaluation metrics...</div>
|
| 420 |
+
</section>
|
| 421 |
+
</div>
|
| 422 |
+
|
| 423 |
+
</main>
|
| 424 |
+
|
| 425 |
+
<script>
|
| 426 |
+
let currentLang = 'EN';
|
| 427 |
+
|
| 428 |
+
const i18n = {
|
| 429 |
+
EN: {
|
| 430 |
+
currentLangLabel: "Language: EN",
|
| 431 |
+
tagline: "⚡ 100% Compatible with OpenAI SDK & LangChain",
|
| 432 |
+
heroTitle: "InferRoute: 1-Minute API Integration Guide",
|
| 433 |
+
heroDesc: "Plug InferRoute into any existing AI application in 1 minute. Redirect base_url to gain 35.8% KV-cache prefill acceleration, multi-provider failover, and up to 87.7% cost reduction.",
|
| 434 |
+
sec1Title: "1-Min SDK Integration Code Snippets",
|
| 435 |
+
lblPySdk: "Python OpenAI SDK (1-Line Base URL Change)",
|
| 436 |
+
lblCurl: "cURL HTTP Endpoint Snippet",
|
| 437 |
+
sec2Title: "Gateway Execution & Failover Architecture",
|
| 438 |
+
step1Title: "1. Radix Trie Cache Matching",
|
| 439 |
+
step1Desc: "Matches prompt prefix in Radix Trie cache. Cuts prefill latency by 35.8% by reusing KV-cache states across requests.",
|
| 440 |
+
step2Title: "2. SLO & Complexity Router",
|
| 441 |
+
step2Desc: "Evaluates prompt difficulty. Dispatches lightweight prompts to Gemini 1.5 / vLLM, and complex code/reasoning to GPT-4o.",
|
| 442 |
+
step3Title: "3. Circuit Breaker & Deduplication",
|
| 443 |
+
step3Desc: "Monitors provider health. Fails over in 10ms if any upstream API drops, and coalesces duplicate concurrent queries via Redis Pub/Sub.",
|
| 444 |
+
proofTitle: "Empirical Benchmark Evidence & Code Verification",
|
| 445 |
+
proofSubTitle: "Benchmark Proof (RouterBench Standard Evaluation)",
|
| 446 |
+
p1Title: "Evaluation Requests",
|
| 447 |
+
p1Sub: "RouterBench Standard Test",
|
| 448 |
+
p2Title: "Prefill Latency",
|
| 449 |
+
p2Sub: "Radix Trie KV-Cache Boost",
|
| 450 |
+
p3Title: "Accuracy Retention",
|
| 451 |
+
p3Sub: "vs Pure GPT-4 Oracle",
|
| 452 |
+
p4Title: "Availability SLA",
|
| 453 |
+
p4Sub: "10ms Circuit Breaker Failover",
|
| 454 |
+
proofDesc: "Our empirical evaluation on 4,682 benchmark queries proves that InferRoute preserves 99.2% of GPT-4's problem-solving accuracy while reducing prefill latencies by 35.8% and cutting API costs by 87.7%.",
|
| 455 |
+
costTitle: "Real-Time Cost Savings & Baseline Comparison",
|
| 456 |
+
calcTitle: "Cost Savings Calculation Formula",
|
| 457 |
+
calcDesc: "For every single request routed through InferRoute, the database logs the exact baseline cost if GPT-4 were used vs the actual cost of the routed model:",
|
| 458 |
+
directTitle: "Direct GPT-4 (Without Gateway)",
|
| 459 |
+
directSub: "100,000 requests @ $0.015 / 1k tokens",
|
| 460 |
+
routedTitle: "Routed via InferRoute Engine",
|
| 461 |
+
routedSub: "Net Savings: $1,315.80 (87.7% Cost Reduction)",
|
| 462 |
+
analyticsTitle: "Unified Analytics & Real-Time Monitor",
|
| 463 |
+
btnRefresh: "🔄 Refresh Real-Time Metrics",
|
| 464 |
+
m1Title: "Total Requests",
|
| 465 |
+
m1Sub: "All Client Aggregation",
|
| 466 |
+
m2Title: "Total Cost Saved ($)",
|
| 467 |
+
m2Sub: "vs Direct GPT-4",
|
| 468 |
+
m3Title: "OpenAI / Agent Requests",
|
| 469 |
+
m3Sub: "High-Reasoning Cluster",
|
| 470 |
+
m4Title: "Gemini / Quant Requests",
|
| 471 |
+
m4Sub: "Fast Trading Cluster",
|
| 472 |
+
navApi: "API Integration",
|
| 473 |
+
navProof: "Empirical Proof",
|
| 474 |
+
navCost: "Cost Savings",
|
| 475 |
+
navAnalytics: "Live Dashboard"
|
| 476 |
+
},
|
| 477 |
+
ZH: {
|
| 478 |
+
currentLangLabel: "语言: 中文",
|
| 479 |
+
tagline: "⚡ 100% 兼容 OpenAI 标准 SDK & LangChain",
|
| 480 |
+
heroTitle: "InferRoute: 1秒极速 API 接入指南",
|
| 481 |
+
heroDesc: "只需 1 分钟即可将 InferRoute 接入任何现有 AI 项目。修改 base_url 即可立刻获得 35.8% 前缀缓存加速、多模型自动熔断降级与高达 87.7% 的成本削减。",
|
| 482 |
+
sec1Title: "1秒 SDK 接入代码示例",
|
| 483 |
+
lblPySdk: "Python OpenAI SDK (仅需修改 1 行 Base URL)",
|
| 484 |
+
lblCurl: "cURL HTTP Endpoint 调用示例",
|
| 485 |
+
sec2Title: "网关执行与熔断降级架构",
|
| 486 |
+
step1Title: "1. Radix Trie 前缀 Cache 匹配",
|
| 487 |
+
step1Desc: "在 Radix Trie 缓存树中自动匹配 Prompt 前缀,通过跨请求复用 KV-Cache 状态降低 35.8% 的 Prefill 延迟。",
|
| 488 |
+
step2Title: "2. SLO 复杂度分级路由器",
|
| 489 |
+
step2Desc: "智能评估 Prompt 难易度,将轻量任务分发至 Gemini 1.5 / vLLM,复杂代码与推理提升分发至 GPT-4o。",
|
| 490 |
+
step3Title: "3. 熔断降级与请求去重",
|
| 491 |
+
step3Desc: "实时监控 Provider 健康度。上游报错在 10ms 内自动熔断降级,并通过 Redis Pub/Sub 实现并发请求去重合并。",
|
| 492 |
+
proofTitle: "实证基准数据与代码效果验证",
|
| 493 |
+
proofSubTitle: "基准实证数据 (RouterBench 标准评估)",
|
| 494 |
+
p1Title: "测试评估样本",
|
| 495 |
+
p1Sub: "RouterBench 标准基准",
|
| 496 |
+
p2Title: "Prefill 延迟降低",
|
| 497 |
+
p2Sub: "Radix Trie KV-Cache 提速",
|
| 498 |
+
p3Title: "解题准确率无损保留",
|
| 499 |
+
p3Sub: "对比单用 GPT-4 Oracle",
|
| 500 |
+
p4Title: "高可用 SLA",
|
| 501 |
+
p4Sub: "10ms 快速熔断降级",
|
| 502 |
+
proofDesc: "在 4,682 条 RouterBench 基准数据集上的测试证明,InferRoute 在保留了 GPT-4 99.2% 的解题准确率的同时,降低了 35.8% 的 Prefill 延迟并削减了 87.7% 的 API 开销。",
|
| 503 |
+
costTitle: "实时节省金额与 Baseline 成本对比",
|
| 504 |
+
calcTitle: "成本节省计算公式",
|
| 505 |
+
calcDesc: "经由 InferRoute 路由的每一笔请求,数据库都会落盘记录若单用 GPT-4 的基准成本与实际路由模型的真实成本:",
|
| 506 |
+
directTitle: "直接盲目调用 GPT-4 (无网关)",
|
| 507 |
+
directSub: "100,000 次请求 @ $0.015 / 1k tokens",
|
| 508 |
+
routedTitle: "经由 InferRoute 智能网关路由",
|
| 509 |
+
routedSub: "净节省: $1,315.80 (降低 87.7% 成本)",
|
| 510 |
+
analyticsTitle: "全量监控与实时数据大屏",
|
| 511 |
+
btnRefresh: "🔄 刷新最新监控",
|
| 512 |
+
m1Title: "总处理请求数",
|
| 513 |
+
m1Sub: "各客户端全量汇总",
|
| 514 |
+
m2Title: "累计节省金额 ($)",
|
| 515 |
+
m2Sub: "对比单用 GPT-4",
|
| 516 |
+
m3Title: "OpenAI / Agent 调次",
|
| 517 |
+
m3Sub: "高推理模型集群",
|
| 518 |
+
m4Title: "Gemini / Quant 调次",
|
| 519 |
+
m4Sub: "极速交易模型集群",
|
| 520 |
+
navApi: "API 快速接入",
|
| 521 |
+
navApps: "生态应用广场",
|
| 522 |
+
navProof: "基准效果实证",
|
| 523 |
+
navCost: "省钱成本对比",
|
| 524 |
+
navAnalytics: "实时监控大屏",
|
| 525 |
+
appsTitle: "生态应用广场 (Hugging Face Spaces 矩阵)",
|
| 526 |
+
appsSubTitle: "接入 InferRoute API 网关驱动的项目",
|
| 527 |
+
appsDesc: "所有部署在 Hugging Face Spaces 或外部服务器的应用均通过 InferRoute 进行 LLM 请求路由。网关将自动记录各应用 Token 消耗、延迟及降本金额。",
|
| 528 |
+
app1Name: "Quant-AI 炒股/量化分析 Agent",
|
| 529 |
+
app1Desc: "股票分析、财报提取与量化策略代码生成。使用 Cascade 路由保障高难度推理准确率。",
|
| 530 |
+
app2Name: "Face & Vision 视觉特征识别 AI",
|
| 531 |
+
app2Desc: "人脸特征属性分析与多模态图像描述。自动将视觉查询路由至 Gemini Flash / Vision 节点。",
|
| 532 |
+
app3Name: "Multi-Agent 多智能体协作应用",
|
| 533 |
+
app3Desc: "自主多智能体任务编排。利用请求去重与 Radix Trie 缓存避免高频 Loop 调用中的重复计费。",
|
| 534 |
+
hfGuideTitle: "💡 Hugging Face 项目如何连接 InferRoute API"
|
| 535 |
+
}
|
| 536 |
+
};
|
| 537 |
+
|
| 538 |
+
function navigateTo(viewName) {
|
| 539 |
+
document.querySelectorAll('.spa-view').forEach(v => v.classList.remove('active'));
|
| 540 |
+
document.querySelectorAll('.nav-link').forEach(n => n.classList.remove('active'));
|
| 541 |
+
|
| 542 |
+
if (viewName === 'api') {
|
| 543 |
+
document.getElementById('viewApi').classList.add('active');
|
| 544 |
+
document.getElementById('navApi').classList.add('active');
|
| 545 |
+
window.location.hash = '#api';
|
| 546 |
+
} else if (viewName === 'apps') {
|
| 547 |
+
document.getElementById('viewApps').classList.add('active');
|
| 548 |
+
document.getElementById('navApps').classList.add('active');
|
| 549 |
+
window.location.hash = '#apps';
|
| 550 |
+
} else if (viewName === 'proof') {
|
| 551 |
+
document.getElementById('viewProof').classList.add('active');
|
| 552 |
+
document.getElementById('navProof').classList.add('active');
|
| 553 |
+
window.location.hash = '#proof';
|
| 554 |
+
} else if (viewName === 'cost') {
|
| 555 |
+
document.getElementById('viewCost').classList.add('active');
|
| 556 |
+
document.getElementById('navCost').classList.add('active');
|
| 557 |
+
window.location.hash = '#cost';
|
| 558 |
+
} else if (viewName === 'analytics') {
|
| 559 |
+
document.getElementById('viewAnalytics').classList.add('active');
|
| 560 |
+
document.getElementById('navAnalytics').classList.add('active');
|
| 561 |
+
window.location.hash = '#analytics';
|
| 562 |
+
fetchAnalyticsSummary();
|
| 563 |
+
}
|
| 564 |
+
|
| 565 |
+
window.scrollTo({ top: 0, behavior: 'smooth' });
|
| 566 |
+
}
|
| 567 |
+
|
| 568 |
+
function toggleLanguage() {
|
| 569 |
+
currentLang = (currentLang === 'EN') ? 'ZH' : 'EN';
|
| 570 |
+
const langData = i18n[currentLang];
|
| 571 |
+
|
| 572 |
+
for (let key in langData) {
|
| 573 |
+
const el = document.getElementById(key);
|
| 574 |
+
if (el) el.innerText = langData[key];
|
| 575 |
+
}
|
| 576 |
+
}
|
| 577 |
+
|
| 578 |
+
async function fetchAnalyticsSummary() {
|
| 579 |
+
const out = document.getElementById('analyticsOutput');
|
| 580 |
+
out.innerText = "🔄 Loading real-time evaluation metrics from eval_results.json...";
|
| 581 |
+
try {
|
| 582 |
+
const res = await fetch('/v1/analytics/summary');
|
| 583 |
+
const data = await res.json();
|
| 584 |
+
out.innerText = JSON.stringify(data, null, 2);
|
| 585 |
+
|
| 586 |
+
if (data.total_requests !== undefined) {
|
| 587 |
+
document.getElementById('valTotalReqs').innerText = data.total_requests.toLocaleString();
|
| 588 |
+
}
|
| 589 |
+
if (data.total_cost_saved_usd !== undefined) {
|
| 590 |
+
document.getElementById('valTotalSaved').innerText = '$' + data.total_cost_saved_usd.toFixed(2);
|
| 591 |
+
}
|
| 592 |
+
if (data.clients && data.clients.antigravity) {
|
| 593 |
+
document.getElementById('valAntigravityReqs').innerText = data.clients.antigravity.requests.toLocaleString();
|
| 594 |
+
}
|
| 595 |
+
if (data.clients && data.clients.quant_app) {
|
| 596 |
+
document.getElementById('valQuantReqs').innerText = data.clients.quant_app.requests.toLocaleString();
|
| 597 |
+
}
|
| 598 |
+
} catch (e) {
|
| 599 |
+
out.innerText = "Error: " + e.message;
|
| 600 |
+
}
|
| 601 |
+
}
|
| 602 |
+
|
| 603 |
+
// Handle initial hash routing
|
| 604 |
+
window.addEventListener('load', () => {
|
| 605 |
+
const hash = window.location.hash.replace('#', '');
|
| 606 |
+
if (['apps', 'proof', 'cost', 'analytics'].includes(hash)) {
|
| 607 |
+
navigateTo(hash);
|
| 608 |
+
} else {
|
| 609 |
+
navigateTo('api');
|
| 610 |
+
}
|
| 611 |
+
});
|
| 612 |
+
</script>
|
| 613 |
+
</body>
|
| 614 |
+
</html>
|
quant/index.html
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<!doctype html>
|
| 2 |
+
<html lang="en">
|
| 3 |
+
<head>
|
| 4 |
+
<meta charset="UTF-8" />
|
| 5 |
+
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
| 6 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
| 7 |
+
<title>Quant.ai Trading Terminal</title>
|
| 8 |
+
<script type="module" crossorigin src="/assets/index-C6qvxrC6.js"></script>
|
| 9 |
+
<link rel="stylesheet" crossorigin href="/assets/index-DCSOhC_L.css">
|
| 10 |
+
</head>
|
| 11 |
+
<body>
|
| 12 |
+
<div id="root"></div>
|
| 13 |
+
</body>
|
| 14 |
+
</html>
|
requirements.txt
CHANGED
|
@@ -25,3 +25,6 @@ pandas>=2.0.0
|
|
| 25 |
numpy>=1.24.0
|
| 26 |
yfinance>=0.2.30
|
| 27 |
scikit-learn>=1.2.0
|
|
|
|
|
|
|
|
|
|
|
|
| 25 |
numpy>=1.24.0
|
| 26 |
yfinance>=0.2.30
|
| 27 |
scikit-learn>=1.2.0
|
| 28 |
+
gradio>=5.0.0
|
| 29 |
+
alpaca-py>=0.20.0
|
| 30 |
+
pytz>=2024.1
|
scratch/deploy_inferroute_space.py
CHANGED
|
@@ -28,10 +28,16 @@ api.upload_folder(
|
|
| 28 |
"*.pyc",
|
| 29 |
"__pycache__/*",
|
| 30 |
"**/__pycache__/*",
|
|
|
|
| 31 |
".git/*",
|
| 32 |
"**/.git/*",
|
|
|
|
| 33 |
".venv/*",
|
| 34 |
"**/.venv/*",
|
|
|
|
|
|
|
|
|
|
|
|
|
| 35 |
"node_modules/*",
|
| 36 |
"**/node_modules/*",
|
| 37 |
"scratch/*",
|
|
|
|
| 28 |
"*.pyc",
|
| 29 |
"__pycache__/*",
|
| 30 |
"**/__pycache__/*",
|
| 31 |
+
".git",
|
| 32 |
".git/*",
|
| 33 |
"**/.git/*",
|
| 34 |
+
".venv",
|
| 35 |
".venv/*",
|
| 36 |
"**/.venv/*",
|
| 37 |
+
".pytest_cache",
|
| 38 |
+
".pytest_cache/*",
|
| 39 |
+
"**/.pytest_cache/*",
|
| 40 |
+
"node_modules",
|
| 41 |
"node_modules/*",
|
| 42 |
"**/node_modules/*",
|
| 43 |
"scratch/*",
|