Choose plot column in table view of response inspector (#106)

* Make vars in inspect table view multiline

* Choose var to use for columns in table view of resp inspector

* Only default to prompt var as column if num vars > 1

* Rebuild react and update package version
This commit is contained in:
ianarawjo 2023-07-23 14:22:13 -04:00 committed by GitHub
parent ee1dc09954
commit a27d092ccc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 93 additions and 36 deletions

View File

@ -1,15 +1,15 @@
{
"files": {
"main.css": "/static/css/main.b5856613.css",
"main.js": "/static/js/main.d97188e6.js",
"main.css": "/static/css/main.fe68699e.css",
"main.js": "/static/js/main.ec9948b8.js",
"static/js/787.4c72bb55.chunk.js": "/static/js/787.4c72bb55.chunk.js",
"index.html": "/index.html",
"main.b5856613.css.map": "/static/css/main.b5856613.css.map",
"main.d97188e6.js.map": "/static/js/main.d97188e6.js.map",
"main.fe68699e.css.map": "/static/css/main.fe68699e.css.map",
"main.ec9948b8.js.map": "/static/js/main.ec9948b8.js.map",
"787.4c72bb55.chunk.js.map": "/static/js/787.4c72bb55.chunk.js.map"
},
"entrypoints": [
"static/css/main.b5856613.css",
"static/js/main.d97188e6.js"
"static/css/main.fe68699e.css",
"static/js/main.ec9948b8.js"
]
}

View File

@ -1 +1 @@
<!doctype html><html lang="en"><head><meta charset="utf-8"/><script async src="https://www.googletagmanager.com/gtag/js?id=G-RN3FDBLMCR"></script><script>function gtag(){dataLayer.push(arguments)}window.dataLayer=window.dataLayer||[],gtag("js",new Date),gtag("config","G-RN3FDBLMCR")</script><link rel="icon" href="/favicon.ico"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><meta name="description" content="A visual programming environment for prompt engineering"/><link rel="apple-touch-icon" href="/logo192.png"/><link rel="manifest" href="/manifest.json"/><title>ChainForge</title><script defer="defer" src="/static/js/main.d97188e6.js"></script><link href="/static/css/main.b5856613.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html>
<!doctype html><html lang="en"><head><meta charset="utf-8"/><script async src="https://www.googletagmanager.com/gtag/js?id=G-RN3FDBLMCR"></script><script>function gtag(){dataLayer.push(arguments)}window.dataLayer=window.dataLayer||[],gtag("js",new Date),gtag("config","G-RN3FDBLMCR")</script><link rel="icon" href="/favicon.ico"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><meta name="description" content="A visual programming environment for prompt engineering"/><link rel="apple-touch-icon" href="/logo192.png"/><link rel="manifest" href="/manifest.json"/><title>ChainForge</title><script defer="defer" src="/static/js/main.ec9948b8.js"></script><link href="/static/css/main.fe68699e.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -5,7 +5,7 @@
* be deployed in multiple locations.
*/
import React, { useState, useEffect, useRef } from 'react';
import { Collapse, Radio, MultiSelect, Group, Table } from '@mantine/core';
import { Collapse, Radio, MultiSelect, Group, Table, NativeSelect } from '@mantine/core';
import { useDisclosure } from '@mantine/hooks';
import { IconTable, IconSitemap } from '@tabler/icons-react';
import * as XLSX from 'xlsx';
@ -128,12 +128,16 @@ const LLMResponseInspector = ({ jsonResponses, wideFormat }) => {
const [receivedResponsesOnce, setReceivedResponsesOnce] = useState(false);
// The type of view to use to display responses. Can be either hierarchy or table.
const [viewFormat, setViewFormat] = useState("hierarchy");
const [viewFormat, setViewFormat] = useState(wideFormat ? "table" : "hierarchy");
// The MultiSelect so people can dynamically set what vars they care about
const [multiSelectVars, setMultiSelectVars] = useState([]);
const [multiSelectValue, setMultiSelectValue] = useState([]);
// The var name to use for columns in the table view
const [tableColVar, setTableColVar] = useState("LLM");
const [userSelectedTableCol, setUserSelectedTableCol] = useState(false);
// Global lookup for what color to use per LLM
const getColorForLLMAndSetIfNotFound = useStore((state) => state.getColorForLLMAndSetIfNotFound);
@ -161,6 +165,14 @@ const LLMResponseInspector = ({ jsonResponses, wideFormat }) => {
{value: `${name}`, label: name}
)).concat({value: 'LLM', label: 'LLM'});
setMultiSelectVars(msvars);
// If only one LLM is present, and user hasn't manually selected one to plot,
// and there's more than one prompt variable as input, default to plotting the
// first found prompt variable as columns instead:
if (!userSelectedTableCol && tableColVar === 'LLM' && found_llms.length === 1 && found_vars.length > 1) {
setTableColVar(found_vars[0]);
return; // useEffect will replot with the new values
}
// If this is the first time receiving responses, set the multiSelectValue to whatever is the first:
if (!receivedResponsesOnce) {
@ -244,39 +256,60 @@ const LLMResponseInspector = ({ jsonResponses, wideFormat }) => {
});
};
// Generate a view of the responses based on the view format set by the user
if (viewFormat === "table") {
// Generate a table, with default columns for: input vars, LLMs queried
// First get column names as input vars + LLMs:
const colnames = found_vars.concat(found_llms);
let var_cols, colnames, getColVal, found_sel_var_vals;
if (tableColVar === 'LLM') {
var_cols = found_vars;
getColVal = (r => r.llm);
found_sel_var_vals = found_llms;
colnames = var_cols.concat(found_llms);
} else {
var_cols = found_vars.filter(v => v !== tableColVar)
.concat(found_llms.length > 1 ? ['LLM'] : []); // only add LLM column if num LLMs > 1
getColVal = (r => r.vars[tableColVar]);
// Get the unique values for the selected variable
found_sel_var_vals = Array.from(responses.reduce((acc, res_obj) => {
acc.add(tableColVar in res_obj.vars ? res_obj.vars[tableColVar] : '(unspecified)');
return acc;
}, new Set()));
colnames = var_cols.concat(found_sel_var_vals);
}
const getVar = (r, v) => v === 'LLM' ? r.llm : r.vars[v];
// Then group responses by prompts. Each prompt will become a separate row of the table (will be treated as unique)
const responses_by_prompt = groupResponsesBy(responses, (r => r.prompt))[0];
let responses_by_prompt = groupResponsesBy(responses, (r => var_cols.map(v => getVar(r, v)).join('|')))[0];
const rows = Object.entries(responses_by_prompt).map(([prompt, resp_objs], idx) => {
// We assume here that prompt input vars will be the same across all responses in this bundle,
// so we just take the value of the first one per each varname:
const vars_cols = found_vars.map(v => v in resp_objs[0].vars ? resp_objs[0].vars[v] : '(unspecified)');
const resp_objs_by_llm = groupResponsesBy(resp_objs, r => r.llm)[0];
const llm_cols = found_llms.map(llm => {
if (llm in resp_objs_by_llm) {
const rs = resp_objs_by_llm[llm];
const var_cols_vals = var_cols.map(v => {
const val = (v === 'LLM') ? resp_objs[0].llm : resp_objs[0].vars[v];
return (val !== undefined) ? val : '(unspecified)';
});
const resp_objs_by_col_var = groupResponsesBy(resp_objs, getColVal)[0];
const sel_var_cols = found_sel_var_vals.map(val => {
if (val in resp_objs_by_col_var) {
const rs = resp_objs_by_col_var[val];
if (rs.length > 1)
console.warn(`Found more than one response object for LLM ${llm} for the same prompt. Only displaying first...`);
console.warn(`Found more than one response object for LLM ${val} for the same prompt. Only displaying first...`);
// Return response divs as response box here:
return generateResponseBoxes(rs, found_vars, 100)[0];
return generateResponseBoxes(rs, var_cols, 100)[0];
} else {
console.warn(`Could not find response object for LLM: ${llm}`);
return (<span>(not queried)</span>);
console.warn(`Could not find response object for column variable ${tableColVar} with value ${val}`);
return (<i>(no data)</i>);
}
});
return (
<tr key={idx} style={{borderBottom: '8px solid #eee'}}>
{vars_cols.map(c => (<td style={{backgroundColor: 'rgb(224, 244, 250)', paddingTop: '10px', borderRight: '1px solid #cde', fontWeight: '500'}}>{c}</td>))}
{llm_cols.map((c, i) => (<td style={{paddingTop: '8px', paddingBottom: '20px', borderRight: '1px solid #eee'}}>{c}</td>))}
{var_cols_vals.map(c => (<td className='inspect-table-var'>{c}</td>))}
{sel_var_cols.map((c, i) => (<td className='inspect-table-llm-resp'>{c}</td>))}
</tr>
);
});
@ -366,7 +399,7 @@ const LLMResponseInspector = ({ jsonResponses, wideFormat }) => {
setResponses(divs);
}
}, [multiSelectValue, jsonResponses, wideFormat, viewFormat]);
}, [multiSelectValue, jsonResponses, wideFormat, viewFormat, tableColVar]);
// When the user clicks an item in the drop-down,
// we want to autoclose the multiselect drop-down:
@ -383,17 +416,29 @@ const LLMResponseInspector = ({ jsonResponses, wideFormat }) => {
{wideFormat ?
<Radio.Group
name="viewFormat"
defaultValue="hierarchy"
value={viewFormat}
onChange={setViewFormat}
>
<Group mt="0px" mb='xs'>
<Radio value="hierarchy" label={<span><IconSitemap size='10pt' style={{marginBottom: '-1px'}}/> Hierarchy</span>} />
<Radio value="table" label={<span><IconTable size='10pt' style={{marginBottom: '-1px'}}/> Table</span>} />
<Radio value="hierarchy" label={<span><IconSitemap size='10pt' style={{marginBottom: '-1px'}}/> Hierarchy</span>} />
</Group>
</Radio.Group>
: <></>}
{viewFormat === "table" ?
<NativeSelect
value={tableColVar}
onChange={(event) => {
setTableColVar(event.currentTarget.value);
setUserSelectedTableCol(true);
}}
data={multiSelectVars}
label="Select the main variable to use for columns:"
mb="sm"
/>
: <></>}
{wideFormat === false || viewFormat === "hierarchy" ?
<div>
<MultiSelect ref={multiSelectRef}

View File

@ -19,13 +19,13 @@ const LLMResponseInspectorModal = forwardRef((props, ref) => {
}));
return (
<Modal size='80%' keepMounted opened={opened} onClose={close} closeOnClickOutside={true} style={{position: 'relative', 'left': '-100px'}} title={
<Modal size='90%' keepMounted opened={opened} onClose={close} closeOnClickOutside={true} style={{position: 'relative', 'left': '-100px'}} title={
<div><span>Response Inspector</span><button className="custom-button" style={{marginTop: 'auto', marginRight: '14px', float: 'right'}} onClick={() => exportToExcel(props.jsonResponses)}>Export data to Excel</button></div>
} styles={{ title: {justifyContent: 'space-between', width: '100%'} }} >
{ props.prompt !== undefined ?
<p className="inspect-modal-prompt-box"><span className='inspect-modal-prompt-prefix'>Root Prompt:&nbsp;</span> <span className="inspect-modal-prompt-text">{props.prompt}</span></p>
: <></>}
<div className="inspect-modal-response-container" style={{padding: '6px'}}>
<div className="inspect-modal-response-container" style={{padding: '6px', overflow: 'scroll'}}>
<LLMResponseInspector jsonResponses={props.jsonResponses} wideFormat={true} />
</div>
</Modal>

View File

@ -284,6 +284,18 @@
.inspect-modal-response-container .num-same-responses {
font-size: 10pt;
}
.inspect-table-var {
background-color: rgb(224, 244, 250);
padding-top: 10px;
border-right: 1px solid #cde;
font-weight: 500;
white-space: pre-wrap;
}
.inspect-table-llm-resp {
padding-top: 8px;
padding-bottom: 20px;
border-right: 1px solid #eee;
}
.response-group-component-header:hover {
color: #05e;
cursor: pointer;

View File

@ -6,7 +6,7 @@ def readme():
setup(
name='chainforge',
version='0.2.1.6',
version='0.2.1.7',
packages=find_packages(),
author="Ian Arawjo",
description="A Visual Programming Environment for Prompt Engineering",