wip join node UI

This commit is contained in:
Ian Arawjo 2023-10-13 13:13:51 -04:00
parent beeffd0ebb
commit d8e734d778
3 changed files with 158 additions and 7 deletions

View File

@ -8,7 +8,7 @@ import ReactFlow, {
} from 'reactflow';
import { Button, Menu, LoadingOverlay, Text, Box, List, Loader, Tooltip } from '@mantine/core';
import { useClipboard } from '@mantine/hooks';
import { IconSettings, IconTextPlus, IconTerminal, IconCsv, IconSettingsAutomation, IconFileSymlink, IconRobot, IconRuler2 } from '@tabler/icons-react';
import { IconSettings, IconTextPlus, IconTerminal, IconCsv, IconSettingsAutomation, IconFileSymlink, IconRobot, IconRuler2, IconArrowMerge } from '@tabler/icons-react';
import RemoveEdge from './RemoveEdge';
import TextFieldsNode from './TextFieldsNode'; // Import a custom node
import PromptNode from './PromptNode';
@ -19,6 +19,7 @@ import ScriptNode from './ScriptNode';
import AlertModal from './AlertModal';
import CsvNode from './CsvNode';
import TabularDataNode from './TabularDataNode';
import JoinNode from './JoinNode';
import CommentNode from './CommentNode';
import GlobalSettingsModal from './GlobalSettingsModal';
import ExampleFlowsModal from './ExampleFlowsModal';
@ -87,6 +88,7 @@ const nodeTypes = {
csv: CsvNode,
table: TabularDataNode,
comment: CommentNode,
join: JoinNode,
};
const edgeTypes = {
@ -197,27 +199,27 @@ const App = () => {
code = "function evaluate(response) {\n return response.text.length;\n}";
addNode({ id: 'evalNode-'+Date.now(), type: 'evaluator', data: { language: progLang, code: code }, position: {x: x-200, y:y-100} });
};
const addVisNode = (event) => {
const addVisNode = () => {
const { x, y } = getViewportCenter();
addNode({ id: 'visNode-'+Date.now(), type: 'vis', data: {}, position: {x: x-200, y:y-100} });
};
const addInspectNode = (event) => {
const addInspectNode = () => {
const { x, y } = getViewportCenter();
addNode({ id: 'inspectNode-'+Date.now(), type: 'inspect', data: {}, position: {x: x-200, y:y-100} });
};
const addScriptNode = (event) => {
const addScriptNode = () => {
const { x, y } = getViewportCenter();
addNode({ id: 'scriptNode-'+Date.now(), type: 'script', data: {}, position: {x: x-200, y:y-100} });
};
const addCsvNode = (event) => {
const addCsvNode = () => {
const { x, y } = getViewportCenter();
addNode({ id: 'csvNode-'+Date.now(), type: 'csv', data: {}, position: {x: x-200, y:y-100} });
};
const addTabularDataNode = (event) => {
const addTabularDataNode = () => {
const { x, y } = getViewportCenter();
addNode({ id: 'table-'+Date.now(), type: 'table', data: {}, position: {x: x-200, y:y-100} });
};
const addCommentNode = (event) => {
const addCommentNode = () => {
const { x, y } = getViewportCenter();
addNode({ id: 'comment-'+Date.now(), type: 'comment', data: {}, position: {x: x-200, y:y-100} });
};
@ -225,6 +227,10 @@ const App = () => {
const { x, y } = getViewportCenter();
addNode({ id: 'llmeval-'+Date.now(), type: 'llmeval', data: {}, position: {x: x-200, y:y-100} });
};
const addJoinNode = () => {
const { x, y } = getViewportCenter();
addNode({ id: 'join-'+Date.now(), type: 'join', data: {}, position: {x: x-200, y:y-100} });
};
const onClickExamples = () => {
if (examplesModal && examplesModal.current)
@ -768,6 +774,11 @@ const App = () => {
<Menu.Item onClick={addInspectNode} icon={'🔍'}> Inspect Node </Menu.Item>
</MenuTooltip>
<Menu.Divider />
<Menu.Label>Processors</Menu.Label>
<MenuTooltip label="Concatenate responses or input data together before passing into later nodes, within or across variables and LLMs.">
<Menu.Item onClick={addJoinNode} icon={<IconArrowMerge size='14pt' />}> Join Node </Menu.Item>
</MenuTooltip>
<Menu.Divider />
<Menu.Label>Misc</Menu.Label>
<MenuTooltip label="Make a comment about your flow.">
<Menu.Item onClick={addCommentNode} icon={'✏️'}> Comment Node </Menu.Item>

136
chainforge/react-server/src/JoinNode.js vendored Normal file
View File

@ -0,0 +1,136 @@
import React, { useState, useEffect } from 'react';
import { Handle } from 'reactflow';
import useStore from './store';
import NodeLabel from './NodeLabelComponent';
import fetch_from_backend from './fetch_from_backend';
import { IconArrowMerge } from '@tabler/icons-react';
import { Divider, NativeSelect, Text } from '@mantine/core';
const JoinNode = ({ data, id }) => {
let is_fetching = false;
const [jsonResponses, setJSONResponses] = useState(null);
const [pastInputs, setPastInputs] = useState([]);
const inputEdgesForNode = useStore((state) => state.inputEdgesForNode);
const setDataPropsForNode = useStore((state) => state.setDataPropsForNode);
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:
if (is_fetching) return;
// Get the ids from the connected input nodes:
const input_node_ids = inputEdgesForNode(id).map(e => e.source);
is_fetching = true;
// Grab responses associated with those ids:
fetch_from_backend('grabResponses', {
'responses': input_node_ids
}).then(function(json) {
if (json.responses && json.responses.length > 0) {
setJSONResponses(json.responses);
}
is_fetching = false;
}).catch(() => {
is_fetching = false;
});
}
const [groupByVar, setGroupByVar] = useState("all text");
const handleChangeGroupByVar = (new_val) => {
setGroupByVar(new_val.target.value);
};
const [groupByLLM, setGroupByLLM] = useState("within");
const handleChangeGroupByLLM = (new_val) => {
setGroupByLLM(new_val.target.value);
};
const [responsesPerPrompt, setResponsesPerPrompt] = useState("all");
const handleChangeResponsesPerPrompt = (new_val) => {
setResponsesPerPrompt(new_val.target.value);
};
if (data.input) {
// If there's a change in inputs...
if (data.input != pastInputs) {
setPastInputs(data.input);
handleOnConnect();
}
}
useEffect(() => {
if (data.refresh && data.refresh === true) {
// Recreate the visualization:
setDataPropsForNode(id, { refresh: false });
handleOnConnect();
}
}, [data, id, handleOnConnect, setDataPropsForNode]);
return (
<div className="join-node cfnode">
<NodeLabel title={data.title || 'Join Node'}
nodeId={id}
icon={<IconArrowMerge size='14pt'/>}
/>
<div style={{display: 'flex', justifyContent: 'left', maxWidth: '100%', marginBottom: '10px'}}>
<Text mt='3px' mr='xs'>Join</Text>
<NativeSelect onChange={handleChangeGroupByVar}
className='nodrag nowheel'
data={["all text", "by country", "by city"]}
size="xs"
value={groupByVar}
miw='80px'
mr='xs' />
</div>
<div style={{display: 'flex', justifyContent: 'left', maxWidth: '100%', marginBottom: '10px'}}>
<NativeSelect onChange={handleChangeGroupByLLM}
className='nodrag nowheel'
data={["within", "across"]}
size="xs"
value={groupByLLM}
maw='80px'
mr='xs' />
<Text mt='3px'>LLM(s)</Text>
</div>
<div style={{display: 'flex', justifyContent: 'left', maxWidth: '100%'}}>
<Text size='sm' mt='3px' mr='xs' color='gray' fs='italic'> take</Text>
<NativeSelect onChange={handleChangeResponsesPerPrompt}
className='nodrag nowheel'
data={["all", "1", "2", "3"]}
size="xs"
value={"1"}
maw='80px'
mr='xs'
color='gray' />
<Text size='sm' mt='3px' color='gray' fs='italic'>resp / prompt</Text>
</div>
<Divider my="xs" label="formatting" labelPosition="center" />
<NativeSelect onChange={handleChangeResponsesPerPrompt}
className='nodrag nowheel'
data={["double newline \\n\\n", "newline \\n", "- dashed list", '["list", "of", "strings"]']}
size="xs"
value={"double newline"}
miw='80px' />
<Handle
type="target"
position="left"
id="input"
className="grouped-handle"
style={{ top: "50%" }}
onConnect={handleOnConnect}
/>
<Handle
type="source"
position="right"
id="output"
className="grouped-handle"
style={{ top: "50%" }}
/>
</div>);
};
export default JoinNode;

View File

@ -531,6 +531,10 @@
border-color: #222;
}
.join-node {
min-width: 200px;
}
.tabular-data-node {
min-width: 280px;
}