Explorer live query: better error, science chips, fallback answer
Browse files- Replace alarming 'Demo mode may be active' with helpful corpus-scope message
- Add 5 example science question chips (click to fill input)
- Show GraphRAG text answer even when 0 entities found (off-topic queries)
- Error message now explains corpus covers physics/biology/chemistry/astronomy
web/src/components/explorer/ExplorerContent.tsx
CHANGED
|
@@ -263,11 +263,14 @@ export function ExplorerContent() {
|
|
| 263 |
? visibleEdges.filter(e => e.source === selectedNode || e.target === selectedNode)
|
| 264 |
: [];
|
| 265 |
|
|
|
|
|
|
|
| 266 |
const runLiveQuery = useCallback(async () => {
|
| 267 |
if (!liveQuery.trim()) return;
|
| 268 |
setLiveLoading(true);
|
| 269 |
setLiveError("");
|
| 270 |
setLiveGraph(null);
|
|
|
|
| 271 |
try {
|
| 272 |
const res = await fetch("/api/compare", {
|
| 273 |
method: "POST",
|
|
@@ -276,13 +279,18 @@ export function ExplorerContent() {
|
|
| 276 |
});
|
| 277 |
const data = await res.json();
|
| 278 |
const entities: string[] = data.graphrag?.entities ?? [];
|
|
|
|
| 279 |
if (entities.length === 0) {
|
| 280 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 281 |
} else {
|
| 282 |
setLiveGraph(buildLiveGraph(entities, liveQuery));
|
| 283 |
}
|
| 284 |
} catch {
|
| 285 |
-
setLiveError("Request failed
|
| 286 |
}
|
| 287 |
setLiveLoading(false);
|
| 288 |
}, [liveQuery]);
|
|
@@ -573,14 +581,35 @@ export function ExplorerContent() {
|
|
| 573 |
{/* Live Query Section */}
|
| 574 |
<div className="card mt-8 animate-fade-in-up">
|
| 575 |
<div className="title-lg mb-2">🔴 Live Entity Query</div>
|
| 576 |
-
<p className="body-sm mb-
|
| 577 |
-
Ask a science question
|
| 578 |
-
|
| 579 |
</p>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 580 |
<div className="flex gap-3 mb-5">
|
| 581 |
<input
|
| 582 |
className="input flex-1"
|
| 583 |
-
placeholder="e.g.
|
| 584 |
value={liveQuery}
|
| 585 |
onChange={e => setLiveQuery(e.target.value)}
|
| 586 |
onKeyDown={e => { if (e.key === "Enter") runLiveQuery(); }}
|
|
@@ -602,6 +631,13 @@ export function ExplorerContent() {
|
|
| 602 |
</div>
|
| 603 |
)}
|
| 604 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 605 |
{liveGraph && (
|
| 606 |
<div>
|
| 607 |
<div className="caption mb-3" style={{ color: "var(--color-muted)" }}>
|
|
|
|
| 263 |
? visibleEdges.filter(e => e.source === selectedNode || e.target === selectedNode)
|
| 264 |
: [];
|
| 265 |
|
| 266 |
+
const [liveAnswer, setLiveAnswer] = useState<string | null>(null);
|
| 267 |
+
|
| 268 |
const runLiveQuery = useCallback(async () => {
|
| 269 |
if (!liveQuery.trim()) return;
|
| 270 |
setLiveLoading(true);
|
| 271 |
setLiveError("");
|
| 272 |
setLiveGraph(null);
|
| 273 |
+
setLiveAnswer(null);
|
| 274 |
try {
|
| 275 |
const res = await fetch("/api/compare", {
|
| 276 |
method: "POST",
|
|
|
|
| 279 |
});
|
| 280 |
const data = await res.json();
|
| 281 |
const entities: string[] = data.graphrag?.entities ?? [];
|
| 282 |
+
const answer: string = data.graphrag?.answer ?? "";
|
| 283 |
if (entities.length === 0) {
|
| 284 |
+
setLiveAnswer(answer || null);
|
| 285 |
+
setLiveError(
|
| 286 |
+
"No graph entities found for this query — the Wikipedia science corpus covers physics, biology, " +
|
| 287 |
+
"chemistry, and astronomy. Try one of the example questions below."
|
| 288 |
+
);
|
| 289 |
} else {
|
| 290 |
setLiveGraph(buildLiveGraph(entities, liveQuery));
|
| 291 |
}
|
| 292 |
} catch {
|
| 293 |
+
setLiveError("Request failed — check that the dev server is running and OPENAI_API_KEY is set in web/.env.");
|
| 294 |
}
|
| 295 |
setLiveLoading(false);
|
| 296 |
}, [liveQuery]);
|
|
|
|
| 581 |
{/* Live Query Section */}
|
| 582 |
<div className="card mt-8 animate-fade-in-up">
|
| 583 |
<div className="title-lg mb-2">🔴 Live Entity Query</div>
|
| 584 |
+
<p className="body-sm mb-4" style={{ color: "var(--color-muted)" }}>
|
| 585 |
+
Ask a <strong>science</strong> question — GraphRAG retrieves entities from TigerGraph and renders them
|
| 586 |
+
as a live graph. Corpus covers physics, biology, chemistry, and astronomy.
|
| 587 |
</p>
|
| 588 |
+
|
| 589 |
+
{/* Example chips */}
|
| 590 |
+
<div className="flex flex-wrap gap-2 mb-5">
|
| 591 |
+
{[
|
| 592 |
+
"How do black holes form?",
|
| 593 |
+
"What is CRISPR and how does it edit DNA?",
|
| 594 |
+
"How does photosynthesis produce oxygen?",
|
| 595 |
+
"What causes quantum entanglement?",
|
| 596 |
+
"How does the immune system fight viruses?",
|
| 597 |
+
].map(q => (
|
| 598 |
+
<button
|
| 599 |
+
key={q}
|
| 600 |
+
onClick={() => setLiveQuery(q)}
|
| 601 |
+
className="badge-outline"
|
| 602 |
+
style={{ cursor: "pointer", fontSize: "0.75rem", border: "none" }}
|
| 603 |
+
>
|
| 604 |
+
{q}
|
| 605 |
+
</button>
|
| 606 |
+
))}
|
| 607 |
+
</div>
|
| 608 |
+
|
| 609 |
<div className="flex gap-3 mb-5">
|
| 610 |
<input
|
| 611 |
className="input flex-1"
|
| 612 |
+
placeholder="e.g. How does DNA encode proteins?"
|
| 613 |
value={liveQuery}
|
| 614 |
onChange={e => setLiveQuery(e.target.value)}
|
| 615 |
onKeyDown={e => { if (e.key === "Enter") runLiveQuery(); }}
|
|
|
|
| 631 |
</div>
|
| 632 |
)}
|
| 633 |
|
| 634 |
+
{liveAnswer && !liveGraph && (
|
| 635 |
+
<div className="card-cream mb-4" style={{ padding: "16px", borderLeft: "3px solid #0072CE" }}>
|
| 636 |
+
<div className="caption-uppercase mb-2" style={{ color: "#0072CE" }}>GraphRAG Answer</div>
|
| 637 |
+
<p className="body-sm" style={{ lineHeight: 1.65, color: "var(--color-body)" }}>{liveAnswer}</p>
|
| 638 |
+
</div>
|
| 639 |
+
)}
|
| 640 |
+
|
| 641 |
{liveGraph && (
|
| 642 |
<div>
|
| 643 |
<div className="caption mb-3" style={{ color: "var(--color-muted)" }}>
|