Ensure LLM colors are unique and the same across nodes

This commit is contained in:
Ian Arawjo 2023-06-05 09:35:44 -04:00
parent 9b0d3a7779
commit 7c26442317

View File

@ -15,6 +15,142 @@ const InspectorNode = ({ data, id }) => {
const inputEdgesForNode = useStore((state) => state.inputEdgesForNode);
const setDataPropsForNode = useStore((state) => state.setDataPropsForNode);
<<<<<<< HEAD
=======
// The MultiSelect so people can dynamically set what vars they care about
const [multiSelectVars, setMultiSelectVars] = useState(data.vars || []);
const [multiSelectValue, setMultiSelectValue] = useState(data.selected_vars || []);
// Global lookup for what color to use per LLM
const getColorForLLMAndSetIfNotFound = useStore((state) => state.getColorForLLMAndSetIfNotFound);
// Update the visualization when the MultiSelect values change:
useEffect(() => {
if (!jsonResponses || (Array.isArray(jsonResponses) && jsonResponses.length === 0))
return;
const responses = jsonResponses;
const selected_vars = multiSelectValue;
// Functions to associate a color to each LLM in responses
// const llm_colors = ['#ace1aeb1', '#f1b963b1', '#e46161b1', '#f8f398b1', '#defcf9b1', '#cadefcb1', '#c3bef0b1', '#cca8e9b1'];
const color_for_llm = (llm) => (getColorForLLMAndSetIfNotFound(llm) + '99');
// const llm_badge_colors = ['green', 'orange', 'red', 'yellow', 'cyan', 'indigo', 'grape'];
const badge_color_for_llm = (llm) => 'blue';
const response_box_colors = ['#eee', '#fff', '#eee', '#ddd', '#eee', '#ddd', '#eee'];
const rgroup_color = (depth) => response_box_colors[depth % response_box_colors.length];
const getHeaderBadge = (key, val) => {
if (val) {
const s = truncStr(val.trim(), 144);
return (<div className="response-var-header">
<span className="response-var-name">{key}&nbsp;=&nbsp;</span><span className="response-var-value">"{s}"</span>
</div>);
} else {
return (<div className="response-var-header">{`unspecified ${key}`}</div>);
}
};
// Now we need to perform groupings by each var in the selected vars list,
// nesting the groupings (preferrably with custom divs) and sorting within
// each group by value of that group's var (so all same values are clumped together).
// :: For instance, for varnames = ['LLM', '$var1', '$var2'] we should get back
// :: nested divs first grouped by LLM (first level), then by var1, then var2 (deepest level).
let leaf_id = 0;
const groupByVars = (resps, varnames, eatenvars, header) => {
if (resps.length === 0) return [];
if (varnames.length === 0) {
// Base case. Display n response(s) to each single prompt, back-to-back:
const resp_boxes = resps.map((res_obj, res_idx) => {
const eval_res_items = res_obj.eval_res ? res_obj.eval_res.items : null;
// Spans for actual individual response texts
const ps = eval_res_items ? (
res_obj.responses.map((r, idx) => (
<div key={idx}>
<pre className="small-response">{r}</pre>
<p className="small-response-metrics">{getEvalResultStr(eval_res_items[idx])}</p>
</div>
))) : (
res_obj.responses.map((r, idx) => (
<pre key={idx} className="small-response">{r}</pre>
)));
// At the deepest level, there may still be some vars left over. We want to display these
// as tags, too, so we need to display only the ones that weren't 'eaten' during the recursive call:
// (e.g., the vars that weren't part of the initial 'varnames' list that form the groupings)
const unused_vars = filterDict(res_obj.vars, v => !eatenvars.includes(v));
const vars = vars_to_str(unused_vars);
const var_tags = vars.map((v) =>
(<Badge key={v} color="blue" size="xs">{v}</Badge>)
);
return (
<div key={"r"+res_idx} className="response-box" style={{ backgroundColor: color_for_llm(res_obj.llm) }}>
{var_tags}
{eatenvars.includes('LLM') ?
ps
: (<div className="response-item-llm-name-wrapper">
{ps}
<h1>{res_obj.llm}</h1>
</div>)
}
</div>
);
});
const className = eatenvars.length > 0 ? "response-group" : "";
const boxesClassName = eatenvars.length > 0 ? "response-boxes-wrapper" : "";
leaf_id += 1;
return (
<div key={'l'+leaf_id} className={className} style={{ backgroundColor: rgroup_color(eatenvars.length) }}>
{header}
<div className={boxesClassName}>
{resp_boxes}
</div>
</div>
);
}
// Bucket responses by the first var in the list, where
// we also bucket any 'leftover' responses that didn't have the requested variable (a kind of 'soft fail')
const group_name = varnames[0];
const [grouped_resps, leftover_resps] = (group_name === 'LLM')
? groupResponsesBy(resps, (r => r.llm))
: groupResponsesBy(resps, (r => ((group_name in r.vars) ? r.vars[group_name] : null)));
const get_header = (group_name === 'LLM')
? ((key, val) => (<Badge key={val} color={badge_color_for_llm(val)} size="sm">{val}</Badge>))
: ((key, val) => getHeaderBadge(key, val));
// Now produce nested divs corresponding to the groups
const remaining_vars = varnames.slice(1);
const updated_eatenvars = eatenvars.concat([group_name]);
const grouped_resps_divs = Object.keys(grouped_resps).map(g => groupByVars(grouped_resps[g], remaining_vars, updated_eatenvars, get_header(group_name, g)));
const leftover_resps_divs = leftover_resps.length > 0 ? groupByVars(leftover_resps, remaining_vars, updated_eatenvars, get_header(group_name, undefined)) : [];
return (<>
{header ?
(<div key={group_name} className="response-group" style={{ backgroundColor: rgroup_color(eatenvars.length) }}>
{header}
<div className="response-boxes-wrapper">
{grouped_resps_divs}
</div>
</div>)
: <div key={group_name}>{grouped_resps_divs}</div>}
{leftover_resps_divs.length === 0 ? (<></>) : (
<div key={'__unspecified_group'} className="response-group">
{leftover_resps_divs}
</div>
)}
</>);
};
// Produce DIV elements grouped by selected vars
const divs = groupByVars(responses, selected_vars, [], null);
setResponses(divs);
}, [multiSelectValue, multiSelectVars]);
>>>>>>> 00ea468 (Ensure LLM colors are unique and the same across nodes)
const handleOnConnect = () => {
// For some reason, 'on connect' is called twice upon connection.
// We detect when an inspector node is already fetching, and disable the second call: