feat: update OmniEditor workflow model
This commit is contained in:
Generated
+12
-1
@@ -62,6 +62,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz",
|
||||||
"integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==",
|
"integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/code-frame": "^7.29.0",
|
"@babel/code-frame": "^7.29.0",
|
||||||
"@babel/generator": "^7.29.0",
|
"@babel/generator": "^7.29.0",
|
||||||
@@ -1904,6 +1905,7 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"baseline-browser-mapping": "^2.9.0",
|
"baseline-browser-mapping": "^2.9.0",
|
||||||
"caniuse-lite": "^1.0.30001759",
|
"caniuse-lite": "^1.0.30001759",
|
||||||
@@ -2371,6 +2373,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz",
|
||||||
"integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==",
|
"integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
|
"peer": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12"
|
"node": ">=12"
|
||||||
}
|
}
|
||||||
@@ -3774,6 +3777,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
|
||||||
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12"
|
"node": ">=12"
|
||||||
},
|
},
|
||||||
@@ -3800,6 +3804,7 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"nanoid": "^3.3.11",
|
"nanoid": "^3.3.11",
|
||||||
"picocolors": "^1.1.1",
|
"picocolors": "^1.1.1",
|
||||||
@@ -3897,6 +3902,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/react/-/react-19.2.4.tgz",
|
"resolved": "https://registry.npmjs.org/react/-/react-19.2.4.tgz",
|
||||||
"integrity": "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==",
|
"integrity": "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
@@ -3906,6 +3912,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.4.tgz",
|
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.4.tgz",
|
||||||
"integrity": "sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==",
|
"integrity": "sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"scheduler": "^0.27.0"
|
"scheduler": "^0.27.0"
|
||||||
},
|
},
|
||||||
@@ -3925,6 +3932,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/react-redux/-/react-redux-9.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/react-redux/-/react-redux-9.2.0.tgz",
|
||||||
"integrity": "sha512-ROY9fvHhwOD9ySfrF0wmvu//bKCQ6AeZZq1nJNtbDC+kk5DuSuNX/n6YWYF/SYy7bSba4D4FSz8DJeKY/S/r+g==",
|
"integrity": "sha512-ROY9fvHhwOD9ySfrF0wmvu//bKCQ6AeZZq1nJNtbDC+kk5DuSuNX/n6YWYF/SYy7bSba4D4FSz8DJeKY/S/r+g==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/use-sync-external-store": "^0.0.6",
|
"@types/use-sync-external-store": "^0.0.6",
|
||||||
"use-sync-external-store": "^1.4.0"
|
"use-sync-external-store": "^1.4.0"
|
||||||
@@ -4037,7 +4045,8 @@
|
|||||||
"version": "5.0.1",
|
"version": "5.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/redux/-/redux-5.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/redux/-/redux-5.0.1.tgz",
|
||||||
"integrity": "sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==",
|
"integrity": "sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==",
|
||||||
"license": "MIT"
|
"license": "MIT",
|
||||||
|
"peer": true
|
||||||
},
|
},
|
||||||
"node_modules/redux-thunk": {
|
"node_modules/redux-thunk": {
|
||||||
"version": "3.1.0",
|
"version": "3.1.0",
|
||||||
@@ -4398,6 +4407,7 @@
|
|||||||
"integrity": "sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==",
|
"integrity": "sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==",
|
||||||
"devOptional": true,
|
"devOptional": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"esbuild": "~0.27.0",
|
"esbuild": "~0.27.0",
|
||||||
"get-tsconfig": "^4.7.5"
|
"get-tsconfig": "^4.7.5"
|
||||||
@@ -4538,6 +4548,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/vite/-/vite-6.4.1.tgz",
|
"resolved": "https://registry.npmjs.org/vite/-/vite-6.4.1.tgz",
|
||||||
"integrity": "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g==",
|
"integrity": "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"esbuild": "^0.25.0",
|
"esbuild": "^0.25.0",
|
||||||
"fdir": "^6.4.4",
|
"fdir": "^6.4.4",
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ import Landing from './pages/Landing';
|
|||||||
import Login from './pages/Login';
|
import Login from './pages/Login';
|
||||||
import SciHub from './pages/SciHub';
|
import SciHub from './pages/SciHub';
|
||||||
import OmniEditor from './pages/OmniEditor';
|
import OmniEditor from './pages/OmniEditor';
|
||||||
import SciDashboard from './pages/SciDashboard';
|
|
||||||
import SimuAnalysis from './pages/SimuAnalysis';
|
import SimuAnalysis from './pages/SimuAnalysis';
|
||||||
import { LanguageProvider } from './context/LanguageContext';
|
import { LanguageProvider } from './context/LanguageContext';
|
||||||
import LanguageSwitcher from './components/LanguageSwitcher';
|
import LanguageSwitcher from './components/LanguageSwitcher';
|
||||||
@@ -22,7 +21,6 @@ export default function App() {
|
|||||||
<Route path="/login" element={<Login />} />
|
<Route path="/login" element={<Login />} />
|
||||||
<Route path="/hub" element={<SciHub />} />
|
<Route path="/hub" element={<SciHub />} />
|
||||||
<Route path="/editor" element={<OmniEditor />} />
|
<Route path="/editor" element={<OmniEditor />} />
|
||||||
<Route path="/dashboard" element={<SciDashboard />} />
|
|
||||||
<Route path="/analysis" element={<SimuAnalysis />} />
|
<Route path="/analysis" element={<SimuAnalysis />} />
|
||||||
<Route path="*" element={<Navigate to="/" replace />} />
|
<Route path="*" element={<Navigate to="/" replace />} />
|
||||||
</Routes>
|
</Routes>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import { Link, useLocation } from 'react-router-dom';
|
import { Link, useLocation } from 'react-router-dom';
|
||||||
import { LayoutGrid, Home, Edit3, Table, Activity, ChevronDown, Globe } from 'lucide-react';
|
import { LayoutGrid, Home, Edit3, Activity, ChevronDown, Globe } from 'lucide-react';
|
||||||
import { motion, AnimatePresence } from 'motion/react';
|
import { motion, AnimatePresence } from 'motion/react';
|
||||||
|
|
||||||
const ModuleSwitcher: React.FC = () => {
|
const ModuleSwitcher: React.FC = () => {
|
||||||
@@ -10,7 +10,6 @@ const ModuleSwitcher: React.FC = () => {
|
|||||||
const modules = [
|
const modules = [
|
||||||
{ name: 'Landing', path: '/', icon: Globe, desc: 'Home' },
|
{ name: 'Landing', path: '/', icon: Globe, desc: 'Home' },
|
||||||
{ name: 'OmniEditor', path: '/editor', icon: Edit3, desc: 'Visual Modeling' },
|
{ name: 'OmniEditor', path: '/editor', icon: Edit3, desc: 'Visual Modeling' },
|
||||||
{ name: 'SciDashboard', path: '/dashboard', icon: Table, desc: 'Parameters' },
|
|
||||||
{ name: 'SimuAnalysis', path: '/analysis', icon: Activity, desc: 'Simulation' },
|
{ name: 'SimuAnalysis', path: '/analysis', icon: Activity, desc: 'Simulation' },
|
||||||
{ name: 'SciHub', path: '/hub', icon: Home, desc: 'Knowledge Base' },
|
{ name: 'SciHub', path: '/hub', icon: Home, desc: 'Knowledge Base' },
|
||||||
];
|
];
|
||||||
|
|||||||
+680
-712
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,41 @@
|
|||||||
|
import assert from 'node:assert/strict';
|
||||||
|
import { createDefaultWorkflow, getBehaviorSectionsForNode, getNodeConnectionAnchor, getParametersForNode } from './workflowModel';
|
||||||
|
|
||||||
|
const baseWorkflow = createDefaultWorkflow();
|
||||||
|
|
||||||
|
assert.deepEqual(
|
||||||
|
baseWorkflow.nodes.map((node) => node.kind),
|
||||||
|
['environment', 'agent'],
|
||||||
|
);
|
||||||
|
|
||||||
|
assert.equal(baseWorkflow.edges.length, 1);
|
||||||
|
|
||||||
|
const gridWorkflow = createDefaultWorkflow('grid');
|
||||||
|
|
||||||
|
assert.deepEqual(
|
||||||
|
gridWorkflow.nodes.map((node) => node.kind),
|
||||||
|
['environment', 'space', 'agent'],
|
||||||
|
);
|
||||||
|
|
||||||
|
assert.equal(gridWorkflow.edges.length, 2);
|
||||||
|
|
||||||
|
const parameterRows = getParametersForNode(gridWorkflow, 'example-agent');
|
||||||
|
|
||||||
|
assert.equal(parameterRows.length, 3);
|
||||||
|
assert.equal(parameterRows[0].name, 'race');
|
||||||
|
assert.equal(parameterRows[0].scope, 'endogenous');
|
||||||
|
|
||||||
|
const behaviorSections = getBehaviorSectionsForNode(gridWorkflow, 'example-agent');
|
||||||
|
|
||||||
|
assert.deepEqual(
|
||||||
|
behaviorSections.map((section) => section.kind),
|
||||||
|
['observable_pool', 'input', 'calculation', 'output'],
|
||||||
|
);
|
||||||
|
|
||||||
|
const sourceAnchor = getNodeConnectionAnchor({ x: 10, y: 20, side: 'right' });
|
||||||
|
const targetAnchor = getNodeConnectionAnchor({ x: 220, y: 20, side: 'left' });
|
||||||
|
|
||||||
|
assert.deepEqual(sourceAnchor, { x: 186, y: 84 });
|
||||||
|
assert.deepEqual(targetAnchor, { x: 220, y: 84 });
|
||||||
|
|
||||||
|
console.log('workflowModel tests passed');
|
||||||
@@ -0,0 +1,340 @@
|
|||||||
|
export type WorkflowNodeKind =
|
||||||
|
| 'environment'
|
||||||
|
| 'space'
|
||||||
|
| 'agent'
|
||||||
|
| 'behavior'
|
||||||
|
| 'output';
|
||||||
|
|
||||||
|
export type WorkflowPortType =
|
||||||
|
| 'ENV_CONTEXT'
|
||||||
|
| 'SPACE_CONTEXT'
|
||||||
|
| 'AGENT_CONTEXT'
|
||||||
|
| 'OBSERVABLE_INFO'
|
||||||
|
| 'BEHAVIOR_OUTPUT'
|
||||||
|
| 'REPORT';
|
||||||
|
|
||||||
|
export type WorkflowNodeStatus = 'ready' | 'warning' | 'error' | 'success';
|
||||||
|
|
||||||
|
export interface WorkflowPort {
|
||||||
|
id: string;
|
||||||
|
label: string;
|
||||||
|
type: WorkflowPortType;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface WorkflowNode {
|
||||||
|
id: string;
|
||||||
|
title: string;
|
||||||
|
subtitle: string;
|
||||||
|
kind: WorkflowNodeKind;
|
||||||
|
x: number;
|
||||||
|
y: number;
|
||||||
|
status: WorkflowNodeStatus;
|
||||||
|
inputs: WorkflowPort[];
|
||||||
|
outputs: WorkflowPort[];
|
||||||
|
config: Array<{ label: string; value: string }>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface WorkflowEdge {
|
||||||
|
id: string;
|
||||||
|
source: string;
|
||||||
|
target: string;
|
||||||
|
type: WorkflowPortType;
|
||||||
|
label: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type AttributeScope = 'endogenous' | 'exogenous' | 'derived';
|
||||||
|
|
||||||
|
export interface WorkflowParameter {
|
||||||
|
id: string;
|
||||||
|
nodeId: string;
|
||||||
|
name: string;
|
||||||
|
type: 'Float' | 'Integer' | 'Boolean' | 'String' | 'Coordinate';
|
||||||
|
scope: AttributeScope;
|
||||||
|
source: string;
|
||||||
|
value: string;
|
||||||
|
status: 'ok' | 'warning' | 'error';
|
||||||
|
description: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type BehaviorSectionKind = 'observable_pool' | 'input' | 'calculation' | 'output';
|
||||||
|
|
||||||
|
export interface BehaviorSection {
|
||||||
|
id: string;
|
||||||
|
nodeId: string;
|
||||||
|
kind: BehaviorSectionKind;
|
||||||
|
title: string;
|
||||||
|
description: string;
|
||||||
|
items: Array<{ label: string; source: string; value: string }>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface WorkflowModel {
|
||||||
|
nodes: WorkflowNode[];
|
||||||
|
edges: WorkflowEdge[];
|
||||||
|
parameters: WorkflowParameter[];
|
||||||
|
behaviorSections: BehaviorSection[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export type InitialSpaceType = 'grid' | 'network' | 'continuous' | 'gis';
|
||||||
|
|
||||||
|
function createSpaceNode(spaceType: InitialSpaceType): WorkflowNode {
|
||||||
|
const spaceConfig: Record<InitialSpaceType, { title: string; subtitle: string; config: Array<{ label: string; value: string }> }> = {
|
||||||
|
grid: {
|
||||||
|
title: 'Grid Space',
|
||||||
|
subtitle: '40 x 40 grid for spatial interaction',
|
||||||
|
config: [
|
||||||
|
{ label: 'width', value: '40' },
|
||||||
|
{ label: 'height', value: '40' },
|
||||||
|
{ label: 'horizontal wrap', value: 'true' },
|
||||||
|
{ label: 'vertical wrap', value: 'true' },
|
||||||
|
{ label: 'agent capacity/cell', value: '1' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
network: {
|
||||||
|
title: 'Network Space',
|
||||||
|
subtitle: 'Nodes and edges for relationship-based interaction',
|
||||||
|
config: [
|
||||||
|
{ label: 'network type', value: 'undirected' },
|
||||||
|
{ label: 'default degree', value: '4' },
|
||||||
|
{ label: 'rewiring', value: 'false' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
continuous: {
|
||||||
|
title: 'Continuous Space',
|
||||||
|
subtitle: 'Coordinate plane for movement and distance-based sensing',
|
||||||
|
config: [
|
||||||
|
{ label: 'width', value: '100' },
|
||||||
|
{ label: 'height', value: '100' },
|
||||||
|
{ label: 'boundary', value: 'bounded' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
gis: {
|
||||||
|
title: 'GIS Space',
|
||||||
|
subtitle: 'Map-coordinate space for real geography',
|
||||||
|
config: [
|
||||||
|
{ label: 'projection', value: 'WGS84' },
|
||||||
|
{ label: 'basemap', value: 'not selected' },
|
||||||
|
{ label: 'boundary layer', value: 'required' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const selected = spaceConfig[spaceType];
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: `${spaceType}-space`,
|
||||||
|
title: selected.title,
|
||||||
|
subtitle: selected.subtitle,
|
||||||
|
kind: 'space',
|
||||||
|
x: 100,
|
||||||
|
y: 80,
|
||||||
|
status: spaceType === 'grid' ? 'warning' : 'ready',
|
||||||
|
inputs: [{ id: 'env', label: 'environment', type: 'ENV_CONTEXT' }],
|
||||||
|
outputs: [{ id: 'space', label: 'space state', type: 'SPACE_CONTEXT' }],
|
||||||
|
config: selected.config,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createDefaultWorkflow(spaceType?: InitialSpaceType): WorkflowModel {
|
||||||
|
const nodes: WorkflowNode[] = [
|
||||||
|
{
|
||||||
|
id: 'environment',
|
||||||
|
title: 'Environment',
|
||||||
|
subtitle: 'Global model container and shared context',
|
||||||
|
kind: 'environment',
|
||||||
|
x: -220,
|
||||||
|
y: -110,
|
||||||
|
status: 'ready',
|
||||||
|
inputs: [],
|
||||||
|
outputs: [{ id: 'env', label: 'environment', type: 'ENV_CONTEXT' }],
|
||||||
|
config: [
|
||||||
|
{ label: 'model type', value: 'ABM + Game Model' },
|
||||||
|
{ label: 'default agent', value: 'Example Agent' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'example-agent',
|
||||||
|
title: 'Example Agent',
|
||||||
|
subtitle: 'Default agent with attributes and behaviors',
|
||||||
|
kind: 'agent',
|
||||||
|
x: 120,
|
||||||
|
y: -95,
|
||||||
|
status: 'ready',
|
||||||
|
inputs: [
|
||||||
|
{ id: 'space', label: 'located in grid', type: 'SPACE_CONTEXT' },
|
||||||
|
{ id: 'env', label: 'environment context', type: 'ENV_CONTEXT' },
|
||||||
|
],
|
||||||
|
outputs: [{ id: 'agent', label: 'agent state', type: 'AGENT_CONTEXT' }],
|
||||||
|
config: [
|
||||||
|
{ label: 'attributes', value: 'endogenous + exogenous' },
|
||||||
|
{ label: 'behavior', value: 'on_sensing' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
if (spaceType) {
|
||||||
|
nodes.splice(1, 0, createSpaceNode(spaceType));
|
||||||
|
}
|
||||||
|
|
||||||
|
const edges: WorkflowEdge[] = [
|
||||||
|
{ id: 'e1', source: 'environment', target: 'example-agent', type: 'ENV_CONTEXT', label: 'default context' },
|
||||||
|
];
|
||||||
|
|
||||||
|
if (spaceType) {
|
||||||
|
const spaceId = `${spaceType}-space`;
|
||||||
|
edges.splice(
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
{ id: 'e1', source: 'environment', target: spaceId, type: 'ENV_CONTEXT', label: 'contains space' },
|
||||||
|
{ id: 'e2', source: spaceId, target: 'example-agent', type: 'SPACE_CONTEXT', label: 'locates agent' },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
nodes,
|
||||||
|
edges,
|
||||||
|
parameters: [
|
||||||
|
{
|
||||||
|
id: 'a1',
|
||||||
|
nodeId: 'example-agent',
|
||||||
|
name: 'race',
|
||||||
|
type: 'String',
|
||||||
|
scope: 'endogenous',
|
||||||
|
source: 'initialization',
|
||||||
|
value: 'A | B',
|
||||||
|
status: 'ok',
|
||||||
|
description: 'Agent group identity used by segregation behavior',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'a2',
|
||||||
|
nodeId: 'example-agent',
|
||||||
|
name: 'position',
|
||||||
|
type: 'Coordinate',
|
||||||
|
scope: 'derived',
|
||||||
|
source: 'Grid Space connection',
|
||||||
|
value: '(x, y)',
|
||||||
|
status: 'ok',
|
||||||
|
description: 'Automatically added when agent is connected to grid space',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'a3',
|
||||||
|
nodeId: 'example-agent',
|
||||||
|
name: 'happiness',
|
||||||
|
type: 'Boolean',
|
||||||
|
scope: 'endogenous',
|
||||||
|
source: 'on_sensing output',
|
||||||
|
value: '--',
|
||||||
|
status: 'warning',
|
||||||
|
description: 'Assigned by behavior after observing neighbors',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 's1',
|
||||||
|
nodeId: 'grid-space',
|
||||||
|
name: 'cell_capacity',
|
||||||
|
type: 'Integer',
|
||||||
|
scope: 'exogenous',
|
||||||
|
source: 'space config',
|
||||||
|
value: '1',
|
||||||
|
status: 'ok',
|
||||||
|
description: 'How many agents can occupy one grid cell',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 's2',
|
||||||
|
nodeId: 'grid-space',
|
||||||
|
name: 'neighbor_radius',
|
||||||
|
type: 'Integer',
|
||||||
|
scope: 'exogenous',
|
||||||
|
source: 'space config',
|
||||||
|
value: '--',
|
||||||
|
status: 'error',
|
||||||
|
description: 'Required for on_sensing neighborhood lookup',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'n1',
|
||||||
|
nodeId: 'neighbor-agent',
|
||||||
|
name: 'race',
|
||||||
|
type: 'String',
|
||||||
|
scope: 'endogenous',
|
||||||
|
source: 'peer attribute',
|
||||||
|
value: 'visible',
|
||||||
|
status: 'ok',
|
||||||
|
description: 'Selectable observable attribute for Example Agent',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
behaviorSections: [
|
||||||
|
{
|
||||||
|
id: 'b1',
|
||||||
|
nodeId: 'example-agent',
|
||||||
|
kind: 'observable_pool',
|
||||||
|
title: 'Observable Information Pool',
|
||||||
|
description: 'All information the behavior may use, grouped by self and observed subjects.',
|
||||||
|
items: [
|
||||||
|
{ label: 'self.race', source: 'Example Agent', value: 'String' },
|
||||||
|
{ label: 'self.position', source: 'Grid Space', value: 'Coordinate' },
|
||||||
|
{ label: 'neighbor.race', source: 'Neighbor Agent', value: 'String' },
|
||||||
|
{ label: 'space.neighbor_count', source: 'Grid Space', value: 'Integer' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'b2',
|
||||||
|
nodeId: 'example-agent',
|
||||||
|
kind: 'input',
|
||||||
|
title: 'Behavior Inputs',
|
||||||
|
description: 'Selected inputs passed into this behavior.',
|
||||||
|
items: [
|
||||||
|
{ label: 'self.position', source: 'Observable pool', value: 'selected' },
|
||||||
|
{ label: 'neighbor.race', source: 'Observable pool', value: 'selected' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'b3',
|
||||||
|
nodeId: 'example-agent',
|
||||||
|
kind: 'calculation',
|
||||||
|
title: 'Calculation or LLM Call',
|
||||||
|
description: 'How inputs are transformed into a decision.',
|
||||||
|
items: [
|
||||||
|
{ label: 'mode', source: 'user choice', value: 'formula' },
|
||||||
|
{ label: 'rule', source: 'behavior editor', value: 'same_race_neighbors / total_neighbors >= threshold' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'b4',
|
||||||
|
nodeId: 'example-agent',
|
||||||
|
kind: 'output',
|
||||||
|
title: 'Output Assignment',
|
||||||
|
description: 'Where the return value is saved.',
|
||||||
|
items: [
|
||||||
|
{ label: 'return', source: 'calculation', value: 'Boolean' },
|
||||||
|
{ label: 'assign to', source: 'Example Agent', value: 'self.happiness' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getParametersForNode(workflow: WorkflowModel, nodeId: string) {
|
||||||
|
return workflow.parameters.filter((parameter) => parameter.nodeId === nodeId);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getBehaviorSectionsForNode(workflow: WorkflowModel, nodeId: string) {
|
||||||
|
return workflow.behaviorSections.filter((section) => section.nodeId === nodeId);
|
||||||
|
}
|
||||||
|
|
||||||
|
export const NODE_CARD_SIZE = {
|
||||||
|
width: 176,
|
||||||
|
height: 128,
|
||||||
|
};
|
||||||
|
|
||||||
|
export function getNodeConnectionAnchor({
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
side,
|
||||||
|
}: {
|
||||||
|
x: number;
|
||||||
|
y: number;
|
||||||
|
side: 'left' | 'right';
|
||||||
|
}) {
|
||||||
|
return {
|
||||||
|
x: side === 'left' ? x : x + NODE_CARD_SIZE.width,
|
||||||
|
y: y + NODE_CARD_SIZE.height / 2,
|
||||||
|
};
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user