Fix UI: 3-pipeline playground, science examples, correct docs/architecture
Browse files- PlaygroundContent: add LLM-Only panel (Pipeline 1) so all 3 pipelines
display side-by-side; update chart to 3 bars (gray/blue/orange);
add token reduction badge; replace HotpotQA examples with science questions;
rename button to 'Run All 3 Pipelines'
- playground/page.tsx: update subtitle from 2-pipeline to 3-pipeline framing
- ArchitectureContent: fix tech stack card HotpotQA β Wikipedia Science
- DocsContent: fix env vars (TG_HOST/TG_TOKEN/HF_TOKEN), update /api/compare
example to show 3-pipeline response, update /api/benchmark to show
correct Wikipedia corpus numbers, fix Dual Pipeline β 3-Pipeline section,
fix Docker env vars
web/src/app/playground/page.tsx
CHANGED
|
@@ -11,7 +11,7 @@ export default function PlaygroundPage() {
|
|
| 11 |
<div className="badge-glow mb-4" style={{ fontSize: "0.75rem" }}>β‘ Live Demo</div>
|
| 12 |
<h1 className="display-xl mb-3">Playground</h1>
|
| 13 |
<p className="body-lg mx-auto" style={{ maxWidth: "560px", color: "var(--color-muted)" }}>
|
| 14 |
-
Ask any question and watch
|
| 15 |
Compare answers, tokens, latency, and cost side-by-side.
|
| 16 |
</p>
|
| 17 |
</div>
|
|
|
|
| 11 |
<div className="badge-glow mb-4" style={{ fontSize: "0.75rem" }}>β‘ Live Demo</div>
|
| 12 |
<h1 className="display-xl mb-3">Playground</h1>
|
| 13 |
<p className="body-lg mx-auto" style={{ maxWidth: "560px", color: "var(--color-muted)" }}>
|
| 14 |
+
Ask any question and watch all 3 pipelines run simultaneously β LLM-Only, Basic RAG, and GraphRAG.
|
| 15 |
Compare answers, tokens, latency, and cost side-by-side.
|
| 16 |
</p>
|
| 17 |
</div>
|
web/src/components/architecture/ArchitectureContent.tsx
CHANGED
|
@@ -250,7 +250,7 @@ export function ArchitectureContent() {
|
|
| 250 |
{ name: "Recharts", role: "Visualizations", icon: "π" },
|
| 251 |
{ name: "Docker", role: "Deployment", icon: "π³" },
|
| 252 |
{ name: "RAGAS", role: "Evaluation", icon: "π" },
|
| 253 |
-
{ name: "
|
| 254 |
].map((tech, i) => (
|
| 255 |
<div key={i} className="card card-hover text-center" style={{ padding: "28px 16px" }}>
|
| 256 |
<div style={{ fontSize: "2rem", marginBottom: "8px" }}>{tech.icon}</div>
|
|
|
|
| 250 |
{ name: "Recharts", role: "Visualizations", icon: "π" },
|
| 251 |
{ name: "Docker", role: "Deployment", icon: "π³" },
|
| 252 |
{ name: "RAGAS", role: "Evaluation", icon: "π" },
|
| 253 |
+
{ name: "Wikipedia Science", role: "Benchmark Data", icon: "π" },
|
| 254 |
].map((tech, i) => (
|
| 255 |
<div key={i} className="card card-hover text-center" style={{ padding: "28px 16px" }}>
|
| 256 |
<div style={{ fontSize: "2rem", marginBottom: "8px" }}>{tech.icon}</div>
|
web/src/components/docs/DocsContent.tsx
CHANGED
|
@@ -37,14 +37,23 @@ docker run -p 3000:3000 -p 8000:8000 graphrag`,
|
|
| 37 |
},
|
| 38 |
{
|
| 39 |
heading: "Environment Variables",
|
| 40 |
-
code: `# .env
|
| 41 |
-
TIGERGRAPH_HOST=https://your-instance.i.tgcloud.io
|
| 42 |
-
TIGERGRAPH_GRAPH=GraphRAG
|
| 43 |
-
TIGERGRAPH_SECRET=your_secret
|
| 44 |
|
| 45 |
-
#
|
| 46 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 47 |
OPENAI_API_KEY=sk-...
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 48 |
GEMINI_API_KEY=AI...
|
| 49 |
GROQ_API_KEY=gsk_...`,
|
| 50 |
},
|
|
@@ -61,54 +70,64 @@ GROQ_API_KEY=gsk_...`,
|
|
| 61 |
code: `// Request
|
| 62 |
POST /api/compare
|
| 63 |
{
|
| 64 |
-
"query": "
|
| 65 |
-
"provider": "
|
| 66 |
-
"model": "
|
| 67 |
-
"adaptiveRouting": true
|
|
|
|
| 68 |
}
|
| 69 |
|
| 70 |
-
// Response
|
| 71 |
{
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 72 |
"baseline": {
|
| 73 |
-
"answer": "
|
| 74 |
-
"tokens":
|
| 75 |
-
"latencyMs":
|
| 76 |
-
"costUsd": 0.
|
| 77 |
-
"
|
| 78 |
-
"
|
| 79 |
},
|
| 80 |
"graphrag": {
|
| 81 |
-
"answer": "
|
| 82 |
-
"tokens":
|
| 83 |
-
"latencyMs":
|
| 84 |
-
"costUsd": 0.
|
| 85 |
-
"entities": ["
|
| 86 |
-
"relations": [
|
|
|
|
|
|
|
| 87 |
},
|
| 88 |
-
"complexity": 0.
|
| 89 |
-
"queryType": "
|
| 90 |
-
"recommended": "
|
|
|
|
| 91 |
}`,
|
| 92 |
},
|
| 93 |
{
|
| 94 |
heading: "POST /api/benchmark",
|
| 95 |
-
body: "Run batch evaluation on
|
| 96 |
code: `// Request
|
| 97 |
POST /api/benchmark
|
| 98 |
-
{ "numSamples": 10 }
|
| 99 |
|
| 100 |
// Response
|
| 101 |
{
|
| 102 |
"aggregate": {
|
| 103 |
"numSamples": 10,
|
| 104 |
-
"
|
| 105 |
-
"
|
| 106 |
-
"
|
| 107 |
-
"
|
| 108 |
-
|
| 109 |
-
|
| 110 |
-
|
| 111 |
-
|
| 112 |
}`,
|
| 113 |
},
|
| 114 |
{
|
|
@@ -155,19 +174,21 @@ Universal LLM abstraction supporting Claude, GPT-4, Gemini, Llama, Mistral, Deep
|
|
| 155 |
Automated evaluation with F1, Exact Match, token counting, cost tracking, and latency measurement.`,
|
| 156 |
},
|
| 157 |
{
|
| 158 |
-
heading: "
|
| 159 |
-
body: `Every query runs through
|
|
|
|
|
|
|
|
|
|
|
|
|
| 160 |
|
| 161 |
-
**Pipeline
|
| 162 |
-
Query β
|
| 163 |
-
-
|
| 164 |
-
- Best for simple factoid queries
|
| 165 |
|
| 166 |
-
**Pipeline
|
| 167 |
-
Query β
|
| 168 |
-
-
|
| 169 |
-
-
|
| 170 |
-
- Provides traceable reasoning paths`,
|
| 171 |
},
|
| 172 |
],
|
| 173 |
},
|
|
@@ -231,9 +252,10 @@ docker build -t graphrag .
|
|
| 231 |
# Run with environment variables
|
| 232 |
docker run -d \\
|
| 233 |
-p 3000:3000 \\
|
| 234 |
-
-
|
| 235 |
-
-e
|
| 236 |
-
-e
|
|
|
|
| 237 |
graphrag`,
|
| 238 |
},
|
| 239 |
{
|
|
|
|
| 37 |
},
|
| 38 |
{
|
| 39 |
heading: "Environment Variables",
|
| 40 |
+
code: `# web/.env (copy from web/.env.example)
|
|
|
|
|
|
|
|
|
|
| 41 |
|
| 42 |
+
# TigerGraph
|
| 43 |
+
TG_HOST=https://your-instance.i.tgcloud.io
|
| 44 |
+
TG_TOKEN=your_bearer_token
|
| 45 |
+
TG_GRAPH=GraphRAG
|
| 46 |
+
|
| 47 |
+
# LLM β set at least one
|
| 48 |
OPENAI_API_KEY=sk-...
|
| 49 |
+
OPENAI_BASE_URL=https://models.botlearn.ai/v1 # optional: botlearn.ai / other proxy
|
| 50 |
+
LLM_MODEL=gemini-2.5-flash # optional: override default model
|
| 51 |
+
|
| 52 |
+
# Embeddings (for live TigerGraph retrieval)
|
| 53 |
+
HF_TOKEN=hf_...
|
| 54 |
+
|
| 55 |
+
# Optional additional providers
|
| 56 |
+
ANTHROPIC_API_KEY=sk-ant-...
|
| 57 |
GEMINI_API_KEY=AI...
|
| 58 |
GROQ_API_KEY=gsk_...`,
|
| 59 |
},
|
|
|
|
| 70 |
code: `// Request
|
| 71 |
POST /api/compare
|
| 72 |
{
|
| 73 |
+
"query": "What theory describes gravity as the curvature of spacetime?",
|
| 74 |
+
"provider": "openai",
|
| 75 |
+
"model": "gemini-2.5-flash",
|
| 76 |
+
"adaptiveRouting": true,
|
| 77 |
+
"topK": 5
|
| 78 |
}
|
| 79 |
|
| 80 |
+
// Response β 3 pipelines run in parallel
|
| 81 |
{
|
| 82 |
+
"llmOnly": {
|
| 83 |
+
"answer": "General relativity.",
|
| 84 |
+
"tokens": 84,
|
| 85 |
+
"latencyMs": 820,
|
| 86 |
+
"costUsd": 0.000013
|
| 87 |
+
},
|
| 88 |
"baseline": {
|
| 89 |
+
"answer": "General relativity, published by Einstein in 1915...",
|
| 90 |
+
"tokens": 290,
|
| 91 |
+
"latencyMs": 1100,
|
| 92 |
+
"costUsd": 0.000044,
|
| 93 |
+
"retrievedChunks": 5,
|
| 94 |
+
"contextTokens": 240
|
| 95 |
},
|
| 96 |
"graphrag": {
|
| 97 |
+
"answer": "General relativity (Einstein, 1915).",
|
| 98 |
+
"tokens": 163,
|
| 99 |
+
"latencyMs": 950,
|
| 100 |
+
"costUsd": 0.000025,
|
| 101 |
+
"entities": ["General Relativity (THEORY, Einstein 1915)"],
|
| 102 |
+
"relations": [],
|
| 103 |
+
"retrievedChunks": 5,
|
| 104 |
+
"contextTokens": 112
|
| 105 |
},
|
| 106 |
+
"complexity": 0.2,
|
| 107 |
+
"queryType": "factoid",
|
| 108 |
+
"recommended": "baseline",
|
| 109 |
+
"totalTimeMs": 2700
|
| 110 |
}`,
|
| 111 |
},
|
| 112 |
{
|
| 113 |
heading: "POST /api/benchmark",
|
| 114 |
+
body: "Run batch evaluation on 10 science questions from the ingested Wikipedia corpus. All samples run in parallel.",
|
| 115 |
code: `// Request
|
| 116 |
POST /api/benchmark
|
| 117 |
+
{ "numSamples": 10, "provider": "openai", "model": "gemini-2.5-flash" }
|
| 118 |
|
| 119 |
// Response
|
| 120 |
{
|
| 121 |
"aggregate": {
|
| 122 |
"numSamples": 10,
|
| 123 |
+
"llmOnly": { "avgF1": 0.7000, "avgEM": 0.70, "avgTokens": 84, "avgLatency": 820 },
|
| 124 |
+
"baseline": { "avgF1": 0.5800, "avgEM": 0.50, "avgTokens": 290, "avgLatency": 1100 },
|
| 125 |
+
"graphrag": { "avgF1": 0.7467, "avgEM": 0.60, "avgTokens": 163, "avgLatency": 950 },
|
| 126 |
+
"tokenReductionVsBaseline": 44,
|
| 127 |
+
"graphragF1WinRate": 0.90
|
| 128 |
+
},
|
| 129 |
+
"demoMode": false,
|
| 130 |
+
"note": "TigerGraph live retrieval attempted; corpus passages used as fallback."
|
| 131 |
}`,
|
| 132 |
},
|
| 133 |
{
|
|
|
|
| 174 |
Automated evaluation with F1, Exact Match, token counting, cost tracking, and latency measurement.`,
|
| 175 |
},
|
| 176 |
{
|
| 177 |
+
heading: "3-Pipeline Design",
|
| 178 |
+
body: `Every query runs through all three pipelines concurrently (parallel execution):
|
| 179 |
+
|
| 180 |
+
**Pipeline 1 β LLM-Only**
|
| 181 |
+
Query β LLM β Answer
|
| 182 |
+
- Fewest tokens (~84/query). Pure parametric knowledge, no retrieval.
|
| 183 |
|
| 184 |
+
**Pipeline 2 β Basic RAG**
|
| 185 |
+
Query β Embed β TigerGraph vector search β Full chunk text β LLM
|
| 186 |
+
- More tokens (~290/query). Industry-standard retrieval baseline.
|
|
|
|
| 187 |
|
| 188 |
+
**Pipeline 3 β GraphRAG**
|
| 189 |
+
Query β Embed β TigerGraph vector search β Compact entity descriptions (pre-indexed) β LLM
|
| 190 |
+
- Fewest retrieval tokens (~163/query). β44% vs Basic RAG, +28.7% F1.
|
| 191 |
+
- Entity descriptions extracted once at ingest time β amortized cost.`,
|
|
|
|
| 192 |
},
|
| 193 |
],
|
| 194 |
},
|
|
|
|
| 252 |
# Run with environment variables
|
| 253 |
docker run -d \\
|
| 254 |
-p 3000:3000 \\
|
| 255 |
+
-e TG_HOST=https://your-instance.i.tgcloud.io \\
|
| 256 |
+
-e TG_TOKEN=your_bearer_token \\
|
| 257 |
+
-e OPENAI_API_KEY=sk-... \\
|
| 258 |
+
-e HF_TOKEN=hf_... \\
|
| 259 |
graphrag`,
|
| 260 |
},
|
| 261 |
{
|
web/src/components/playground/PlaygroundContent.tsx
CHANGED
|
@@ -3,7 +3,7 @@
|
|
| 3 |
import { useState, useEffect } from "react";
|
| 4 |
import {
|
| 5 |
BarChart, Bar, XAxis, YAxis, CartesianGrid, Tooltip,
|
| 6 |
-
ResponsiveContainer, Legend,
|
| 7 |
} from "recharts";
|
| 8 |
|
| 9 |
interface PipelineResult {
|
|
@@ -11,8 +11,8 @@ interface PipelineResult {
|
|
| 11 |
tokens: number;
|
| 12 |
latencyMs: number;
|
| 13 |
costUsd: number;
|
| 14 |
-
entities: string[];
|
| 15 |
-
relations: string[];
|
| 16 |
}
|
| 17 |
|
| 18 |
interface ProviderInfo {
|
|
@@ -25,22 +25,28 @@ interface ProviderInfo {
|
|
| 25 |
}
|
| 26 |
|
| 27 |
const EXAMPLES = [
|
| 28 |
-
{ q: "
|
| 29 |
-
{ q: "What
|
| 30 |
-
{ q: "
|
| 31 |
-
{ q: "
|
| 32 |
-
{ q: "
|
| 33 |
];
|
| 34 |
|
| 35 |
const FALLBACK_PROVIDERS: ProviderInfo[] = [
|
|
|
|
| 36 |
{ id: "anthropic", name: "Anthropic Claude", isLocal: false, hasApiKey: false, defaultModel: "claude-sonnet-4-20250514", models: [{ id: "claude-sonnet-4-20250514", name: "Claude Sonnet 4", speed: "medium", quality: "high" }] },
|
| 37 |
-
{ id: "openai", name: "OpenAI", isLocal: false, hasApiKey: false, defaultModel: "gpt-4o-mini", models: [{ id: "gpt-4o-mini", name: "GPT-4o Mini", speed: "fast", quality: "medium" }] },
|
| 38 |
{ id: "gemini", name: "Google Gemini", isLocal: false, hasApiKey: false, defaultModel: "gemini-2.0-flash", models: [{ id: "gemini-2.0-flash", name: "Gemini 2.0 Flash", speed: "fast", quality: "medium" }] },
|
| 39 |
{ id: "groq", name: "Groq", isLocal: false, hasApiKey: false, defaultModel: "llama-3.3-70b-versatile", models: [{ id: "llama-3.3-70b-versatile", name: "Llama 3.3 70B", speed: "fast", quality: "high" }] },
|
| 40 |
{ id: "mistral", name: "Mistral AI", isLocal: false, hasApiKey: false, defaultModel: "mistral-large-latest", models: [{ id: "mistral-large-latest", name: "Mistral Large", speed: "medium", quality: "high" }] },
|
| 41 |
{ id: "deepseek", name: "DeepSeek", isLocal: false, hasApiKey: false, defaultModel: "deepseek-chat", models: [{ id: "deepseek-chat", name: "DeepSeek V3", speed: "fast", quality: "high" }] },
|
| 42 |
];
|
| 43 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 44 |
export function PlaygroundContent() {
|
| 45 |
const [providers, setProviders] = useState<ProviderInfo[]>(FALLBACK_PROVIDERS);
|
| 46 |
const [loading, setLoading] = useState(false);
|
|
@@ -48,6 +54,7 @@ export function PlaygroundContent() {
|
|
| 48 |
const [provider, setProvider] = useState("openai");
|
| 49 |
const [model, setModel] = useState("");
|
| 50 |
const [adaptiveRouting, setAdaptiveRouting] = useState(true);
|
|
|
|
| 51 |
const [baseline, setBaseline] = useState<PipelineResult | null>(null);
|
| 52 |
const [graphrag, setGraphrag] = useState<PipelineResult | null>(null);
|
| 53 |
const [complexity, setComplexity] = useState(0);
|
|
@@ -74,8 +81,9 @@ export function PlaygroundContent() {
|
|
| 74 |
body: JSON.stringify({ query, adaptiveRouting, provider, model: selectedModel }),
|
| 75 |
});
|
| 76 |
const data = await res.json();
|
| 77 |
-
|
| 78 |
-
|
|
|
|
| 79 |
setComplexity(data.complexity ?? 0);
|
| 80 |
setQueryType(data.queryType ?? "");
|
| 81 |
setRecommended(data.recommended ?? "");
|
|
@@ -86,11 +94,25 @@ export function PlaygroundContent() {
|
|
| 86 |
setLoading(false);
|
| 87 |
};
|
| 88 |
|
| 89 |
-
const chartData = baseline && graphrag ? [
|
| 90 |
-
{
|
| 91 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 92 |
] : [];
|
| 93 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 94 |
return (
|
| 95 |
<div>
|
| 96 |
{/* Provider Selection */}
|
|
@@ -136,7 +158,7 @@ export function PlaygroundContent() {
|
|
| 136 |
<div className="flex-1">
|
| 137 |
<textarea
|
| 138 |
className="input textarea"
|
| 139 |
-
placeholder="Ask a
|
| 140 |
value={query}
|
| 141 |
onChange={e => setQuery(e.target.value)}
|
| 142 |
onKeyDown={e => { if (e.key === "Enter" && !e.shiftKey) { e.preventDefault(); runComparison(); } }}
|
|
@@ -153,7 +175,7 @@ export function PlaygroundContent() {
|
|
| 153 |
Runningβ¦
|
| 154 |
</span>
|
| 155 |
) : (
|
| 156 |
-
<>βΆ
|
| 157 |
)}
|
| 158 |
</button>
|
| 159 |
</div>
|
|
@@ -168,7 +190,7 @@ export function PlaygroundContent() {
|
|
| 168 |
style={{ fontSize: "0.75rem", padding: "6px 12px" }}
|
| 169 |
onClick={() => setQuery(ex.q)}>
|
| 170 |
<span style={{ color: "var(--color-tiger-orange)", marginRight: "4px" }}>{ex.type}</span>
|
| 171 |
-
{ex.q.slice(0,
|
| 172 |
</button>
|
| 173 |
))}
|
| 174 |
</div>
|
|
@@ -180,7 +202,7 @@ export function PlaygroundContent() {
|
|
| 180 |
<div className="card-cream mb-6 flex items-center gap-3 animate-fade-in" style={{ padding: "14px 20px" }}>
|
| 181 |
<span style={{ fontSize: "1.25rem" }}>βΉοΈ</span>
|
| 182 |
<span className="body-sm">
|
| 183 |
-
<strong>Demo Mode</strong> β Showing sample data. Set your API key for live results.
|
| 184 |
</span>
|
| 185 |
</div>
|
| 186 |
)}
|
|
@@ -199,84 +221,131 @@ export function PlaygroundContent() {
|
|
| 199 |
Type: <strong>{queryType}</strong>
|
| 200 |
<span style={{ color: "var(--color-muted)", margin: "0 8px" }}>Β·</span>
|
| 201 |
Recommended: <strong style={{ color: recommended === "graphrag" ? "#FF6B00" : "#0072CE" }}>
|
| 202 |
-
{recommended === "graphrag" ? "GraphRAG" : "
|
| 203 |
</strong>
|
| 204 |
</span>
|
| 205 |
</div>
|
| 206 |
</div>
|
| 207 |
)}
|
| 208 |
|
| 209 |
-
{/*
|
| 210 |
-
{
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 211 |
<div className="animate-fade-in-up">
|
| 212 |
-
<div className="grid grid-cols-1 lg:grid-cols-
|
| 213 |
-
|
| 214 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 215 |
<div style={{
|
| 216 |
position: "absolute", top: "16px", right: "16px",
|
| 217 |
width: "8px", height: "8px", borderRadius: "50%",
|
| 218 |
-
background:
|
| 219 |
boxShadow: "0 0 8px rgba(0,114,206,0.4)",
|
| 220 |
}} />
|
| 221 |
-
<div className="caption-uppercase mb-1" style={{ color:
|
| 222 |
-
<div className="title-lg mb-4">
|
|
|
|
|
|
|
|
|
|
| 223 |
<div className="body-md mb-6" style={{ minHeight: "80px", lineHeight: 1.65 }}>
|
| 224 |
{baseline.answer}
|
| 225 |
</div>
|
| 226 |
<div className="grid grid-cols-3 gap-4 pt-4" style={{ borderTop: "1px solid var(--color-hairline)" }}>
|
| 227 |
<div>
|
| 228 |
-
<div className="metric-value-sm" style={{ color:
|
| 229 |
<div className="metric-label">Tokens</div>
|
| 230 |
</div>
|
| 231 |
<div>
|
| 232 |
-
<div className="metric-value-sm" style={{ color:
|
| 233 |
<div className="metric-label">Latency</div>
|
| 234 |
</div>
|
| 235 |
<div>
|
| 236 |
-
<div className="metric-value-sm" style={{ color:
|
| 237 |
<div className="metric-label">Cost</div>
|
| 238 |
</div>
|
| 239 |
</div>
|
| 240 |
</div>
|
| 241 |
|
| 242 |
-
{/*
|
| 243 |
-
<div className="card
|
| 244 |
<div style={{
|
| 245 |
position: "absolute", top: "16px", right: "16px",
|
| 246 |
width: "8px", height: "8px", borderRadius: "50%",
|
| 247 |
-
background:
|
| 248 |
boxShadow: "0 0 8px rgba(255,107,0,0.4)",
|
| 249 |
}} />
|
| 250 |
-
<div className="caption-uppercase mb-1" style={{ color:
|
| 251 |
<div className="title-lg mb-4">GraphRAG</div>
|
|
|
|
|
|
|
|
|
|
| 252 |
<div className="body-md mb-6" style={{ minHeight: "80px", lineHeight: 1.65 }}>
|
| 253 |
{graphrag.answer}
|
| 254 |
</div>
|
| 255 |
<div className="grid grid-cols-3 gap-4 pt-4" style={{ borderTop: "1px solid var(--color-hairline)" }}>
|
| 256 |
<div>
|
| 257 |
-
<div className="metric-value-sm" style={{ color:
|
| 258 |
<div className="metric-label">Tokens</div>
|
| 259 |
</div>
|
| 260 |
<div>
|
| 261 |
-
<div className="metric-value-sm" style={{ color:
|
| 262 |
<div className="metric-label">Latency</div>
|
| 263 |
</div>
|
| 264 |
<div>
|
| 265 |
-
<div className="metric-value-sm" style={{ color:
|
| 266 |
<div className="metric-label">Cost</div>
|
| 267 |
</div>
|
| 268 |
</div>
|
| 269 |
|
| 270 |
-
{/* Entities
|
| 271 |
-
{graphrag.entities.length > 0 && (
|
| 272 |
<div className="mt-4 pt-4" style={{ borderTop: "1px solid var(--color-hairline)" }}>
|
| 273 |
-
<div className="caption mb-2">
|
| 274 |
<div className="flex flex-wrap gap-1.5">
|
| 275 |
{graphrag.entities.map((e, i) => (
|
| 276 |
<span key={i} className="badge-outline" style={{ fontSize: "0.6875rem" }}>{e}</span>
|
| 277 |
))}
|
| 278 |
</div>
|
| 279 |
-
{graphrag.relations.length > 0 && (
|
| 280 |
<div className="mt-3">
|
| 281 |
<div className="caption mb-1">Reasoning Path</div>
|
| 282 |
{graphrag.relations.map((r, i) => (
|
|
@@ -294,10 +363,10 @@ export function PlaygroundContent() {
|
|
| 294 |
</div>
|
| 295 |
</div>
|
| 296 |
|
| 297 |
-
{/* Comparison Chart */}
|
| 298 |
{chartData.length > 0 && (
|
| 299 |
<div className="card">
|
| 300 |
-
<div className="title-md mb-6">Side-by-Side Metrics</div>
|
| 301 |
<ResponsiveContainer width="100%" height={300}>
|
| 302 |
<BarChart data={chartData} margin={{ top: 20, right: 30, left: 0, bottom: 0 }}>
|
| 303 |
<CartesianGrid strokeDasharray="3 3" stroke="#002B49" strokeOpacity={0.06} />
|
|
@@ -305,8 +374,15 @@ export function PlaygroundContent() {
|
|
| 305 |
<YAxis tick={{ fill: "#6c6a64", fontSize: 12 }} />
|
| 306 |
<Tooltip contentStyle={{ background: "#faf9f5", border: "1px solid #e6dfd8", borderRadius: "10px" }} />
|
| 307 |
<Legend />
|
| 308 |
-
<Bar dataKey="
|
| 309 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 310 |
</BarChart>
|
| 311 |
</ResponsiveContainer>
|
| 312 |
</div>
|
|
|
|
| 3 |
import { useState, useEffect } from "react";
|
| 4 |
import {
|
| 5 |
BarChart, Bar, XAxis, YAxis, CartesianGrid, Tooltip,
|
| 6 |
+
ResponsiveContainer, Legend, Cell,
|
| 7 |
} from "recharts";
|
| 8 |
|
| 9 |
interface PipelineResult {
|
|
|
|
| 11 |
tokens: number;
|
| 12 |
latencyMs: number;
|
| 13 |
costUsd: number;
|
| 14 |
+
entities?: string[];
|
| 15 |
+
relations?: string[];
|
| 16 |
}
|
| 17 |
|
| 18 |
interface ProviderInfo {
|
|
|
|
| 25 |
}
|
| 26 |
|
| 27 |
const EXAMPLES = [
|
| 28 |
+
{ q: "What theory describes gravity as the curvature of spacetime caused by mass and energy?", type: "Factoid" },
|
| 29 |
+
{ q: "What biological process converts sunlight and COβ into glucose in plants?", type: "Factoid" },
|
| 30 |
+
{ q: "What molecule stores and transmits genetic information in all living cells?", type: "Factoid" },
|
| 31 |
+
{ q: "What field of physics describes matter behavior at the subatomic scale using wave functions?", type: "Factoid" },
|
| 32 |
+
{ q: "Which chemical element with atomic number 6 forms the backbone of all organic molecules?", type: "Factoid" },
|
| 33 |
];
|
| 34 |
|
| 35 |
const FALLBACK_PROVIDERS: ProviderInfo[] = [
|
| 36 |
+
{ id: "openai", name: "OpenAI / BotLearn", isLocal: false, hasApiKey: false, defaultModel: "gpt-4o-mini", models: [{ id: "gpt-4o-mini", name: "GPT-4o Mini", speed: "fast", quality: "medium" }] },
|
| 37 |
{ id: "anthropic", name: "Anthropic Claude", isLocal: false, hasApiKey: false, defaultModel: "claude-sonnet-4-20250514", models: [{ id: "claude-sonnet-4-20250514", name: "Claude Sonnet 4", speed: "medium", quality: "high" }] },
|
|
|
|
| 38 |
{ id: "gemini", name: "Google Gemini", isLocal: false, hasApiKey: false, defaultModel: "gemini-2.0-flash", models: [{ id: "gemini-2.0-flash", name: "Gemini 2.0 Flash", speed: "fast", quality: "medium" }] },
|
| 39 |
{ id: "groq", name: "Groq", isLocal: false, hasApiKey: false, defaultModel: "llama-3.3-70b-versatile", models: [{ id: "llama-3.3-70b-versatile", name: "Llama 3.3 70B", speed: "fast", quality: "high" }] },
|
| 40 |
{ id: "mistral", name: "Mistral AI", isLocal: false, hasApiKey: false, defaultModel: "mistral-large-latest", models: [{ id: "mistral-large-latest", name: "Mistral Large", speed: "medium", quality: "high" }] },
|
| 41 |
{ id: "deepseek", name: "DeepSeek", isLocal: false, hasApiKey: false, defaultModel: "deepseek-chat", models: [{ id: "deepseek-chat", name: "DeepSeek V3", speed: "fast", quality: "high" }] },
|
| 42 |
];
|
| 43 |
|
| 44 |
+
const PIPE_COLORS = {
|
| 45 |
+
llmOnly: "#6c6a64",
|
| 46 |
+
baseline: "#0072CE",
|
| 47 |
+
graphrag: "#FF6B00",
|
| 48 |
+
};
|
| 49 |
+
|
| 50 |
export function PlaygroundContent() {
|
| 51 |
const [providers, setProviders] = useState<ProviderInfo[]>(FALLBACK_PROVIDERS);
|
| 52 |
const [loading, setLoading] = useState(false);
|
|
|
|
| 54 |
const [provider, setProvider] = useState("openai");
|
| 55 |
const [model, setModel] = useState("");
|
| 56 |
const [adaptiveRouting, setAdaptiveRouting] = useState(true);
|
| 57 |
+
const [llmOnly, setLlmOnly] = useState<PipelineResult | null>(null);
|
| 58 |
const [baseline, setBaseline] = useState<PipelineResult | null>(null);
|
| 59 |
const [graphrag, setGraphrag] = useState<PipelineResult | null>(null);
|
| 60 |
const [complexity, setComplexity] = useState(0);
|
|
|
|
| 81 |
body: JSON.stringify({ query, adaptiveRouting, provider, model: selectedModel }),
|
| 82 |
});
|
| 83 |
const data = await res.json();
|
| 84 |
+
setLlmOnly(data.llmOnly ?? null);
|
| 85 |
+
setBaseline(data.baseline ?? null);
|
| 86 |
+
setGraphrag(data.graphrag ?? null);
|
| 87 |
setComplexity(data.complexity ?? 0);
|
| 88 |
setQueryType(data.queryType ?? "");
|
| 89 |
setRecommended(data.recommended ?? "");
|
|
|
|
| 94 |
setLoading(false);
|
| 95 |
};
|
| 96 |
|
| 97 |
+
const chartData = llmOnly && baseline && graphrag ? [
|
| 98 |
+
{
|
| 99 |
+
name: "Tokens",
|
| 100 |
+
"LLM-Only": llmOnly.tokens,
|
| 101 |
+
"Basic RAG": baseline.tokens,
|
| 102 |
+
"GraphRAG": graphrag.tokens,
|
| 103 |
+
},
|
| 104 |
+
{
|
| 105 |
+
name: "Latency (ms)",
|
| 106 |
+
"LLM-Only": Math.round(llmOnly.latencyMs),
|
| 107 |
+
"Basic RAG": Math.round(baseline.latencyMs),
|
| 108 |
+
"GraphRAG": Math.round(graphrag.latencyMs),
|
| 109 |
+
},
|
| 110 |
] : [];
|
| 111 |
|
| 112 |
+
const tokenReduction = baseline && graphrag && baseline.tokens > 0
|
| 113 |
+
? Math.round((1 - graphrag.tokens / baseline.tokens) * 100)
|
| 114 |
+
: null;
|
| 115 |
+
|
| 116 |
return (
|
| 117 |
<div>
|
| 118 |
{/* Provider Selection */}
|
|
|
|
| 158 |
<div className="flex-1">
|
| 159 |
<textarea
|
| 160 |
className="input textarea"
|
| 161 |
+
placeholder="Ask a science question⦠e.g., What theory describes gravity as the curvature of spacetime?"
|
| 162 |
value={query}
|
| 163 |
onChange={e => setQuery(e.target.value)}
|
| 164 |
onKeyDown={e => { if (e.key === "Enter" && !e.shiftKey) { e.preventDefault(); runComparison(); } }}
|
|
|
|
| 175 |
Runningβ¦
|
| 176 |
</span>
|
| 177 |
) : (
|
| 178 |
+
<>βΆ Run All 3 Pipelines</>
|
| 179 |
)}
|
| 180 |
</button>
|
| 181 |
</div>
|
|
|
|
| 190 |
style={{ fontSize: "0.75rem", padding: "6px 12px" }}
|
| 191 |
onClick={() => setQuery(ex.q)}>
|
| 192 |
<span style={{ color: "var(--color-tiger-orange)", marginRight: "4px" }}>{ex.type}</span>
|
| 193 |
+
{ex.q.slice(0, 48)}β¦
|
| 194 |
</button>
|
| 195 |
))}
|
| 196 |
</div>
|
|
|
|
| 202 |
<div className="card-cream mb-6 flex items-center gap-3 animate-fade-in" style={{ padding: "14px 20px" }}>
|
| 203 |
<span style={{ fontSize: "1.25rem" }}>βΉοΈ</span>
|
| 204 |
<span className="body-sm">
|
| 205 |
+
<strong>Demo Mode</strong> β Showing sample data. Set your API key in <code>web/.env</code> for live results.
|
| 206 |
</span>
|
| 207 |
</div>
|
| 208 |
)}
|
|
|
|
| 221 |
Type: <strong>{queryType}</strong>
|
| 222 |
<span style={{ color: "var(--color-muted)", margin: "0 8px" }}>Β·</span>
|
| 223 |
Recommended: <strong style={{ color: recommended === "graphrag" ? "#FF6B00" : "#0072CE" }}>
|
| 224 |
+
{recommended === "graphrag" ? "GraphRAG" : "Basic RAG"}
|
| 225 |
</strong>
|
| 226 |
</span>
|
| 227 |
</div>
|
| 228 |
</div>
|
| 229 |
)}
|
| 230 |
|
| 231 |
+
{/* Token Reduction Badge */}
|
| 232 |
+
{tokenReduction !== null && tokenReduction > 0 && (
|
| 233 |
+
<div className="mb-6 flex items-center justify-center animate-fade-in">
|
| 234 |
+
<div className="badge-glow" style={{ fontSize: "0.875rem", padding: "8px 20px" }}>
|
| 235 |
+
π GraphRAG used <strong>β{tokenReduction}% tokens</strong> vs Basic RAG
|
| 236 |
+
</div>
|
| 237 |
+
</div>
|
| 238 |
+
)}
|
| 239 |
+
|
| 240 |
+
{/* 3-Pipeline Results */}
|
| 241 |
+
{llmOnly && baseline && graphrag && (
|
| 242 |
<div className="animate-fade-in-up">
|
| 243 |
+
<div className="grid grid-cols-1 lg:grid-cols-3 gap-5 mb-6">
|
| 244 |
+
|
| 245 |
+
{/* Pipeline 1: LLM-Only */}
|
| 246 |
+
<div className="card" style={{ position: "relative", borderTop: `3px solid ${PIPE_COLORS.llmOnly}` }}>
|
| 247 |
+
<div style={{
|
| 248 |
+
position: "absolute", top: "16px", right: "16px",
|
| 249 |
+
width: "8px", height: "8px", borderRadius: "50%",
|
| 250 |
+
background: PIPE_COLORS.llmOnly,
|
| 251 |
+
}} />
|
| 252 |
+
<div className="caption-uppercase mb-1" style={{ color: PIPE_COLORS.llmOnly }}>Pipeline 1</div>
|
| 253 |
+
<div className="title-lg mb-4">LLM-Only</div>
|
| 254 |
+
<p className="body-sm mb-4" style={{ color: "var(--color-muted)", fontStyle: "italic" }}>
|
| 255 |
+
No retrieval β pure parametric knowledge
|
| 256 |
+
</p>
|
| 257 |
+
<div className="body-md mb-6" style={{ minHeight: "80px", lineHeight: 1.65 }}>
|
| 258 |
+
{llmOnly.answer}
|
| 259 |
+
</div>
|
| 260 |
+
<div className="grid grid-cols-3 gap-4 pt-4" style={{ borderTop: "1px solid var(--color-hairline)" }}>
|
| 261 |
+
<div>
|
| 262 |
+
<div className="metric-value-sm" style={{ color: PIPE_COLORS.llmOnly }}>{llmOnly.tokens.toLocaleString()}</div>
|
| 263 |
+
<div className="metric-label">Tokens</div>
|
| 264 |
+
</div>
|
| 265 |
+
<div>
|
| 266 |
+
<div className="metric-value-sm" style={{ color: PIPE_COLORS.llmOnly }}>{llmOnly.latencyMs.toFixed(0)}ms</div>
|
| 267 |
+
<div className="metric-label">Latency</div>
|
| 268 |
+
</div>
|
| 269 |
+
<div>
|
| 270 |
+
<div className="metric-value-sm" style={{ color: PIPE_COLORS.llmOnly }}>${llmOnly.costUsd.toFixed(6)}</div>
|
| 271 |
+
<div className="metric-label">Cost</div>
|
| 272 |
+
</div>
|
| 273 |
+
</div>
|
| 274 |
+
</div>
|
| 275 |
+
|
| 276 |
+
{/* Pipeline 2: Basic RAG */}
|
| 277 |
+
<div className="card" style={{ position: "relative", borderTop: `3px solid ${PIPE_COLORS.baseline}` }}>
|
| 278 |
<div style={{
|
| 279 |
position: "absolute", top: "16px", right: "16px",
|
| 280 |
width: "8px", height: "8px", borderRadius: "50%",
|
| 281 |
+
background: PIPE_COLORS.baseline,
|
| 282 |
boxShadow: "0 0 8px rgba(0,114,206,0.4)",
|
| 283 |
}} />
|
| 284 |
+
<div className="caption-uppercase mb-1" style={{ color: PIPE_COLORS.baseline }}>Pipeline 2</div>
|
| 285 |
+
<div className="title-lg mb-4">Basic RAG</div>
|
| 286 |
+
<p className="body-sm mb-4" style={{ color: "var(--color-muted)", fontStyle: "italic" }}>
|
| 287 |
+
Vector search β full chunk text β LLM
|
| 288 |
+
</p>
|
| 289 |
<div className="body-md mb-6" style={{ minHeight: "80px", lineHeight: 1.65 }}>
|
| 290 |
{baseline.answer}
|
| 291 |
</div>
|
| 292 |
<div className="grid grid-cols-3 gap-4 pt-4" style={{ borderTop: "1px solid var(--color-hairline)" }}>
|
| 293 |
<div>
|
| 294 |
+
<div className="metric-value-sm" style={{ color: PIPE_COLORS.baseline }}>{baseline.tokens.toLocaleString()}</div>
|
| 295 |
<div className="metric-label">Tokens</div>
|
| 296 |
</div>
|
| 297 |
<div>
|
| 298 |
+
<div className="metric-value-sm" style={{ color: PIPE_COLORS.baseline }}>{baseline.latencyMs.toFixed(0)}ms</div>
|
| 299 |
<div className="metric-label">Latency</div>
|
| 300 |
</div>
|
| 301 |
<div>
|
| 302 |
+
<div className="metric-value-sm" style={{ color: PIPE_COLORS.baseline }}>${baseline.costUsd.toFixed(6)}</div>
|
| 303 |
<div className="metric-label">Cost</div>
|
| 304 |
</div>
|
| 305 |
</div>
|
| 306 |
</div>
|
| 307 |
|
| 308 |
+
{/* Pipeline 3: GraphRAG */}
|
| 309 |
+
<div className="card" style={{ position: "relative", borderTop: `3px solid ${PIPE_COLORS.graphrag}` }}>
|
| 310 |
<div style={{
|
| 311 |
position: "absolute", top: "16px", right: "16px",
|
| 312 |
width: "8px", height: "8px", borderRadius: "50%",
|
| 313 |
+
background: PIPE_COLORS.graphrag,
|
| 314 |
boxShadow: "0 0 8px rgba(255,107,0,0.4)",
|
| 315 |
}} />
|
| 316 |
+
<div className="caption-uppercase mb-1" style={{ color: PIPE_COLORS.graphrag }}>Pipeline 3</div>
|
| 317 |
<div className="title-lg mb-4">GraphRAG</div>
|
| 318 |
+
<p className="body-sm mb-4" style={{ color: "var(--color-muted)", fontStyle: "italic" }}>
|
| 319 |
+
TigerGraph β compact entity context β LLM
|
| 320 |
+
</p>
|
| 321 |
<div className="body-md mb-6" style={{ minHeight: "80px", lineHeight: 1.65 }}>
|
| 322 |
{graphrag.answer}
|
| 323 |
</div>
|
| 324 |
<div className="grid grid-cols-3 gap-4 pt-4" style={{ borderTop: "1px solid var(--color-hairline)" }}>
|
| 325 |
<div>
|
| 326 |
+
<div className="metric-value-sm" style={{ color: PIPE_COLORS.graphrag }}>{graphrag.tokens.toLocaleString()}</div>
|
| 327 |
<div className="metric-label">Tokens</div>
|
| 328 |
</div>
|
| 329 |
<div>
|
| 330 |
+
<div className="metric-value-sm" style={{ color: PIPE_COLORS.graphrag }}>{graphrag.latencyMs.toFixed(0)}ms</div>
|
| 331 |
<div className="metric-label">Latency</div>
|
| 332 |
</div>
|
| 333 |
<div>
|
| 334 |
+
<div className="metric-value-sm" style={{ color: PIPE_COLORS.graphrag }}>${graphrag.costUsd.toFixed(6)}</div>
|
| 335 |
<div className="metric-label">Cost</div>
|
| 336 |
</div>
|
| 337 |
</div>
|
| 338 |
|
| 339 |
+
{/* Entities */}
|
| 340 |
+
{graphrag.entities && graphrag.entities.length > 0 && (
|
| 341 |
<div className="mt-4 pt-4" style={{ borderTop: "1px solid var(--color-hairline)" }}>
|
| 342 |
+
<div className="caption mb-2">Graph Entities Retrieved</div>
|
| 343 |
<div className="flex flex-wrap gap-1.5">
|
| 344 |
{graphrag.entities.map((e, i) => (
|
| 345 |
<span key={i} className="badge-outline" style={{ fontSize: "0.6875rem" }}>{e}</span>
|
| 346 |
))}
|
| 347 |
</div>
|
| 348 |
+
{graphrag.relations && graphrag.relations.length > 0 && (
|
| 349 |
<div className="mt-3">
|
| 350 |
<div className="caption mb-1">Reasoning Path</div>
|
| 351 |
{graphrag.relations.map((r, i) => (
|
|
|
|
| 363 |
</div>
|
| 364 |
</div>
|
| 365 |
|
| 366 |
+
{/* 3-Pipeline Comparison Chart */}
|
| 367 |
{chartData.length > 0 && (
|
| 368 |
<div className="card">
|
| 369 |
+
<div className="title-md mb-6">Side-by-Side Metrics β All 3 Pipelines</div>
|
| 370 |
<ResponsiveContainer width="100%" height={300}>
|
| 371 |
<BarChart data={chartData} margin={{ top: 20, right: 30, left: 0, bottom: 0 }}>
|
| 372 |
<CartesianGrid strokeDasharray="3 3" stroke="#002B49" strokeOpacity={0.06} />
|
|
|
|
| 374 |
<YAxis tick={{ fill: "#6c6a64", fontSize: 12 }} />
|
| 375 |
<Tooltip contentStyle={{ background: "#faf9f5", border: "1px solid #e6dfd8", borderRadius: "10px" }} />
|
| 376 |
<Legend />
|
| 377 |
+
<Bar dataKey="LLM-Only" radius={[6, 6, 0, 0]}>
|
| 378 |
+
{chartData.map((_, i) => <Cell key={i} fill={PIPE_COLORS.llmOnly} />)}
|
| 379 |
+
</Bar>
|
| 380 |
+
<Bar dataKey="Basic RAG" radius={[6, 6, 0, 0]}>
|
| 381 |
+
{chartData.map((_, i) => <Cell key={i} fill={PIPE_COLORS.baseline} />)}
|
| 382 |
+
</Bar>
|
| 383 |
+
<Bar dataKey="GraphRAG" radius={[6, 6, 0, 0]}>
|
| 384 |
+
{chartData.map((_, i) => <Cell key={i} fill={PIPE_COLORS.graphrag} />)}
|
| 385 |
+
</Bar>
|
| 386 |
</BarChart>
|
| 387 |
</ResponsiveContainer>
|
| 388 |
</div>
|