Skip to content

Commit 2e18612

Browse files
committed
refactored eval visualizer into a separate component
1 parent c2ac08e commit 2e18612

File tree

3 files changed

+289
-269
lines changed

3 files changed

+289
-269
lines changed

.eslintrc.cjs

+1
Original file line numberDiff line numberDiff line change
@@ -17,5 +17,6 @@ module.exports = {
1717
'warn',
1818
{ allowConstantExport: true },
1919
],
20+
'react/prop-types': 0
2021
},
2122
}

src/App.jsx

+5-269
Original file line numberDiff line numberDiff line change
@@ -1,46 +1,10 @@
11
import { useState, useEffect } from 'react';
22
import chroma from "chroma-js";
33
import { format } from 'sql-formatter';
4+
import EvalVisualizerSingle from './components/EvalVisualizerSingle';
45
import './App.css';
56

67
function App() {
7-
const [dataset, setDataset] = useState("classic_new");
8-
const [searchPattern, setSearchPattern] = useState(null);
9-
const [maxConfidence, setMaxConfidence] = useState(1);
10-
const [categories, setCategories] = useState([]);
11-
const [data, setData] = useState([]);
12-
const [siderVisible, setSiderVisible] = useState(false);
13-
const [selectedItem, setSelectedItem] = useState(null);
14-
const [showProbs, setShowProbs] = useState(false);
15-
const [selectedToken, setSelectedToken] = useState(null);
16-
17-
const getData = async () => {
18-
const response = await fetch(`/${dataset}.json`);
19-
let data = await response.json()
20-
21-
if (searchPattern) {
22-
data = data.filter((item) => item.question.toLowerCase().includes(searchPattern) || item.generated_query.toLowerCase().includes(searchPattern));
23-
}
24-
25-
if (maxConfidence) {
26-
// check if the min of all rank_1_probs is less than maxConfidence
27-
data = data.filter((item) => {
28-
return item.logprobs.reduce((acc, item) => {
29-
return Math.min(acc, item.rank_1_prob);
30-
}, 1) <= maxConfidence;
31-
});
32-
}
33-
34-
const cats = [];
35-
data.forEach((item) => {
36-
if (!cats.includes(item.query_category)) {
37-
cats.push(item.query_category);
38-
}
39-
});
40-
setData(data);
41-
setCategories(cats);
42-
}
43-
448
const getBackgroundColor = (prob) => {
459
return chroma.scale(['pink', 'yellow', 'lightgreen']).domain([0.15, 0.3, 1])(prob).hex();
4610
}
@@ -61,240 +25,12 @@ function App() {
6125
}
6226
}
6327

64-
useEffect(() => {
65-
getData(dataset);
66-
}, [dataset, searchPattern, maxConfidence]);
67-
6828
return (
6929
<div className="App">
70-
<h1>Eval Visualizer</h1>
71-
<div className="flex flex-padded">
72-
<div id="options">
73-
<h3>Eval Type</h3>
74-
<select
75-
style={{ width: 120 }}
76-
value={dataset}
77-
onChange={
78-
(ev) => {
79-
setDataset(ev.target.value);
80-
}
81-
}
82-
>
83-
<option value="classic_new">v1</option>
84-
<option value="basic_new">Basic</option>
85-
<option value="advanced_new">Advanced</option>
86-
</select>
87-
</div>
88-
89-
<div id="search">
90-
<h3>Question or SQL Pattern</h3>
91-
<input
92-
type="text"
93-
placeholder="Pattern..."
94-
style={{ width: 200 }}
95-
value={searchPattern}
96-
onChange={(ev) => {
97-
setSearchPattern(ev.target.value.toLowerCase());
98-
}}
99-
/>
100-
</div>
101-
102-
<div id="slider-confidence">
103-
<h3>Min Top Prob Threshold</h3>
104-
<input
105-
type="range"
106-
min={0}
107-
max={1}
108-
step={0.01}
109-
value={maxConfidence}
110-
style={{ width: 200 }}
111-
onChange={(ev) => {
112-
setMaxConfidence(parseFloat(ev.target.value));
113-
}}
114-
/>
115-
<span>{maxConfidence}</span>
116-
</div>
117-
</div>
118-
119-
<div id="summary-statistics">
120-
<h3>Summary Statistics</h3>
121-
<div className="flex flex-padded">
122-
<p><b>Number of records</b>: {data.length}</p>
123-
<p><b>Total Correct</b>: {data.filter((item) => item.correct === 1).length} ({100*(data.filter((item) => item.correct === 1).length/data.length).toFixed(3)})%</p>
124-
<p><b>Total Incorrect</b>: {data.filter((item) => item.correct === 0).length} ({100*(data.filter((item) => item.correct === 0).length/data.length).toFixed(3)})%</p>
125-
</div>
126-
127-
</div>
128-
<div id="charts">
129-
{categories.map((category) => {
130-
return (
131-
<>
132-
<h4>{category} ({
133-
100*(data.filter((item) => item.query_category == category).reduce(
134-
(acc, item) => {
135-
return acc + (item.correct === 1 ? 1 : 0);
136-
}, 0
137-
) / data.filter((item) => item.query_category == category).length).toFixed(2)
138-
} %)
139-
</h4>
140-
<div
141-
key={category}
142-
className="flex"
143-
>
144-
{data.filter((item) => item.query_category === category)
145-
.sort((item) => item.correct === 1 ? -1 : 1)
146-
.map((item) => {
147-
return (
148-
// a table of hoverable cells with at most 10 columns
149-
// each cell is colored by the correct/incorrect status
150-
<span
151-
key={item.question}
152-
className={item.error_db_exec === 1 ? "cell error-cell" : "cell"}
153-
style={{
154-
backgroundColor: item.correct === 1 ? "green" : "red",
155-
}}
156-
onClick={() => {
157-
selectedItem?.question === item.question && siderVisible ?
158-
setSiderVisible(false) :
159-
setSiderVisible(true)
160-
setSelectedItem(item);
161-
}}
162-
>
163-
&nbsp;
164-
</span>
165-
);
166-
})}
167-
</div>
168-
</>
169-
);})}
170-
</div>
171-
<div
172-
id="right-sider"
173-
style={{
174-
position: "fixed",
175-
right: 0,
176-
top: 0,
177-
width: 600,
178-
maxWidth: "100%",
179-
height: "100%",
180-
backgroundColor: "white",
181-
padding: 20,
182-
borderLeft: "1px solid black",
183-
boxShadow: "-2px 0 5px -1px rgba(0,0,0,0.2)",
184-
zIndex: 100,
185-
visibility: siderVisible ? "visible" : "hidden",
186-
overflowY: "scroll",
187-
}}
188-
>
189-
{/* this is closable sider on the right of the screen */}
190-
<h3>Details <button onClick={() => {setSiderVisible(false);}}>Close</button></h3>
191-
<p
192-
style={{
193-
backgroundColor: selectedItem?.correct === 1 ? "lightgreen" : "pink"
194-
}}
195-
>
196-
Question: <i>{selectedItem?.question}</i>
197-
</p>
198-
<p>Database: <b>{selectedItem?.db_name}</b></p>
199-
{selectedItem?.instructions ? <p>Instructions: <pre>{selectedItem.instructions}</pre></p> : null}
200-
201-
<div>
202-
<p>Golden Query: <pre>{formatSql(selectedItem?.query)}</pre></p>
203-
</div>
204-
205-
<p>
206-
Formatted Query
207-
<label className="switch" style={{
208-
marginBottom: -8,
209-
marginLeft: 5,
210-
marginRight: 5,
211-
}}>
212-
<input
213-
type="checkbox"
214-
className="slider"
215-
id="detail-button"
216-
checked={showProbs}
217-
onChange={() => setShowProbs(
218-
(prev) => !prev
219-
)} />
220-
<span className="slider round"></span>
221-
</label>
222-
Probabilities
223-
</p>
224-
225-
<p>Generated Query:</p>
226-
{
227-
showProbs ?
228-
<div>
229-
<div style={{
230-
width: "80%",
231-
paddingBottom: 200
232-
}}>
233-
{(selectedItem?.logprobs || []).map(
234-
(item, idx) => {
235-
return (
236-
<span
237-
key={idx}
238-
style={{
239-
backgroundColor: getBackgroundColor(item?.rank_1_prob),
240-
padding: 1,
241-
margin: 1,
242-
borderRadius: 3,
243-
// hover pointer
244-
cursor: "pointer",
245-
}}
246-
// on hover, show the query in a floating div
247-
onMouseEnter={(ev) => {
248-
setSelectedToken(item);
249-
const floatingDiv = document.getElementById("floating-div");
250-
floatingDiv.style.visibility = "visible";
251-
floatingDiv.style.top = ev.clientY + 10 + "px";
252-
floatingDiv.style.left = ev.clientX + "px";
253-
}}
254-
onMouseLeave={() => {
255-
const floatingDiv = document.getElementById("floating-div");
256-
floatingDiv.style.visibility = "hidden";
257-
}}
258-
>
259-
{item['rank_1_token']}
260-
</span>
261-
);
262-
}
263-
)}
264-
{/* floating div */}
265-
<div
266-
id="floating-div"
267-
style={{
268-
position: "fixed",
269-
top: 0,
270-
left: 0,
271-
width: 250,
272-
height: 250,
273-
backgroundColor: "white",
274-
border: "1px solid black",
275-
zIndex: 1000,
276-
visibility: "hidden",
277-
}}
278-
>
279-
<div>Top token: <code>{selectedToken?.rank_1_token}</code></div>
280-
<div>Probability of top token: {selectedToken?.rank_1_prob.toFixed(2)}</div>
281-
<div>Second token: <code>{selectedToken?.rank_2_token}</code></div>
282-
<div>Probability of second token: {selectedToken?.rank_2_prob.toFixed(2)}</div>
283-
<div
284-
style={{
285-
backgroundColor: getBackgroundColor(selectedToken?.rank_1_prob - selectedToken?.rank_2_prob),
286-
padding: 1,
287-
margin: 1,
288-
borderRadius: 3,
289-
}}
290-
>Probability difference: {(selectedToken?.rank_1_prob - selectedToken?.rank_2_prob).toFixed(2)}</div>
291-
</div>
292-
</div>
293-
</div> :
294-
<pre>{formatSql(selectedItem?.generated_query)}</pre>
295-
}
296-
{selectedItem?.error_db_exec === 1 ? <p>Error Message: <pre>{selectedItem?.error_msg}</pre></p> : null}
297-
</div>
30+
<EvalVisualizerSingle
31+
getBackgroundColor={getBackgroundColor}
32+
formatSql={formatSql}
33+
/>
29834
</div>
29935
);
30036
}

0 commit comments

Comments
 (0)