mirror of
https://github.com/ianarawjo/ChainForge.git
synced 2025-03-14 08:16:37 +00:00
wip join node UI
This commit is contained in:
parent
beeffd0ebb
commit
d8e734d778
25
chainforge/react-server/src/App.js
vendored
25
chainforge/react-server/src/App.js
vendored
@ -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
136
chainforge/react-server/src/JoinNode.js
vendored
Normal 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;
|
@ -531,6 +531,10 @@
|
||||
border-color: #222;
|
||||
}
|
||||
|
||||
.join-node {
|
||||
min-width: 200px;
|
||||
}
|
||||
|
||||
.tabular-data-node {
|
||||
min-width: 280px;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user