muthuk1 commited on
Commit
f26d060
Β·
1 Parent(s): d52b559

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 Baseline RAG race against GraphRAG in real-time.
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: "HotpotQA", 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>
 
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 file
41
- TIGERGRAPH_HOST=https://your-instance.i.tgcloud.io
42
- TIGERGRAPH_GRAPH=GraphRAG
43
- TIGERGRAPH_SECRET=your_secret
44
 
45
- # LLM Providers (set at least one)
46
- ANTHROPIC_API_KEY=sk-ant-...
 
 
 
 
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": "Were Scott Derrickson and Ed Wood of the same nationality?",
65
- "provider": "anthropic",
66
- "model": "claude-sonnet-4-20250514",
67
- "adaptiveRouting": true
 
68
  }
69
 
70
- // Response
71
  {
 
 
 
 
 
 
72
  "baseline": {
73
- "answer": "Yes, both are American.",
74
- "tokens": 950,
75
- "latencyMs": 1200,
76
- "costUsd": 0.003800,
77
- "entities": [],
78
- "relations": []
79
  },
80
  "graphrag": {
81
- "answer": "Yes, both directors are American...",
82
- "tokens": 2400,
83
- "latencyMs": 1800,
84
- "costUsd": 0.009600,
85
- "entities": ["Scott Derrickson", "Ed Wood", "United States"],
86
- "relations": ["BORN_IN", "LOCATED_IN"]
 
 
87
  },
88
- "complexity": 0.72,
89
- "queryType": "bridge",
90
- "recommended": "graphrag"
 
91
  }`,
92
  },
93
  {
94
  heading: "POST /api/benchmark",
95
- body: "Run batch evaluation on HotpotQA samples.",
96
  code: `// Request
97
  POST /api/benchmark
98
- { "numSamples": 10 }
99
 
100
  // Response
101
  {
102
  "aggregate": {
103
  "numSamples": 10,
104
- "baseline": { "avgF1": 0.6234, "avgEM": 0.4000, ... },
105
- "graphrag": { "avgF1": 0.7567, "avgEM": 0.5000, ... },
106
- "graphragF1WinRate": 0.70,
107
- "byType": {
108
- "bridge": { "count": 5, "baselineF1": 0.58, "graphragF1": 0.79 },
109
- "comparison": { "count": 5, "baselineF1": 0.67, "graphragF1": 0.72 }
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: "Dual Pipeline Design",
159
- body: `Every query runs through two pipelines simultaneously:
 
 
 
 
160
 
161
- **Pipeline A β€” Baseline RAG**
162
- Query β†’ Vector similarity search β†’ Retrieved passages β†’ LLM generation
163
- - Fast and cheap
164
- - Best for simple factoid queries
165
 
166
- **Pipeline B β€” GraphRAG**
167
- Query β†’ Entity extraction β†’ Graph traversal β†’ Structured evidence β†’ LLM generation
168
- - More tokens, but higher accuracy
169
- - Best for multi-hop bridge and comparison questions
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
- -p 8000:8000 \\
235
- -e TIGERGRAPH_HOST=https://your-instance.i.tgcloud.io \\
236
- -e ANTHROPIC_API_KEY=sk-ant-... \\
 
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: "Were Scott Derrickson and Ed Wood of the same nationality?", type: "Bridge" },
29
- { q: "What government position was held by the woman who portrayed Nora Batty?", type: "Multi-hop" },
30
- { q: "Which magazine was started first, Arthur's Magazine or First for Women?", type: "Comparison" },
31
- { q: "Who was born first, Arthur Conan Doyle or Agatha Christie?", type: "Comparison" },
32
- { q: "What is the capital of the country where the Eiffel Tower is located?", type: "Bridge" },
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
- setBaseline(data.baseline);
78
- setGraphrag(data.graphrag);
 
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
- { name: "Tokens", Baseline: baseline.tokens, GraphRAG: graphrag.tokens },
91
- { name: "Latency (ms)", Baseline: Math.round(baseline.latencyMs), GraphRAG: Math.round(graphrag.latencyMs) },
 
 
 
 
 
 
 
 
 
 
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 multi-hop question… e.g., Were Scott Derrickson and Ed Wood of the same nationality?"
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
- <>β–Ά Compare Pipelines</>
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, 45)}…
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" : "Baseline RAG"}
203
  </strong>
204
  </span>
205
  </div>
206
  </div>
207
  )}
208
 
209
- {/* Results */}
210
- {baseline && graphrag && (
 
 
 
 
 
 
 
 
 
211
  <div className="animate-fade-in-up">
212
- <div className="grid grid-cols-1 lg:grid-cols-2 gap-6 mb-6">
213
- {/* Baseline Card */}
214
- <div className="card pipeline-baseline" style={{ position: "relative" }}>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
215
  <div style={{
216
  position: "absolute", top: "16px", right: "16px",
217
  width: "8px", height: "8px", borderRadius: "50%",
218
- background: "#0072CE",
219
  boxShadow: "0 0 8px rgba(0,114,206,0.4)",
220
  }} />
221
- <div className="caption-uppercase mb-1" style={{ color: "#0072CE" }}>Pipeline A</div>
222
- <div className="title-lg mb-4">Baseline RAG</div>
 
 
 
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: "#0072CE" }}>{baseline.tokens.toLocaleString()}</div>
229
  <div className="metric-label">Tokens</div>
230
  </div>
231
  <div>
232
- <div className="metric-value-sm" style={{ color: "#0072CE" }}>{baseline.latencyMs.toFixed(0)}ms</div>
233
  <div className="metric-label">Latency</div>
234
  </div>
235
  <div>
236
- <div className="metric-value-sm" style={{ color: "#0072CE" }}>${baseline.costUsd.toFixed(6)}</div>
237
  <div className="metric-label">Cost</div>
238
  </div>
239
  </div>
240
  </div>
241
 
242
- {/* GraphRAG Card */}
243
- <div className="card pipeline-graphrag" style={{ position: "relative" }}>
244
  <div style={{
245
  position: "absolute", top: "16px", right: "16px",
246
  width: "8px", height: "8px", borderRadius: "50%",
247
- background: "#FF6B00",
248
  boxShadow: "0 0 8px rgba(255,107,0,0.4)",
249
  }} />
250
- <div className="caption-uppercase mb-1" style={{ color: "#FF6B00" }}>Pipeline B</div>
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: "#FF6B00" }}>{graphrag.tokens.toLocaleString()}</div>
258
  <div className="metric-label">Tokens</div>
259
  </div>
260
  <div>
261
- <div className="metric-value-sm" style={{ color: "#FF6B00" }}>{graphrag.latencyMs.toFixed(0)}ms</div>
262
  <div className="metric-label">Latency</div>
263
  </div>
264
  <div>
265
- <div className="metric-value-sm" style={{ color: "#FF6B00" }}>${graphrag.costUsd.toFixed(6)}</div>
266
  <div className="metric-label">Cost</div>
267
  </div>
268
  </div>
269
 
270
- {/* Entities & Relations */}
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">Extracted Entities</div>
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="Baseline" fill="#0072CE" radius={[6, 6, 0, 0]} />
309
- <Bar dataKey="GraphRAG" fill="#FF6B00" radius={[6, 6, 0, 0]} />
 
 
 
 
 
 
 
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>