import { useEffect, useState, useMemo } from "react"; // ── Types ──────────────────────────────────────────────────────────────── interface Row { query_id: string; rationale: string; selected_indices: number[]; k_requested: number; k_effective: number; excerpt: string; new_trajectory: string; direct_answer: boolean; tool_call_counts: Record; total_tool_calls: number; status: string; } // ── Block parsers ──────────────────────────────────────────────────────── type BlockType = "reasoning" | "tool_call" | "tool_result" | "final_answer" | "unknown"; interface Block { type: BlockType; label?: string; content: string } const BLOCK_STYLES: Record = { reasoning: { border: "border-purple-700", labelColor: "text-purple-400", bg: "bg-purple-950/30" }, tool_call: { border: "border-blue-700", labelColor: "text-blue-400", bg: "bg-blue-950/30" }, tool_result: { border: "border-gray-600", labelColor: "text-gray-400", bg: "bg-gray-800/30" }, final_answer: { border: "border-green-700", labelColor: "text-green-400", bg: "bg-green-950/30" }, unknown: { border: "border-gray-700", labelColor: "text-gray-500", bg: "" }, }; /** * Parse the `excerpt` column (reference trajectory format): * [Reasoning]: text * [Tool call] tool_name\narguments:\n{...} * [Tool result]:\n[...] */ function parseExcerpt(text: string): Block[] { if (!text) return []; const blocks: Block[] = []; const parts = text.split(/\n\n(?=\[)/); for (const part of parts) { const p = part.trim(); if (p.startsWith("[Reasoning]:")) { blocks.push({ type: "reasoning", label: "Reasoning", content: p.slice("[Reasoning]:".length).trim() }); } else if (p.startsWith("[Tool call]")) { blocks.push({ type: "tool_call", label: "Tool Call", content: p.slice("[Tool call]".length).trim() }); } else if (p.startsWith("[Tool result]:")) { blocks.push({ type: "tool_result", label: "Tool Result", content: p.slice("[Tool result]:".length).trim() }); } else if (p.startsWith("[Final answer]:")) { blocks.push({ type: "final_answer", label: "Final Answer", content: p.slice("[Final answer]:".length).trim() }); } else if (p) { blocks.push({ type: "unknown", label: "—", content: p }); } } return blocks; } /** * Parse the `new_trajectory` column (our formatted output): * [Reasoning]\ntext * [Tool Call: name]\nArguments:\n{...}\n\n[Tool Result]\n{...} * [Final Answer]\ntext * Blocks separated by \n\n---\n\n */ function parseNewTrajectory(text: string): Block[] { if (!text) return []; const blocks: Block[] = []; const parts = text.split("\n\n---\n\n"); for (const part of parts) { const p = part.trim(); if (p.startsWith("[Reasoning]\n")) { blocks.push({ type: "reasoning", label: "Reasoning", content: p.slice("[Reasoning]\n".length).trim() }); } else if (p.startsWith("[Tool Call:")) { // Split into call and result at the embedded [Tool Result] marker const resultMarker = "\n\n[Tool Result]\n"; const resultIdx = p.indexOf(resultMarker); const headerEnd = p.indexOf("]\n"); const toolName = headerEnd >= 0 ? p.slice("[Tool Call:".length, headerEnd).trim() : "unknown"; if (resultIdx >= 0) { const callContent = p.slice(0, resultIdx).replace(/^\[Tool Call:[^\]]*\]\n/, "").trim(); const resultContent = p.slice(resultIdx + resultMarker.length).trim(); blocks.push({ type: "tool_call", label: `Tool Call: ${toolName}`, content: callContent }); blocks.push({ type: "tool_result", label: "Tool Result", content: resultContent }); } else { const callContent = p.replace(/^\[Tool Call:[^\]]*\]\n/, "").trim(); blocks.push({ type: "tool_call", label: `Tool Call: ${toolName}`, content: callContent }); } } else if (p.startsWith("[Final Answer]\n")) { blocks.push({ type: "final_answer", label: "Final Answer", content: p.slice("[Final Answer]\n".length).trim() }); } else if (p) { blocks.push({ type: "unknown", label: "—", content: p }); } } return blocks; } // ── Trajectory renderer ────────────────────────────────────────────────── function TrajectoryView({ blocks }: { blocks: Block[] }) { if (blocks.length === 0) return
No steps.
; return (
{blocks.map((b, i) => { const s = BLOCK_STYLES[b.type]; return (
{b.label ?? b.type}
{b.content}
); })}
); } // ── Filter type ────────────────────────────────────────────────────────── type FilterMode = "all" | "direct" | "searched"; // ── Main component ─────────────────────────────────────────────────────── export default function SelectedToolsApp() { const [data, setData] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [selectedIdx, setSelectedIdx] = useState(0); const [search, setSearch] = useState(""); const [filter, setFilter] = useState("all"); useEffect(() => { setLoading(true); fetch("/api/selected-tools/") .then(r => { if (!r.ok) throw new Error(r.statusText); return r.json(); }) .then((d: { rows: Row[] }) => { setData(d.rows); setLoading(false); }) .catch(e => { setError(e.message); setLoading(false); }); }, []); const filtered = useMemo(() => { let rows = data; if (filter === "direct") rows = rows.filter(r => r.direct_answer); if (filter === "searched") rows = rows.filter(r => !r.direct_answer); if (search.trim()) { const q = search.toLowerCase(); rows = rows.filter(r => r.query_id.includes(q)); } return rows; }, [data, search, filter]); const current = filtered[selectedIdx] ?? null; const excerptBlocks = useMemo(() => current ? parseExcerpt(current.excerpt) : [], [current]); const trajectoryBlocks = useMemo(() => current ? parseNewTrajectory(current.new_trajectory) : [], [current]); // Stats const directCount = data.filter(r => r.direct_answer).length; const directPct = data.length ? Math.round(100 * directCount / data.length) : 0; if (loading) return
Loading from HuggingFace…
; if (error) return
Error: {error}
; return (
{/* ── Sidebar ──────────────────────────────────────────────── */}
{/* Stats banner */}
Direct-answer rate
{directPct}%
{directCount} / {data.length} no tool calls
{/* Filter toggles */}
{(["all", "direct", "searched"] as FilterMode[]).map(m => ( ))}
{/* Search */}
{ setSearch(e.target.value); setSelectedIdx(0); }} className="w-full bg-gray-800 border border-gray-700 text-gray-200 text-xs rounded px-2 py-1.5 placeholder-gray-600" />
{filtered.length} / {data.length}
{/* Query list */}
{filtered.map((row, i) => ( ))}
{/* ── Main: two-column side-by-side ──────────────────────── */} {current ? (
{/* Header */}
Query #{current.query_id} {current.direct_answer ? Direct answer : {current.total_tool_calls} tool calls } k={current.k_effective} steps selected {current.status}
{/* Selected indices */}
Selected steps {current.selected_indices.map(idx => ( #{idx} ))}
{/* Rationale */}
Rationale {current.rationale}
{/* Side-by-side columns */}
{/* Left: excerpt (selected tool calls from reference trajectory) */}
Selected Tool Calls reference trajectory · {current.k_effective} steps
{/* Right: new trajectory */}
New Trajectory gpt-oss-120b · conditioned on selected steps
) : (
No query selected.
)}
); }