Spaces:
Running
Running
Upload 13 files
Browse files- .env.local +1 -0
- .gitignore +24 -0
- App.tsx +408 -0
- Dockerfile +16 -0
- README.md +8 -10
- constants.ts +663 -0
- index.html +44 -0
- index.tsx +15 -0
- metadata.json +5 -0
- package.json +24 -0
- tsconfig.json +29 -0
- types.ts +84 -0
- vite.config.ts +23 -0
.env.local
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
GEMINI_API_KEY=PLACEHOLDER_API_KEY
|
.gitignore
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Logs
|
| 2 |
+
logs
|
| 3 |
+
*.log
|
| 4 |
+
npm-debug.log*
|
| 5 |
+
yarn-debug.log*
|
| 6 |
+
yarn-error.log*
|
| 7 |
+
pnpm-debug.log*
|
| 8 |
+
lerna-debug.log*
|
| 9 |
+
|
| 10 |
+
node_modules
|
| 11 |
+
dist
|
| 12 |
+
dist-ssr
|
| 13 |
+
*.local
|
| 14 |
+
|
| 15 |
+
# Editor directories and files
|
| 16 |
+
.vscode/*
|
| 17 |
+
!.vscode/extensions.json
|
| 18 |
+
.idea
|
| 19 |
+
.DS_Store
|
| 20 |
+
*.suo
|
| 21 |
+
*.ntvs*
|
| 22 |
+
*.njsproj
|
| 23 |
+
*.sln
|
| 24 |
+
*.sw?
|
App.tsx
ADDED
|
@@ -0,0 +1,408 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import React, { useCallback, useRef, useState, useMemo, useEffect } from 'react';
|
| 2 |
+
import ReactFlow, {
|
| 3 |
+
ReactFlowProvider,
|
| 4 |
+
addEdge,
|
| 5 |
+
useNodesState,
|
| 6 |
+
useEdgesState,
|
| 7 |
+
Controls,
|
| 8 |
+
Background,
|
| 9 |
+
Connection,
|
| 10 |
+
Edge,
|
| 11 |
+
Node,
|
| 12 |
+
BackgroundVariant,
|
| 13 |
+
Panel,
|
| 14 |
+
ConnectionMode
|
| 15 |
+
} from 'reactflow';
|
| 16 |
+
import { FileText, CheckCircle, AlertTriangle, X, Copy, Check, Lightbulb, Key } from 'lucide-react';
|
| 17 |
+
import Sidebar from './components/Sidebar';
|
| 18 |
+
import PropertiesPanel from './components/PropertiesPanel';
|
| 19 |
+
import CustomNode from './components/CustomNode';
|
| 20 |
+
import CodeViewer from './components/CodeViewer';
|
| 21 |
+
import AIBuilderModal from './components/AIBuilderModal';
|
| 22 |
+
import SuggestionsModal from './components/SuggestionsModal';
|
| 23 |
+
import ApiKeyModal from './components/ApiKeyModal';
|
| 24 |
+
import { INITIAL_NODES, INITIAL_EDGES, LAYER_DEFINITIONS, TEMPLATES } from './constants';
|
| 25 |
+
import { generateRefinedPrompt, validateArchitecture, getArchitectureSuggestions, getUserApiKey, implementArchitectureSuggestions } from './services/geminiService';
|
| 26 |
+
import { NodeData, LayerType } from './types';
|
| 27 |
+
|
| 28 |
+
let id = 1000;
|
| 29 |
+
const getId = () => `${id++}`;
|
| 30 |
+
|
| 31 |
+
const AppContent = () => {
|
| 32 |
+
const reactFlowWrapper = useRef<HTMLDivElement>(null);
|
| 33 |
+
const [nodes, setNodes, onNodesChange] = useNodesState(INITIAL_NODES);
|
| 34 |
+
const [edges, setEdges, onEdgesChange] = useEdgesState(INITIAL_EDGES);
|
| 35 |
+
const [reactFlowInstance, setReactFlowInstance] = useState<any>(null);
|
| 36 |
+
const [selectedNodeId, setSelectedNodeId] = useState<string | null>(null);
|
| 37 |
+
|
| 38 |
+
const [generatedPrompt, setGeneratedPrompt] = useState('');
|
| 39 |
+
const [isPromptViewerOpen, setIsPromptViewerOpen] = useState(false);
|
| 40 |
+
const [isGeneratingPrompt, setIsGeneratingPrompt] = useState(false);
|
| 41 |
+
|
| 42 |
+
const [validationMsg, setValidationMsg] = useState<string | null>(null);
|
| 43 |
+
const [layoutCopied, setLayoutCopied] = useState(false);
|
| 44 |
+
|
| 45 |
+
// Suggestions State
|
| 46 |
+
const [isSuggestionsOpen, setIsSuggestionsOpen] = useState(false);
|
| 47 |
+
const [suggestionsText, setSuggestionsText] = useState('');
|
| 48 |
+
const [isSuggestionsLoading, setIsSuggestionsLoading] = useState(false);
|
| 49 |
+
const [isImplementingSuggestions, setIsImplementingSuggestions] = useState(false);
|
| 50 |
+
|
| 51 |
+
// AI Builder and Template State
|
| 52 |
+
const [isAIBuilderOpen, setIsAIBuilderOpen] = useState(false);
|
| 53 |
+
const [isTemplateMenuOpen, setIsTemplateMenuOpen] = useState(false);
|
| 54 |
+
|
| 55 |
+
// API Key State
|
| 56 |
+
const [isApiKeyModalOpen, setIsApiKeyModalOpen] = useState(false);
|
| 57 |
+
const [isConnected, setIsConnected] = useState(false);
|
| 58 |
+
|
| 59 |
+
// Load connection status on mount
|
| 60 |
+
useEffect(() => {
|
| 61 |
+
setIsConnected(!!getUserApiKey());
|
| 62 |
+
}, []);
|
| 63 |
+
|
| 64 |
+
// Define custom node types
|
| 65 |
+
const nodeTypes = useMemo(() => ({ custom: CustomNode }), []);
|
| 66 |
+
|
| 67 |
+
const onConnect = useCallback(
|
| 68 |
+
(params: Connection) => setEdges((eds) => addEdge({ ...params, animated: true, style: { stroke: '#94a3b8' } }, eds)),
|
| 69 |
+
[setEdges]
|
| 70 |
+
);
|
| 71 |
+
|
| 72 |
+
const onDragOver = useCallback((event: React.DragEvent) => {
|
| 73 |
+
event.preventDefault();
|
| 74 |
+
event.dataTransfer.dropEffect = 'move';
|
| 75 |
+
}, []);
|
| 76 |
+
|
| 77 |
+
const onDrop = useCallback(
|
| 78 |
+
(event: React.DragEvent) => {
|
| 79 |
+
event.preventDefault();
|
| 80 |
+
|
| 81 |
+
if (!reactFlowWrapper.current || !reactFlowInstance) return;
|
| 82 |
+
|
| 83 |
+
const type = event.dataTransfer.getData('application/reactflow') as LayerType;
|
| 84 |
+
|
| 85 |
+
if (!type) return;
|
| 86 |
+
|
| 87 |
+
const position = reactFlowInstance.screenToFlowPosition({
|
| 88 |
+
x: event.clientX,
|
| 89 |
+
y: event.clientY,
|
| 90 |
+
});
|
| 91 |
+
|
| 92 |
+
const newNode: Node<NodeData> = {
|
| 93 |
+
id: getId(),
|
| 94 |
+
type: 'custom',
|
| 95 |
+
position,
|
| 96 |
+
data: {
|
| 97 |
+
label: LAYER_DEFINITIONS[type].label,
|
| 98 |
+
type: type,
|
| 99 |
+
params: LAYER_DEFINITIONS[type].parameters.reduce((acc, p) => ({...acc, [p.name]: p.default}), {})
|
| 100 |
+
},
|
| 101 |
+
};
|
| 102 |
+
|
| 103 |
+
setNodes((nds) => nds.concat(newNode));
|
| 104 |
+
setSelectedNodeId(newNode.id);
|
| 105 |
+
},
|
| 106 |
+
[reactFlowInstance, setNodes]
|
| 107 |
+
);
|
| 108 |
+
|
| 109 |
+
const onNodeClick = useCallback((_: React.MouseEvent, node: Node) => {
|
| 110 |
+
setSelectedNodeId(node.id);
|
| 111 |
+
}, []);
|
| 112 |
+
|
| 113 |
+
const onPaneClick = useCallback(() => {
|
| 114 |
+
setSelectedNodeId(null);
|
| 115 |
+
}, []);
|
| 116 |
+
|
| 117 |
+
// Update node params from Properties Panel
|
| 118 |
+
const updateNodeParams = (id: string, newParams: Record<string, any>) => {
|
| 119 |
+
setNodes((nds) =>
|
| 120 |
+
nds.map((node) => {
|
| 121 |
+
if (node.id === id) {
|
| 122 |
+
return {
|
| 123 |
+
...node,
|
| 124 |
+
data: { ...node.data, params: newParams }
|
| 125 |
+
};
|
| 126 |
+
}
|
| 127 |
+
return node;
|
| 128 |
+
})
|
| 129 |
+
);
|
| 130 |
+
};
|
| 131 |
+
|
| 132 |
+
const deleteNode = (id: string) => {
|
| 133 |
+
setNodes((nds) => nds.filter((node) => node.id !== id));
|
| 134 |
+
setEdges((eds) => eds.filter((edge) => edge.source !== id && edge.target !== id));
|
| 135 |
+
setSelectedNodeId(null);
|
| 136 |
+
};
|
| 137 |
+
|
| 138 |
+
const handleGeneratePrompt = async () => {
|
| 139 |
+
if (!isConnected) { setIsApiKeyModalOpen(true); return; }
|
| 140 |
+
setIsGeneratingPrompt(true);
|
| 141 |
+
setIsPromptViewerOpen(true);
|
| 142 |
+
try {
|
| 143 |
+
const promptText = await generateRefinedPrompt(nodes, edges);
|
| 144 |
+
setGeneratedPrompt(promptText);
|
| 145 |
+
} catch (e) {
|
| 146 |
+
setGeneratedPrompt("Failed to generate prompt. Please try again.");
|
| 147 |
+
} finally {
|
| 148 |
+
setIsGeneratingPrompt(false);
|
| 149 |
+
}
|
| 150 |
+
};
|
| 151 |
+
|
| 152 |
+
const handleValidate = async () => {
|
| 153 |
+
if (!isConnected) { setIsApiKeyModalOpen(true); return; }
|
| 154 |
+
setValidationMsg("Validating...");
|
| 155 |
+
const result = await validateArchitecture(nodes, edges);
|
| 156 |
+
setValidationMsg(result);
|
| 157 |
+
// Auto hide after 5 seconds if brief
|
| 158 |
+
if (result.length < 50) {
|
| 159 |
+
setTimeout(() => setValidationMsg(null), 5000);
|
| 160 |
+
}
|
| 161 |
+
};
|
| 162 |
+
|
| 163 |
+
const handleGetSuggestions = async () => {
|
| 164 |
+
if (!isConnected) { setIsApiKeyModalOpen(true); return; }
|
| 165 |
+
setIsSuggestionsOpen(true);
|
| 166 |
+
setIsSuggestionsLoading(true);
|
| 167 |
+
try {
|
| 168 |
+
const suggestions = await getArchitectureSuggestions(nodes, edges);
|
| 169 |
+
setSuggestionsText(suggestions);
|
| 170 |
+
} catch (error) {
|
| 171 |
+
setSuggestionsText("Failed to get suggestions.");
|
| 172 |
+
} finally {
|
| 173 |
+
setIsSuggestionsLoading(false);
|
| 174 |
+
}
|
| 175 |
+
};
|
| 176 |
+
|
| 177 |
+
const handleImplementSuggestions = async () => {
|
| 178 |
+
if (!suggestionsText || isImplementingSuggestions) return;
|
| 179 |
+
setIsImplementingSuggestions(true);
|
| 180 |
+
try {
|
| 181 |
+
const result = await implementArchitectureSuggestions(nodes, edges, suggestionsText);
|
| 182 |
+
if (result && result.nodes) {
|
| 183 |
+
// Fix types and defaults
|
| 184 |
+
const processedNodes = result.nodes.map((n: any) => ({
|
| 185 |
+
...n,
|
| 186 |
+
type: 'custom',
|
| 187 |
+
data: { ...n.data, label: n.data.label || n.data.type }
|
| 188 |
+
}));
|
| 189 |
+
const processedEdges = result.edges.map((e: any) => ({
|
| 190 |
+
...e, animated: true, style: { stroke: '#94a3b8' }
|
| 191 |
+
}));
|
| 192 |
+
|
| 193 |
+
setNodes(processedNodes);
|
| 194 |
+
setEdges(processedEdges);
|
| 195 |
+
setIsSuggestionsOpen(false); // Close modal on success
|
| 196 |
+
}
|
| 197 |
+
} catch (e) {
|
| 198 |
+
console.error(e);
|
| 199 |
+
alert("Failed to implement suggestions automatically.");
|
| 200 |
+
} finally {
|
| 201 |
+
setIsImplementingSuggestions(false);
|
| 202 |
+
}
|
| 203 |
+
};
|
| 204 |
+
|
| 205 |
+
const handleCopyLayout = () => {
|
| 206 |
+
const layout = {
|
| 207 |
+
nodes,
|
| 208 |
+
edges
|
| 209 |
+
};
|
| 210 |
+
navigator.clipboard.writeText(JSON.stringify(layout, null, 2));
|
| 211 |
+
setLayoutCopied(true);
|
| 212 |
+
setTimeout(() => setLayoutCopied(false), 2000);
|
| 213 |
+
};
|
| 214 |
+
|
| 215 |
+
const handleApplyAIBuild = (newNodes: any[], newEdges: any[]) => {
|
| 216 |
+
setNodes(newNodes);
|
| 217 |
+
setEdges(newEdges);
|
| 218 |
+
};
|
| 219 |
+
|
| 220 |
+
const loadTemplate = (templateKey: string) => {
|
| 221 |
+
const template = TEMPLATES[templateKey];
|
| 222 |
+
if (template) {
|
| 223 |
+
// Need to deep clone to avoid reference issues if loaded multiple times
|
| 224 |
+
const clonedNodes = JSON.parse(JSON.stringify(template.nodes));
|
| 225 |
+
const clonedEdges = JSON.parse(JSON.stringify(template.edges));
|
| 226 |
+
|
| 227 |
+
// Update styling and functional props that aren't in JSON
|
| 228 |
+
const hydratedNodes = clonedNodes.map((n: any) => ({
|
| 229 |
+
...n,
|
| 230 |
+
data: {
|
| 231 |
+
...n.data,
|
| 232 |
+
label: LAYER_DEFINITIONS[n.data.type as LayerType]?.label || n.data.label
|
| 233 |
+
}
|
| 234 |
+
}));
|
| 235 |
+
|
| 236 |
+
const hydratedEdges = clonedEdges.map((e: any) => ({
|
| 237 |
+
...e,
|
| 238 |
+
animated: true,
|
| 239 |
+
style: { stroke: '#94a3b8' }
|
| 240 |
+
}));
|
| 241 |
+
|
| 242 |
+
setNodes(hydratedNodes);
|
| 243 |
+
setEdges(hydratedEdges);
|
| 244 |
+
setIsTemplateMenuOpen(false);
|
| 245 |
+
}
|
| 246 |
+
};
|
| 247 |
+
|
| 248 |
+
const selectedNode = useMemo(() =>
|
| 249 |
+
nodes.find((n) => n.id === selectedNodeId) || null,
|
| 250 |
+
[nodes, selectedNodeId]
|
| 251 |
+
);
|
| 252 |
+
|
| 253 |
+
return (
|
| 254 |
+
<div className="flex h-screen w-screen bg-slate-950">
|
| 255 |
+
<Sidebar
|
| 256 |
+
onOpenAIBuilder={() => isConnected ? setIsAIBuilderOpen(true) : setIsApiKeyModalOpen(true)}
|
| 257 |
+
onSelectTemplate={(id) => id === 'menu' ? setIsTemplateMenuOpen(true) : loadTemplate(id)}
|
| 258 |
+
isConnected={isConnected}
|
| 259 |
+
/>
|
| 260 |
+
|
| 261 |
+
<div className="flex-1 h-full relative" ref={reactFlowWrapper}>
|
| 262 |
+
<ReactFlow
|
| 263 |
+
nodes={nodes}
|
| 264 |
+
edges={edges}
|
| 265 |
+
onNodesChange={onNodesChange}
|
| 266 |
+
onEdgesChange={onEdgesChange}
|
| 267 |
+
onConnect={onConnect}
|
| 268 |
+
onInit={setReactFlowInstance}
|
| 269 |
+
onDrop={onDrop}
|
| 270 |
+
onDragOver={onDragOver}
|
| 271 |
+
onNodeClick={onNodeClick}
|
| 272 |
+
onPaneClick={onPaneClick}
|
| 273 |
+
nodeTypes={nodeTypes}
|
| 274 |
+
connectionMode={ConnectionMode.Loose}
|
| 275 |
+
fitView
|
| 276 |
+
className="bg-slate-950"
|
| 277 |
+
>
|
| 278 |
+
<Background color="#1e293b" variant={BackgroundVariant.Dots} gap={20} size={1} />
|
| 279 |
+
<Controls className="!bg-slate-800 !border-slate-700 [&>button]:!fill-slate-300 [&>button:hover]:!bg-slate-700" />
|
| 280 |
+
|
| 281 |
+
<Panel position="top-right" className="flex gap-2">
|
| 282 |
+
<button
|
| 283 |
+
onClick={handleCopyLayout}
|
| 284 |
+
className="flex items-center gap-2 px-4 py-2 bg-slate-800 hover:bg-slate-700 border border-slate-700 rounded-lg text-slate-200 text-sm font-medium transition-colors shadow-lg"
|
| 285 |
+
>
|
| 286 |
+
{layoutCopied ? <Check size={16} className="text-emerald-500" /> : <Copy size={16} className="text-slate-400" />}
|
| 287 |
+
{layoutCopied ? 'Copied' : 'Copy Layout'}
|
| 288 |
+
</button>
|
| 289 |
+
<button
|
| 290 |
+
onClick={() => setIsApiKeyModalOpen(true)}
|
| 291 |
+
className="flex items-center gap-2 px-4 py-2 bg-slate-800 hover:bg-slate-700 border border-slate-700 rounded-lg text-slate-200 text-sm font-medium transition-colors shadow-lg"
|
| 292 |
+
>
|
| 293 |
+
<Key size={16} className={isConnected ? "text-emerald-500" : "text-slate-400"} />
|
| 294 |
+
{isConnected ? 'Key Active' : 'Add Key'}
|
| 295 |
+
</button>
|
| 296 |
+
<button
|
| 297 |
+
onClick={handleGetSuggestions}
|
| 298 |
+
className="flex items-center gap-2 px-4 py-2 bg-slate-800 hover:bg-slate-700 border border-slate-700 rounded-lg text-slate-200 text-sm font-medium transition-colors shadow-lg"
|
| 299 |
+
>
|
| 300 |
+
<Lightbulb size={16} className="text-amber-400" />
|
| 301 |
+
Suggestions
|
| 302 |
+
</button>
|
| 303 |
+
<button
|
| 304 |
+
onClick={handleValidate}
|
| 305 |
+
className="flex items-center gap-2 px-4 py-2 bg-slate-800 hover:bg-slate-700 border border-slate-700 rounded-lg text-slate-200 text-sm font-medium transition-colors shadow-lg"
|
| 306 |
+
>
|
| 307 |
+
<CheckCircle size={16} className="text-emerald-500" />
|
| 308 |
+
Validate
|
| 309 |
+
</button>
|
| 310 |
+
<button
|
| 311 |
+
onClick={handleGeneratePrompt}
|
| 312 |
+
className="flex items-center gap-2 px-4 py-2 bg-blue-600 hover:bg-blue-500 rounded-lg text-white text-sm font-medium transition-colors shadow-lg shadow-blue-900/20"
|
| 313 |
+
>
|
| 314 |
+
<FileText size={16} />
|
| 315 |
+
Generate Prompt
|
| 316 |
+
</button>
|
| 317 |
+
</Panel>
|
| 318 |
+
|
| 319 |
+
{validationMsg && (
|
| 320 |
+
<Panel position="bottom-center" className="mb-8">
|
| 321 |
+
<div className="bg-slate-900/90 backdrop-blur border border-slate-700 text-slate-200 px-6 py-4 rounded-xl shadow-2xl max-w-lg">
|
| 322 |
+
<div className="flex items-start gap-3">
|
| 323 |
+
<AlertTriangle className="text-amber-500 shrink-0 mt-0.5" size={18} />
|
| 324 |
+
<p className="text-sm whitespace-pre-wrap">{validationMsg}</p>
|
| 325 |
+
</div>
|
| 326 |
+
<button
|
| 327 |
+
onClick={() => setValidationMsg(null)}
|
| 328 |
+
className="mt-3 text-xs text-slate-500 hover:text-slate-300 underline"
|
| 329 |
+
>
|
| 330 |
+
Dismiss
|
| 331 |
+
</button>
|
| 332 |
+
</div>
|
| 333 |
+
</Panel>
|
| 334 |
+
)}
|
| 335 |
+
|
| 336 |
+
{/* Template Selection Modal Overlay */}
|
| 337 |
+
{isTemplateMenuOpen && (
|
| 338 |
+
<div className="absolute inset-0 z-50 flex items-center justify-center bg-black/50 backdrop-blur-sm">
|
| 339 |
+
<div className="bg-slate-900 border border-slate-700 rounded-xl shadow-2xl p-6 w-96 max-w-full">
|
| 340 |
+
<div className="flex justify-between items-center mb-4">
|
| 341 |
+
<h3 className="text-lg font-bold text-white">Select Template</h3>
|
| 342 |
+
<button onClick={() => setIsTemplateMenuOpen(false)}><X size={20} className="text-slate-500 hover:text-white"/></button>
|
| 343 |
+
</div>
|
| 344 |
+
<div className="grid gap-3">
|
| 345 |
+
{Object.entries(TEMPLATES).map(([key, t]) => (
|
| 346 |
+
<button
|
| 347 |
+
key={key}
|
| 348 |
+
onClick={() => loadTemplate(key)}
|
| 349 |
+
className="text-left p-3 rounded bg-slate-800 hover:bg-slate-750 border border-slate-700 hover:border-blue-500/50 transition-all group"
|
| 350 |
+
>
|
| 351 |
+
<div className="font-semibold text-slate-200 group-hover:text-blue-400">{t.name}</div>
|
| 352 |
+
<div className="text-xs text-slate-500">{t.description}</div>
|
| 353 |
+
</button>
|
| 354 |
+
))}
|
| 355 |
+
</div>
|
| 356 |
+
</div>
|
| 357 |
+
</div>
|
| 358 |
+
)}
|
| 359 |
+
|
| 360 |
+
</ReactFlow>
|
| 361 |
+
</div>
|
| 362 |
+
|
| 363 |
+
<PropertiesPanel
|
| 364 |
+
selectedNode={selectedNode}
|
| 365 |
+
onChange={updateNodeParams}
|
| 366 |
+
onDelete={deleteNode}
|
| 367 |
+
onClose={() => setSelectedNodeId(null)}
|
| 368 |
+
/>
|
| 369 |
+
|
| 370 |
+
<CodeViewer
|
| 371 |
+
isOpen={isPromptViewerOpen}
|
| 372 |
+
onClose={() => setIsPromptViewerOpen(false)}
|
| 373 |
+
code={generatedPrompt}
|
| 374 |
+
isLoading={isGeneratingPrompt}
|
| 375 |
+
/>
|
| 376 |
+
|
| 377 |
+
<SuggestionsModal
|
| 378 |
+
isOpen={isSuggestionsOpen}
|
| 379 |
+
onClose={() => setIsSuggestionsOpen(false)}
|
| 380 |
+
suggestions={suggestionsText}
|
| 381 |
+
isLoading={isSuggestionsLoading}
|
| 382 |
+
onImplement={handleImplementSuggestions}
|
| 383 |
+
isImplementing={isImplementingSuggestions}
|
| 384 |
+
/>
|
| 385 |
+
|
| 386 |
+
<AIBuilderModal
|
| 387 |
+
isOpen={isAIBuilderOpen}
|
| 388 |
+
onClose={() => setIsAIBuilderOpen(false)}
|
| 389 |
+
onApply={handleApplyAIBuild}
|
| 390 |
+
currentNodes={nodes}
|
| 391 |
+
/>
|
| 392 |
+
|
| 393 |
+
<ApiKeyModal
|
| 394 |
+
isOpen={isApiKeyModalOpen}
|
| 395 |
+
onClose={() => setIsApiKeyModalOpen(false)}
|
| 396 |
+
onSuccess={() => setIsConnected(true)}
|
| 397 |
+
/>
|
| 398 |
+
</div>
|
| 399 |
+
);
|
| 400 |
+
};
|
| 401 |
+
|
| 402 |
+
const App = () => (
|
| 403 |
+
<ReactFlowProvider>
|
| 404 |
+
<AppContent />
|
| 405 |
+
</ReactFlowProvider>
|
| 406 |
+
);
|
| 407 |
+
|
| 408 |
+
export default App;
|
Dockerfile
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Stage 1: Build React/Vite app
|
| 2 |
+
FROM node:18-alpine AS builder
|
| 3 |
+
WORKDIR /app
|
| 4 |
+
COPY package*.json ./
|
| 5 |
+
RUN npm install
|
| 6 |
+
COPY . .
|
| 7 |
+
ARG GEMINI_API_KEY
|
| 8 |
+
ENV GEMINI_API_KEY=$GEMINI_API_KEY
|
| 9 |
+
RUN npm run build
|
| 10 |
+
|
| 11 |
+
# Stage 2: Serve with Python
|
| 12 |
+
FROM python:3.11-slim
|
| 13 |
+
WORKDIR /app
|
| 14 |
+
COPY --from=builder /app/dist .
|
| 15 |
+
EXPOSE 7860
|
| 16 |
+
CMD ["python3", "-m", "http.server", "7860", "--bind", "0.0.0.0"]
|
README.md
CHANGED
|
@@ -1,10 +1,8 @@
|
|
| 1 |
-
---
|
| 2 |
-
title:
|
| 3 |
-
emoji:
|
| 4 |
-
colorFrom:
|
| 5 |
-
colorTo:
|
| 6 |
-
sdk: docker
|
| 7 |
-
|
| 8 |
-
---
|
| 9 |
-
|
| 10 |
-
Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
|
|
|
|
| 1 |
+
---
|
| 2 |
+
title: Dockerized React App
|
| 3 |
+
emoji: 🐳
|
| 4 |
+
colorFrom: purple
|
| 5 |
+
colorTo: gray
|
| 6 |
+
sdk: docker
|
| 7 |
+
app_port: 7860
|
| 8 |
+
---
|
|
|
|
|
|
constants.ts
ADDED
|
@@ -0,0 +1,663 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { LayerDefinition, LayerType, GraphTemplate } from './types';
|
| 2 |
+
|
| 3 |
+
export const LAYER_DEFINITIONS: Record<LayerType, LayerDefinition> = {
|
| 4 |
+
// --- CORE ---
|
| 5 |
+
[LayerType.INPUT]: {
|
| 6 |
+
type: LayerType.INPUT,
|
| 7 |
+
label: 'Input Data',
|
| 8 |
+
description: 'Entry point for data tensors',
|
| 9 |
+
category: 'Core',
|
| 10 |
+
parameters: [
|
| 11 |
+
{ name: 'modality', type: 'select', label: 'Modality', default: 'Tensor', options: ['Tensor', 'Image', 'Text', 'Audio', 'Video', 'Latent', 'State'] },
|
| 12 |
+
{ name: 'shape', type: 'string', label: 'Shape (e.g. 3,224,224)', default: '3, 224, 224' },
|
| 13 |
+
{ name: 'batch_size', type: 'number', label: 'Batch Size', default: 32 },
|
| 14 |
+
{ name: 'dtype', type: 'select', label: 'Data Type', default: 'float32', options: ['float32', 'int64', 'bool'] }
|
| 15 |
+
]
|
| 16 |
+
},
|
| 17 |
+
[LayerType.LINEAR]: {
|
| 18 |
+
type: LayerType.LINEAR,
|
| 19 |
+
label: 'Linear (Dense)',
|
| 20 |
+
description: 'Fully connected layer',
|
| 21 |
+
category: 'Core',
|
| 22 |
+
parameters: [
|
| 23 |
+
{ name: 'in_features', type: 'number', label: 'In Features (Opt)', default: 0, description: "0 = Auto-infer" },
|
| 24 |
+
{ name: 'out_features', type: 'number', label: 'Output Features', default: 128 },
|
| 25 |
+
{ name: 'bias', type: 'boolean', label: 'Use Bias', default: true }
|
| 26 |
+
]
|
| 27 |
+
},
|
| 28 |
+
[LayerType.OUTPUT]: {
|
| 29 |
+
type: LayerType.OUTPUT,
|
| 30 |
+
label: 'Output Head',
|
| 31 |
+
description: 'Final model output',
|
| 32 |
+
category: 'Core',
|
| 33 |
+
parameters: [
|
| 34 |
+
{ name: 'num_classes', type: 'number', label: 'Classes', default: 10 },
|
| 35 |
+
{ name: 'activation', type: 'select', label: 'Activation', default: 'Softmax', options: ['None', 'Softmax', 'Sigmoid', 'LogSoftmax'] }
|
| 36 |
+
]
|
| 37 |
+
},
|
| 38 |
+
[LayerType.EMBEDDING]: {
|
| 39 |
+
type: LayerType.EMBEDDING,
|
| 40 |
+
label: 'Embedding',
|
| 41 |
+
description: 'Lookup table for embeddings',
|
| 42 |
+
category: 'Core',
|
| 43 |
+
parameters: [
|
| 44 |
+
{ name: 'num_embeddings', type: 'number', label: 'Vocab Size', default: 10000 },
|
| 45 |
+
{ name: 'embedding_dim', type: 'number', label: 'Embed Dim', default: 256 },
|
| 46 |
+
{ name: 'padding_idx', type: 'number', label: 'Padding Index', default: 0 },
|
| 47 |
+
{ name: 'max_norm', type: 'number', label: 'Max Norm', default: 0, description: "0 = None" }
|
| 48 |
+
]
|
| 49 |
+
},
|
| 50 |
+
[LayerType.POS_EMBED]: {
|
| 51 |
+
type: LayerType.POS_EMBED,
|
| 52 |
+
label: 'Positional Embed',
|
| 53 |
+
description: 'Learnable positional embeddings',
|
| 54 |
+
category: 'Core',
|
| 55 |
+
parameters: [
|
| 56 |
+
{ name: 'num_embeddings', type: 'number', label: 'Max Positions', default: 1024 },
|
| 57 |
+
{ name: 'embedding_dim', type: 'number', label: 'Embed Dim', default: 256 }
|
| 58 |
+
]
|
| 59 |
+
},
|
| 60 |
+
|
| 61 |
+
// --- MERGE ---
|
| 62 |
+
[LayerType.CONCAT]: {
|
| 63 |
+
type: LayerType.CONCAT,
|
| 64 |
+
label: 'Concatenate',
|
| 65 |
+
description: 'Merge inputs along a dim',
|
| 66 |
+
category: 'Merge',
|
| 67 |
+
parameters: [
|
| 68 |
+
{ name: 'dim', type: 'number', label: 'Dimension', default: 1 }
|
| 69 |
+
]
|
| 70 |
+
},
|
| 71 |
+
[LayerType.ADD]: {
|
| 72 |
+
type: LayerType.ADD,
|
| 73 |
+
label: 'Add (Sum)',
|
| 74 |
+
description: 'Element-wise addition (Residual)',
|
| 75 |
+
category: 'Merge',
|
| 76 |
+
parameters: []
|
| 77 |
+
},
|
| 78 |
+
|
| 79 |
+
// --- GENAI / ADVANCED ---
|
| 80 |
+
[LayerType.RMSNORM]: {
|
| 81 |
+
type: LayerType.RMSNORM,
|
| 82 |
+
label: 'RMSNorm',
|
| 83 |
+
description: 'Root Mean Square Norm (LLMs)',
|
| 84 |
+
category: 'GenAI',
|
| 85 |
+
parameters: [
|
| 86 |
+
{ name: 'dim', type: 'number', label: 'Dimension', default: 512 },
|
| 87 |
+
{ name: 'eps', type: 'number', label: 'Epsilon', default: 1e-6 }
|
| 88 |
+
]
|
| 89 |
+
},
|
| 90 |
+
[LayerType.ROPE]: {
|
| 91 |
+
type: LayerType.ROPE,
|
| 92 |
+
label: 'RoPE',
|
| 93 |
+
description: 'Rotary Positional Embedding',
|
| 94 |
+
category: 'GenAI',
|
| 95 |
+
parameters: [
|
| 96 |
+
{ name: 'dim', type: 'number', label: 'Head Dim', default: 64 },
|
| 97 |
+
{ name: 'max_position', type: 'number', label: 'Max Pos', default: 2048 }
|
| 98 |
+
]
|
| 99 |
+
},
|
| 100 |
+
[LayerType.PATCH_EMBED]: {
|
| 101 |
+
type: LayerType.PATCH_EMBED,
|
| 102 |
+
label: 'Patch Embed (ViT)',
|
| 103 |
+
description: 'Image to Sequence Patches',
|
| 104 |
+
category: 'GenAI',
|
| 105 |
+
parameters: [
|
| 106 |
+
{ name: 'img_size', type: 'number', label: 'Image Size', default: 224 },
|
| 107 |
+
{ name: 'patch_size', type: 'number', label: 'Patch Size', default: 16 },
|
| 108 |
+
{ name: 'in_chans', type: 'number', label: 'In Channels', default: 3 },
|
| 109 |
+
{ name: 'embed_dim', type: 'number', label: 'Embed Dim', default: 768 }
|
| 110 |
+
]
|
| 111 |
+
},
|
| 112 |
+
[LayerType.MOE_BLOCK]: {
|
| 113 |
+
type: LayerType.MOE_BLOCK,
|
| 114 |
+
label: 'Sparse MoE Block',
|
| 115 |
+
description: 'Mixture of Experts Layer',
|
| 116 |
+
category: 'GenAI',
|
| 117 |
+
parameters: [
|
| 118 |
+
{ name: 'num_experts', type: 'number', label: 'Num Experts', default: 8 },
|
| 119 |
+
{ name: 'top_k', type: 'number', label: 'Top K (Active)', default: 2 },
|
| 120 |
+
{ name: 'hidden_dim', type: 'number', label: 'Hidden Dim', default: 512 },
|
| 121 |
+
{ name: 'expert_dim', type: 'number', label: 'Expert Dim', default: 2048 }
|
| 122 |
+
]
|
| 123 |
+
},
|
| 124 |
+
[LayerType.ACTION_HEAD]: {
|
| 125 |
+
type: LayerType.ACTION_HEAD,
|
| 126 |
+
label: 'Action Head',
|
| 127 |
+
description: 'Decision output for Agents/LAMs',
|
| 128 |
+
category: 'GenAI',
|
| 129 |
+
parameters: [
|
| 130 |
+
{ name: 'input_dim', type: 'number', label: 'Input Dim', default: 512 },
|
| 131 |
+
{ name: 'num_actions', type: 'number', label: 'Num Actions', default: 50 },
|
| 132 |
+
{ name: 'action_type', type: 'select', label: 'Type', default: 'Discrete', options: ['Discrete', 'Continuous'] }
|
| 133 |
+
]
|
| 134 |
+
},
|
| 135 |
+
[LayerType.TIME_EMBEDDING]: {
|
| 136 |
+
type: LayerType.TIME_EMBEDDING,
|
| 137 |
+
label: 'Time Embedding',
|
| 138 |
+
description: 'Sinusoidal Time Embed (Diffusion)',
|
| 139 |
+
category: 'GenAI',
|
| 140 |
+
parameters: [
|
| 141 |
+
{ name: 'dim', type: 'number', label: 'Dimension', default: 256 }
|
| 142 |
+
]
|
| 143 |
+
},
|
| 144 |
+
[LayerType.SAM_PROMPT_ENCODER]: {
|
| 145 |
+
type: LayerType.SAM_PROMPT_ENCODER,
|
| 146 |
+
label: 'SAM Prompt Enc',
|
| 147 |
+
description: 'Encodes points/boxes (SAM)',
|
| 148 |
+
category: 'GenAI',
|
| 149 |
+
parameters: [
|
| 150 |
+
{ name: 'embed_dim', type: 'number', label: 'Embed Dim', default: 256 }
|
| 151 |
+
]
|
| 152 |
+
},
|
| 153 |
+
[LayerType.SAM_MASK_DECODER]: {
|
| 154 |
+
type: LayerType.SAM_MASK_DECODER,
|
| 155 |
+
label: 'SAM Mask Dec',
|
| 156 |
+
description: 'Decodes segmentation masks',
|
| 157 |
+
category: 'GenAI',
|
| 158 |
+
parameters: [
|
| 159 |
+
{ name: 'transformer_dim', type: 'number', label: 'Model Dim', default: 256 },
|
| 160 |
+
{ name: 'num_multimask_outputs', type: 'number', label: 'Num Masks', default: 3 }
|
| 161 |
+
]
|
| 162 |
+
},
|
| 163 |
+
|
| 164 |
+
// --- CONVOLUTION ---
|
| 165 |
+
[LayerType.CONV1D]: {
|
| 166 |
+
type: LayerType.CONV1D,
|
| 167 |
+
label: 'Conv1D',
|
| 168 |
+
description: '1D Convolution (Audio/Text)',
|
| 169 |
+
category: 'Convolution',
|
| 170 |
+
parameters: [
|
| 171 |
+
{ name: 'in_channels', type: 'number', label: 'In Channels (Opt)', default: 0 },
|
| 172 |
+
{ name: 'out_channels', type: 'number', label: 'Filters', default: 32 },
|
| 173 |
+
{ name: 'kernel_size', type: 'number', label: 'Kernel Size', default: 3 },
|
| 174 |
+
{ name: 'stride', type: 'number', label: 'Stride', default: 1 },
|
| 175 |
+
{ name: 'padding', type: 'number', label: 'Padding', default: 1 }
|
| 176 |
+
]
|
| 177 |
+
},
|
| 178 |
+
[LayerType.CONV2D]: {
|
| 179 |
+
type: LayerType.CONV2D,
|
| 180 |
+
label: 'Conv2D',
|
| 181 |
+
description: '2D Convolutional Layer',
|
| 182 |
+
category: 'Convolution',
|
| 183 |
+
parameters: [
|
| 184 |
+
{ name: 'in_channels', type: 'number', label: 'In Channels (Opt)', default: 0 },
|
| 185 |
+
{ name: 'out_channels', type: 'number', label: 'Filters', default: 64 },
|
| 186 |
+
{ name: 'kernel_size', type: 'number', label: 'Kernel Size', default: 3 },
|
| 187 |
+
{ name: 'stride', type: 'number', label: 'Stride', default: 1 },
|
| 188 |
+
{ name: 'padding', type: 'number', label: 'Padding', default: 1 },
|
| 189 |
+
{ name: 'dilation', type: 'number', label: 'Dilation', default: 1 },
|
| 190 |
+
{ name: 'groups', type: 'number', label: 'Groups', default: 1, description: "For depthwise separable" },
|
| 191 |
+
{ name: 'bias', type: 'boolean', label: 'Bias', default: true }
|
| 192 |
+
]
|
| 193 |
+
},
|
| 194 |
+
[LayerType.CONV_TRANSPOSE2D]: {
|
| 195 |
+
type: LayerType.CONV_TRANSPOSE2D,
|
| 196 |
+
label: 'ConvTranspose2D',
|
| 197 |
+
description: 'Deconvolution (Upsampling)',
|
| 198 |
+
category: 'Convolution',
|
| 199 |
+
parameters: [
|
| 200 |
+
{ name: 'out_channels', type: 'number', label: 'Filters', default: 64 },
|
| 201 |
+
{ name: 'kernel_size', type: 'number', label: 'Kernel Size', default: 2 },
|
| 202 |
+
{ name: 'stride', type: 'number', label: 'Stride', default: 2 },
|
| 203 |
+
{ name: 'padding', type: 'number', label: 'Padding', default: 0 }
|
| 204 |
+
]
|
| 205 |
+
},
|
| 206 |
+
[LayerType.MAXPOOL]: {
|
| 207 |
+
type: LayerType.MAXPOOL,
|
| 208 |
+
label: 'MaxPool2D',
|
| 209 |
+
description: 'Max pooling operation',
|
| 210 |
+
category: 'Convolution',
|
| 211 |
+
parameters: [
|
| 212 |
+
{ name: 'kernel_size', type: 'number', label: 'Kernel Size', default: 2 },
|
| 213 |
+
{ name: 'stride', type: 'number', label: 'Stride', default: 2 }
|
| 214 |
+
]
|
| 215 |
+
},
|
| 216 |
+
[LayerType.AVGPOOL]: {
|
| 217 |
+
type: LayerType.AVGPOOL,
|
| 218 |
+
label: 'AvgPool2D',
|
| 219 |
+
description: 'Average pooling operation',
|
| 220 |
+
category: 'Convolution',
|
| 221 |
+
parameters: [
|
| 222 |
+
{ name: 'kernel_size', type: 'number', label: 'Kernel Size', default: 2 },
|
| 223 |
+
{ name: 'stride', type: 'number', label: 'Stride', default: 2 }
|
| 224 |
+
]
|
| 225 |
+
},
|
| 226 |
+
[LayerType.ADAPTIVEAVGPOOL]: {
|
| 227 |
+
type: LayerType.ADAPTIVEAVGPOOL,
|
| 228 |
+
label: 'AdaptAvgPool2D',
|
| 229 |
+
description: 'Pools to specific output size',
|
| 230 |
+
category: 'Convolution',
|
| 231 |
+
parameters: [
|
| 232 |
+
{ name: 'output_size', type: 'number', label: 'Output Size (NxN)', default: 1 }
|
| 233 |
+
]
|
| 234 |
+
},
|
| 235 |
+
[LayerType.UPSAMPLE]: {
|
| 236 |
+
type: LayerType.UPSAMPLE,
|
| 237 |
+
label: 'Upsample',
|
| 238 |
+
description: 'Increases spatial size',
|
| 239 |
+
category: 'Convolution',
|
| 240 |
+
parameters: [
|
| 241 |
+
{ name: 'scale_factor', type: 'number', label: 'Scale Factor', default: 2 },
|
| 242 |
+
{ name: 'mode', type: 'select', label: 'Mode', default: 'nearest', options: ['nearest', 'bilinear', 'bicubic'] }
|
| 243 |
+
]
|
| 244 |
+
},
|
| 245 |
+
|
| 246 |
+
// --- RECURRENT ---
|
| 247 |
+
[LayerType.LSTM]: {
|
| 248 |
+
type: LayerType.LSTM,
|
| 249 |
+
label: 'LSTM',
|
| 250 |
+
description: 'Long Short-Term Memory',
|
| 251 |
+
category: 'Recurrent',
|
| 252 |
+
parameters: [
|
| 253 |
+
{ name: 'input_size', type: 'number', label: 'Input Size (Opt)', default: 0 },
|
| 254 |
+
{ name: 'hidden_size', type: 'number', label: 'Hidden Size', default: 128 },
|
| 255 |
+
{ name: 'num_layers', type: 'number', label: 'Num Layers', default: 1 },
|
| 256 |
+
{ name: 'bidirectional', type: 'boolean', label: 'Bidirectional', default: false },
|
| 257 |
+
{ name: 'dropout', type: 'number', label: 'Dropout', default: 0.0 },
|
| 258 |
+
{ name: 'batch_first', type: 'boolean', label: 'Batch First', default: true }
|
| 259 |
+
]
|
| 260 |
+
},
|
| 261 |
+
[LayerType.GRU]: {
|
| 262 |
+
type: LayerType.GRU,
|
| 263 |
+
label: 'GRU',
|
| 264 |
+
description: 'Gated Recurrent Unit',
|
| 265 |
+
category: 'Recurrent',
|
| 266 |
+
parameters: [
|
| 267 |
+
{ name: 'input_size', type: 'number', label: 'Input Size (Opt)', default: 0 },
|
| 268 |
+
{ name: 'hidden_size', type: 'number', label: 'Hidden Size', default: 128 },
|
| 269 |
+
{ name: 'num_layers', type: 'number', label: 'Num Layers', default: 1 },
|
| 270 |
+
{ name: 'bidirectional', type: 'boolean', label: 'Bidirectional', default: false },
|
| 271 |
+
{ name: 'dropout', type: 'number', label: 'Dropout', default: 0.0 },
|
| 272 |
+
{ name: 'batch_first', type: 'boolean', label: 'Batch First', default: true }
|
| 273 |
+
]
|
| 274 |
+
},
|
| 275 |
+
|
| 276 |
+
// --- UTILITY / ACTIVATION ---
|
| 277 |
+
[LayerType.RELU]: {
|
| 278 |
+
type: LayerType.RELU,
|
| 279 |
+
label: 'ReLU',
|
| 280 |
+
description: 'Rectified Linear Unit',
|
| 281 |
+
category: 'Utility',
|
| 282 |
+
parameters: []
|
| 283 |
+
},
|
| 284 |
+
[LayerType.SWIGLU]: {
|
| 285 |
+
type: LayerType.SWIGLU,
|
| 286 |
+
label: 'SwiGLU',
|
| 287 |
+
description: 'Swish-Gated Linear Unit',
|
| 288 |
+
category: 'Utility',
|
| 289 |
+
parameters: [
|
| 290 |
+
{ name: 'dim', type: 'number', label: 'Dim (Opt)', default: 0 }
|
| 291 |
+
]
|
| 292 |
+
},
|
| 293 |
+
[LayerType.LEAKYRELU]: {
|
| 294 |
+
type: LayerType.LEAKYRELU,
|
| 295 |
+
label: 'LeakyReLU',
|
| 296 |
+
description: 'Leaky ReLU Activation',
|
| 297 |
+
category: 'Utility',
|
| 298 |
+
parameters: [
|
| 299 |
+
{ name: 'negative_slope', type: 'number', label: 'Negative Slope', default: 0.01 }
|
| 300 |
+
]
|
| 301 |
+
},
|
| 302 |
+
[LayerType.GELU]: {
|
| 303 |
+
type: LayerType.GELU,
|
| 304 |
+
label: 'GELU',
|
| 305 |
+
description: 'Gaussian Error Linear Unit',
|
| 306 |
+
category: 'Utility',
|
| 307 |
+
parameters: []
|
| 308 |
+
},
|
| 309 |
+
[LayerType.SILU]: {
|
| 310 |
+
type: LayerType.SILU,
|
| 311 |
+
label: 'SiLU (Swish)',
|
| 312 |
+
description: 'Sigmoid Linear Unit',
|
| 313 |
+
category: 'Utility',
|
| 314 |
+
parameters: []
|
| 315 |
+
},
|
| 316 |
+
[LayerType.SIGMOID]: {
|
| 317 |
+
type: LayerType.SIGMOID,
|
| 318 |
+
label: 'Sigmoid',
|
| 319 |
+
description: 'Sigmoid Activation',
|
| 320 |
+
category: 'Utility',
|
| 321 |
+
parameters: []
|
| 322 |
+
},
|
| 323 |
+
[LayerType.TANH]: {
|
| 324 |
+
type: LayerType.TANH,
|
| 325 |
+
label: 'Tanh',
|
| 326 |
+
description: 'Hyperbolic Tangent',
|
| 327 |
+
category: 'Utility',
|
| 328 |
+
parameters: []
|
| 329 |
+
},
|
| 330 |
+
[LayerType.DROPOUT]: {
|
| 331 |
+
type: LayerType.DROPOUT,
|
| 332 |
+
label: 'Dropout',
|
| 333 |
+
description: 'Random zeroing of elements',
|
| 334 |
+
category: 'Utility',
|
| 335 |
+
parameters: [
|
| 336 |
+
{ name: 'p', type: 'number', label: 'Probability', default: 0.5 }
|
| 337 |
+
]
|
| 338 |
+
},
|
| 339 |
+
[LayerType.FLATTEN]: {
|
| 340 |
+
type: LayerType.FLATTEN,
|
| 341 |
+
label: 'Flatten',
|
| 342 |
+
description: 'Flattens input to 1D',
|
| 343 |
+
category: 'Utility',
|
| 344 |
+
parameters: []
|
| 345 |
+
},
|
| 346 |
+
[LayerType.CUSTOM]: {
|
| 347 |
+
type: LayerType.CUSTOM,
|
| 348 |
+
label: 'Custom / Code',
|
| 349 |
+
description: 'Any PyTorch/Python Module',
|
| 350 |
+
category: 'Utility',
|
| 351 |
+
parameters: [
|
| 352 |
+
{ name: 'class_name', type: 'string', label: 'Class Name', default: 'MyCustomLayer', description: "e.g., 'torch.nn.Identity' or 'MyBlock'" },
|
| 353 |
+
{ name: 'args', type: 'string', label: 'Arguments', default: '', description: "e.g., 'dim=128, p=0.1'" }
|
| 354 |
+
]
|
| 355 |
+
},
|
| 356 |
+
[LayerType.IDENTITY]: {
|
| 357 |
+
type: LayerType.IDENTITY,
|
| 358 |
+
label: 'Identity',
|
| 359 |
+
description: 'Passthrough layer',
|
| 360 |
+
category: 'Utility',
|
| 361 |
+
parameters: []
|
| 362 |
+
},
|
| 363 |
+
|
| 364 |
+
// --- NORMALIZATION ---
|
| 365 |
+
[LayerType.BATCHNORM]: {
|
| 366 |
+
type: LayerType.BATCHNORM,
|
| 367 |
+
label: 'BatchNorm2D',
|
| 368 |
+
description: 'Batch Normalization',
|
| 369 |
+
category: 'Normalization',
|
| 370 |
+
parameters: [
|
| 371 |
+
{ name: 'num_features', type: 'number', label: 'Num Features', default: 64 }
|
| 372 |
+
]
|
| 373 |
+
},
|
| 374 |
+
[LayerType.LAYERNORM]: {
|
| 375 |
+
type: LayerType.LAYERNORM,
|
| 376 |
+
label: 'LayerNorm',
|
| 377 |
+
description: 'Layer Normalization',
|
| 378 |
+
category: 'Normalization',
|
| 379 |
+
parameters: [
|
| 380 |
+
{ name: 'normalized_shape', type: 'string', label: 'Norm Shape', default: '128' }
|
| 381 |
+
]
|
| 382 |
+
},
|
| 383 |
+
[LayerType.INSTANCENORM]: {
|
| 384 |
+
type: LayerType.INSTANCENORM,
|
| 385 |
+
label: 'InstanceNorm2d',
|
| 386 |
+
description: 'Instance Normalization',
|
| 387 |
+
category: 'Normalization',
|
| 388 |
+
parameters: [
|
| 389 |
+
{ name: 'num_features', type: 'number', label: 'Num Features', default: 64 },
|
| 390 |
+
{ name: 'affine', type: 'boolean', label: 'Learnable', default: false }
|
| 391 |
+
]
|
| 392 |
+
},
|
| 393 |
+
|
| 394 |
+
// --- TRANSFORMER ---
|
| 395 |
+
[LayerType.ATTENTION]: {
|
| 396 |
+
type: LayerType.ATTENTION,
|
| 397 |
+
label: 'Self Attention',
|
| 398 |
+
description: 'Multi-Head Self Attention',
|
| 399 |
+
category: 'Transformer',
|
| 400 |
+
parameters: [
|
| 401 |
+
{ name: 'embed_dim', type: 'number', label: 'Embed Dim', default: 512 },
|
| 402 |
+
{ name: 'num_heads', type: 'number', label: 'Num Heads', default: 8 },
|
| 403 |
+
{ name: 'dropout', type: 'number', label: 'Dropout', default: 0.1 },
|
| 404 |
+
{ name: 'batch_first', type: 'boolean', label: 'Batch First', default: true }
|
| 405 |
+
]
|
| 406 |
+
},
|
| 407 |
+
[LayerType.CROSS_ATTENTION]: {
|
| 408 |
+
type: LayerType.CROSS_ATTENTION,
|
| 409 |
+
label: 'Cross Attention',
|
| 410 |
+
description: 'Attention between two sequences',
|
| 411 |
+
category: 'Transformer',
|
| 412 |
+
parameters: [
|
| 413 |
+
{ name: 'embed_dim', type: 'number', label: 'Embed Dim', default: 512 },
|
| 414 |
+
{ name: 'num_heads', type: 'number', label: 'Num Heads', default: 8 },
|
| 415 |
+
{ name: 'dropout', type: 'number', label: 'Dropout', default: 0.1 }
|
| 416 |
+
]
|
| 417 |
+
},
|
| 418 |
+
[LayerType.TRANSFORMER_ENCODER]: {
|
| 419 |
+
type: LayerType.TRANSFORMER_ENCODER,
|
| 420 |
+
label: 'Encoder Layer',
|
| 421 |
+
description: 'Standard Transformer Encoder',
|
| 422 |
+
category: 'Transformer',
|
| 423 |
+
parameters: [
|
| 424 |
+
{ name: 'd_model', type: 'number', label: 'D Model', default: 512 },
|
| 425 |
+
{ name: 'nhead', type: 'number', label: 'Heads', default: 8 },
|
| 426 |
+
{ name: 'dim_feedforward', type: 'number', label: 'FF Dim', default: 2048 },
|
| 427 |
+
{ name: 'dropout', type: 'number', label: 'Dropout', default: 0.1 },
|
| 428 |
+
{ name: 'activation', type: 'select', label: 'Activation', default: 'relu', options: ['relu', 'gelu'] }
|
| 429 |
+
]
|
| 430 |
+
},
|
| 431 |
+
[LayerType.TRANSFORMER_DECODER]: {
|
| 432 |
+
type: LayerType.TRANSFORMER_DECODER,
|
| 433 |
+
label: 'Decoder Layer',
|
| 434 |
+
description: 'Standard Transformer Decoder',
|
| 435 |
+
category: 'Transformer',
|
| 436 |
+
parameters: [
|
| 437 |
+
{ name: 'd_model', type: 'number', label: 'D Model', default: 512 },
|
| 438 |
+
{ name: 'nhead', type: 'number', label: 'Heads', default: 8 },
|
| 439 |
+
{ name: 'dim_feedforward', type: 'number', label: 'FF Dim', default: 2048 },
|
| 440 |
+
{ name: 'dropout', type: 'number', label: 'Dropout', default: 0.1 }
|
| 441 |
+
]
|
| 442 |
+
},
|
| 443 |
+
[LayerType.TRANSFORMER_BLOCK]: {
|
| 444 |
+
type: LayerType.TRANSFORMER_BLOCK,
|
| 445 |
+
label: 'Transformer Block',
|
| 446 |
+
description: 'Generic Block',
|
| 447 |
+
category: 'Transformer',
|
| 448 |
+
parameters: [
|
| 449 |
+
{ name: 'd_model', type: 'number', label: 'D Model', default: 512 },
|
| 450 |
+
{ name: 'nhead', type: 'number', label: 'Heads', default: 8 },
|
| 451 |
+
{ name: 'dim_feedforward', type: 'number', label: 'FF Dim', default: 2048 }
|
| 452 |
+
]
|
| 453 |
+
},
|
| 454 |
+
};
|
| 455 |
+
|
| 456 |
+
export const INITIAL_NODES = [
|
| 457 |
+
{
|
| 458 |
+
id: '1',
|
| 459 |
+
type: 'custom',
|
| 460 |
+
position: { x: 250, y: 50 },
|
| 461 |
+
data: { label: 'Input Data', type: LayerType.INPUT, params: LAYER_DEFINITIONS[LayerType.INPUT].parameters.reduce((acc, p) => ({...acc, [p.name]: p.default}), {}) }
|
| 462 |
+
},
|
| 463 |
+
{
|
| 464 |
+
id: '2',
|
| 465 |
+
type: 'custom',
|
| 466 |
+
position: { x: 250, y: 200 },
|
| 467 |
+
data: { label: 'Conv2D', type: LayerType.CONV2D, params: LAYER_DEFINITIONS[LayerType.CONV2D].parameters.reduce((acc, p) => ({...acc, [p.name]: p.default}), {}) }
|
| 468 |
+
},
|
| 469 |
+
{
|
| 470 |
+
id: '3',
|
| 471 |
+
type: 'custom',
|
| 472 |
+
position: { x: 250, y: 350 },
|
| 473 |
+
data: { label: 'ReLU', type: LayerType.RELU, params: {} }
|
| 474 |
+
}
|
| 475 |
+
];
|
| 476 |
+
|
| 477 |
+
export const INITIAL_EDGES = [
|
| 478 |
+
{ id: 'e1-2', source: '1', target: '2', animated: true, style: { stroke: '#94a3b8' } },
|
| 479 |
+
{ id: 'e2-3', source: '2', target: '3', animated: true, style: { stroke: '#94a3b8' } }
|
| 480 |
+
];
|
| 481 |
+
|
| 482 |
+
export const TEMPLATES: Record<string, GraphTemplate> = {
|
| 483 |
+
'gpt_style': {
|
| 484 |
+
id: 'gpt_style',
|
| 485 |
+
name: 'LLM (GPT Style)',
|
| 486 |
+
description: 'Decoder-only Transformer with RoPE & RMSNorm.',
|
| 487 |
+
nodes: [
|
| 488 |
+
{ id: 'in', type: 'custom', position: {x: 300, y: 0}, data: {label: 'Token Input', type: LayerType.INPUT, params: {modality: 'Text', shape: '128', dtype: 'int64'}} },
|
| 489 |
+
{ id: 'emb', type: 'custom', position: {x: 300, y: 100}, data: {label: 'Token Embed', type: LayerType.EMBEDDING, params: {num_embeddings: 50257, embedding_dim: 768}} },
|
| 490 |
+
{ id: 'rope', type: 'custom', position: {x: 300, y: 200}, data: {label: 'RoPE', type: LayerType.ROPE, params: {dim: 64}} },
|
| 491 |
+
{ id: 'blk1', type: 'custom', position: {x: 300, y: 300}, data: {label: 'Decoder Block 1', type: LayerType.TRANSFORMER_DECODER, params: {d_model: 768, nhead: 12}} },
|
| 492 |
+
{ id: 'blk2', type: 'custom', position: {x: 300, y: 400}, data: {label: 'Decoder Block 2', type: LayerType.TRANSFORMER_DECODER, params: {d_model: 768, nhead: 12}} },
|
| 493 |
+
{ id: 'ln', type: 'custom', position: {x: 300, y: 500}, data: {label: 'RMSNorm', type: LayerType.RMSNORM, params: {dim: 768}} },
|
| 494 |
+
{ id: 'head', type: 'custom', position: {x: 300, y: 600}, data: {label: 'LM Head', type: LayerType.LINEAR, params: {out_features: 50257, bias: false}} }
|
| 495 |
+
],
|
| 496 |
+
edges: [
|
| 497 |
+
{ id: '1', source: 'in', target: 'emb' }, { id: '2', source: 'emb', target: 'rope' },
|
| 498 |
+
{ id: '3', source: 'rope', target: 'blk1' }, { id: '4', source: 'blk1', target: 'blk2' },
|
| 499 |
+
{ id: '5', source: 'blk2', target: 'ln' }, { id: '6', source: 'ln', target: 'head' }
|
| 500 |
+
]
|
| 501 |
+
},
|
| 502 |
+
'bert_encoder': {
|
| 503 |
+
id: 'bert_encoder',
|
| 504 |
+
name: 'BERT (Encoder)',
|
| 505 |
+
description: 'Bidirectional Transformer Encoder (NLP)',
|
| 506 |
+
nodes: [
|
| 507 |
+
{ id: 'in', type: 'custom', position: {x: 300, y: 0}, data: {label: 'Token Input', type: LayerType.INPUT, params: {modality: 'Text'}} },
|
| 508 |
+
{ id: 'emb', type: 'custom', position: {x: 300, y: 100}, data: {label: 'Embedding', type: LayerType.EMBEDDING, params: {embedding_dim: 768}} },
|
| 509 |
+
{ id: 'pos', type: 'custom', position: {x: 300, y: 200}, data: {label: 'Pos Embed', type: LayerType.POS_EMBED, params: {embedding_dim: 768}} },
|
| 510 |
+
{ id: 'add', type: 'custom', position: {x: 300, y: 300}, data: {label: 'Combine', type: LayerType.ADD, params: {}} },
|
| 511 |
+
{ id: 'enc1', type: 'custom', position: {x: 300, y: 400}, data: {label: 'Encoder Layer 1', type: LayerType.TRANSFORMER_ENCODER, params: {d_model: 768, nhead: 12}} },
|
| 512 |
+
{ id: 'enc2', type: 'custom', position: {x: 300, y: 500}, data: {label: 'Encoder Layer 2', type: LayerType.TRANSFORMER_ENCODER, params: {d_model: 768, nhead: 12}} },
|
| 513 |
+
{ id: 'pool', type: 'custom', position: {x: 300, y: 600}, data: {label: 'Pooler', type: LayerType.LINEAR, params: {out_features: 768}} },
|
| 514 |
+
],
|
| 515 |
+
edges: [
|
| 516 |
+
{ id: '1', source: 'in', target: 'emb' }, { id: '2', source: 'emb', target: 'add' },
|
| 517 |
+
{ id: '3', source: 'pos', target: 'add' }, { id: '4', source: 'add', target: 'enc1' },
|
| 518 |
+
{ id: '5', source: 'enc1', target: 'enc2' }, { id: '6', source: 'enc2', target: 'pool' }
|
| 519 |
+
]
|
| 520 |
+
},
|
| 521 |
+
'autoencoder_conv': {
|
| 522 |
+
id: 'autoencoder_conv',
|
| 523 |
+
name: 'Autoencoder (Conv)',
|
| 524 |
+
description: 'Image compression and reconstruction.',
|
| 525 |
+
nodes: [
|
| 526 |
+
{ id: 'in', type: 'custom', position: {x: 300, y: 0}, data: {label: 'Image', type: LayerType.INPUT, params: {modality: 'Image', shape: '1,28,28'}} },
|
| 527 |
+
// Encoder
|
| 528 |
+
{ id: 'enc1', type: 'custom', position: {x: 300, y: 100}, data: {label: 'Conv 1', type: LayerType.CONV2D, params: {out_channels: 16, kernel_size: 3, stride: 2, padding: 1}} },
|
| 529 |
+
{ id: 'act1', type: 'custom', position: {x: 300, y: 180}, data: {label: 'ReLU', type: LayerType.RELU, params: {}} },
|
| 530 |
+
{ id: 'enc2', type: 'custom', position: {x: 300, y: 260}, data: {label: 'Conv 2', type: LayerType.CONV2D, params: {out_channels: 32, kernel_size: 3, stride: 2, padding: 1}} },
|
| 531 |
+
{ id: 'act2', type: 'custom', position: {x: 300, y: 340}, data: {label: 'ReLU', type: LayerType.RELU, params: {}} },
|
| 532 |
+
// Decoder
|
| 533 |
+
{ id: 'dec1', type: 'custom', position: {x: 300, y: 420}, data: {label: 'Deconv 1', type: LayerType.CONV_TRANSPOSE2D, params: {out_channels: 16, kernel_size: 3, stride: 2, padding: 1}} }, // Needs output_padding often in code but usually inferred
|
| 534 |
+
{ id: 'act3', type: 'custom', position: {x: 300, y: 500}, data: {label: 'ReLU', type: LayerType.RELU, params: {}} },
|
| 535 |
+
{ id: 'dec2', type: 'custom', position: {x: 300, y: 580}, data: {label: 'Deconv 2', type: LayerType.CONV_TRANSPOSE2D, params: {out_channels: 1, kernel_size: 3, stride: 2, padding: 1}} },
|
| 536 |
+
{ id: 'sig', type: 'custom', position: {x: 300, y: 660}, data: {label: 'Sigmoid', type: LayerType.SIGMOID, params: {}} }
|
| 537 |
+
],
|
| 538 |
+
edges: [
|
| 539 |
+
{ id: '1', source: 'in', target: 'enc1' }, { id: '2', source: 'enc1', target: 'act1' }, { id: '3', source: 'act1', target: 'enc2' },
|
| 540 |
+
{ id: '4', source: 'enc2', target: 'act2' }, { id: '5', source: 'act2', target: 'dec1' }, { id: '6', source: 'dec1', target: 'act3' },
|
| 541 |
+
{ id: '7', source: 'act3', target: 'dec2' }, { id: '8', source: 'dec2', target: 'sig' }
|
| 542 |
+
]
|
| 543 |
+
},
|
| 544 |
+
'resnet_mini': {
|
| 545 |
+
id: 'resnet_mini',
|
| 546 |
+
name: 'ResNet Block (Mini)',
|
| 547 |
+
description: 'CNN with Skip Connection.',
|
| 548 |
+
nodes: [
|
| 549 |
+
{ id: 'in', type: 'custom', position: {x: 200, y: 0}, data: {label: 'Input', type: LayerType.INPUT, params: {modality: 'Image'}} },
|
| 550 |
+
{ id: 'conv1', type: 'custom', position: {x: 200, y: 100}, data: {label: 'Conv1', type: LayerType.CONV2D, params: {out_channels: 64}} },
|
| 551 |
+
{ id: 'relu1', type: 'custom', position: {x: 200, y: 200}, data: {label: 'ReLU', type: LayerType.RELU, params: {}} },
|
| 552 |
+
{ id: 'conv2', type: 'custom', position: {x: 200, y: 300}, data: {label: 'Conv2', type: LayerType.CONV2D, params: {out_channels: 64}} },
|
| 553 |
+
|
| 554 |
+
// Skip connection path handled by edges, but node layout implies it
|
| 555 |
+
{ id: 'add', type: 'custom', position: {x: 200, y: 450}, data: {label: 'Residual Add', type: LayerType.ADD, params: {}} },
|
| 556 |
+
{ id: 'relu2', type: 'custom', position: {x: 200, y: 550}, data: {label: 'Final ReLU', type: LayerType.RELU, params: {}} }
|
| 557 |
+
],
|
| 558 |
+
edges: [
|
| 559 |
+
{ id: '1', source: 'in', target: 'conv1' }, { id: '2', source: 'conv1', target: 'relu1' },
|
| 560 |
+
{ id: '3', source: 'relu1', target: 'conv2' }, { id: '4', source: 'conv2', target: 'add' },
|
| 561 |
+
{ id: '5', source: 'in', target: 'add' }, // Skip connection
|
| 562 |
+
{ id: '6', source: 'add', target: 'relu2' }
|
| 563 |
+
]
|
| 564 |
+
},
|
| 565 |
+
'moe_transformer': {
|
| 566 |
+
id: 'moe_transformer',
|
| 567 |
+
name: 'Mixture of Experts',
|
| 568 |
+
description: 'Sparse MoE model with routing.',
|
| 569 |
+
nodes: [
|
| 570 |
+
{ id: 'in', type: 'custom', position: {x: 300, y: 0}, data: {label: 'Input', type: LayerType.INPUT, params: {modality: 'Text'}} },
|
| 571 |
+
{ id: 'emb', type: 'custom', position: {x: 300, y: 100}, data: {label: 'Embedding', type: LayerType.EMBEDDING, params: {}} },
|
| 572 |
+
{ id: 'att', type: 'custom', position: {x: 300, y: 200}, data: {label: 'Self Attention', type: LayerType.ATTENTION, params: {}} },
|
| 573 |
+
{ id: 'moe', type: 'custom', position: {x: 300, y: 300}, data: {label: 'Sparse MoE Block', type: LayerType.MOE_BLOCK, params: {num_experts: 8, top_k: 2}} },
|
| 574 |
+
{ id: 'norm', type: 'custom', position: {x: 300, y: 400}, data: {label: 'RMSNorm', type: LayerType.RMSNORM, params: {}} },
|
| 575 |
+
{ id: 'out', type: 'custom', position: {x: 300, y: 500}, data: {label: 'Output', type: LayerType.LINEAR, params: {}} }
|
| 576 |
+
],
|
| 577 |
+
edges: [
|
| 578 |
+
{ id: '1', source: 'in', target: 'emb' }, { id: '2', source: 'emb', target: 'att' },
|
| 579 |
+
{ id: '3', source: 'att', target: 'moe' }, { id: '4', source: 'moe', target: 'norm' },
|
| 580 |
+
{ id: '5', source: 'norm', target: 'out' }
|
| 581 |
+
]
|
| 582 |
+
},
|
| 583 |
+
'vlm_llava': {
|
| 584 |
+
id: 'vlm_llava',
|
| 585 |
+
name: 'VLM (LlaVA Style)',
|
| 586 |
+
description: 'Visual Language Model connecting Vision Encoder to LLM.',
|
| 587 |
+
nodes: [
|
| 588 |
+
// Vision Branch
|
| 589 |
+
{ id: 'img', type: 'custom', position: {x: 100, y: 0}, data: {label: 'Image Input', type: LayerType.INPUT, params: {modality: 'Image'}} },
|
| 590 |
+
{ id: 'patch', type: 'custom', position: {x: 100, y: 100}, data: {label: 'Patch Embed', type: LayerType.PATCH_EMBED, params: {patch_size: 14}} },
|
| 591 |
+
{ id: 'vit', type: 'custom', position: {x: 100, y: 200}, data: {label: 'ViT Encoder', type: LayerType.TRANSFORMER_ENCODER, params: {}} },
|
| 592 |
+
{ id: 'proj', type: 'custom', position: {x: 100, y: 300}, data: {label: 'Projection', type: LayerType.LINEAR, params: {out_features: 4096}} },
|
| 593 |
+
|
| 594 |
+
// Text Branch
|
| 595 |
+
{ id: 'txt', type: 'custom', position: {x: 500, y: 0}, data: {label: 'Text Prompts', type: LayerType.INPUT, params: {modality: 'Text'}} },
|
| 596 |
+
{ id: 'temb', type: 'custom', position: {x: 500, y: 200}, data: {label: 'Text Embed', type: LayerType.EMBEDDING, params: {embedding_dim: 4096}} },
|
| 597 |
+
|
| 598 |
+
// Merge
|
| 599 |
+
{ id: 'cat', type: 'custom', position: {x: 300, y: 400}, data: {label: 'Concat Tokens', type: LayerType.CONCAT, params: {dim: 1}} },
|
| 600 |
+
{ id: 'llm', type: 'custom', position: {x: 300, y: 500}, data: {label: 'LLM Decoder', type: LayerType.TRANSFORMER_DECODER, params: {d_model: 4096}} },
|
| 601 |
+
{ id: 'out', type: 'custom', position: {x: 300, y: 600}, data: {label: 'Response', type: LayerType.OUTPUT, params: {}} }
|
| 602 |
+
],
|
| 603 |
+
edges: [
|
| 604 |
+
{ id: '1', source: 'img', target: 'patch' }, { id: '2', source: 'patch', target: 'vit' }, { id: '3', source: 'vit', target: 'proj' },
|
| 605 |
+
{ id: '4', source: 'txt', target: 'temb' },
|
| 606 |
+
{ id: '5', source: 'proj', target: 'cat' }, { id: '6', source: 'temb', target: 'cat' },
|
| 607 |
+
{ id: '7', source: 'cat', target: 'llm' }, { id: '8', source: 'llm', target: 'out' }
|
| 608 |
+
]
|
| 609 |
+
},
|
| 610 |
+
'sam_model': {
|
| 611 |
+
id: 'sam_model',
|
| 612 |
+
name: 'Segment Anything (SAM)',
|
| 613 |
+
description: 'Image Encoder + Prompt Encoder + Mask Decoder.',
|
| 614 |
+
nodes: [
|
| 615 |
+
{ id: 'img', type: 'custom', position: {x: 100, y: 0}, data: {label: 'Image', type: LayerType.INPUT, params: {modality: 'Image'}} },
|
| 616 |
+
{ id: 'enc', type: 'custom', position: {x: 100, y: 150}, data: {label: 'Image Encoder (ViT)', type: LayerType.TRANSFORMER_ENCODER, params: {}} },
|
| 617 |
+
|
| 618 |
+
{ id: 'prm', type: 'custom', position: {x: 500, y: 0}, data: {label: 'Points/Boxes', type: LayerType.INPUT, params: {modality: 'Tensor'}} },
|
| 619 |
+
{ id: 'penc', type: 'custom', position: {x: 500, y: 150}, data: {label: 'Prompt Enc', type: LayerType.SAM_PROMPT_ENCODER, params: {}} },
|
| 620 |
+
|
| 621 |
+
{ id: 'dec', type: 'custom', position: {x: 300, y: 300}, data: {label: 'Mask Decoder', type: LayerType.SAM_MASK_DECODER, params: {}} },
|
| 622 |
+
{ id: 'out', type: 'custom', position: {x: 300, y: 400}, data: {label: 'Masks', type: LayerType.OUTPUT, params: {}} }
|
| 623 |
+
],
|
| 624 |
+
edges: [
|
| 625 |
+
{ id: '1', source: 'img', target: 'enc' }, { id: '2', source: 'enc', target: 'dec' },
|
| 626 |
+
{ id: '3', source: 'prm', target: 'penc' }, { id: '4', source: 'penc', target: 'dec' },
|
| 627 |
+
{ id: '5', source: 'dec', target: 'out' }
|
| 628 |
+
]
|
| 629 |
+
},
|
| 630 |
+
'lam_agent': {
|
| 631 |
+
id: 'lam_agent',
|
| 632 |
+
name: 'Large Action Model (LAM)',
|
| 633 |
+
description: 'LLM backbone with Action Head for agents.',
|
| 634 |
+
nodes: [
|
| 635 |
+
{ id: 'state', type: 'custom', position: {x: 300, y: 0}, data: {label: 'Env State', type: LayerType.INPUT, params: {modality: 'State'}} },
|
| 636 |
+
{ id: 'llm', type: 'custom', position: {x: 300, y: 150}, data: {label: 'LLM Backbone', type: LayerType.TRANSFORMER_DECODER, params: {d_model: 1024}} },
|
| 637 |
+
{ id: 'head', type: 'custom', position: {x: 300, y: 300}, data: {label: 'Action Head', type: LayerType.ACTION_HEAD, params: {num_actions: 50, action_type: 'Discrete'}} },
|
| 638 |
+
{ id: 'out', type: 'custom', position: {x: 300, y: 400}, data: {label: 'Action Logits', type: LayerType.OUTPUT, params: {}} }
|
| 639 |
+
],
|
| 640 |
+
edges: [
|
| 641 |
+
{ id: '1', source: 'state', target: 'llm' }, { id: '2', source: 'llm', target: 'head' }, { id: '3', source: 'head', target: 'out' }
|
| 642 |
+
]
|
| 643 |
+
},
|
| 644 |
+
'lcm_diff': {
|
| 645 |
+
id: 'lcm_diff',
|
| 646 |
+
name: 'Latent Consistency (LCM)',
|
| 647 |
+
description: 'Diffusion backbone with Time Embeddings.',
|
| 648 |
+
nodes: [
|
| 649 |
+
{ id: 'lat', type: 'custom', position: {x: 200, y: 0}, data: {label: 'Latent Input', type: LayerType.INPUT, params: {modality: 'Latent', shape: '4,64,64'}} },
|
| 650 |
+
{ id: 'time', type: 'custom', position: {x: 500, y: 0}, data: {label: 'Time Step', type: LayerType.INPUT, params: {modality: 'Tensor', shape: '1'}} },
|
| 651 |
+
{ id: 'temb', type: 'custom', position: {x: 500, y: 100}, data: {label: 'Time Embed', type: LayerType.TIME_EMBEDDING, params: {}} },
|
| 652 |
+
|
| 653 |
+
{ id: 'cat', type: 'custom', position: {x: 350, y: 250}, data: {label: 'Inject Time', type: LayerType.ADD, params: {}} },
|
| 654 |
+
{ id: 'unet', type: 'custom', position: {x: 350, y: 350}, data: {label: 'UNet Block', type: LayerType.CONV2D, params: {}} },
|
| 655 |
+
{ id: 'out', type: 'custom', position: {x: 350, y: 450}, data: {label: 'Denoised', type: LayerType.OUTPUT, params: {}} }
|
| 656 |
+
],
|
| 657 |
+
edges: [
|
| 658 |
+
{ id: '1', source: 'lat', target: 'cat' },
|
| 659 |
+
{ id: '2', source: 'time', target: 'temb' }, { id: '3', source: 'temb', target: 'cat' },
|
| 660 |
+
{ id: '4', source: 'cat', target: 'unet' }, { id: '5', source: 'unet', target: 'out' }
|
| 661 |
+
]
|
| 662 |
+
}
|
| 663 |
+
};
|
index.html
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<!DOCTYPE html>
|
| 2 |
+
<html lang="en">
|
| 3 |
+
<head>
|
| 4 |
+
<meta charset="UTF-8" />
|
| 5 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
| 6 |
+
<title>Architecture Agents</title>
|
| 7 |
+
<script src="https://cdn.tailwindcss.com"></script>
|
| 8 |
+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/reactflow@11.10.1/dist/style.min.css" />
|
| 9 |
+
<style>
|
| 10 |
+
/* Custom scrollbar for dark theme */
|
| 11 |
+
::-webkit-scrollbar {
|
| 12 |
+
width: 8px;
|
| 13 |
+
height: 8px;
|
| 14 |
+
}
|
| 15 |
+
::-webkit-scrollbar-track {
|
| 16 |
+
background: #0f172a;
|
| 17 |
+
}
|
| 18 |
+
::-webkit-scrollbar-thumb {
|
| 19 |
+
background: #334155;
|
| 20 |
+
border-radius: 4px;
|
| 21 |
+
}
|
| 22 |
+
::-webkit-scrollbar-thumb:hover {
|
| 23 |
+
background: #475569;
|
| 24 |
+
}
|
| 25 |
+
</style>
|
| 26 |
+
<script type="importmap">
|
| 27 |
+
{
|
| 28 |
+
"imports": {
|
| 29 |
+
"react-dom/": "https://aistudiocdn.com/react-dom@^19.2.1/",
|
| 30 |
+
"react/": "https://aistudiocdn.com/react@^19.2.1/",
|
| 31 |
+
"react": "https://aistudiocdn.com/react@^19.2.1",
|
| 32 |
+
"@google/genai": "https://aistudiocdn.com/@google/genai@^1.31.0",
|
| 33 |
+
"lucide-react": "https://aistudiocdn.com/lucide-react@^0.556.0",
|
| 34 |
+
"reactflow": "https://aistudiocdn.com/reactflow@^11.11.4"
|
| 35 |
+
}
|
| 36 |
+
}
|
| 37 |
+
</script>
|
| 38 |
+
<link rel="stylesheet" href="/index.css">
|
| 39 |
+
</head>
|
| 40 |
+
<body class="bg-slate-950 text-slate-100 overflow-hidden">
|
| 41 |
+
<div id="root"></div>
|
| 42 |
+
<script type="module" src="/index.tsx"></script>
|
| 43 |
+
</body>
|
| 44 |
+
</html>
|
index.tsx
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import React from 'react';
|
| 2 |
+
import ReactDOM from 'react-dom/client';
|
| 3 |
+
import App from './App';
|
| 4 |
+
|
| 5 |
+
const rootElement = document.getElementById('root');
|
| 6 |
+
if (!rootElement) {
|
| 7 |
+
throw new Error("Could not find root element to mount to");
|
| 8 |
+
}
|
| 9 |
+
|
| 10 |
+
const root = ReactDOM.createRoot(rootElement);
|
| 11 |
+
root.render(
|
| 12 |
+
<React.StrictMode>
|
| 13 |
+
<App />
|
| 14 |
+
</React.StrictMode>
|
| 15 |
+
);
|
metadata.json
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"name": "Architecture Agents",
|
| 3 |
+
"description": "Visual AI Architect for Deep Learning. Design with agents, build with code.",
|
| 4 |
+
"requestFramePermissions": []
|
| 5 |
+
}
|
package.json
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"name": "architecture-agents",
|
| 3 |
+
"private": true,
|
| 4 |
+
"version": "0.0.0",
|
| 5 |
+
"type": "module",
|
| 6 |
+
"scripts": {
|
| 7 |
+
"dev": "vite",
|
| 8 |
+
"build": "vite build",
|
| 9 |
+
"preview": "vite preview"
|
| 10 |
+
},
|
| 11 |
+
"dependencies": {
|
| 12 |
+
"react-dom": "^19.2.1",
|
| 13 |
+
"react": "^19.2.1",
|
| 14 |
+
"@google/genai": "^1.31.0",
|
| 15 |
+
"lucide-react": "^0.556.0",
|
| 16 |
+
"reactflow": "^11.11.4"
|
| 17 |
+
},
|
| 18 |
+
"devDependencies": {
|
| 19 |
+
"@types/node": "^22.14.0",
|
| 20 |
+
"@vitejs/plugin-react": "^5.0.0",
|
| 21 |
+
"typescript": "~5.8.2",
|
| 22 |
+
"vite": "^6.2.0"
|
| 23 |
+
}
|
| 24 |
+
}
|
tsconfig.json
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"compilerOptions": {
|
| 3 |
+
"target": "ES2022",
|
| 4 |
+
"experimentalDecorators": true,
|
| 5 |
+
"useDefineForClassFields": false,
|
| 6 |
+
"module": "ESNext",
|
| 7 |
+
"lib": [
|
| 8 |
+
"ES2022",
|
| 9 |
+
"DOM",
|
| 10 |
+
"DOM.Iterable"
|
| 11 |
+
],
|
| 12 |
+
"skipLibCheck": true,
|
| 13 |
+
"types": [
|
| 14 |
+
"node"
|
| 15 |
+
],
|
| 16 |
+
"moduleResolution": "bundler",
|
| 17 |
+
"isolatedModules": true,
|
| 18 |
+
"moduleDetection": "force",
|
| 19 |
+
"allowJs": true,
|
| 20 |
+
"jsx": "react-jsx",
|
| 21 |
+
"paths": {
|
| 22 |
+
"@/*": [
|
| 23 |
+
"./*"
|
| 24 |
+
]
|
| 25 |
+
},
|
| 26 |
+
"allowImportingTsExtensions": true,
|
| 27 |
+
"noEmit": true
|
| 28 |
+
}
|
| 29 |
+
}
|
types.ts
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
export enum LayerType {
|
| 2 |
+
INPUT = 'Input',
|
| 3 |
+
LINEAR = 'Linear',
|
| 4 |
+
CONV1D = 'Conv1D',
|
| 5 |
+
CONV2D = 'Conv2D',
|
| 6 |
+
CONV_TRANSPOSE2D = 'ConvTranspose2D',
|
| 7 |
+
RELU = 'ReLU',
|
| 8 |
+
LEAKYRELU = 'LeakyReLU',
|
| 9 |
+
GELU = 'GELU',
|
| 10 |
+
SILU = 'SiLU',
|
| 11 |
+
SWIGLU = 'SwiGLU',
|
| 12 |
+
SIGMOID = 'Sigmoid',
|
| 13 |
+
TANH = 'Tanh',
|
| 14 |
+
MAXPOOL = 'MaxPool2D',
|
| 15 |
+
AVGPOOL = 'AvgPool2D',
|
| 16 |
+
ADAPTIVEAVGPOOL = 'AdaptiveAvgPool2D',
|
| 17 |
+
UPSAMPLE = 'Upsample',
|
| 18 |
+
DROPOUT = 'Dropout',
|
| 19 |
+
BATCHNORM = 'BatchNorm2D',
|
| 20 |
+
LAYERNORM = 'LayerNorm',
|
| 21 |
+
RMSNORM = 'RMSNorm',
|
| 22 |
+
INSTANCENORM = 'InstanceNorm2d',
|
| 23 |
+
FLATTEN = 'Flatten',
|
| 24 |
+
EMBEDDING = 'Embedding',
|
| 25 |
+
PATCH_EMBED = 'PatchEmbed',
|
| 26 |
+
POS_EMBED = 'PositionalEmbed',
|
| 27 |
+
ROPE = 'RotaryPosEmbed',
|
| 28 |
+
TIME_EMBEDDING = 'TimeEmbedding',
|
| 29 |
+
LSTM = 'LSTM',
|
| 30 |
+
GRU = 'GRU',
|
| 31 |
+
ATTENTION = 'SelfAttention',
|
| 32 |
+
CROSS_ATTENTION = 'CrossAttention',
|
| 33 |
+
TRANSFORMER_BLOCK = 'TransformerBlock',
|
| 34 |
+
TRANSFORMER_ENCODER = 'TransformerEncoderLayer',
|
| 35 |
+
TRANSFORMER_DECODER = 'TransformerDecoderLayer',
|
| 36 |
+
MOE_BLOCK = 'SparseMoEBlock',
|
| 37 |
+
ACTION_HEAD = 'ActionHead',
|
| 38 |
+
SAM_PROMPT_ENCODER = 'SamPromptEncoder',
|
| 39 |
+
SAM_MASK_DECODER = 'SamMaskDecoder',
|
| 40 |
+
CONCAT = 'Concat',
|
| 41 |
+
ADD = 'Add',
|
| 42 |
+
OUTPUT = 'Output',
|
| 43 |
+
CUSTOM = 'CustomLayer',
|
| 44 |
+
IDENTITY = 'Identity'
|
| 45 |
+
}
|
| 46 |
+
|
| 47 |
+
export interface LayerParameter {
|
| 48 |
+
name: string;
|
| 49 |
+
type: 'number' | 'string' | 'select' | 'boolean';
|
| 50 |
+
label: string;
|
| 51 |
+
default: any;
|
| 52 |
+
options?: string[]; // For select types
|
| 53 |
+
description?: string;
|
| 54 |
+
}
|
| 55 |
+
|
| 56 |
+
export interface LayerDefinition {
|
| 57 |
+
type: LayerType;
|
| 58 |
+
label: string;
|
| 59 |
+
description: string;
|
| 60 |
+
category: 'Core' | 'Convolution' | 'Recurrent' | 'Normalization' | 'Transformer' | 'GenAI' | 'Utility' | 'Merge';
|
| 61 |
+
parameters: LayerParameter[];
|
| 62 |
+
}
|
| 63 |
+
|
| 64 |
+
export interface NodeData {
|
| 65 |
+
label: string;
|
| 66 |
+
type: LayerType;
|
| 67 |
+
params: Record<string, any>;
|
| 68 |
+
isValid?: boolean;
|
| 69 |
+
error?: string;
|
| 70 |
+
onDelete?: (id: string) => void;
|
| 71 |
+
}
|
| 72 |
+
|
| 73 |
+
export interface GraphTemplate {
|
| 74 |
+
id: string;
|
| 75 |
+
name: string;
|
| 76 |
+
description: string;
|
| 77 |
+
nodes: any[];
|
| 78 |
+
edges: any[];
|
| 79 |
+
}
|
| 80 |
+
|
| 81 |
+
export interface GeneratedCodeResponse {
|
| 82 |
+
code: string;
|
| 83 |
+
explanation: string;
|
| 84 |
+
}
|
vite.config.ts
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import path from 'path';
|
| 2 |
+
import { defineConfig, loadEnv } from 'vite';
|
| 3 |
+
import react from '@vitejs/plugin-react';
|
| 4 |
+
|
| 5 |
+
export default defineConfig(({ mode }) => {
|
| 6 |
+
const env = loadEnv(mode, '.', '');
|
| 7 |
+
return {
|
| 8 |
+
server: {
|
| 9 |
+
port: 3000,
|
| 10 |
+
host: '0.0.0.0',
|
| 11 |
+
},
|
| 12 |
+
plugins: [react()],
|
| 13 |
+
define: {
|
| 14 |
+
'process.env.API_KEY': JSON.stringify(env.GEMINI_API_KEY),
|
| 15 |
+
'process.env.GEMINI_API_KEY': JSON.stringify(env.GEMINI_API_KEY)
|
| 16 |
+
},
|
| 17 |
+
resolve: {
|
| 18 |
+
alias: {
|
| 19 |
+
'@': path.resolve(__dirname, '.'),
|
| 20 |
+
}
|
| 21 |
+
}
|
| 22 |
+
};
|
| 23 |
+
});
|