Hemang Thakur
commited on
Commit
·
8286427
1
Parent(s):
1364348
fixed evaluation
Browse files
frontend/src/Components/AiComponents/ChatComponents/Evaluate.js
CHANGED
@@ -1,142 +1,244 @@
|
|
1 |
-
import
|
2 |
-
import
|
3 |
-
import {
|
4 |
-
import
|
5 |
-
import
|
6 |
-
import
|
7 |
-
import '
|
8 |
-
|
9 |
-
|
10 |
-
|
11 |
-
|
12 |
-
|
13 |
-
|
14 |
-
|
15 |
-
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
// Called when the user starts resizing the sidebar.
|
28 |
-
const startResize = (e) => {
|
29 |
-
e.preventDefault();
|
30 |
-
sidebarRef.current.classList.add("resizing"); // Add the "resizing" class to the sidebar when resizing
|
31 |
-
document.addEventListener("mousemove", resizeSidebar);
|
32 |
-
document.addEventListener("mouseup", stopResize);
|
33 |
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
34 |
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
40 |
};
|
41 |
|
42 |
-
const
|
43 |
-
|
44 |
-
document.removeEventListener("mousemove", resizeSidebar);
|
45 |
-
document.removeEventListener("mouseup", stopResize);
|
46 |
};
|
47 |
|
48 |
-
//
|
49 |
-
const
|
50 |
-
|
51 |
-
|
52 |
-
|
|
|
53 |
};
|
54 |
|
55 |
-
|
56 |
-
|
57 |
-
|
58 |
-
|
59 |
-
|
|
|
|
|
|
|
60 |
}
|
61 |
-
|
62 |
-
|
63 |
-
|
64 |
-
|
65 |
-
|
66 |
-
|
67 |
-
|
68 |
-
|
69 |
-
|
70 |
-
|
71 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
72 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
73 |
};
|
74 |
|
75 |
return (
|
76 |
-
|
77 |
-
|
78 |
-
|
79 |
-
|
80 |
-
|
81 |
-
|
82 |
-
|
83 |
-
<
|
84 |
-
|
85 |
-
|
86 |
-
|
87 |
-
|
88 |
-
|
89 |
-
|
90 |
-
|
91 |
-
|
92 |
-
|
93 |
-
|
94 |
-
|
95 |
-
|
96 |
-
|
97 |
-
|
98 |
-
|
99 |
-
<
|
100 |
-
|
101 |
-
|
102 |
-
|
103 |
-
|
104 |
-
|
105 |
-
|
106 |
-
|
107 |
-
|
108 |
-
|
109 |
-
|
110 |
-
|
111 |
-
|
112 |
-
|
113 |
-
|
114 |
-
|
115 |
-
|
116 |
-
|
117 |
-
|
118 |
-
|
119 |
-
|
120 |
-
|
121 |
-
|
122 |
-
|
123 |
-
|
124 |
-
|
125 |
-
|
126 |
-
|
127 |
-
|
128 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
129 |
)}
|
130 |
-
|
131 |
-
|
132 |
-
|
133 |
-
|
134 |
-
|
135 |
-
|
136 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
137 |
)}
|
138 |
-
|
139 |
);
|
140 |
-
}
|
141 |
-
|
142 |
-
export default RightSidebar;
|
|
|
1 |
+
import * as React from 'react';
|
2 |
+
import ReactMarkdown from 'react-markdown';
|
3 |
+
import { useTheme } from '@mui/material/styles';
|
4 |
+
import Box from '@mui/material/Box';
|
5 |
+
import OutlinedInput from '@mui/material/OutlinedInput';
|
6 |
+
import InputLabel from '@mui/material/InputLabel';
|
7 |
+
import MenuItem from '@mui/material/MenuItem';
|
8 |
+
import FormControl from '@mui/material/FormControl';
|
9 |
+
import Select from '@mui/material/Select';
|
10 |
+
import Chip from '@mui/material/Chip';
|
11 |
+
import Button from '@mui/material/Button';
|
12 |
+
import Typography from '@mui/material/Typography';
|
13 |
+
import './Evaluate.css';
|
14 |
+
|
15 |
+
const MenuProps = {
|
16 |
+
PaperProps: {
|
17 |
+
className: 'evaluate-menu',
|
18 |
+
},
|
19 |
+
disableScrollLock: true
|
20 |
+
};
|
21 |
+
|
22 |
+
function getStyles(name, selectedNames, theme) {
|
23 |
+
return {
|
24 |
+
fontWeight: selectedNames.includes(name.toLowerCase())
|
25 |
+
? theme.typography.fontWeightMedium
|
26 |
+
: theme.typography.fontWeightRegular,
|
|
|
|
|
|
|
|
|
|
|
|
|
27 |
};
|
28 |
+
}
|
29 |
+
|
30 |
+
export default function MultipleSelectChip({ evaluation }) {
|
31 |
+
const theme = useTheme();
|
32 |
+
const [personName, setPersonName] = React.useState([]);
|
33 |
+
const [selectedMetrics, setSelectedMetrics] = React.useState([]);
|
34 |
+
const [evaluationResult, setEvaluationResult] = React.useState("");
|
35 |
+
const [isEvaluating, setIsEvaluating] = React.useState(false);
|
36 |
+
const [localLoading, setLocalLoading] = React.useState(false);
|
37 |
+
const [noMetricsError, setNoMetricsError] = React.useState("");
|
38 |
+
const [metricOptions, setMetricOptions] = React.useState([]);
|
39 |
+
|
40 |
+
React.useEffect(() => {
|
41 |
+
// If 'contents' is undefined in the payload
|
42 |
+
if (evaluation && evaluation.contents === undefined) {
|
43 |
+
setMetricOptions([
|
44 |
+
"Bias",
|
45 |
+
"Toxicity",
|
46 |
+
"Summarization",
|
47 |
+
"Answer Correctness",
|
48 |
+
]);
|
49 |
+
} else {
|
50 |
+
// Else, all except "Answer Correctness"
|
51 |
+
setMetricOptions([
|
52 |
+
"Bias",
|
53 |
+
"Toxicity",
|
54 |
+
"Summarization",
|
55 |
+
"Faithfulness",
|
56 |
+
"Hallucination",
|
57 |
+
"Answer Relevancy",
|
58 |
+
"Contextual Relevancy",
|
59 |
+
"Contextual Recall",
|
60 |
+
]);
|
61 |
+
}
|
62 |
+
}, [evaluation]);
|
63 |
|
64 |
+
// Reset the form fields
|
65 |
+
React.useEffect(() => {
|
66 |
+
// Reset the form and evaluation result
|
67 |
+
setPersonName([]);
|
68 |
+
setSelectedMetrics([]);
|
69 |
+
setEvaluationResult("");
|
70 |
+
setLocalLoading(true);
|
71 |
+
setNoMetricsError("");
|
72 |
+
|
73 |
+
// Simulate a loading delay
|
74 |
+
const timer = setTimeout(() => {
|
75 |
+
setLocalLoading(false);
|
76 |
+
}, 500);
|
77 |
+
return () => clearTimeout(timer);
|
78 |
+
}, [evaluation]);
|
79 |
+
|
80 |
+
const handleChange = (event) => {
|
81 |
+
const { target: { value } } = event;
|
82 |
+
const metrics = typeof value === 'string' ? value.split(',') : value;
|
83 |
+
setPersonName(metrics);
|
84 |
+
setSelectedMetrics(metrics);
|
85 |
+
setNoMetricsError("");
|
86 |
};
|
87 |
|
88 |
+
const handleDelete = (chipToDelete) => {
|
89 |
+
setPersonName((chips) => chips.filter((chip) => chip !== chipToDelete));
|
|
|
|
|
90 |
};
|
91 |
|
92 |
+
// Function to convert a string to title case.
|
93 |
+
const titleCase = (str) => {
|
94 |
+
return str
|
95 |
+
.split(' ')
|
96 |
+
.map(word => word.charAt(0).toUpperCase() + word.slice(1))
|
97 |
+
.join(' ');
|
98 |
};
|
99 |
|
100 |
+
const handleEvaluateClick = async () => {
|
101 |
+
// Clear previous evaluation result immediately.
|
102 |
+
setEvaluationResult("");
|
103 |
+
|
104 |
+
// Check if no metrics selected
|
105 |
+
if (selectedMetrics.length === 0) {
|
106 |
+
setNoMetricsError("No metrics selected");
|
107 |
+
return;
|
108 |
}
|
109 |
+
|
110 |
+
setNoMetricsError("");
|
111 |
+
setIsEvaluating(true);
|
112 |
+
|
113 |
+
const payload = { ...evaluation, metrics: selectedMetrics };
|
114 |
+
try {
|
115 |
+
const res = await fetch("http://localhost:8000/action/evaluate", {
|
116 |
+
method: "POST",
|
117 |
+
headers: { "Content-Type": "application/json" },
|
118 |
+
body: JSON.stringify(payload),
|
119 |
+
});
|
120 |
+
if (!res.ok) {
|
121 |
+
const error = await res.json();
|
122 |
+
throw new Error(`Evaluation Error: ${error.error}`);
|
123 |
+
}
|
124 |
+
|
125 |
+
const data = await res.json();
|
126 |
+
if (!data.result) {
|
127 |
+
throw new Error("No results returned from evaluation");
|
128 |
+
}
|
129 |
+
|
130 |
+
// Format the JSON into Markdown.
|
131 |
+
let markdown = "### Result\n\n";
|
132 |
+
for (const [metric, details] of Object.entries(data.result)) {
|
133 |
+
let score = details.score;
|
134 |
+
if (typeof score === "number") {
|
135 |
+
const percentage = score * 100;
|
136 |
+
score = Number.isInteger(percentage)
|
137 |
+
? percentage.toFixed(0) + "%"
|
138 |
+
: percentage.toFixed(2) + "%";
|
139 |
+
}
|
140 |
+
let reason = details.reason;
|
141 |
+
markdown += `**${titleCase(metric)}:** ${score}\n\n${reason}\n\n`;
|
142 |
+
}
|
143 |
+
setEvaluationResult(markdown);
|
144 |
+
} catch (err) {
|
145 |
+
// Use the callback to trigger the error block in ChatWindow
|
146 |
+
if (evaluation.onError && evaluation.blockId) {
|
147 |
+
evaluation.onError(evaluation.blockId, err.message || "Evaluation failed");
|
148 |
+
}
|
149 |
+
else {
|
150 |
+
console.error("Evaluation prop is missing or incomplete:", evaluation);
|
151 |
+
}
|
152 |
}
|
153 |
+
setIsEvaluating(false);
|
154 |
+
};
|
155 |
+
|
156 |
+
// Finds the matching display name for a metric.
|
157 |
+
const getDisplayName = (lowerValue) => {
|
158 |
+
const found = metricOptions.find(n => n.toLowerCase() === lowerValue);
|
159 |
+
return found ? found : lowerValue;
|
160 |
};
|
161 |
|
162 |
return (
|
163 |
+
<Box className="evaluate-container">
|
164 |
+
{localLoading ? (
|
165 |
+
<Box>
|
166 |
+
<Typography variant="body2">Loading Evaluation...</Typography>
|
167 |
+
</Box>
|
168 |
+
) : (
|
169 |
+
<>
|
170 |
+
<FormControl className="evaluate-form-control">
|
171 |
+
<InputLabel id="chip-label">Select Metrics</InputLabel>
|
172 |
+
<Select
|
173 |
+
labelId="chip-label"
|
174 |
+
id="multiple-chip"
|
175 |
+
multiple
|
176 |
+
value={personName}
|
177 |
+
onChange={handleChange}
|
178 |
+
input={
|
179 |
+
<OutlinedInput
|
180 |
+
id="select-multiple-chip"
|
181 |
+
label="Select Metrics"
|
182 |
+
className="evaluate-outlined-input"
|
183 |
+
/>
|
184 |
+
}
|
185 |
+
renderValue={(selected) => (
|
186 |
+
<Box className="chip-container">
|
187 |
+
{selected.map((value) => (
|
188 |
+
<Chip
|
189 |
+
className="evaluate-chip"
|
190 |
+
key={value}
|
191 |
+
label={getDisplayName(value)}
|
192 |
+
onDelete={() => handleDelete(value)}
|
193 |
+
onMouseDown={(event) => event.stopPropagation()}
|
194 |
+
/>
|
195 |
+
))}
|
196 |
+
</Box>
|
197 |
+
)}
|
198 |
+
MenuProps={MenuProps}
|
199 |
+
>
|
200 |
+
{metricOptions.map((name) => (
|
201 |
+
<MenuItem
|
202 |
+
key={name}
|
203 |
+
value={name.toLowerCase()} // underlying value is lowercase
|
204 |
+
style={getStyles(name, personName, theme)}
|
205 |
+
>
|
206 |
+
{name}
|
207 |
+
</MenuItem>
|
208 |
+
))}
|
209 |
+
</Select>
|
210 |
+
</FormControl>
|
211 |
+
|
212 |
+
<Box mt={1}>
|
213 |
+
<Button
|
214 |
+
variant="contained"
|
215 |
+
onClick={handleEvaluateClick}
|
216 |
+
className="evaluate-button"
|
217 |
+
>
|
218 |
+
Evaluate
|
219 |
+
</Button>
|
220 |
+
</Box>
|
221 |
+
|
222 |
+
{noMetricsError && (
|
223 |
+
<Box className="no-metrics-message">
|
224 |
+
{noMetricsError}
|
225 |
+
</Box>
|
226 |
)}
|
227 |
+
|
228 |
+
{isEvaluating && (
|
229 |
+
<Box mt={1} display="flex" alignItems="center">
|
230 |
+
<Box className="custom-spinner" />
|
231 |
+
<Box ml={1}>Evaluating...</Box>
|
232 |
+
</Box>
|
233 |
+
)}
|
234 |
+
|
235 |
+
{evaluationResult && (
|
236 |
+
<Box mt={2}>
|
237 |
+
<ReactMarkdown>{evaluationResult}</ReactMarkdown>
|
238 |
+
</Box>
|
239 |
+
)}
|
240 |
+
</>
|
241 |
)}
|
242 |
+
</Box>
|
243 |
);
|
244 |
+
}
|
|
|
|