WIP - using deep agent to create dog using workflow
This commit is contained in:
@@ -1,43 +1,153 @@
|
||||
import { Background, Controls, MiniMap, ReactFlow, useEdgesState, useNodesState } from '@xyflow/react'
|
||||
import { useCallback, useEffect } from 'react'
|
||||
import {
|
||||
Background,
|
||||
Controls,
|
||||
MiniMap,
|
||||
ReactFlow,
|
||||
useEdgesState,
|
||||
useNodesState,
|
||||
addEdge,
|
||||
Connection,
|
||||
Edge,
|
||||
Node,
|
||||
Panel,
|
||||
} from '@xyflow/react'
|
||||
import '@xyflow/react/dist/style.css'
|
||||
import './styles.css'
|
||||
|
||||
const initialNodes = [
|
||||
{ id: 'start', data: { label: 'Start' }, position: { x: 0, y: 0 } },
|
||||
{ id: 'llm', data: { label: 'LLM Decision' }, position: { x: 220, y: 0 } },
|
||||
{ id: 'tool', data: { label: 'Tool Node' }, position: { x: 440, y: 0 } },
|
||||
{ id: 'end', data: { label: 'End' }, position: { x: 660, y: 0 } },
|
||||
const nodeTypes = {
|
||||
Start: { style: { background: '#22c55e', color: 'white', padding: 10, borderRadius: 5 } },
|
||||
LLMDecisionNode: { style: { background: '#3b82f6', color: 'white', padding: 10, borderRadius: 5 } },
|
||||
ToolNode: { style: { background: '#f59e0b', color: 'white', padding: 10, borderRadius: 5 } },
|
||||
HumanInputNode: { style: { background: '#8b5cf6', color: 'white', padding: 10, borderRadius: 5 } },
|
||||
End: { style: { background: '#ef4444', color: 'white', padding: 10, borderRadius: 5 } },
|
||||
}
|
||||
|
||||
const initialNodes: Node[] = [
|
||||
{
|
||||
id: 'start-1',
|
||||
type: 'default',
|
||||
data: { label: '🟢 Start', type: 'Start' },
|
||||
position: { x: 250, y: 50 },
|
||||
style: nodeTypes.Start.style,
|
||||
},
|
||||
{
|
||||
id: 'end-1',
|
||||
type: 'default',
|
||||
data: { label: '🔴 End', type: 'End' },
|
||||
position: { x: 250, y: 400 },
|
||||
style: nodeTypes.End.style,
|
||||
},
|
||||
]
|
||||
|
||||
const initialEdges = [
|
||||
{ id: 'e1-2', source: 'start', target: 'llm' },
|
||||
{ id: 'e2-3', source: 'llm', target: 'tool' },
|
||||
{ id: 'e3-4', source: 'tool', target: 'end' },
|
||||
]
|
||||
const initialEdges: Edge[] = []
|
||||
|
||||
export const App = () => {
|
||||
const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes)
|
||||
const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges)
|
||||
|
||||
const onConnect = useCallback(
|
||||
(params: Connection) => setEdges((eds) => addEdge(params, eds)),
|
||||
[setEdges]
|
||||
)
|
||||
|
||||
// Send graph updates to parent window
|
||||
const notifyParent = useCallback(() => {
|
||||
const graphData = {
|
||||
id: 'process-graph',
|
||||
name: 'Process',
|
||||
nodes: nodes.map((node) => ({
|
||||
id: node.id,
|
||||
type: node.data.type || 'Start',
|
||||
position: node.position,
|
||||
data: node.data,
|
||||
})),
|
||||
edges: edges.map((edge) => ({
|
||||
id: edge.id,
|
||||
source: edge.source,
|
||||
target: edge.target,
|
||||
condition: edge.data?.condition,
|
||||
})),
|
||||
}
|
||||
|
||||
window.parent.postMessage(
|
||||
{
|
||||
type: 'GRAPH_UPDATED',
|
||||
payload: graphData,
|
||||
},
|
||||
'*'
|
||||
)
|
||||
}, [nodes, edges])
|
||||
|
||||
// Listen for graph load from parent
|
||||
useEffect(() => {
|
||||
const handleMessage = (event: MessageEvent) => {
|
||||
if (event.data.type === 'LOAD_GRAPH') {
|
||||
const graph = event.data.payload
|
||||
if (graph && graph.nodes && graph.edges) {
|
||||
setNodes(
|
||||
graph.nodes.map((node: any) => ({
|
||||
id: node.id,
|
||||
type: 'default',
|
||||
data: { label: node.data.label || node.type, ...node.data },
|
||||
position: node.position,
|
||||
style: nodeTypes[node.type as keyof typeof nodeTypes]?.style || {},
|
||||
}))
|
||||
)
|
||||
setEdges(
|
||||
graph.edges.map((edge: any) => ({
|
||||
id: edge.id,
|
||||
source: edge.source,
|
||||
target: edge.target,
|
||||
data: edge.condition ? { condition: edge.condition } : undefined,
|
||||
}))
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
window.addEventListener('message', handleMessage)
|
||||
return () => window.removeEventListener('message', handleMessage)
|
||||
}, [setNodes, setEdges])
|
||||
|
||||
// Notify parent on changes
|
||||
useEffect(() => {
|
||||
notifyParent()
|
||||
}, [nodes, edges, notifyParent])
|
||||
|
||||
const addNode = (type: string) => {
|
||||
const newNode: Node = {
|
||||
id: `${type.toLowerCase()}-${Date.now()}`,
|
||||
type: 'default',
|
||||
data: { label: `${type}`, type },
|
||||
position: { x: Math.random() * 400 + 50, y: Math.random() * 300 + 100 },
|
||||
style: nodeTypes[type as keyof typeof nodeTypes]?.style || {},
|
||||
}
|
||||
setNodes((nds) => nds.concat(newNode))
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="editor-shell">
|
||||
<header className="editor-header">
|
||||
<h1>AI Process Builder</h1>
|
||||
<p>Design tenant workflows with deterministic execution.</p>
|
||||
</header>
|
||||
<div className="editor-canvas">
|
||||
<ReactFlow
|
||||
nodes={nodes}
|
||||
edges={edges}
|
||||
onNodesChange={onNodesChange}
|
||||
onEdgesChange={onEdgesChange}
|
||||
fitView
|
||||
>
|
||||
<MiniMap />
|
||||
<Controls />
|
||||
<Background />
|
||||
</ReactFlow>
|
||||
</div>
|
||||
<ReactFlow
|
||||
nodes={nodes}
|
||||
edges={edges}
|
||||
onNodesChange={onNodesChange}
|
||||
onEdgesChange={onEdgesChange}
|
||||
onConnect={onConnect}
|
||||
fitView
|
||||
>
|
||||
<Panel position="top-left" className="node-palette">
|
||||
<h3>Node Palette</h3>
|
||||
<button onClick={() => addNode('LLMDecisionNode')}>🔵 LLM Decision</button>
|
||||
<button onClick={() => addNode('ToolNode')}>🟠 Tool</button>
|
||||
<button onClick={() => addNode('HumanInputNode')}>🟣 Human Input</button>
|
||||
<button onClick={() => addNode('End')}>🔴 End</button>
|
||||
</Panel>
|
||||
<MiniMap />
|
||||
<Controls />
|
||||
<Background />
|
||||
</ReactFlow>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -4,30 +4,68 @@ body {
|
||||
color: #0f172a;
|
||||
}
|
||||
|
||||
#root {
|
||||
width: 100%;
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
.editor-shell {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100vh;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.editor-header {
|
||||
padding: 16px 20px;
|
||||
border-bottom: 1px solid #e2e8f0;
|
||||
background: #fff;
|
||||
.node-palette {
|
||||
background: white;
|
||||
border: 1px solid #e2e8f0;
|
||||
border-radius: 8px;
|
||||
padding: 12px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||
min-width: 150px;
|
||||
}
|
||||
|
||||
.editor-header h1 {
|
||||
margin: 0 0 4px;
|
||||
font-size: 18px;
|
||||
.node-palette h3 {
|
||||
margin: 0 0 12px 0;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: #1e293b;
|
||||
}
|
||||
|
||||
.editor-header p {
|
||||
margin: 0;
|
||||
.node-palette button {
|
||||
display: block;
|
||||
width: 100%;
|
||||
padding: 8px 12px;
|
||||
margin-bottom: 6px;
|
||||
background: white;
|
||||
border: 1px solid #e2e8f0;
|
||||
border-radius: 6px;
|
||||
font-size: 13px;
|
||||
cursor: pointer;
|
||||
text-align: left;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.node-palette button:hover {
|
||||
background: #f1f5f9;
|
||||
border-color: #cbd5e1;
|
||||
}
|
||||
|
||||
.node-palette button:active {
|
||||
background: #e2e8f0;
|
||||
}
|
||||
|
||||
.react-flow__node {
|
||||
font-size: 12px;
|
||||
color: #64748b;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.editor-canvas {
|
||||
flex: 1;
|
||||
background: #f8fafc;
|
||||
.react-flow__edge-path {
|
||||
stroke: #64748b;
|
||||
stroke-width: 2;
|
||||
}
|
||||
|
||||
.react-flow__edge.selected .react-flow__edge-path {
|
||||
stroke: #3b82f6;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user