Spaces:
Running
Running
Update components/Builder.tsx
Browse files- components/Builder.tsx +64 -10
components/Builder.tsx
CHANGED
|
@@ -12,7 +12,7 @@ import ReactFlow, {
|
|
| 12 |
Panel,
|
| 13 |
ConnectionMode
|
| 14 |
} from 'reactflow';
|
| 15 |
-
import { FileText, CheckCircle, AlertTriangle, X, Copy, Check, Lightbulb, Key, Wrench, Menu, Activity } from 'lucide-react';
|
| 16 |
import Sidebar from './Sidebar';
|
| 17 |
import PropertiesPanel from './PropertiesPanel';
|
| 18 |
import CustomNode from './CustomNode';
|
|
@@ -22,9 +22,18 @@ import SuggestionsModal from './SuggestionsModal';
|
|
| 22 |
import ApiKeyModal from './ApiKeyModal';
|
| 23 |
import FixerModal from './FixerModal';
|
| 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, LogEntry } from '../types';
|
| 27 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 28 |
let id = 1000;
|
| 29 |
const getId = () => `${id++}`;
|
| 30 |
|
|
@@ -68,6 +77,10 @@ const Builder: React.FC<BuilderProps> = ({ onBackToHome }) => {
|
|
| 68 |
const [isApiKeyModalOpen, setIsApiKeyModalOpen] = useState(false);
|
| 69 |
const [isConnected, setIsConnected] = useState(false);
|
| 70 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 71 |
// Activity Log State
|
| 72 |
const [logs, setLogs] = useState<LogEntry[]>([]);
|
| 73 |
|
|
@@ -207,9 +220,9 @@ const Builder: React.FC<BuilderProps> = ({ onBackToHome }) => {
|
|
| 207 |
if (!isConnected) { setIsApiKeyModalOpen(true); return; }
|
| 208 |
setIsGeneratingPrompt(true);
|
| 209 |
setIsPromptViewerOpen(true);
|
| 210 |
-
addLog(
|
| 211 |
try {
|
| 212 |
-
const promptText = await generateRefinedPrompt(nodes, edges);
|
| 213 |
setGeneratedPrompt(promptText);
|
| 214 |
addLog("Code prompt generated successfully.", 'success');
|
| 215 |
} catch (e) {
|
|
@@ -223,9 +236,9 @@ const Builder: React.FC<BuilderProps> = ({ onBackToHome }) => {
|
|
| 223 |
const handleValidate = async () => {
|
| 224 |
if (!isConnected) { setIsApiKeyModalOpen(true); return; }
|
| 225 |
setValidationMsg("Validating...");
|
| 226 |
-
addLog(
|
| 227 |
try {
|
| 228 |
-
const result = await validateArchitecture(nodes, edges);
|
| 229 |
setValidationMsg(result);
|
| 230 |
if (result.toLowerCase().includes("valid") && !result.toLowerCase().includes("invalid") && result.length < 50) {
|
| 231 |
setTimeout(() => setValidationMsg(null), 5000);
|
|
@@ -244,7 +257,7 @@ const Builder: React.FC<BuilderProps> = ({ onBackToHome }) => {
|
|
| 244 |
setIsSuggestionsOpen(true);
|
| 245 |
setIsSuggestionsLoading(true);
|
| 246 |
try {
|
| 247 |
-
const suggestions = await getArchitectureSuggestions(nodes, edges);
|
| 248 |
setSuggestionsText(suggestions);
|
| 249 |
} catch (error) {
|
| 250 |
setSuggestionsText("Failed to get suggestions. API Error.");
|
|
@@ -257,9 +270,9 @@ const Builder: React.FC<BuilderProps> = ({ onBackToHome }) => {
|
|
| 257 |
const handleImplementSuggestions = async () => {
|
| 258 |
if (!suggestionsText || isImplementingSuggestions) return;
|
| 259 |
setIsImplementingSuggestions(true);
|
| 260 |
-
addLog(
|
| 261 |
try {
|
| 262 |
-
const result = await implementArchitectureSuggestions(nodes, edges, suggestionsText);
|
| 263 |
if (result && result.nodes) {
|
| 264 |
// Fix types and defaults safely
|
| 265 |
const processedNodes = result.nodes.map((n: any) => {
|
|
@@ -329,8 +342,9 @@ const Builder: React.FC<BuilderProps> = ({ onBackToHome }) => {
|
|
| 329 |
}
|
| 330 |
}));
|
| 331 |
|
| 332 |
-
const hydratedEdges = clonedEdges.map((e: any) => ({
|
| 333 |
...e,
|
|
|
|
| 334 |
animated: true,
|
| 335 |
style: { stroke: '#94a3b8' }
|
| 336 |
}));
|
|
@@ -412,6 +426,44 @@ const Builder: React.FC<BuilderProps> = ({ onBackToHome }) => {
|
|
| 412 |
<Key size={16} className={isConnected ? "text-emerald-500" : "text-slate-400"} />
|
| 413 |
<span className="hidden md:inline">{isConnected ? 'Key Active' : 'Add Key'}</span>
|
| 414 |
</button>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 415 |
<button
|
| 416 |
onClick={handleGetSuggestions}
|
| 417 |
className="flex items-center gap-2 px-3 py-2 bg-slate-800/80 backdrop-blur hover:bg-slate-700 border border-slate-700 rounded-lg text-slate-200 text-xs md:text-sm font-medium transition-colors shadow-lg"
|
|
@@ -537,6 +589,7 @@ const Builder: React.FC<BuilderProps> = ({ onBackToHome }) => {
|
|
| 537 |
onClose={() => setIsAIBuilderOpen(false)}
|
| 538 |
onApply={handleApplyAIBuild}
|
| 539 |
currentNodes={nodes}
|
|
|
|
| 540 |
/>
|
| 541 |
|
| 542 |
<FixerModal
|
|
@@ -546,6 +599,7 @@ const Builder: React.FC<BuilderProps> = ({ onBackToHome }) => {
|
|
| 546 |
errorMsg={validationMsg || ''}
|
| 547 |
nodes={nodes}
|
| 548 |
edges={edges}
|
|
|
|
| 549 |
/>
|
| 550 |
|
| 551 |
<ApiKeyModal
|
|
|
|
| 12 |
Panel,
|
| 13 |
ConnectionMode
|
| 14 |
} from 'reactflow';
|
| 15 |
+
import { FileText, CheckCircle, AlertTriangle, X, Copy, Check, Lightbulb, Key, Wrench, Menu, Activity, ChevronDown, Cpu } from 'lucide-react';
|
| 16 |
import Sidebar from './Sidebar';
|
| 17 |
import PropertiesPanel from './PropertiesPanel';
|
| 18 |
import CustomNode from './CustomNode';
|
|
|
|
| 22 |
import ApiKeyModal from './ApiKeyModal';
|
| 23 |
import FixerModal from './FixerModal';
|
| 24 |
import { INITIAL_NODES, INITIAL_EDGES, LAYER_DEFINITIONS, TEMPLATES } from '../constants';
|
| 25 |
+
import { generateRefinedPrompt, validateArchitecture, getArchitectureSuggestions, getUserApiKey, implementArchitectureSuggestions, DEFAULT_MODEL } from '../services/geminiService';
|
| 26 |
import { NodeData, LayerType, LogEntry } from '../types';
|
| 27 |
|
| 28 |
+
const AVAILABLE_MODELS = [
|
| 29 |
+
{ id: 'gemini-2.5-flash', name: 'Gemini 2.5 Flash', description: 'Fast & Efficient' },
|
| 30 |
+
{ id: 'gemini-2.5-flash-lite', name: 'Gemini 2.5 Flash-Lite', description: 'Ultra-Fast & Efficient' },
|
| 31 |
+
{ id: 'gemini-2.5-pro', name: 'Gemini 2.5 Pro', description: 'Balanced Performance' },
|
| 32 |
+
{ id: 'gemini-3-flash-preview', name: 'Gemini 3 Flash', description: 'Next-Gen Speed' },
|
| 33 |
+
{ id: 'gemini-3.1-flash-lite-preview', name: 'Gemini 3.1 Flash Lite', description: 'Lightweight & Fast' },
|
| 34 |
+
{ id: 'gemini-3.1-pro-preview', name: 'Gemini 3.1 Pro', description: 'Complex Reasoning' },
|
| 35 |
+
];
|
| 36 |
+
|
| 37 |
let id = 1000;
|
| 38 |
const getId = () => `${id++}`;
|
| 39 |
|
|
|
|
| 77 |
const [isApiKeyModalOpen, setIsApiKeyModalOpen] = useState(false);
|
| 78 |
const [isConnected, setIsConnected] = useState(false);
|
| 79 |
|
| 80 |
+
// Model Selection State
|
| 81 |
+
const [selectedModel, setSelectedModel] = useState(DEFAULT_MODEL);
|
| 82 |
+
const [isModelMenuOpen, setIsModelMenuOpen] = useState(false);
|
| 83 |
+
|
| 84 |
// Activity Log State
|
| 85 |
const [logs, setLogs] = useState<LogEntry[]>([]);
|
| 86 |
|
|
|
|
| 220 |
if (!isConnected) { setIsApiKeyModalOpen(true); return; }
|
| 221 |
setIsGeneratingPrompt(true);
|
| 222 |
setIsPromptViewerOpen(true);
|
| 223 |
+
addLog(`Generating PyTorch code prompt using ${AVAILABLE_MODELS.find(m => m.id === selectedModel)?.name}...`, 'info');
|
| 224 |
try {
|
| 225 |
+
const promptText = await generateRefinedPrompt(nodes, edges, selectedModel);
|
| 226 |
setGeneratedPrompt(promptText);
|
| 227 |
addLog("Code prompt generated successfully.", 'success');
|
| 228 |
} catch (e) {
|
|
|
|
| 236 |
const handleValidate = async () => {
|
| 237 |
if (!isConnected) { setIsApiKeyModalOpen(true); return; }
|
| 238 |
setValidationMsg("Validating...");
|
| 239 |
+
addLog(`Running architecture validation using ${AVAILABLE_MODELS.find(m => m.id === selectedModel)?.name}...`, 'info');
|
| 240 |
try {
|
| 241 |
+
const result = await validateArchitecture(nodes, edges, selectedModel);
|
| 242 |
setValidationMsg(result);
|
| 243 |
if (result.toLowerCase().includes("valid") && !result.toLowerCase().includes("invalid") && result.length < 50) {
|
| 244 |
setTimeout(() => setValidationMsg(null), 5000);
|
|
|
|
| 257 |
setIsSuggestionsOpen(true);
|
| 258 |
setIsSuggestionsLoading(true);
|
| 259 |
try {
|
| 260 |
+
const suggestions = await getArchitectureSuggestions(nodes, edges, selectedModel);
|
| 261 |
setSuggestionsText(suggestions);
|
| 262 |
} catch (error) {
|
| 263 |
setSuggestionsText("Failed to get suggestions. API Error.");
|
|
|
|
| 270 |
const handleImplementSuggestions = async () => {
|
| 271 |
if (!suggestionsText || isImplementingSuggestions) return;
|
| 272 |
setIsImplementingSuggestions(true);
|
| 273 |
+
addLog(`AI Agents implementing suggestions using ${AVAILABLE_MODELS.find(m => m.id === selectedModel)?.name}...`, 'info');
|
| 274 |
try {
|
| 275 |
+
const result = await implementArchitectureSuggestions(nodes, edges, suggestionsText, selectedModel);
|
| 276 |
if (result && result.nodes) {
|
| 277 |
// Fix types and defaults safely
|
| 278 |
const processedNodes = result.nodes.map((n: any) => {
|
|
|
|
| 342 |
}
|
| 343 |
}));
|
| 344 |
|
| 345 |
+
const hydratedEdges = clonedEdges.map((e: any, idx: number) => ({
|
| 346 |
...e,
|
| 347 |
+
id: e.id || `e-template-${idx}-${Math.random().toString(36).substr(2, 4)}`,
|
| 348 |
animated: true,
|
| 349 |
style: { stroke: '#94a3b8' }
|
| 350 |
}));
|
|
|
|
| 426 |
<Key size={16} className={isConnected ? "text-emerald-500" : "text-slate-400"} />
|
| 427 |
<span className="hidden md:inline">{isConnected ? 'Key Active' : 'Add Key'}</span>
|
| 428 |
</button>
|
| 429 |
+
|
| 430 |
+
{/* Model Selection Button */}
|
| 431 |
+
<div className="relative">
|
| 432 |
+
<button
|
| 433 |
+
onClick={() => setIsModelMenuOpen(!isModelMenuOpen)}
|
| 434 |
+
className="flex items-center gap-2 px-3 py-2 bg-slate-800/80 backdrop-blur hover:bg-slate-700 border border-slate-700 rounded-lg text-slate-200 text-xs md:text-sm font-medium transition-colors shadow-lg"
|
| 435 |
+
title="Select Gemini Model"
|
| 436 |
+
>
|
| 437 |
+
<Cpu size={16} className="text-blue-400" />
|
| 438 |
+
<span className="hidden md:inline">{AVAILABLE_MODELS.find(m => m.id === selectedModel)?.name.split(' ')[1] || 'Model'}</span>
|
| 439 |
+
<ChevronDown size={14} className={`text-slate-500 transition-transform ${isModelMenuOpen ? 'rotate-180' : ''}`} />
|
| 440 |
+
</button>
|
| 441 |
+
|
| 442 |
+
{isModelMenuOpen && (
|
| 443 |
+
<div className="absolute right-0 mt-2 w-64 bg-slate-900 border border-slate-700 rounded-xl shadow-2xl z-[100] overflow-hidden animate-in fade-in slide-in-from-top-2 duration-200">
|
| 444 |
+
<div className="p-2">
|
| 445 |
+
{AVAILABLE_MODELS.map((model) => (
|
| 446 |
+
<button
|
| 447 |
+
key={model.id}
|
| 448 |
+
onClick={() => {
|
| 449 |
+
setSelectedModel(model.id);
|
| 450 |
+
setIsModelMenuOpen(false);
|
| 451 |
+
addLog(`Switched to model: ${model.name}`, 'info');
|
| 452 |
+
}}
|
| 453 |
+
className={`w-full text-left p-3 rounded-lg transition-colors ${selectedModel === model.id ? 'bg-blue-600/20 border border-blue-500/30' : 'hover:bg-slate-800 border border-transparent'}`}
|
| 454 |
+
>
|
| 455 |
+
<div className="flex items-center justify-between">
|
| 456 |
+
<span className={`font-semibold text-sm ${selectedModel === model.id ? 'text-blue-400' : 'text-slate-200'}`}>{model.name}</span>
|
| 457 |
+
{selectedModel === model.id && <Check size={14} className="text-blue-400" />}
|
| 458 |
+
</div>
|
| 459 |
+
<div className="text-xs text-slate-500 mt-0.5">{model.description}</div>
|
| 460 |
+
</button>
|
| 461 |
+
))}
|
| 462 |
+
</div>
|
| 463 |
+
</div>
|
| 464 |
+
)}
|
| 465 |
+
</div>
|
| 466 |
+
|
| 467 |
<button
|
| 468 |
onClick={handleGetSuggestions}
|
| 469 |
className="flex items-center gap-2 px-3 py-2 bg-slate-800/80 backdrop-blur hover:bg-slate-700 border border-slate-700 rounded-lg text-slate-200 text-xs md:text-sm font-medium transition-colors shadow-lg"
|
|
|
|
| 589 |
onClose={() => setIsAIBuilderOpen(false)}
|
| 590 |
onApply={handleApplyAIBuild}
|
| 591 |
currentNodes={nodes}
|
| 592 |
+
selectedModel={selectedModel}
|
| 593 |
/>
|
| 594 |
|
| 595 |
<FixerModal
|
|
|
|
| 599 |
errorMsg={validationMsg || ''}
|
| 600 |
nodes={nodes}
|
| 601 |
edges={edges}
|
| 602 |
+
selectedModel={selectedModel}
|
| 603 |
/>
|
| 604 |
|
| 605 |
<ApiKeyModal
|