wuhp commited on
Commit
bb1767b
·
verified ·
1 Parent(s): 5577100

Upload 13 files

Browse files
Files changed (13) hide show
  1. .env.local +1 -0
  2. .gitignore +24 -0
  3. App.tsx +408 -0
  4. Dockerfile +16 -0
  5. README.md +8 -10
  6. constants.ts +663 -0
  7. index.html +44 -0
  8. index.tsx +15 -0
  9. metadata.json +5 -0
  10. package.json +24 -0
  11. tsconfig.json +29 -0
  12. types.ts +84 -0
  13. 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: Testarcbuilder
3
- emoji: 🐢
4
- colorFrom: blue
5
- colorTo: yellow
6
- sdk: docker
7
- pinned: false
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
+ });