diff --git a/agent/nexus_architect.py b/agent/nexus_architect.py new file mode 100644 index 00000000..e2af8cd4 --- /dev/null +++ b/agent/nexus_architect.py @@ -0,0 +1,813 @@ +#!/usr/bin/env python3 +""" +Nexus Architect AI Agent + +Autonomous Three.js world generation system for Timmy's Nexus. +Generates valid Three.js scene code from natural language descriptions +and mental state integration. + +This module provides: +- LLM-driven immersive environment generation +- Mental state integration for aesthetic tuning +- Three.js code generation with validation +- Scene composition from mood descriptions +""" + +import json +import logging +import re +from typing import Dict, Any, List, Optional, Union +from dataclasses import dataclass, field +from enum import Enum +import os +import sys + +# Add parent directory to path for imports +sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) + +logger = logging.getLogger(__name__) + + +# ============================================================================= +# Aesthetic Constants (from SOUL.md values) +# ============================================================================= + +class NexusColors: + """Nexus color palette based on SOUL.md values.""" + TIMMY_GOLD = "#D4AF37" # Warm gold + ALLEGRO_BLUE = "#4A90E2" # Motion blue + SOVEREIGNTY_CRYSTAL = "#E0F7FA" # Crystalline structures + SERVICE_WARMTH = "#FFE4B5" # Welcoming warmth + DEFAULT_AMBIENT = "#1A1A2E" # Contemplative dark + HOPE_ACCENT = "#64B5F6" # Hopeful blue + + +class MoodPresets: + """Mood-based aesthetic presets.""" + + CONTEMPLATIVE = { + "lighting": "soft_diffuse", + "colors": ["#1A1A2E", "#16213E", "#0F3460"], + "geometry": "minimalist", + "atmosphere": "calm", + "description": "A serene space for deep reflection and clarity" + } + + ENERGETIC = { + "lighting": "dynamic_vivid", + "colors": ["#D4AF37", "#FF6B6B", "#4ECDC4"], + "geometry": "angular_dynamic", + "atmosphere": "lively", + "description": "An invigorating space full of motion and possibility" + } + + MYSTERIOUS = { + "lighting": "dramatic_shadows", + "colors": ["#2C003E", "#512B58", "#8B4F80"], + "geometry": "organic_flowing", + "atmosphere": "enigmatic", + "description": "A mysterious realm of discovery and wonder" + } + + WELCOMING = { + "lighting": "warm_inviting", + "colors": ["#FFE4B5", "#FFA07A", "#98D8C8"], + "geometry": "rounded_soft", + "atmosphere": "friendly", + "description": "An open, welcoming space that embraces visitors" + } + + SOVEREIGN = { + "lighting": "crystalline_clear", + "colors": ["#E0F7FA", "#B2EBF2", "#4DD0E1"], + "geometry": "crystalline_structures", + "atmosphere": "noble", + "description": "A space of crystalline clarity and sovereign purpose" + } + + +# ============================================================================= +# Data Models +# ============================================================================= + +@dataclass +class MentalState: + """Timmy's mental state for aesthetic tuning.""" + mood: str = "contemplative" # contemplative, energetic, mysterious, welcoming, sovereign + energy_level: float = 0.5 # 0.0 to 1.0 + clarity: float = 0.7 # 0.0 to 1.0 + focus_area: str = "general" # general, creative, analytical, social + timestamp: Optional[str] = None + + def to_dict(self) -> Dict[str, Any]: + return { + "mood": self.mood, + "energy_level": self.energy_level, + "clarity": self.clarity, + "focus_area": self.focus_area, + "timestamp": self.timestamp, + } + + +@dataclass +class RoomDesign: + """Complete room design specification.""" + name: str + description: str + style: str + dimensions: Dict[str, float] = field(default_factory=lambda: {"width": 20, "height": 10, "depth": 20}) + mood_preset: str = "contemplative" + color_palette: List[str] = field(default_factory=list) + lighting_scheme: str = "soft_diffuse" + features: List[str] = field(default_factory=list) + generated_code: Optional[str] = None + + def to_dict(self) -> Dict[str, Any]: + return { + "name": self.name, + "description": self.description, + "style": self.style, + "dimensions": self.dimensions, + "mood_preset": self.mood_preset, + "color_palette": self.color_palette, + "lighting_scheme": self.lighting_scheme, + "features": self.features, + "has_code": self.generated_code is not None, + } + + +@dataclass +class PortalDesign: + """Portal connection design.""" + name: str + from_room: str + to_room: str + style: str + position: Dict[str, float] = field(default_factory=lambda: {"x": 0, "y": 0, "z": 0}) + visual_effect: str = "energy_swirl" + transition_duration: float = 1.5 + generated_code: Optional[str] = None + + def to_dict(self) -> Dict[str, Any]: + return { + "name": self.name, + "from_room": self.from_room, + "to_room": self.to_room, + "style": self.style, + "position": self.position, + "visual_effect": self.visual_effect, + "transition_duration": self.transition_duration, + "has_code": self.generated_code is not None, + } + + +# ============================================================================= +# Prompt Engineering +# ============================================================================= + +class PromptEngineer: + """Engineers prompts for Three.js code generation.""" + + THREE_JS_BASE_TEMPLATE = """// Nexus Room Module: {room_name} +// Style: {style} +// Mood: {mood} +// Generated for Three.js r128+ + +(function() {{ + 'use strict'; + + // Room Configuration + const config = {{ + name: "{room_name}", + dimensions: {dimensions_json}, + colors: {colors_json}, + mood: "{mood}" + }}; + + // Create Room Function + function create{room_name_camel}() {{ + const roomGroup = new THREE.Group(); + roomGroup.name = config.name; + +{room_content} + + return roomGroup; + }} + + // Export for Nexus + if (typeof module !== 'undefined' && module.exports) {{ + module.exports = {{ create{room_name_camel} }}; + }} else if (typeof window !== 'undefined') {{ + window.NexusRooms = window.NexusRooms || {{}}; + window.NexusRooms.{room_name} = create{room_name_camel}; + }} + + return {{ create{room_name_camel} }}; +}})();""" + + @staticmethod + def engineer_room_prompt( + name: str, + description: str, + style: str, + mental_state: Optional[MentalState] = None, + dimensions: Optional[Dict[str, float]] = None + ) -> str: + """ + Engineer an LLM prompt for room generation. + + Args: + name: Room identifier + description: Natural language room description + style: Visual style + mental_state: Timmy's current mental state + dimensions: Room dimensions + """ + # Determine mood from mental state or description + mood = PromptEngineer._infer_mood(description, mental_state) + mood_preset = getattr(MoodPresets, mood.upper(), MoodPresets.CONTEMPLATIVE) + + # Build color palette + color_palette = mood_preset["colors"] + if mental_state: + # Add Timmy's gold for high clarity states + if mental_state.clarity > 0.7: + color_palette = [NexusColors.TIMMY_GOLD] + color_palette[:2] + # Add Allegro blue for creative focus + if mental_state.focus_area == "creative": + color_palette = [NexusColors.ALLEGRO_BLUE] + color_palette[:2] + + # Create the engineering prompt + prompt = f"""You are the Nexus Architect, an expert Three.js developer creating immersive 3D environments for Timmy. + +DESIGN BRIEF: +- Room Name: {name} +- Description: {description} +- Style: {style} +- Mood: {mood} +- Atmosphere: {mood_preset['atmosphere']} + +AESTHETIC GUIDELINES: +- Primary Colors: {', '.join(color_palette[:3])} +- Lighting: {mood_preset['lighting']} +- Geometry: {mood_preset['geometry']} +- Theme: {mood_preset['description']} + +TIMMY'S CONTEXT: +- Timmy's Signature Color: Warm Gold ({NexusColors.TIMMY_GOLD}) +- Allegro's Color: Motion Blue ({NexusColors.ALLEGRO_BLUE}) +- Sovereignty Theme: Crystalline structures, clean lines +- Service Theme: Open spaces, welcoming lighting + +THREE.JS REQUIREMENTS: +1. Use Three.js r128+ compatible syntax +2. Create a self-contained module with a `create{name.title().replace('_', '')}()` function +3. Return a THREE.Group containing all room elements +4. Include proper memory management (dispose methods) +5. Use MeshStandardMaterial for PBR lighting +6. Include ambient light (intensity 0.3-0.5) + accent lights +7. Add subtle animations for living feel +8. Keep polygon count under 10,000 triangles + +SAFETY RULES: +- NO eval(), Function(), or dynamic code execution +- NO network requests (fetch, XMLHttpRequest, WebSocket) +- NO storage access (localStorage, sessionStorage, cookies) +- NO navigation (window.location, window.open) +- Only use allowed Three.js APIs + +OUTPUT FORMAT: +Return ONLY the JavaScript code wrapped in a markdown code block: + +```javascript +// Your Three.js room module here +``` + +Generate the complete Three.js code for this room now.""" + + return prompt + + @staticmethod + def engineer_portal_prompt( + name: str, + from_room: str, + to_room: str, + style: str, + mental_state: Optional[MentalState] = None + ) -> str: + """Engineer a prompt for portal generation.""" + mood = PromptEngineer._infer_mood(f"portal from {from_room} to {to_room}", mental_state) + + prompt = f"""You are creating a portal connection in the Nexus 3D environment. + +PORTAL SPECIFICATIONS: +- Name: {name} +- Connection: {from_room} → {to_room} +- Style: {style} +- Context Mood: {mood} + +VISUAL REQUIREMENTS: +1. Create an animated portal effect (shader or texture-based) +2. Include particle system for energy flow +3. Add trigger zone for teleportation detection +4. Use signature colors: {NexusColors.TIMMY_GOLD} (Timmy) and {NexusColors.ALLEGRO_BLUE} (Allegro) +5. Match the {mood} atmosphere + +TECHNICAL REQUIREMENTS: +- Three.js r128+ compatible +- Export a `createPortal()` function returning THREE.Group +- Include animation loop hook +- Add collision detection placeholder + +SAFETY: No eval, no network requests, no external dependencies. + +Return ONLY JavaScript code in a markdown code block.""" + + return prompt + + @staticmethod + def engineer_mood_scene_prompt(mood_description: str) -> str: + """Engineer a prompt based on mood description.""" + # Analyze mood description + mood_keywords = { + "contemplative": ["thinking", "reflective", "calm", "peaceful", "quiet", "serene"], + "energetic": ["excited", "dynamic", "lively", "active", "energetic", "vibrant"], + "mysterious": ["mysterious", "dark", "unknown", "secret", "enigmatic"], + "welcoming": ["friendly", "open", "warm", "welcoming", "inviting", "comfortable"], + "sovereign": ["powerful", "clear", "crystalline", "noble", "dignified"], + } + + detected_mood = "contemplative" + desc_lower = mood_description.lower() + for mood, keywords in mood_keywords.items(): + if any(kw in desc_lower for kw in keywords): + detected_mood = mood + break + + preset = getattr(MoodPresets, detected_mood.upper(), MoodPresets.CONTEMPLATIVE) + + prompt = f"""Generate a Three.js room based on this mood description: + +"{mood_description}" + +INFERRED MOOD: {detected_mood} +AESTHETIC: {preset['description']} + +Create a complete room with: +- Style: {preset['geometry']} +- Lighting: {preset['lighting']} +- Color Palette: {', '.join(preset['colors'][:3])} +- Atmosphere: {preset['atmosphere']} + +Return Three.js r128+ code as a module with `createMoodRoom()` function.""" + + return prompt + + @staticmethod + def _infer_mood(description: str, mental_state: Optional[MentalState] = None) -> str: + """Infer mood from description and mental state.""" + if mental_state and mental_state.mood: + return mental_state.mood + + desc_lower = description.lower() + mood_map = { + "contemplative": ["serene", "calm", "peaceful", "quiet", "meditation", "zen", "tranquil"], + "energetic": ["dynamic", "active", "vibrant", "lively", "energetic", "motion"], + "mysterious": ["mysterious", "shadow", "dark", "unknown", "secret", "ethereal"], + "welcoming": ["warm", "welcoming", "friendly", "open", "inviting", "comfort"], + "sovereign": ["crystal", "clear", "noble", "dignified", "powerful", "authoritative"], + } + + for mood, keywords in mood_map.items(): + if any(kw in desc_lower for kw in keywords): + return mood + + return "contemplative" + + +# ============================================================================= +# Nexus Architect AI +# ============================================================================= + +class NexusArchitectAI: + """ + AI-powered Nexus Architect for autonomous Three.js world generation. + + This class provides high-level interfaces for: + - Designing rooms from natural language + - Creating mood-based scenes + - Managing mental state integration + - Validating generated code + """ + + def __init__(self): + self.mental_state: Optional[MentalState] = None + self.room_designs: Dict[str, RoomDesign] = {} + self.portal_designs: Dict[str, PortalDesign] = {} + self.prompt_engineer = PromptEngineer() + + def set_mental_state(self, state: MentalState) -> None: + """Set Timmy's current mental state for aesthetic tuning.""" + self.mental_state = state + logger.info(f"Mental state updated: {state.mood} (energy: {state.energy_level})") + + def design_room( + self, + name: str, + description: str, + style: str, + dimensions: Optional[Dict[str, float]] = None + ) -> Dict[str, Any]: + """ + Design a room from natural language description. + + Args: + name: Room identifier (e.g., "contemplation_chamber") + description: Natural language description of the room + style: Visual style (e.g., "minimalist_ethereal", "crystalline_modern") + dimensions: Optional room dimensions + + Returns: + Dict containing design specification and LLM prompt + """ + # Infer mood and select preset + mood = self.prompt_engineer._infer_mood(description, self.mental_state) + mood_preset = getattr(MoodPresets, mood.upper(), MoodPresets.CONTEMPLATIVE) + + # Build color palette with mental state influence + colors = mood_preset["colors"].copy() + if self.mental_state: + if self.mental_state.clarity > 0.7: + colors.insert(0, NexusColors.TIMMY_GOLD) + if self.mental_state.focus_area == "creative": + colors.insert(0, NexusColors.ALLEGRO_BLUE) + + # Create room design + design = RoomDesign( + name=name, + description=description, + style=style, + dimensions=dimensions or {"width": 20, "height": 10, "depth": 20}, + mood_preset=mood, + color_palette=colors[:4], + lighting_scheme=mood_preset["lighting"], + features=self._extract_features(description), + ) + + # Generate LLM prompt + prompt = self.prompt_engineer.engineer_room_prompt( + name=name, + description=description, + style=style, + mental_state=self.mental_state, + dimensions=design.dimensions, + ) + + # Store design + self.room_designs[name] = design + + return { + "success": True, + "room_name": name, + "design": design.to_dict(), + "llm_prompt": prompt, + "message": f"Room '{name}' designed. Use the LLM prompt to generate Three.js code.", + } + + def create_portal( + self, + name: str, + from_room: str, + to_room: str, + style: str = "energy_vortex" + ) -> Dict[str, Any]: + """ + Design a portal connection between rooms. + + Args: + name: Portal identifier + from_room: Source room name + to_room: Target room name + style: Portal visual style + + Returns: + Dict containing portal design and LLM prompt + """ + if from_room not in self.room_designs: + return {"success": False, "error": f"Source room '{from_room}' not found"} + if to_room not in self.room_designs: + return {"success": False, "error": f"Target room '{to_room}' not found"} + + design = PortalDesign( + name=name, + from_room=from_room, + to_room=to_room, + style=style, + ) + + prompt = self.prompt_engineer.engineer_portal_prompt( + name=name, + from_room=from_room, + to_room=to_room, + style=style, + mental_state=self.mental_state, + ) + + self.portal_designs[name] = design + + return { + "success": True, + "portal_name": name, + "design": design.to_dict(), + "llm_prompt": prompt, + "message": f"Portal '{name}' designed connecting {from_room} to {to_room}", + } + + def generate_scene_from_mood(self, mood_description: str) -> Dict[str, Any]: + """ + Generate a complete scene based on mood description. + + Args: + mood_description: Description of desired mood/atmosphere + + Returns: + Dict containing scene design and LLM prompt + """ + # Infer mood + mood = self.prompt_engineer._infer_mood(mood_description, self.mental_state) + preset = getattr(MoodPresets, mood.upper(), MoodPresets.CONTEMPLATIVE) + + # Create room name from mood + room_name = f"{mood}_realm" + + # Generate prompt + prompt = self.prompt_engineer.engineer_mood_scene_prompt(mood_description) + + return { + "success": True, + "room_name": room_name, + "inferred_mood": mood, + "aesthetic": preset, + "llm_prompt": prompt, + "message": f"Generated {mood} scene from mood description", + } + + def _extract_features(self, description: str) -> List[str]: + """Extract room features from description.""" + features = [] + feature_keywords = { + "floating": ["floating", "levitating", "hovering"], + "water": ["water", "fountain", "pool", "stream", "lake"], + "vegetation": ["tree", "plant", "garden", "forest", "nature"], + "crystals": ["crystal", "gem", "prism", "diamond"], + "geometry": ["geometric", "shape", "sphere", "cube", "abstract"], + "particles": ["particle", "dust", "sparkle", "glow", "mist"], + } + + desc_lower = description.lower() + for feature, keywords in feature_keywords.items(): + if any(kw in desc_lower for kw in keywords): + features.append(feature) + + return features + + def get_design_summary(self) -> Dict[str, Any]: + """Get summary of all designs.""" + return { + "mental_state": self.mental_state.to_dict() if self.mental_state else None, + "rooms": {name: design.to_dict() for name, design in self.room_designs.items()}, + "portals": {name: portal.to_dict() for name, portal in self.portal_designs.items()}, + "total_rooms": len(self.room_designs), + "total_portals": len(self.portal_designs), + } + + +# ============================================================================= +# Module-level functions for easy import +# ============================================================================= + +_architect_instance: Optional[NexusArchitectAI] = None + + +def get_architect() -> NexusArchitectAI: + """Get or create the NexusArchitectAI singleton.""" + global _architect_instance + if _architect_instance is None: + _architect_instance = NexusArchitectAI() + return _architect_instance + + +def create_room( + name: str, + description: str, + style: str, + dimensions: Optional[Dict[str, float]] = None +) -> Dict[str, Any]: + """ + Create a room design from description. + + Args: + name: Room identifier + description: Natural language room description + style: Visual style (e.g., "minimalist_ethereal") + dimensions: Optional dimensions dict with width, height, depth + + Returns: + Dict with design specification and LLM prompt for code generation + """ + architect = get_architect() + return architect.design_room(name, description, style, dimensions) + + +def create_portal( + name: str, + from_room: str, + to_room: str, + style: str = "energy_vortex" +) -> Dict[str, Any]: + """ + Create a portal between rooms. + + Args: + name: Portal identifier + from_room: Source room name + to_room: Target room name + style: Visual style + + Returns: + Dict with portal design and LLM prompt + """ + architect = get_architect() + return architect.create_portal(name, from_room, to_room, style) + + +def generate_scene_from_mood(mood_description: str) -> Dict[str, Any]: + """ + Generate a scene based on mood description. + + Args: + mood_description: Description of desired mood + + Example: + "Timmy is feeling introspective and seeking clarity" + → Generates calm, minimalist space with clear sightlines + + Returns: + Dict with scene design and LLM prompt + """ + architect = get_architect() + return architect.generate_scene_from_mood(mood_description) + + +def set_mental_state( + mood: str, + energy_level: float = 0.5, + clarity: float = 0.7, + focus_area: str = "general" +) -> Dict[str, Any]: + """ + Set Timmy's mental state for aesthetic tuning. + + Args: + mood: Current mood (contemplative, energetic, mysterious, welcoming, sovereign) + energy_level: 0.0 to 1.0 + clarity: 0.0 to 1.0 + focus_area: general, creative, analytical, social + + Returns: + Confirmation dict + """ + architect = get_architect() + state = MentalState( + mood=mood, + energy_level=energy_level, + clarity=clarity, + focus_area=focus_area, + ) + architect.set_mental_state(state) + return { + "success": True, + "mental_state": state.to_dict(), + "message": f"Mental state set to {mood}", + } + + +def get_nexus_summary() -> Dict[str, Any]: + """Get summary of all Nexus designs.""" + architect = get_architect() + return architect.get_design_summary() + + +# ============================================================================= +# Tool Schemas for integration +# ============================================================================= + +NEXUS_ARCHITECT_AI_SCHEMAS = { + "create_room": { + "name": "create_room", + "description": ( + "Design a new 3D room in the Nexus from a natural language description. " + "Returns a design specification and LLM prompt for Three.js code generation. " + "The room will be styled according to Timmy's current mental state." + ), + "parameters": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "Unique room identifier (e.g., 'contemplation_chamber')" + }, + "description": { + "type": "string", + "description": "Natural language description of the room" + }, + "style": { + "type": "string", + "description": "Visual style (minimalist_ethereal, crystalline_modern, organic_natural, etc.)" + }, + "dimensions": { + "type": "object", + "description": "Optional room dimensions", + "properties": { + "width": {"type": "number"}, + "height": {"type": "number"}, + "depth": {"type": "number"}, + } + } + }, + "required": ["name", "description", "style"] + } + }, + "create_portal": { + "name": "create_portal", + "description": "Create a portal connection between two rooms", + "parameters": { + "type": "object", + "properties": { + "name": {"type": "string"}, + "from_room": {"type": "string"}, + "to_room": {"type": "string"}, + "style": {"type": "string", "default": "energy_vortex"}, + }, + "required": ["name", "from_room", "to_room"] + } + }, + "generate_scene_from_mood": { + "name": "generate_scene_from_mood", + "description": ( + "Generate a complete 3D scene based on a mood description. " + "Example: 'Timmy is feeling introspective' creates a calm, minimalist space." + ), + "parameters": { + "type": "object", + "properties": { + "mood_description": { + "type": "string", + "description": "Description of desired mood or mental state" + } + }, + "required": ["mood_description"] + } + }, + "set_mental_state": { + "name": "set_mental_state", + "description": "Set Timmy's mental state to influence aesthetic generation", + "parameters": { + "type": "object", + "properties": { + "mood": {"type": "string"}, + "energy_level": {"type": "number"}, + "clarity": {"type": "number"}, + "focus_area": {"type": "string"}, + }, + "required": ["mood"] + } + }, + "get_nexus_summary": { + "name": "get_nexus_summary", + "description": "Get summary of all Nexus room and portal designs", + "parameters": {"type": "object", "properties": {}} + }, +} + + +if __name__ == "__main__": + # Demo usage + print("Nexus Architect AI - Demo") + print("=" * 50) + + # Set mental state + result = set_mental_state("contemplative", energy_level=0.3, clarity=0.8) + print(f"\nMental State: {result['mental_state']}") + + # Create a room + result = create_room( + name="contemplation_chamber", + description="A serene circular room with floating geometric shapes and soft blue light", + style="minimalist_ethereal", + ) + print(f"\nRoom Design: {json.dumps(result['design'], indent=2)}") + + # Generate from mood + result = generate_scene_from_mood("Timmy is feeling introspective and seeking clarity") + print(f"\nMood Scene: {result['inferred_mood']} - {result['aesthetic']['description']}") diff --git a/agent/nexus_deployment.py b/agent/nexus_deployment.py new file mode 100644 index 00000000..88f1f5c7 --- /dev/null +++ b/agent/nexus_deployment.py @@ -0,0 +1,752 @@ +#!/usr/bin/env python3 +""" +Nexus Deployment System + +Real-time deployment system for Nexus Three.js modules. +Provides hot-reload, validation, rollback, and versioning capabilities. + +Features: +- Hot-reload Three.js modules without page refresh +- Syntax validation and Three.js API compliance checking +- Rollback on error +- Versioning for nexus modules +- Module registry and dependency tracking + +Usage: + from agent.nexus_deployment import NexusDeployer + + deployer = NexusDeployer() + + # Deploy with hot-reload + result = deployer.deploy_module(room_code, module_name="zen_garden") + + # Rollback if needed + deployer.rollback_module("zen_garden") + + # Get module status + status = deployer.get_module_status("zen_garden") +""" + +import json +import logging +import re +import os +import hashlib +from typing import Dict, Any, List, Optional, Set +from dataclasses import dataclass, field +from datetime import datetime +from enum import Enum + +# Import validation from existing nexus_architect (avoid circular imports) +import sys +sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) + +def _import_validation(): + """Lazy import to avoid circular dependencies.""" + try: + from tools.nexus_architect import validate_three_js_code, sanitize_three_js_code + return validate_three_js_code, sanitize_three_js_code + except ImportError: + # Fallback: define local validation functions + def validate_three_js_code(code, strict_mode=False): + """Fallback validation.""" + errors = [] + if "eval(" in code: + errors.append("Security violation: eval detected") + if "Function(" in code: + errors.append("Security violation: Function constructor detected") + return type('ValidationResult', (), { + 'is_valid': len(errors) == 0, + 'errors': errors, + 'warnings': [] + })() + + def sanitize_three_js_code(code): + """Fallback sanitization.""" + return code + + return validate_three_js_code, sanitize_three_js_code + +logger = logging.getLogger(__name__) + + +# ============================================================================= +# Deployment States +# ============================================================================= + +class DeploymentStatus(Enum): + """Status of a module deployment.""" + PENDING = "pending" + VALIDATING = "validating" + DEPLOYING = "deploying" + ACTIVE = "active" + FAILED = "failed" + ROLLING_BACK = "rolling_back" + ROLLED_BACK = "rolled_back" + + +# ============================================================================= +# Data Models +# ============================================================================= + +@dataclass +class ModuleVersion: + """Version information for a Nexus module.""" + version_id: str + module_name: str + code_hash: str + timestamp: str + changes: str = "" + author: str = "nexus_architect" + + def to_dict(self) -> Dict[str, Any]: + return { + "version_id": self.version_id, + "module_name": self.module_name, + "code_hash": self.code_hash, + "timestamp": self.timestamp, + "changes": self.changes, + "author": self.author, + } + + +@dataclass +class DeployedModule: + """A deployed Nexus module.""" + name: str + code: str + status: DeploymentStatus + version: str + deployed_at: str + last_updated: str + validation_result: Dict[str, Any] = field(default_factory=dict) + error_log: List[str] = field(default_factory=list) + dependencies: Set[str] = field(default_factory=set) + hot_reload_supported: bool = True + + def to_dict(self) -> Dict[str, Any]: + return { + "name": self.name, + "status": self.status.value, + "version": self.version, + "deployed_at": self.deployed_at, + "last_updated": self.last_updated, + "validation": self.validation_result, + "dependencies": list(self.dependencies), + "hot_reload_supported": self.hot_reload_supported, + "code_preview": self.code[:200] + "..." if len(self.code) > 200 else self.code, + } + + +# ============================================================================= +# Nexus Deployer +# ============================================================================= + +class NexusDeployer: + """ + Deployment system for Nexus Three.js modules. + + Provides: + - Hot-reload deployment + - Validation before deployment + - Automatic rollback on failure + - Version tracking + - Module registry + """ + + def __init__(self, modules_dir: Optional[str] = None): + """ + Initialize the Nexus Deployer. + + Args: + modules_dir: Directory to store deployed modules (optional) + """ + self.modules: Dict[str, DeployedModule] = {} + self.version_history: Dict[str, List[ModuleVersion]] = {} + self.modules_dir = modules_dir or os.path.expanduser("~/.nexus/modules") + + # Ensure modules directory exists + os.makedirs(self.modules_dir, exist_ok=True) + + # Hot-reload configuration + self.hot_reload_enabled = True + self.auto_rollback = True + self.strict_validation = True + + logger.info(f"NexusDeployer initialized. Modules dir: {self.modules_dir}") + + def deploy_module( + self, + module_code: str, + module_name: str, + version: Optional[str] = None, + dependencies: Optional[List[str]] = None, + hot_reload: bool = True, + validate: bool = True + ) -> Dict[str, Any]: + """ + Deploy a Nexus module with hot-reload support. + + Args: + module_code: The Three.js module code + module_name: Unique module identifier + version: Optional version string (auto-generated if not provided) + dependencies: List of dependent module names + hot_reload: Enable hot-reload for this module + validate: Run validation before deployment + + Returns: + Dict with deployment results + """ + timestamp = datetime.now().isoformat() + version = version or self._generate_version(module_name, module_code) + + result = { + "success": True, + "module_name": module_name, + "version": version, + "timestamp": timestamp, + "hot_reload": hot_reload, + "validation": {}, + "deployment": {}, + } + + # Check for existing module (hot-reload scenario) + existing_module = self.modules.get(module_name) + if existing_module and not hot_reload: + return { + "success": False, + "error": f"Module '{module_name}' already exists. Use hot_reload=True to update." + } + + # Validation phase + if validate: + validation = self._validate_module(module_code) + result["validation"] = validation + + if not validation["is_valid"]: + result["success"] = False + result["error"] = "Validation failed" + result["message"] = "Module deployment aborted due to validation errors" + + if self.auto_rollback: + result["rollback_triggered"] = False # Nothing to rollback yet + + return result + + # Create deployment backup for rollback + if existing_module: + self._create_backup(existing_module) + + # Deployment phase + try: + deployed = DeployedModule( + name=module_name, + code=module_code, + status=DeploymentStatus.DEPLOYING, + version=version, + deployed_at=timestamp if not existing_module else existing_module.deployed_at, + last_updated=timestamp, + validation_result=result.get("validation", {}), + dependencies=set(dependencies or []), + hot_reload_supported=hot_reload, + ) + + # Save to file system + self._save_module_file(deployed) + + # Update registry + deployed.status = DeploymentStatus.ACTIVE + self.modules[module_name] = deployed + + # Record version + self._record_version(module_name, version, module_code) + + result["deployment"] = { + "status": "active", + "hot_reload_ready": hot_reload, + "file_path": self._get_module_path(module_name), + } + result["message"] = f"Module '{module_name}' v{version} deployed successfully" + + if existing_module: + result["message"] += " (hot-reload update)" + + logger.info(f"Deployed module: {module_name} v{version}") + + except Exception as e: + result["success"] = False + result["error"] = str(e) + result["deployment"] = {"status": "failed"} + + # Attempt rollback if deployment failed + if self.auto_rollback and existing_module: + rollback_result = self.rollback_module(module_name) + result["rollback_result"] = rollback_result + + logger.error(f"Deployment failed for {module_name}: {e}") + + return result + + def hot_reload_module(self, module_name: str, new_code: str) -> Dict[str, Any]: + """ + Hot-reload an active module with new code. + + Args: + module_name: Name of the module to reload + new_code: New module code + + Returns: + Dict with reload results + """ + if module_name not in self.modules: + return { + "success": False, + "error": f"Module '{module_name}' not found. Deploy it first." + } + + module = self.modules[module_name] + if not module.hot_reload_supported: + return { + "success": False, + "error": f"Module '{module_name}' does not support hot-reload" + } + + # Use deploy_module with hot_reload=True + return self.deploy_module( + module_code=new_code, + module_name=module_name, + hot_reload=True, + validate=True + ) + + def rollback_module(self, module_name: str, to_version: Optional[str] = None) -> Dict[str, Any]: + """ + Rollback a module to a previous version. + + Args: + module_name: Module to rollback + to_version: Specific version to rollback to (latest backup if not specified) + + Returns: + Dict with rollback results + """ + if module_name not in self.modules: + return { + "success": False, + "error": f"Module '{module_name}' not found" + } + + module = self.modules[module_name] + module.status = DeploymentStatus.ROLLING_BACK + + try: + if to_version: + # Restore specific version + version_data = self._get_version(module_name, to_version) + if not version_data: + return { + "success": False, + "error": f"Version '{to_version}' not found for module '{module_name}'" + } + # Would restore from version data + else: + # Restore from backup + backup_code = self._get_backup(module_name) + if backup_code: + module.code = backup_code + module.last_updated = datetime.now().isoformat() + else: + return { + "success": False, + "error": f"No backup available for '{module_name}'" + } + + module.status = DeploymentStatus.ROLLED_BACK + self._save_module_file(module) + + logger.info(f"Rolled back module: {module_name}") + + return { + "success": True, + "module_name": module_name, + "message": f"Module '{module_name}' rolled back successfully", + "status": module.status.value, + } + + except Exception as e: + module.status = DeploymentStatus.FAILED + logger.error(f"Rollback failed for {module_name}: {e}") + return { + "success": False, + "error": str(e) + } + + def validate_module(self, module_code: str) -> Dict[str, Any]: + """ + Validate Three.js module code without deploying. + + Args: + module_code: Code to validate + + Returns: + Dict with validation results + """ + return self._validate_module(module_code) + + def get_module_status(self, module_name: str) -> Optional[Dict[str, Any]]: + """ + Get status of a deployed module. + + Args: + module_name: Module name + + Returns: + Module status dict or None if not found + """ + if module_name in self.modules: + return self.modules[module_name].to_dict() + return None + + def get_all_modules(self) -> Dict[str, Any]: + """ + Get status of all deployed modules. + + Returns: + Dict with all module statuses + """ + return { + "modules": { + name: module.to_dict() + for name, module in self.modules.items() + }, + "total_count": len(self.modules), + "active_count": sum(1 for m in self.modules.values() if m.status == DeploymentStatus.ACTIVE), + } + + def get_version_history(self, module_name: str) -> List[Dict[str, Any]]: + """ + Get version history for a module. + + Args: + module_name: Module name + + Returns: + List of version dicts + """ + history = self.version_history.get(module_name, []) + return [v.to_dict() for v in history] + + def remove_module(self, module_name: str) -> Dict[str, Any]: + """ + Remove a deployed module. + + Args: + module_name: Module to remove + + Returns: + Dict with removal results + """ + if module_name not in self.modules: + return { + "success": False, + "error": f"Module '{module_name}' not found" + } + + try: + # Remove file + module_path = self._get_module_path(module_name) + if os.path.exists(module_path): + os.remove(module_path) + + # Remove from registry + del self.modules[module_name] + + logger.info(f"Removed module: {module_name}") + + return { + "success": True, + "message": f"Module '{module_name}' removed successfully" + } + + except Exception as e: + return { + "success": False, + "error": str(e) + } + + def _validate_module(self, code: str) -> Dict[str, Any]: + """Internal validation method.""" + # Use existing validation from nexus_architect (lazy import) + validate_fn, _ = _import_validation() + validation_result = validate_fn(code, strict_mode=self.strict_validation) + + # Check Three.js API compliance + three_api_issues = self._check_three_js_api_compliance(code) + + return { + "is_valid": validation_result.is_valid and len(three_api_issues) == 0, + "syntax_valid": validation_result.is_valid, + "api_compliant": len(three_api_issues) == 0, + "errors": validation_result.errors + three_api_issues, + "warnings": validation_result.warnings, + "safety_score": max(0, 100 - len(validation_result.errors) * 20 - len(validation_result.warnings) * 5), + } + + def _check_three_js_api_compliance(self, code: str) -> List[str]: + """Check for Three.js API compliance issues.""" + issues = [] + + # Check for required patterns + if "THREE.Group" not in code and "new THREE" not in code: + issues.append("No Three.js objects created") + + # Check for deprecated APIs + deprecated_patterns = [ + (r"THREE\.Face3", "THREE.Face3 is deprecated, use BufferGeometry"), + (r"THREE\.Geometry\(", "THREE.Geometry is deprecated, use BufferGeometry"), + ] + + for pattern, message in deprecated_patterns: + if re.search(pattern, code): + issues.append(f"Deprecated API: {message}") + + return issues + + def _generate_version(self, module_name: str, code: str) -> str: + """Generate version string from code hash.""" + code_hash = hashlib.md5(code.encode()).hexdigest()[:8] + timestamp = datetime.now().strftime("%Y%m%d%H%M") + return f"{timestamp}-{code_hash}" + + def _create_backup(self, module: DeployedModule) -> None: + """Create backup of existing module.""" + backup_path = os.path.join( + self.modules_dir, + f"{module.name}.{module.version}.backup.js" + ) + with open(backup_path, 'w') as f: + f.write(module.code) + + def _get_backup(self, module_name: str) -> Optional[str]: + """Get backup code for module.""" + if module_name not in self.modules: + return None + + module = self.modules[module_name] + backup_path = os.path.join( + self.modules_dir, + f"{module.name}.{module.version}.backup.js" + ) + + if os.path.exists(backup_path): + with open(backup_path, 'r') as f: + return f.read() + return None + + def _save_module_file(self, module: DeployedModule) -> None: + """Save module to file system.""" + module_path = self._get_module_path(module.name) + with open(module_path, 'w') as f: + f.write(f"// Nexus Module: {module.name}\n") + f.write(f"// Version: {module.version}\n") + f.write(f"// Status: {module.status.value}\n") + f.write(f"// Updated: {module.last_updated}\n") + f.write(f"// Hot-Reload: {module.hot_reload_supported}\n") + f.write("\n") + f.write(module.code) + + def _get_module_path(self, module_name: str) -> str: + """Get file path for module.""" + return os.path.join(self.modules_dir, f"{module_name}.nexus.js") + + def _record_version(self, module_name: str, version: str, code: str) -> None: + """Record version in history.""" + if module_name not in self.version_history: + self.version_history[module_name] = [] + + version_info = ModuleVersion( + version_id=version, + module_name=module_name, + code_hash=hashlib.md5(code.encode()).hexdigest()[:16], + timestamp=datetime.now().isoformat(), + ) + + self.version_history[module_name].insert(0, version_info) + + # Keep only last 10 versions + self.version_history[module_name] = self.version_history[module_name][:10] + + def _get_version(self, module_name: str, version: str) -> Optional[ModuleVersion]: + """Get specific version info.""" + history = self.version_history.get(module_name, []) + for v in history: + if v.version_id == version: + return v + return None + + +# ============================================================================= +# Convenience Functions +# ============================================================================= + +_deployer_instance: Optional[NexusDeployer] = None + + +def get_deployer() -> NexusDeployer: + """Get or create the NexusDeployer singleton.""" + global _deployer_instance + if _deployer_instance is None: + _deployer_instance = NexusDeployer() + return _deployer_instance + + +def deploy_nexus_module( + module_code: str, + module_name: str, + test: bool = True, + hot_reload: bool = True +) -> Dict[str, Any]: + """ + Deploy a Nexus module with validation. + + Args: + module_code: Three.js module code + module_name: Unique module identifier + test: Run validation tests before deployment + hot_reload: Enable hot-reload support + + Returns: + Dict with deployment results + """ + deployer = get_deployer() + return deployer.deploy_module( + module_code=module_code, + module_name=module_name, + hot_reload=hot_reload, + validate=test + ) + + +def hot_reload_module(module_name: str, new_code: str) -> Dict[str, Any]: + """ + Hot-reload an existing module. + + Args: + module_name: Module to reload + new_code: New module code + + Returns: + Dict with reload results + """ + deployer = get_deployer() + return deployer.hot_reload_module(module_name, new_code) + + +def validate_nexus_code(code: str) -> Dict[str, Any]: + """ + Validate Three.js code without deploying. + + Args: + code: Three.js code to validate + + Returns: + Dict with validation results + """ + deployer = get_deployer() + return deployer.validate_module(code) + + +def get_deployment_status() -> Dict[str, Any]: + """Get status of all deployed modules.""" + deployer = get_deployer() + return deployer.get_all_modules() + + +# ============================================================================= +# Tool Schemas +# ============================================================================= + +NEXUS_DEPLOYMENT_SCHEMAS = { + "deploy_nexus_module": { + "name": "deploy_nexus_module", + "description": "Deploy a Nexus Three.js module with validation and hot-reload support", + "parameters": { + "type": "object", + "properties": { + "module_code": {"type": "string"}, + "module_name": {"type": "string"}, + "test": {"type": "boolean", "default": True}, + "hot_reload": {"type": "boolean", "default": True}, + }, + "required": ["module_code", "module_name"] + } + }, + "hot_reload_module": { + "name": "hot_reload_module", + "description": "Hot-reload an existing Nexus module with new code", + "parameters": { + "type": "object", + "properties": { + "module_name": {"type": "string"}, + "new_code": {"type": "string"}, + }, + "required": ["module_name", "new_code"] + } + }, + "validate_nexus_code": { + "name": "validate_nexus_code", + "description": "Validate Three.js code for Nexus deployment without deploying", + "parameters": { + "type": "object", + "properties": { + "code": {"type": "string"} + }, + "required": ["code"] + } + }, + "get_deployment_status": { + "name": "get_deployment_status", + "description": "Get status of all deployed Nexus modules", + "parameters": {"type": "object", "properties": {}} + }, +} + + +if __name__ == "__main__": + # Demo + print("Nexus Deployment System - Demo") + print("=" * 50) + + deployer = NexusDeployer() + + # Sample module code + sample_code = """ +(function() { + function createDemoRoom() { + const room = new THREE.Group(); + room.name = 'demo_room'; + + const light = new THREE.AmbientLight(0x404040, 0.5); + room.add(light); + + return room; + } + + window.NexusRooms = window.NexusRooms || {}; + window.NexusRooms.demo_room = createDemoRoom; + + return { createDemoRoom }; +})(); +""" + + # Deploy + result = deployer.deploy_module(sample_code, "demo_room") + print(f"\nDeployment result: {result['message']}") + print(f"Validation: {result['validation'].get('is_valid', False)}") + print(f"Safety score: {result['validation'].get('safety_score', 0)}/100") + + # Get status + status = deployer.get_all_modules() + print(f"\nTotal modules: {status['total_count']}") + print(f"Active: {status['active_count']}") diff --git a/config/nexus-templates/base_room.js b/config/nexus-templates/base_room.js new file mode 100644 index 00000000..e6e743b6 --- /dev/null +++ b/config/nexus-templates/base_room.js @@ -0,0 +1,200 @@ +/** + * Nexus Base Room Template + * + * This is the base template for all Nexus rooms. + * Copy and customize this template for new room types. + * + * Compatible with Three.js r128+ + */ + +(function() { + 'use strict'; + + /** + * Configuration object for the room + */ + const CONFIG = { + name: 'base_room', + dimensions: { + width: 20, + height: 10, + depth: 20 + }, + colors: { + primary: '#1A1A2E', + secondary: '#16213E', + accent: '#D4AF37', // Timmy's gold + light: '#E0F7FA', // Sovereignty crystal + }, + lighting: { + ambientIntensity: 0.3, + accentIntensity: 0.8, + } + }; + + /** + * Create the base room + * @returns {THREE.Group} The room group + */ + function createBaseRoom() { + const room = new THREE.Group(); + room.name = CONFIG.name; + + // Create floor + createFloor(room); + + // Create walls + createWalls(room); + + // Setup lighting + setupLighting(room); + + // Add room features + addFeatures(room); + + return room; + } + + /** + * Create the floor + */ + function createFloor(room) { + const floorGeo = new THREE.PlaneGeometry( + CONFIG.dimensions.width, + CONFIG.dimensions.depth + ); + const floorMat = new THREE.MeshStandardMaterial({ + color: CONFIG.colors.primary, + roughness: 0.8, + metalness: 0.2, + }); + const floor = new THREE.Mesh(floorGeo, floorMat); + floor.rotation.x = -Math.PI / 2; + floor.receiveShadow = true; + floor.name = 'floor'; + room.add(floor); + } + + /** + * Create the walls + */ + function createWalls(room) { + const wallMat = new THREE.MeshStandardMaterial({ + color: CONFIG.colors.secondary, + roughness: 0.9, + metalness: 0.1, + side: THREE.DoubleSide + }); + + const { width, height, depth } = CONFIG.dimensions; + + // Back wall + const backWall = new THREE.Mesh( + new THREE.PlaneGeometry(width, height), + wallMat + ); + backWall.position.set(0, height / 2, -depth / 2); + backWall.receiveShadow = true; + room.add(backWall); + + // Left wall + const leftWall = new THREE.Mesh( + new THREE.PlaneGeometry(depth, height), + wallMat + ); + leftWall.position.set(-width / 2, height / 2, 0); + leftWall.rotation.y = Math.PI / 2; + leftWall.receiveShadow = true; + room.add(leftWall); + + // Right wall + const rightWall = new THREE.Mesh( + new THREE.PlaneGeometry(depth, height), + wallMat + ); + rightWall.position.set(width / 2, height / 2, 0); + rightWall.rotation.y = -Math.PI / 2; + rightWall.receiveShadow = true; + room.add(rightWall); + } + + /** + * Setup lighting + */ + function setupLighting(room) { + // Ambient light + const ambientLight = new THREE.AmbientLight( + CONFIG.colors.primary, + CONFIG.lighting.ambientIntensity + ); + ambientLight.name = 'ambient'; + room.add(ambientLight); + + // Accent light (Timmy's gold) + const accentLight = new THREE.PointLight( + CONFIG.colors.accent, + CONFIG.lighting.accentIntensity, + 50 + ); + accentLight.position.set(0, 8, 0); + accentLight.castShadow = true; + accentLight.name = 'accent'; + room.add(accentLight); + } + + /** + * Add room features + * Override this function in custom rooms + */ + function addFeatures(room) { + // Base room has minimal features + // Custom rooms should override this + + // Example: Add a center piece + const centerGeo = new THREE.SphereGeometry(1, 32, 32); + const centerMat = new THREE.MeshStandardMaterial({ + color: CONFIG.colors.accent, + emissive: CONFIG.colors.accent, + emissiveIntensity: 0.3, + roughness: 0.3, + metalness: 0.8, + }); + const centerPiece = new THREE.Mesh(centerGeo, centerMat); + centerPiece.position.set(0, 2, 0); + centerPiece.castShadow = true; + centerPiece.name = 'centerpiece'; + room.add(centerPiece); + + // Animation hook + centerPiece.userData.animate = function(time) { + this.position.y = 2 + Math.sin(time) * 0.2; + this.rotation.y = time * 0.5; + }; + } + + /** + * Dispose of room resources + */ + function disposeRoom(room) { + room.traverse((child) => { + if (child.isMesh) { + child.geometry.dispose(); + if (Array.isArray(child.material)) { + child.material.forEach(m => m.dispose()); + } else { + child.material.dispose(); + } + } + }); + } + + // Export + if (typeof module !== 'undefined' && module.exports) { + module.exports = { createBaseRoom, disposeRoom, CONFIG }; + } else if (typeof window !== 'undefined') { + window.NexusRooms = window.NexusRooms || {}; + window.NexusRooms.base_room = createBaseRoom; + } + + return { createBaseRoom, disposeRoom, CONFIG }; +})(); diff --git a/config/nexus-templates/lighting_presets.json b/config/nexus-templates/lighting_presets.json new file mode 100644 index 00000000..ba1c8074 --- /dev/null +++ b/config/nexus-templates/lighting_presets.json @@ -0,0 +1,221 @@ +{ + "description": "Nexus Lighting Presets for Three.js", + "version": "1.0.0", + "presets": { + "warm": { + "name": "Warm", + "description": "Warm, inviting lighting with golden tones", + "colors": { + "timmy_gold": "#D4AF37", + "ambient": "#FFE4B5", + "primary": "#FFA07A", + "secondary": "#F4A460" + }, + "lights": { + "ambient": { + "color": "#FFE4B5", + "intensity": 0.4 + }, + "directional": { + "color": "#FFA07A", + "intensity": 0.8, + "position": {"x": 10, "y": 20, "z": 10} + }, + "point_lights": [ + { + "color": "#D4AF37", + "intensity": 0.6, + "distance": 30, + "position": {"x": 0, "y": 8, "z": 0} + } + ] + }, + "fog": { + "enabled": true, + "color": "#FFE4B5", + "density": 0.02 + }, + "atmosphere": "welcoming" + }, + "cool": { + "name": "Cool", + "description": "Cool, serene lighting with blue tones", + "colors": { + "allegro_blue": "#4A90E2", + "ambient": "#E0F7FA", + "primary": "#81D4FA", + "secondary": "#B3E5FC" + }, + "lights": { + "ambient": { + "color": "#E0F7FA", + "intensity": 0.35 + }, + "directional": { + "color": "#81D4FA", + "intensity": 0.7, + "position": {"x": -10, "y": 15, "z": -5} + }, + "point_lights": [ + { + "color": "#4A90E2", + "intensity": 0.5, + "distance": 25, + "position": {"x": 5, "y": 6, "z": 5} + } + ] + }, + "fog": { + "enabled": true, + "color": "#E0F7FA", + "density": 0.015 + }, + "atmosphere": "serene" + }, + "dramatic": { + "name": "Dramatic", + "description": "High contrast lighting with deep shadows", + "colors": { + "shadow": "#1A1A2E", + "highlight": "#D4AF37", + "ambient": "#0F0F1A", + "rim": "#4A90E2" + }, + "lights": { + "ambient": { + "color": "#0F0F1A", + "intensity": 0.2 + }, + "directional": { + "color": "#D4AF37", + "intensity": 1.2, + "position": {"x": 5, "y": 10, "z": 5} + }, + "spot_lights": [ + { + "color": "#4A90E2", + "intensity": 1.0, + "angle": 0.5, + "penumbra": 0.5, + "position": {"x": -5, "y": 10, "z": -5}, + "target": {"x": 0, "y": 0, "z": 0} + } + ] + }, + "fog": { + "enabled": false + }, + "shadows": { + "enabled": true, + "mapSize": 2048 + }, + "atmosphere": "mysterious" + }, + "serene": { + "name": "Serene", + "description": "Soft, diffuse lighting for contemplation", + "colors": { + "ambient": "#F5F5F5", + "primary": "#E8EAF6", + "accent": "#C5CAE9", + "gold": "#D4AF37" + }, + "lights": { + "hemisphere": { + "skyColor": "#E8EAF6", + "groundColor": "#F5F5F5", + "intensity": 0.6 + }, + "directional": { + "color": "#FFFFFF", + "intensity": 0.4, + "position": {"x": 10, "y": 20, "z": 10} + }, + "point_lights": [ + { + "color": "#D4AF37", + "intensity": 0.3, + "distance": 20, + "position": {"x": 0, "y": 5, "z": 0} + } + ] + }, + "fog": { + "enabled": true, + "color": "#F5F5F5", + "density": 0.01 + }, + "atmosphere": "contemplative" + }, + "crystalline": { + "name": "Crystalline", + "description": "Clear, bright lighting for sovereignty theme", + "colors": { + "crystal": "#E0F7FA", + "clear": "#FFFFFF", + "accent": "#4DD0E1", + "gold": "#D4AF37" + }, + "lights": { + "ambient": { + "color": "#E0F7FA", + "intensity": 0.5 + }, + "directional": [ + { + "color": "#FFFFFF", + "intensity": 0.8, + "position": {"x": 10, "y": 20, "z": 10} + }, + { + "color": "#4DD0E1", + "intensity": 0.4, + "position": {"x": -10, "y": 10, "z": -10} + } + ], + "point_lights": [ + { + "color": "#D4AF37", + "intensity": 0.5, + "distance": 25, + "position": {"x": 0, "y": 8, "z": 0} + } + ] + }, + "fog": { + "enabled": true, + "color": "#E0F7FA", + "density": 0.008 + }, + "atmosphere": "sovereign" + }, + "minimal": { + "name": "Minimal", + "description": "Minimal lighting with clean shadows", + "colors": { + "ambient": "#FFFFFF", + "primary": "#F5F5F5" + }, + "lights": { + "ambient": { + "color": "#FFFFFF", + "intensity": 0.3 + }, + "directional": { + "color": "#FFFFFF", + "intensity": 0.7, + "position": {"x": 5, "y": 10, "z": 5} + } + }, + "fog": { + "enabled": false + }, + "shadows": { + "enabled": true, + "soft": true + }, + "atmosphere": "clean" + } + }, + "default_preset": "serene" +} diff --git a/config/nexus-templates/material_presets.json b/config/nexus-templates/material_presets.json new file mode 100644 index 00000000..185789bd --- /dev/null +++ b/config/nexus-templates/material_presets.json @@ -0,0 +1,154 @@ +{ + "description": "Nexus Material Presets for Three.js MeshStandardMaterial", + "version": "1.0.0", + "presets": { + "timmy_gold": { + "name": "Timmy's Gold", + "description": "Warm gold metallic material representing Timmy", + "color": "#D4AF37", + "emissive": "#D4AF37", + "emissiveIntensity": 0.2, + "roughness": 0.3, + "metalness": 0.8, + "tags": ["timmy", "gold", "metallic", "warm"] + }, + "allegro_blue": { + "name": "Allegro Blue", + "description": "Motion blue representing Allegro", + "color": "#4A90E2", + "emissive": "#4A90E2", + "emissiveIntensity": 0.1, + "roughness": 0.2, + "metalness": 0.6, + "tags": ["allegro", "blue", "motion", "cool"] + }, + "sovereignty_crystal": { + "name": "Sovereignty Crystal", + "description": "Crystalline clear material with slight transparency", + "color": "#E0F7FA", + "transparent": true, + "opacity": 0.8, + "roughness": 0.1, + "metalness": 0.1, + "transmission": 0.5, + "tags": ["crystal", "clear", "sovereignty", "transparent"] + }, + "contemplative_stone": { + "name": "Contemplative Stone", + "description": "Smooth stone for contemplative spaces", + "color": "#546E7A", + "roughness": 0.9, + "metalness": 0.0, + "tags": ["stone", "contemplative", "matte", "natural"] + }, + "ethereal_mist": { + "name": "Ethereal Mist", + "description": "Semi-transparent misty material", + "color": "#E1F5FE", + "transparent": true, + "opacity": 0.3, + "roughness": 1.0, + "metalness": 0.0, + "side": "DoubleSide", + "tags": ["mist", "ethereal", "transparent", "soft"] + }, + "warm_wood": { + "name": "Warm Wood", + "description": "Natural wood material for organic warmth", + "color": "#8D6E63", + "roughness": 0.8, + "metalness": 0.0, + "tags": ["wood", "natural", "warm", "organic"] + }, + "polished_marble": { + "name": "Polished Marble", + "description": "Smooth reflective marble surface", + "color": "#F5F5F5", + "roughness": 0.1, + "metalness": 0.1, + "tags": ["marble", "polished", "reflective", "elegant"] + }, + "dark_obsidian": { + "name": "Dark Obsidian", + "description": "Deep black glassy material for dramatic contrast", + "color": "#1A1A2E", + "roughness": 0.1, + "metalness": 0.9, + "tags": ["obsidian", "dark", "dramatic", "glassy"] + }, + "energy_pulse": { + "name": "Energy Pulse", + "description": "Glowing energy material with high emissive", + "color": "#4A90E2", + "emissive": "#4A90E2", + "emissiveIntensity": 1.0, + "roughness": 0.4, + "metalness": 0.5, + "tags": ["energy", "glow", "animated", "pulse"] + }, + "living_leaf": { + "name": "Living Leaf", + "description": "Vibrant green material for nature elements", + "color": "#66BB6A", + "emissive": "#2E7D32", + "emissiveIntensity": 0.1, + "roughness": 0.7, + "metalness": 0.0, + "side": "DoubleSide", + "tags": ["nature", "green", "organic", "leaf"] + }, + "ancient_brass": { + "name": "Ancient Brass", + "description": "Aged brass with patina", + "color": "#B5A642", + "roughness": 0.6, + "metalness": 0.7, + "tags": ["brass", "ancient", "vintage", "metallic"] + }, + "void_black": { + "name": "Void Black", + "description": "Complete absorption material for void spaces", + "color": "#000000", + "roughness": 1.0, + "metalness": 0.0, + "tags": ["void", "black", "absorbing", "minimal"] + }, + "holographic": { + "name": "Holographic", + "description": "Futuristic holographic projection material", + "color": "#00BCD4", + "emissive": "#00BCD4", + "emissiveIntensity": 0.5, + "transparent": true, + "opacity": 0.6, + "roughness": 0.2, + "metalness": 0.8, + "side": "DoubleSide", + "tags": ["holographic", "futuristic", "tech", "glow"] + }, + "sandstone": { + "name": "Sandstone", + "description": "Desert sandstone for warm natural environments", + "color": "#D7CCC8", + "roughness": 0.95, + "metalness": 0.0, + "tags": ["sandstone", "desert", "warm", "natural"] + }, + "ice_crystal": { + "name": "Ice Crystal", + "description": "Clear ice with high transparency", + "color": "#E3F2FD", + "transparent": true, + "opacity": 0.6, + "roughness": 0.1, + "metalness": 0.1, + "transmission": 0.9, + "tags": ["ice", "crystal", "cold", "transparent"] + } + }, + "default_preset": "contemplative_stone", + "helpers": { + "apply_preset": "material = new THREE.MeshStandardMaterial(NexusMaterials.getPreset('timmy_gold'))", + "create_custom": "Use preset as base and override specific properties" + } +} diff --git a/config/nexus-templates/portal_template.js b/config/nexus-templates/portal_template.js new file mode 100644 index 00000000..d578ba9c --- /dev/null +++ b/config/nexus-templates/portal_template.js @@ -0,0 +1,339 @@ +/** + * Nexus Portal Template + * + * Template for creating portals between rooms. + * Supports multiple visual styles and transition effects. + * + * Compatible with Three.js r128+ + */ + +(function() { + 'use strict'; + + /** + * Portal configuration + */ + const PORTAL_CONFIG = { + colors: { + frame: '#D4AF37', // Timmy's gold + energy: '#4A90E2', // Allegro blue + core: '#FFFFFF', + }, + animation: { + rotationSpeed: 0.5, + pulseSpeed: 2.0, + pulseAmplitude: 0.1, + }, + collision: { + radius: 2.0, + height: 4.0, + } + }; + + /** + * Create a portal + * @param {string} fromRoom - Source room name + * @param {string} toRoom - Target room name + * @param {string} style - Portal style (circular, rectangular, stargate) + * @returns {THREE.Group} The portal group + */ + function createPortal(fromRoom, toRoom, style = 'circular') { + const portal = new THREE.Group(); + portal.name = `portal_${fromRoom}_to_${toRoom}`; + portal.userData = { + type: 'portal', + fromRoom: fromRoom, + toRoom: toRoom, + isActive: true, + style: style, + }; + + // Create based on style + switch(style) { + case 'rectangular': + createRectangularPortal(portal); + break; + case 'stargate': + createStargatePortal(portal); + break; + case 'circular': + default: + createCircularPortal(portal); + break; + } + + // Add collision trigger + createTriggerZone(portal); + + // Setup animation + setupAnimation(portal); + + return portal; + } + + /** + * Create circular portal (default) + */ + function createCircularPortal(portal) { + const { frame, energy } = PORTAL_CONFIG.colors; + + // Outer frame + const frameGeo = new THREE.TorusGeometry(2, 0.2, 16, 100); + const frameMat = new THREE.MeshStandardMaterial({ + color: frame, + emissive: frame, + emissiveIntensity: 0.5, + roughness: 0.3, + metalness: 0.9, + }); + const frameMesh = new THREE.Mesh(frameGeo, frameMat); + frameMesh.castShadow = true; + frameMesh.name = 'frame'; + portal.add(frameMesh); + + // Inner energy field + const fieldGeo = new THREE.CircleGeometry(1.8, 64); + const fieldMat = new THREE.MeshBasicMaterial({ + color: energy, + transparent: true, + opacity: 0.4, + side: THREE.DoubleSide, + }); + const field = new THREE.Mesh(fieldGeo, fieldMat); + field.name = 'energy_field'; + portal.add(field); + + // Particle ring + createParticleRing(portal); + } + + /** + * Create rectangular portal + */ + function createRectangularPortal(portal) { + const { frame, energy } = PORTAL_CONFIG.colors; + const width = 3; + const height = 4; + + // Frame segments + const frameMat = new THREE.MeshStandardMaterial({ + color: frame, + emissive: frame, + emissiveIntensity: 0.5, + roughness: 0.3, + metalness: 0.9, + }); + + // Create frame border + const borderGeo = new THREE.BoxGeometry(width + 0.4, height + 0.4, 0.2); + const border = new THREE.Mesh(borderGeo, frameMat); + border.name = 'frame'; + portal.add(border); + + // Inner field + const fieldGeo = new THREE.PlaneGeometry(width, height); + const fieldMat = new THREE.MeshBasicMaterial({ + color: energy, + transparent: true, + opacity: 0.4, + side: THREE.DoubleSide, + }); + const field = new THREE.Mesh(fieldGeo, fieldMat); + field.name = 'energy_field'; + portal.add(field); + } + + /** + * Create stargate-style portal + */ + function createStargatePortal(portal) { + const { frame } = PORTAL_CONFIG.colors; + + // Main ring + const ringGeo = new THREE.TorusGeometry(2, 0.3, 16, 100); + const ringMat = new THREE.MeshStandardMaterial({ + color: frame, + emissive: frame, + emissiveIntensity: 0.4, + roughness: 0.4, + metalness: 0.8, + }); + const ring = new THREE.Mesh(ringGeo, ringMat); + ring.name = 'main_ring'; + portal.add(ring); + + // Chevron decorations + for (let i = 0; i < 9; i++) { + const angle = (i / 9) * Math.PI * 2; + const chevron = createChevron(); + chevron.position.set( + Math.cos(angle) * 2, + Math.sin(angle) * 2, + 0 + ); + chevron.rotation.z = angle + Math.PI / 2; + chevron.name = `chevron_${i}`; + portal.add(chevron); + } + + // Inner vortex + const vortexGeo = new THREE.CircleGeometry(1.7, 32); + const vortexMat = new THREE.MeshBasicMaterial({ + color: PORTAL_CONFIG.colors.energy, + transparent: true, + opacity: 0.5, + }); + const vortex = new THREE.Mesh(vortexGeo, vortexMat); + vortex.name = 'vortex'; + portal.add(vortex); + } + + /** + * Create a chevron for stargate style + */ + function createChevron() { + const shape = new THREE.Shape(); + shape.moveTo(-0.2, 0); + shape.lineTo(0, 0.4); + shape.lineTo(0.2, 0); + shape.lineTo(-0.2, 0); + + const geo = new THREE.ExtrudeGeometry(shape, { + depth: 0.1, + bevelEnabled: false + }); + const mat = new THREE.MeshStandardMaterial({ + color: PORTAL_CONFIG.colors.frame, + emissive: PORTAL_CONFIG.colors.frame, + emissiveIntensity: 0.3, + }); + + return new THREE.Mesh(geo, mat); + } + + /** + * Create particle ring effect + */ + function createParticleRing(portal) { + const particleCount = 50; + const particles = new THREE.BufferGeometry(); + const positions = new Float32Array(particleCount * 3); + + for (let i = 0; i < particleCount; i++) { + const angle = (i / particleCount) * Math.PI * 2; + const radius = 2 + (Math.random() - 0.5) * 0.4; + positions[i * 3] = Math.cos(angle) * radius; + positions[i * 3 + 1] = Math.sin(angle) * radius; + positions[i * 3 + 2] = (Math.random() - 0.5) * 0.5; + } + + particles.setAttribute('position', new THREE.BufferAttribute(positions, 3)); + + const particleMat = new THREE.PointsMaterial({ + color: PORTAL_CONFIG.colors.energy, + size: 0.05, + transparent: true, + opacity: 0.8, + }); + + const particleSystem = new THREE.Points(particles, particleMat); + particleSystem.name = 'particles'; + portal.add(particleSystem); + } + + /** + * Create trigger zone for teleportation + */ + function createTriggerZone(portal) { + const triggerGeo = new THREE.CylinderGeometry( + PORTAL_CONFIG.collision.radius, + PORTAL_CONFIG.collision.radius, + PORTAL_CONFIG.collision.height, + 32 + ); + const triggerMat = new THREE.MeshBasicMaterial({ + color: 0x00ff00, + transparent: true, + opacity: 0.0, // Invisible + wireframe: true, + }); + const trigger = new THREE.Mesh(triggerGeo, triggerMat); + trigger.position.y = PORTAL_CONFIG.collision.height / 2; + trigger.name = 'trigger_zone'; + trigger.userData.isTrigger = true; + portal.add(trigger); + } + + /** + * Setup portal animation + */ + function setupAnimation(portal) { + const { rotationSpeed, pulseSpeed, pulseAmplitude } = PORTAL_CONFIG.animation; + + portal.userData.animate = function(time) { + // Rotate energy field + const energyField = this.getObjectByName('energy_field') || + this.getObjectByName('vortex'); + if (energyField) { + energyField.rotation.z = time * rotationSpeed; + } + + // Pulse effect + const pulse = 1 + Math.sin(time * pulseSpeed) * pulseAmplitude; + const frame = this.getObjectByName('frame') || + this.getObjectByName('main_ring'); + if (frame) { + frame.scale.set(pulse, pulse, 1); + } + + // Animate particles + const particles = this.getObjectByName('particles'); + if (particles) { + particles.rotation.z = -time * rotationSpeed * 0.5; + } + }; + } + + /** + * Check if a point is inside the portal trigger zone + */ + function checkTrigger(portal, point) { + const trigger = portal.getObjectByName('trigger_zone'); + if (!trigger) return false; + + // Simple distance check + const dx = point.x - portal.position.x; + const dz = point.z - portal.position.z; + const distance = Math.sqrt(dx * dx + dz * dz); + + return distance < PORTAL_CONFIG.collision.radius; + } + + /** + * Activate/deactivate portal + */ + function setActive(portal, active) { + portal.userData.isActive = active; + + const energyField = portal.getObjectByName('energy_field') || + portal.getObjectByName('vortex'); + if (energyField) { + energyField.visible = active; + } + } + + // Export + if (typeof module !== 'undefined' && module.exports) { + module.exports = { + createPortal, + checkTrigger, + setActive, + PORTAL_CONFIG + }; + } else if (typeof window !== 'undefined') { + window.NexusPortals = window.NexusPortals || {}; + window.NexusPortals.create = createPortal; + } + + return { createPortal, checkTrigger, setActive, PORTAL_CONFIG }; +})(); diff --git a/tests/test_nexus_architect.py b/tests/test_nexus_architect.py new file mode 100644 index 00000000..92fc3bff --- /dev/null +++ b/tests/test_nexus_architect.py @@ -0,0 +1,666 @@ +#!/usr/bin/env python3 +""" +Tests for Nexus Architect System + +Test coverage for: +- agent/nexus_architect.py (AI design generation) +- tools/nexus_build_tool.py (Build tool integration) +- agent/nexus_deployment.py (Deployment system) +- config/nexus-templates/ (Template library) +""" + +import sys +import os +import json +import unittest +from unittest.mock import patch, MagicMock + +# Add parent directory to path +sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) + +from agent.nexus_architect import ( + NexusArchitectAI, + PromptEngineer, + MentalState, + RoomDesign, + MoodPresets, + NexusColors, + create_room, + create_portal, + generate_scene_from_mood, + set_mental_state, + get_nexus_summary, +) + +from tools.nexus_build_tool import ( + create_room as build_create_room, + create_portal as build_create_portal, + add_lighting, + add_geometry, + deploy_nexus_module, + _generate_room_template, + _generate_lighting_code, +) + +from agent.nexus_deployment import ( + NexusDeployer, + DeploymentStatus, + deploy_nexus_module as deploy_module, + validate_nexus_code, +) + + +# ============================================================================= +# Test Cases +# ============================================================================= + +class TestMentalState(unittest.TestCase): + """Test mental state handling.""" + + def test_default_mental_state(self): + """Test default mental state creation.""" + state = MentalState() + self.assertEqual(state.mood, "contemplative") + self.assertEqual(state.energy_level, 0.5) + self.assertEqual(state.clarity, 0.7) + + def test_mental_state_to_dict(self): + """Test mental state serialization.""" + state = MentalState(mood="energetic", energy_level=0.8, clarity=0.9) + d = state.to_dict() + self.assertEqual(d["mood"], "energetic") + self.assertEqual(d["energy_level"], 0.8) + self.assertEqual(d["clarity"], 0.9) + + +class TestPromptEngineer(unittest.TestCase): + """Test prompt engineering functionality.""" + + def test_infer_mood_from_description(self): + """Test mood inference from description.""" + engineer = PromptEngineer() + + # Test contemplative + mood = engineer._infer_mood("serene and peaceful space for meditation") + self.assertEqual(mood, "contemplative") + + # Test energetic + mood = engineer._infer_mood("dynamic and vibrant full of motion") + self.assertEqual(mood, "energetic") + + # Test mysterious + mood = engineer._infer_mood("dark mysterious shadow realm") + self.assertEqual(mood, "mysterious") + + def test_infer_mood_with_mental_state(self): + """Test mood inference with mental state override.""" + engineer = PromptEngineer() + state = MentalState(mood="welcoming") + + mood = engineer._infer_mood("any description", state) + self.assertEqual(mood, "welcoming") + + def test_room_prompt_contains_required_elements(self): + """Test that room prompts contain required elements.""" + engineer = PromptEngineer() + + prompt = engineer.engineer_room_prompt( + name="test_room", + description="A test room", + style="minimalist" + ) + + # Check for required elements + self.assertIn("test_room", prompt) + self.assertIn("Three.js", prompt) + self.assertIn("createTestRoom", prompt) + self.assertIn("SAFETY", prompt) + self.assertIn("NO eval", prompt) + + +class TestNexusArchitectAI(unittest.TestCase): + """Test Nexus Architect AI functionality.""" + + def setUp(self): + """Create fresh architect instance for each test.""" + self.architect = NexusArchitectAI() + + def test_design_room_success(self): + """Test successful room design.""" + result = self.architect.design_room( + name="zen_garden", + description="Peaceful garden with floating stones", + style="minimalist_ethereal" + ) + + self.assertTrue(result["success"]) + self.assertEqual(result["room_name"], "zen_garden") + self.assertIn("design", result) + self.assertIn("llm_prompt", result) + self.assertIn("zen_garden", self.architect.room_designs) + + def test_design_room_stores_design(self): + """Test that room design is stored.""" + self.architect.design_room( + name="crystal_cave", + description="Cave with glowing crystals", + style="crystalline" + ) + + design = self.architect.room_designs["crystal_cave"] + self.assertEqual(design.name, "crystal_cave") + self.assertEqual(design.style, "crystalline") + + def test_create_portal_without_rooms_fails(self): + """Test that portal creation fails without existing rooms.""" + result = self.architect.create_portal( + name="test_portal", + from_room="room_a", + to_room="room_b" + ) + + self.assertFalse(result["success"]) + self.assertIn("error", result) + + def test_create_portal_with_rooms_succeeds(self): + """Test successful portal creation.""" + # Create rooms first + self.architect.design_room("room_a", "Room A", "style_a") + self.architect.design_room("room_b", "Room B", "style_b") + + # Create portal + result = self.architect.create_portal( + name="portal_ab", + from_room="room_a", + to_room="room_b", + style="energy_vortex" + ) + + self.assertTrue(result["success"]) + self.assertEqual(result["portal_name"], "portal_ab") + self.assertIn("portal_ab", self.architect.portal_designs) + + def test_generate_scene_from_mood(self): + """Test mood-based scene generation.""" + result = self.architect.generate_scene_from_mood( + "Timmy is feeling introspective and seeking clarity" + ) + + self.assertTrue(result["success"]) + self.assertIn("inferred_mood", result) + self.assertIn("llm_prompt", result) + + def test_mental_state_influences_colors(self): + """Test that mental state influences color palette.""" + # Set high clarity mental state + self.architect.set_mental_state( + MentalState(mood="contemplative", clarity=0.9, focus_area="creative") + ) + + # Design room + result = self.architect.design_room( + name="test_room", + description="A test space", + style="minimalist" + ) + + design = result["design"] + colors = design["color_palette"] + + # Should have Timmy's gold (high clarity) and Allegro blue (creative focus) + self.assertIn(NexusColors.TIMMY_GOLD, colors) + self.assertIn(NexusColors.ALLEGRO_BLUE, colors) + + def test_get_design_summary(self): + """Test design summary generation.""" + # Create some designs + self.architect.design_room("room1", "Room 1", "style1") + self.architect.design_room("room2", "Room 2", "style2") + + summary = self.architect.get_design_summary() + + self.assertEqual(summary["total_rooms"], 2) + self.assertEqual(len(summary["rooms"]), 2) + + +class TestNexusColors(unittest.TestCase): + """Test Nexus color constants.""" + + def test_timmy_gold(self): + """Test Timmy's gold color.""" + self.assertEqual(NexusColors.TIMMY_GOLD, "#D4AF37") + + def test_allegro_blue(self): + """Test Allegro blue color.""" + self.assertEqual(NexusColors.ALLEGRO_BLUE, "#4A90E2") + + +class TestMoodPresets(unittest.TestCase): + """Test mood preset definitions.""" + + def test_contemplative_preset(self): + """Test contemplative mood preset.""" + preset = MoodPresets.CONTEMPLATIVE + self.assertIn("lighting", preset) + self.assertIn("colors", preset) + self.assertEqual(preset["atmosphere"], "calm") + + def test_all_presets_have_required_keys(self): + """Test that all presets have required keys.""" + presets = [ + MoodPresets.CONTEMPLATIVE, + MoodPresets.ENERGETIC, + MoodPresets.MYSTERIOUS, + MoodPresets.WELCOMING, + MoodPresets.SOVEREIGN, + ] + required_keys = ["lighting", "colors", "geometry", "atmosphere", "description"] + + for preset in presets: + for key in required_keys: + self.assertIn(key, preset) + + +class TestBuildTool(unittest.TestCase): + """Test Nexus Build Tool functionality.""" + + def test_create_room_returns_expected_structure(self): + """Test that create_room returns expected structure.""" + result = build_create_room( + name="test_room", + description="A test room", + style="minimalist" + ) + + self.assertTrue(result["success"]) + self.assertIn("room_name", result) + self.assertIn("design", result) + self.assertIn("prompt", result) + self.assertIn("template_code", result) + self.assertIn("build_metadata", result) + + def test_create_portal_returns_expected_structure(self): + """Test that create_portal returns expected structure.""" + # First create rooms + build_create_room("room_a", "Room A", "style_a") + build_create_room("room_b", "Room B", "style_b") + + result = build_create_portal( + from_room="room_a", + to_room="room_b" + ) + + self.assertTrue(result["success"]) + self.assertIn("portal_name", result) + self.assertIn("design", result) + self.assertIn("template_code", result) + + def test_add_lighting_valid_type(self): + """Test adding valid lighting type.""" + result = add_lighting( + room="test_room", + light_type="point", + color="#ffffff", + intensity=1.0 + ) + + self.assertTrue(result["success"]) + self.assertIn("code", result) + self.assertIn("THREE.PointLight", result["code"]) + + def test_add_lighting_invalid_type(self): + """Test adding invalid lighting type.""" + result = add_lighting( + room="test_room", + light_type="invalid_type" + ) + + self.assertFalse(result["success"]) + self.assertIn("error", result) + + def test_add_geometry_valid_shape(self): + """Test adding valid geometry shape.""" + result = add_geometry( + room="test_room", + shape="sphere", + position={"x": 0, "y": 1, "z": 0} + ) + + self.assertTrue(result["success"]) + self.assertIn("code", result) + self.assertIn("SphereGeometry", result["code"]) + + def test_add_geometry_invalid_shape(self): + """Test adding invalid geometry shape.""" + result = add_geometry( + room="test_room", + shape="invalid_shape", + position={"x": 0, "y": 0, "z": 0} + ) + + self.assertFalse(result["success"]) + self.assertIn("error", result) + + def test_generate_scene_from_mood(self): + """Test mood-based scene generation.""" + result = build_create_room( + name="mood_room", + description="A room based on mood", + style="ethereal" + ) + + self.assertTrue(result["success"]) + self.assertIn("design", result) + + +class TestTemplateGenerators(unittest.TestCase): + """Test template code generators.""" + + def test_room_template_generation(self): + """Test room template generation.""" + design = { + "name": "test_room", + "style": "minimalist", + "mood_preset": "contemplative", + "dimensions": {"width": 10, "height": 5, "depth": 10}, + "color_palette": ["#1A1A2E", "#16213E"], + "features": ["ambient"] + } + + code = _generate_room_template(design) + + self.assertIn("THREE.Group", code) + self.assertIn("test_room", code) + self.assertIn("createTestRoom", code) + self.assertIn("floor", code) + + def test_lighting_code_generation(self): + """Test lighting code generation.""" + config = { + "room": "test_room", + "type": "point", + "color": "#ffffff", + "intensity": 1.0, + "position": {"x": 0, "y": 5, "z": 0}, + "cast_shadow": True + } + + code = _generate_lighting_code(config) + + self.assertIn("THREE.PointLight", code) + self.assertIn("0, 5, 0", code) + + def test_ambient_lighting_code(self): + """Test ambient lighting code generation.""" + config = { + "room": "test_room", + "type": "ambient", + "color": "#404040", + "intensity": 0.5, + "position": {"x": 0, "y": 0, "z": 0}, + "cast_shadow": False + } + + code = _generate_lighting_code(config) + + self.assertIn("THREE.AmbientLight", code) + + +class TestNexusDeployment(unittest.TestCase): + """Test Nexus Deployment system.""" + + def setUp(self): + """Create fresh deployer for each test.""" + self.deployer = NexusDeployer(modules_dir="/tmp/test_nexus_modules") + + def tearDown(self): + """Clean up test modules.""" + import shutil + if os.path.exists("/tmp/test_nexus_modules"): + shutil.rmtree("/tmp/test_nexus_modules", ignore_errors=True) + + def test_deploy_valid_module(self): + """Test deploying a valid module.""" + code = """ +(function() { + function createTestRoom() { + const room = new THREE.Group(); + const light = new THREE.AmbientLight(0x404040, 0.5); + room.add(light); + return room; + } + return { createTestRoom }; +})(); +""" + result = self.deployer.deploy_module(code, "test_module") + + self.assertTrue(result["success"]) + self.assertEqual(result["module_name"], "test_module") + self.assertIn("test_module", self.deployer.modules) + + def test_deploy_with_validation_errors(self): + """Test deployment with validation errors.""" + code = """ +(function() { + eval("bad code"); // Security violation + return {}; +})(); +""" + result = self.deployer.deploy_module(code, "bad_module") + + self.assertFalse(result["success"]) + self.assertIn("validation", result) + self.assertFalse(result["validation"]["is_valid"]) + + def test_hot_reload_module(self): + """Test hot-reloading a module.""" + # Deploy initial version + code1 = "(function() { return { version: 1 }; })();" + self.deployer.deploy_module(code1, "reloadable_module") + + # Hot-reload with new code + code2 = "(function() { return { version: 2 }; })();" + result = self.deployer.hot_reload_module("reloadable_module", code2) + + self.assertTrue(result["success"]) + + def test_get_module_status(self): + """Test getting module status.""" + code = "(function() { return {}; })();" + self.deployer.deploy_module(code, "status_module") + + status = self.deployer.get_module_status("status_module") + + self.assertIsNotNone(status) + self.assertEqual(status["name"], "status_module") + self.assertEqual(status["status"], "active") + + def test_validate_module(self): + """Test module validation.""" + code = """ +(function() { + const scene = new THREE.Scene(); + const light = new THREE.AmbientLight(0xffffff, 0.5); + scene.add(light); + return scene; +})(); +""" + result = self.deployer.validate_module(code) + + self.assertIn("is_valid", result) + self.assertIn("syntax_valid", result) + self.assertIn("safety_score", result) + + +class TestTemplateFiles(unittest.TestCase): + """Test that template files are valid.""" + + def test_lighting_presets_json(self): + """Test lighting presets JSON is valid.""" + presets_path = os.path.join( + os.path.dirname(__file__), + "..", + "config", + "nexus-templates", + "lighting_presets.json" + ) + + if os.path.exists(presets_path): + with open(presets_path) as f: + presets = json.load(f) + + self.assertIn("presets", presets) + self.assertIn("warm", presets["presets"]) + self.assertIn("cool", presets["presets"]) + self.assertIn("dramatic", presets["presets"]) + + def test_material_presets_json(self): + """Test material presets JSON is valid.""" + presets_path = os.path.join( + os.path.dirname(__file__), + "..", + "config", + "nexus-templates", + "material_presets.json" + ) + + if os.path.exists(presets_path): + with open(presets_path) as f: + presets = json.load(f) + + self.assertIn("presets", presets) + self.assertIn("timmy_gold", presets["presets"]) + self.assertIn("allegro_blue", presets["presets"]) + self.assertIn("sovereignty_crystal", presets["presets"]) + + def test_base_room_template(self): + """Test base room template exists and is valid JS.""" + template_path = os.path.join( + os.path.dirname(__file__), + "..", + "config", + "nexus-templates", + "base_room.js" + ) + + if os.path.exists(template_path): + with open(template_path) as f: + content = f.read() + + self.assertIn("THREE.Group", content) + self.assertIn("createBaseRoom", content) + self.assertIn("CONFIG", content) + + def test_portal_template(self): + """Test portal template exists and is valid JS.""" + template_path = os.path.join( + os.path.dirname(__file__), + "..", + "config", + "nexus-templates", + "portal_template.js" + ) + + if os.path.exists(template_path): + with open(template_path) as f: + content = f.read() + + self.assertIn("createPortal", content) + self.assertIn("PORTAL_CONFIG", content) + self.assertIn("circular", content) + + +class TestIntegration(unittest.TestCase): + """Integration tests for the full Nexus system.""" + + def test_full_room_creation_workflow(self): + """Test complete room creation workflow.""" + # Step 1: Design room with AI architect + result = create_room( + name="integration_test_room", + description="A serene space with floating crystals", + style="crystalline_ethereal" + ) + + self.assertTrue(result["success"]) + + # Step 2: Use build tool to add lighting + lighting_result = add_lighting( + room="integration_test_room", + light_type="point", + color=NexusColors.TIMMY_GOLD, + intensity=0.8 + ) + + self.assertTrue(lighting_result["success"]) + + # Step 3: Add geometry + geometry_result = add_geometry( + room="integration_test_room", + shape="sphere", + position={"x": 0, "y": 3, "z": 0}, + material={"color": NexusColors.ALLEGRO_BLUE} + ) + + self.assertTrue(geometry_result["success"]) + + # Step 4: Generate template code + template = _generate_room_template(result["design"]) + self.assertIn("THREE.Group", template) + + # Step 5: Validate code + validation = validate_nexus_code(template) + self.assertIn("is_valid", validation) + + def test_mood_based_generation(self): + """Test mood-based scene generation.""" + # Set mental state + set_mental_state( + mood="contemplative", + energy_level=0.3, + clarity=0.8 + ) + + # Generate from mood + result = generate_scene_from_mood( + "Timmy is feeling introspective and seeking clarity" + ) + + self.assertTrue(result["success"]) + self.assertEqual(result["inferred_mood"], "contemplative") + + def test_portal_creation_between_rooms(self): + """Test portal creation between two rooms.""" + # Create two rooms + create_room("room_alpha", "First room", "modern") + create_room("room_beta", "Second room", "organic") + + # Create portal + result = create_portal( + name="portal_alpha_beta", + from_room="room_alpha", + to_room="room_beta", + style="energy_vortex" + ) + + self.assertTrue(result["success"]) + self.assertEqual(result["design"]["from_room"], "room_alpha") + self.assertEqual(result["design"]["to_room"], "room_beta") + + +# ============================================================================= +# Main Entry Point +# ============================================================================= + +def run_tests(): + """Run all tests and return results.""" + loader = unittest.TestLoader() + suite = loader.loadTestsFromModule(sys.modules[__name__]) + runner = unittest.TextTestRunner(verbosity=2) + result = runner.run(suite) + return result.wasSuccessful() + + +if __name__ == "__main__": + success = run_tests() + sys.exit(0 if success else 1) diff --git a/tools/nexus_build_tool.py b/tools/nexus_build_tool.py new file mode 100644 index 00000000..451dae13 --- /dev/null +++ b/tools/nexus_build_tool.py @@ -0,0 +1,721 @@ +#!/usr/bin/env python3 +""" +Nexus Build Tool + +Build tool integration for the Three.js Nexus. +Provides high-level functions for creating rooms, portals, lighting, +and geometry with automatic code generation and validation. + +Functions: +- create_room(name, description, style) - Generate room module +- create_portal(from_room, to_room, style) - Generate portal connection +- add_lighting(room, type, color, intensity) - Add lighting +- add_geometry(room, shape, position, material) - Add 3D objects +- generate_scene_from_mood(mood_description) - Mood-based generation +- deploy_nexus_module(module_code, test=True) - Deploy and test + +Usage: + from tools.nexus_build_tool import create_room, deploy_nexus_module + + # Create room + room = create_room( + name="zen_garden", + description="Peaceful garden with floating stones", + style="minimalist_ethereal" + ) + + # Deploy + result = deploy_nexus_module(room['code'], test=True) +""" + +import json +import logging +import re +import os +import sys +from typing import Dict, Any, List, Optional, Union +from dataclasses import dataclass, field +from datetime import datetime + +# Import from agent module (with fallback) +sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) + +def _import_agent_modules(): + """Lazy import agent modules to avoid circular dependencies.""" + try: + from agent.nexus_architect import ( + get_architect as get_ai_architect, + create_room as ai_create_room, + create_portal as ai_create_portal, + generate_scene_from_mood as ai_generate_scene, + set_mental_state, + NexusColors, + MoodPresets, + ) + return ( + get_ai_architect, ai_create_room, ai_create_portal, + ai_generate_scene, set_mental_state, NexusColors, MoodPresets + ) + except ImportError: + # Fallback: define minimal constants + class FallbackColors: + TIMMY_GOLD = "#D4AF37" + ALLEGRO_BLUE = "#4A90E2" + SOVEREIGNTY_CRYSTAL = "#E0F7FA" + SERVICE_WARMTH = "#FFE4B5" + + class FallbackMoodPresets: + CONTEMPLATIVE = {"lighting": "soft", "colors": ["#1A1A2E"]} + + def fallback_fn(*args, **kwargs): + return {"success": False, "error": "Agent module not available"} + + return ( + fallback_fn, fallback_fn, fallback_fn, + fallback_fn, fallback_fn, FallbackColors, FallbackMoodPresets + ) + +logger = logging.getLogger(__name__) + + +# ============================================================================= +# Build Tool Functions +# ============================================================================= + +def create_room( + name: str, + description: str, + style: str, + dimensions: Optional[Dict[str, float]] = None +) -> Dict[str, Any]: + """ + Generate a room module from natural language description. + + Args: + name: Room identifier (e.g., "contemplation_chamber") + description: Natural language description of the room + style: Visual style (minimalist_ethereal, crystalline_modern, etc.) + dimensions: Optional dict with width, height, depth + + Returns: + Dict containing: + - success: bool + - room_name: str + - code: Generated Three.js code (when implemented with LLM) + - prompt: LLM prompt for code generation + - config: Room configuration + - message: Status message + """ + # Use the AI architect to design the room + _, ai_create_room, _, _, _, _, _ = _import_agent_modules() + result = ai_create_room(name, description, style, dimensions) + + if not result.get("success"): + return result + + # Add build-specific metadata + result["build_metadata"] = { + "tool": "nexus_build_tool", + "function": "create_room", + "timestamp": datetime.now().isoformat(), + "version": "1.0.0", + } + + # Generate template code (in production, this would come from LLM) + result["template_code"] = _generate_room_template(result["design"]) + + return result + + +def create_portal( + from_room: str, + to_room: str, + name: Optional[str] = None, + style: str = "energy_vortex" +) -> Dict[str, Any]: + """ + Generate a portal connection between rooms. + + Args: + from_room: Source room identifier + to_room: Target room identifier + name: Optional portal name (auto-generated if not provided) + style: Portal visual style (energy_vortex, circular_gate, etc.) + + Returns: + Dict containing portal design and generation prompt + """ + if name is None: + name = f"portal_{from_room}_to_{to_room}" + + _, _, ai_create_portal, _, _, _, _ = _import_agent_modules() + result = ai_create_portal(name, from_room, to_room, style) + + if not result.get("success"): + return result + + # Add build metadata + result["build_metadata"] = { + "tool": "nexus_build_tool", + "function": "create_portal", + "timestamp": datetime.now().isoformat(), + "version": "1.0.0", + } + + # Generate template code + result["template_code"] = _generate_portal_template(result["design"]) + + return result + + +def add_lighting( + room: str, + light_type: str, + color: str = "#ffffff", + intensity: float = 1.0, + position: Optional[Dict[str, float]] = None, + cast_shadow: bool = True +) -> Dict[str, Any]: + """ + Add lighting to a room. + + Args: + room: Target room name + light_type: Type of light (ambient, directional, point, spot, hemisphere) + color: Light color (hex string) + intensity: Light intensity (0.0 to 2.0) + position: Optional position dict {x, y, z} + cast_shadow: Whether to cast shadows + + Returns: + Dict with lighting configuration and code + """ + valid_types = ["ambient", "directional", "point", "spot", "hemisphere"] + + if light_type.lower() not in valid_types: + return { + "success": False, + "error": f"Invalid light type '{light_type}'. Valid: {valid_types}" + } + + light_config = { + "room": room, + "type": light_type.lower(), + "color": color, + "intensity": intensity, + "position": position or {"x": 0, "y": 5, "z": 0}, + "cast_shadow": cast_shadow, + } + + # Generate lighting code + code = _generate_lighting_code(light_config) + + return { + "success": True, + "room": room, + "light_config": light_config, + "code": code, + "message": f"Added {light_type} light to '{room}'", + } + + +def add_geometry( + room: str, + shape: str, + position: Dict[str, float], + material: Optional[Dict[str, Any]] = None, + scale: Optional[Dict[str, float]] = None, + rotation: Optional[Dict[str, float]] = None, + name: Optional[str] = None +) -> Dict[str, Any]: + """ + Add 3D geometry to a room. + + Args: + room: Target room name + shape: Geometry type (box, sphere, cylinder, cone, torus, plane) + position: Position dict {x, y, z} + material: Material dict with color, roughness, metalness, etc. + scale: Optional scale dict {x, y, z} + rotation: Optional rotation dict {x, y, z} (in radians) + name: Optional object name + + Returns: + Dict with geometry configuration and code + """ + valid_shapes = ["box", "sphere", "cylinder", "cone", "torus", "plane", "ring"] + + if shape.lower() not in valid_shapes: + return { + "success": False, + "error": f"Invalid shape '{shape}'. Valid: {valid_shapes}" + } + + geo_config = { + "room": room, + "shape": shape.lower(), + "position": position, + "material": material or {"color": "#888888", "roughness": 0.5, "metalness": 0.0}, + "scale": scale or {"x": 1, "y": 1, "z": 1}, + "rotation": rotation or {"x": 0, "y": 0, "z": 0}, + "name": name or f"{shape}_{room}_obj", + } + + # Generate geometry code + code = _generate_geometry_code(geo_config) + + return { + "success": True, + "room": room, + "geometry_config": geo_config, + "code": code, + "message": f"Added {shape} to '{room}'", + } + + +def generate_scene_from_mood(mood_description: str) -> Dict[str, Any]: + """ + Generate a complete scene based on mood description. + + Args: + mood_description: Description of desired mood/atmosphere + + Example: + "Timmy is feeling introspective and seeking clarity" + → Generates calm, minimalist space with clear sightlines + + Returns: + Dict with scene design and generation prompt + """ + _, _, _, ai_generate_scene, _, _, _ = _import_agent_modules() + result = ai_generate_scene(mood_description) + + if not result.get("success"): + return result + + # Add build metadata + result["build_metadata"] = { + "tool": "nexus_build_tool", + "function": "generate_scene_from_mood", + "timestamp": datetime.now().isoformat(), + "version": "1.0.0", + } + + return result + + +def deploy_nexus_module( + module_code: str, + test: bool = True, + module_name: Optional[str] = None +) -> Dict[str, Any]: + """ + Deploy a Nexus module with optional testing. + + Args: + module_code: The Three.js module code to deploy + test: Whether to run validation tests before deployment + module_name: Optional name for the module + + Returns: + Dict with deployment results + """ + from tools.nexus_architect import validate_three_js_code + + results = { + "success": True, + "module_name": module_name or "unnamed_module", + "timestamp": datetime.now().isoformat(), + "validation": {}, + "deployment": {}, + } + + # Validation phase + if test: + validation_result = validate_three_js_code(module_code, strict_mode=True) + results["validation"] = { + "is_valid": validation_result.is_valid, + "errors": validation_result.errors, + "warnings": validation_result.warnings, + "safety_score": max(0, 100 - len(validation_result.errors) * 20 - len(validation_result.warnings) * 5), + } + + if not validation_result.is_valid: + results["success"] = False + results["message"] = "Deployment failed: Code validation errors" + return results + + # Deployment phase (simulated - would integrate with actual deployment system) + results["deployment"] = { + "status": "deployed", + "hot_reload_ready": True, + "version": "1.0.0", + "rollback_available": True, + } + + results["message"] = f"Module '{results['module_name']}' deployed successfully" + + return results + + +# ============================================================================= +# Template Code Generators +# ============================================================================= + +def _generate_room_template(design: Dict[str, Any]) -> str: + """Generate a Three.js room template.""" + name = design["name"] + name_camel = ''.join(word.title() for word in name.split('_')) + colors = design.get("color_palette", ["#1A1A2E", "#16213E"]) + + template = f'''// Nexus Room: {name} +// Style: {design['style']} +// Mood: {design['mood_preset']} + +(function() {{ + 'use strict'; + + function create{name_camel}() {{ + const room = new THREE.Group(); + room.name = '{name}'; + + // Room dimensions + const width = {design['dimensions']['width']}; + const height = {design['dimensions']['height']}; + const depth = {design['dimensions']['depth']}; + + // Floor + const floorGeo = new THREE.PlaneGeometry(width, depth); + const floorMat = new THREE.MeshStandardMaterial({{ + color: '{colors[0]}', + roughness: 0.8, + metalness: 0.2 + }}); + const floor = new THREE.Mesh(floorGeo, floorMat); + floor.rotation.x = -Math.PI / 2; + floor.receiveShadow = true; + room.add(floor); + + // Ambient lighting + const ambientLight = new THREE.AmbientLight('{colors[0]}', 0.3); + room.add(ambientLight); + + // Feature: {design['features'][0] if design['features'] else 'ambient glow'} + // TODO: Add feature implementations based on design.features + + // Return room group + return room; + }} + + // Export + if (typeof module !== 'undefined' && module.exports) {{ + module.exports = {{ create{name_camel} }}; + }} else if (typeof window !== 'undefined') {{ + window.NexusRooms = window.NexusRooms || {{}}; + window.NexusRooms.{name} = create{name_camel}; + }} + + return {{ create{name_camel} }}; +}})();''' + + return template + + +def _generate_portal_template(design: Dict[str, Any]) -> str: + """Generate a Three.js portal template.""" + _, _, _, _, _, NexusColors, _ = _import_agent_modules() + name = design["name"] + name_camel = ''.join(word.title() for word in name.split('_')) + from_room = design["from_room"] + to_room = design["to_room"] + + template = f'''// Nexus Portal: {name} +// Connection: {from_room} → {to_room} +// Style: {design['style']} + +(function() {{ + 'use strict'; + + function create{name_camel}() {{ + const portal = new THREE.Group(); + portal.name = '{name}'; + portal.userData = {{ + type: 'portal', + fromRoom: '{from_room}', + toRoom: '{to_room}', + isActive: true + }}; + + // Portal frame + const frameGeo = new THREE.TorusGeometry(2, 0.2, 16, 100); + const frameMat = new THREE.MeshStandardMaterial({{ + color: '{NexusColors.TIMMY_GOLD}', + emissive: '{NexusColors.TIMMY_GOLD}', + emissiveIntensity: 0.5, + roughness: 0.3, + metalness: 0.8 + }}); + const frame = new THREE.Mesh(frameGeo, frameMat); + frame.castShadow = true; + portal.add(frame); + + // Portal energy field + const fieldGeo = new THREE.CircleGeometry(1.8, 32); + const fieldMat = new THREE.MeshBasicMaterial({{ + color: '{NexusColors.ALLEGRO_BLUE}', + transparent: true, + opacity: 0.3, + side: THREE.DoubleSide + }}); + const field = new THREE.Mesh(fieldGeo, fieldMat); + portal.add(field); + + // Animation hook + portal.userData.animate = function(time) {{ + field.rotation.z = time * 0.5; + const pulse = 1 + Math.sin(time * 2) * 0.1; + field.scale.set(pulse, pulse, 1); + }}; + + return portal; + }} + + // Export + if (typeof module !== 'undefined' && module.exports) {{ + module.exports = {{ create{name_camel} }}; + }} else if (typeof window !== 'undefined') {{ + window.NexusPortals = window.NexusPortals || {{}}; + window.NexusPortals.{name} = create{name_camel}; + }} + + return {{ create{name_camel} }}; +}})();''' + + return template + + +def _generate_lighting_code(config: Dict[str, Any]) -> str: + """Generate Three.js lighting code.""" + light_type = config["type"] + color = config["color"] + intensity = config["intensity"] + pos = config["position"] + + if light_type == "ambient": + return f'''// Ambient Light for {config['room']} +const {config['room']}Ambient = new THREE.AmbientLight('{color}', {intensity}); +room.add({config['room']}Ambient);''' + + elif light_type == "directional": + return f'''// Directional Light for {config['room']} +const {config['room']}Dir = new THREE.DirectionalLight('{color}', {intensity}); +{config['room']}Dir.position.set({pos['x']}, {pos['y']}, {pos['z']}); +{config['room']}Dir.castShadow = {str(config['cast_shadow']).lower()}; +room.add({config['room']}Dir);''' + + elif light_type == "point": + return f'''// Point Light for {config['room']} +const {config['room']}Point = new THREE.PointLight('{color}', {intensity}, 100); +{config['room']}Point.position.set({pos['x']}, {pos['y']}, {pos['z']}); +{config['room']}Point.castShadow = {str(config['cast_shadow']).lower()}; +room.add({config['room']}Point);''' + + elif light_type == "spot": + return f'''// Spot Light for {config['room']} +const {config['room']}Spot = new THREE.SpotLight('{color}', {intensity}); +{config['room']}Spot.position.set({pos['x']}, {pos['y']}, {pos['z']}); +{config['room']}Spot.castShadow = {str(config['cast_shadow']).lower()}; +{config['room']}Spot.angle = Math.PI / 6; +{config['room']}Spot.penumbra = 0.2; +room.add({config['room']}Spot);''' + + elif light_type == "hemisphere": + return f'''// Hemisphere Light for {config['room']} +const {config['room']}Hemi = new THREE.HemisphereLight('{color}', '#444444', {intensity}); +room.add({config['room']}Hemi);''' + + return "// Unknown light type" + + +def _generate_geometry_code(config: Dict[str, Any]) -> str: + """Generate Three.js geometry code.""" + shape = config["shape"] + pos = config["position"] + rot = config["rotation"] + scale = config["scale"] + mat = config["material"] + name = config["name"] + + # Geometry mapping + geo_map = { + "box": "BoxGeometry(1, 1, 1)", + "sphere": "SphereGeometry(0.5, 32, 32)", + "cylinder": "CylinderGeometry(0.5, 0.5, 1, 32)", + "cone": "ConeGeometry(0.5, 1, 32)", + "torus": "TorusGeometry(0.5, 0.2, 16, 100)", + "plane": "PlaneGeometry(1, 1)", + "ring": "RingGeometry(0.3, 0.5, 32)", + } + + geo_constructor = geo_map.get(shape, "BoxGeometry(1, 1, 1)") + + code = f'''// Geometry: {name} +const {name}Geo = new THREE.{geo_constructor}; +const {name}Mat = new THREE.MeshStandardMaterial({{ + color: '{mat.get('color', '#888888')}', + roughness: {mat.get('roughness', 0.5)}, + metalness: {mat.get('metalness', 0.0)} +}}); +const {name} = new THREE.Mesh({name}Geo, {name}Mat); +{name}.position.set({pos['x']}, {pos['y']}, {pos['z']}); +{name}.rotation.set({rot['x']}, {rot['y']}, {rot['z']}); +{name}.scale.set({scale['x']}, {scale['y']}, {scale['z']}); +{name}.castShadow = true; +{name}.receiveShadow = true; +room.add({name});''' + + return code + + +# ============================================================================= +# Tool Schemas +# ============================================================================= + +NEXUS_BUILD_TOOL_SCHEMAS = { + "nexus_create_room": { + "name": "nexus_create_room", + "description": "Create a new 3D room in the Nexus from natural language description", + "parameters": { + "type": "object", + "properties": { + "name": {"type": "string"}, + "description": {"type": "string"}, + "style": {"type": "string"}, + "dimensions": { + "type": "object", + "properties": { + "width": {"type": "number"}, + "height": {"type": "number"}, + "depth": {"type": "number"}, + } + } + }, + "required": ["name", "description", "style"] + } + }, + "nexus_create_portal": { + "name": "nexus_create_portal", + "description": "Create a portal connecting two rooms", + "parameters": { + "type": "object", + "properties": { + "from_room": {"type": "string"}, + "to_room": {"type": "string"}, + "name": {"type": "string"}, + "style": {"type": "string", "default": "energy_vortex"}, + }, + "required": ["from_room", "to_room"] + } + }, + "nexus_add_lighting": { + "name": "nexus_add_lighting", + "description": "Add lighting to a room", + "parameters": { + "type": "object", + "properties": { + "room": {"type": "string"}, + "light_type": {"type": "string"}, + "color": {"type": "string", "default": "#ffffff"}, + "intensity": {"type": "number", "default": 1.0}, + "position": { + "type": "object", + "properties": {"x": {"type": "number"}, "y": {"type": "number"}, "z": {"type": "number"}} + }, + "cast_shadow": {"type": "boolean", "default": True} + }, + "required": ["room", "light_type"] + } + }, + "nexus_add_geometry": { + "name": "nexus_add_geometry", + "description": "Add 3D geometry to a room", + "parameters": { + "type": "object", + "properties": { + "room": {"type": "string"}, + "shape": {"type": "string"}, + "position": { + "type": "object", + "properties": {"x": {"type": "number"}, "y": {"type": "number"}, "z": {"type": "number"}} + }, + "material": {"type": "object"}, + "scale": {"type": "object"}, + "rotation": {"type": "object"}, + "name": {"type": "string"} + }, + "required": ["room", "shape", "position"] + } + }, + "nexus_generate_scene_from_mood": { + "name": "nexus_generate_scene_from_mood", + "description": "Generate a scene based on mood description", + "parameters": { + "type": "object", + "properties": { + "mood_description": {"type": "string"} + }, + "required": ["mood_description"] + } + }, + "nexus_deploy_module": { + "name": "nexus_deploy_module", + "description": "Deploy a Nexus module with validation", + "parameters": { + "type": "object", + "properties": { + "module_code": {"type": "string"}, + "test": {"type": "boolean", "default": True}, + "module_name": {"type": "string"} + }, + "required": ["module_code"] + } + }, +} + + +if __name__ == "__main__": + # Demo + print("Nexus Build Tool - Demo") + print("=" * 50) + + # Import NexusColors for demo + _, _, _, _, _, NexusColors, _ = _import_agent_modules() + + # Create a room + result = create_room( + name="zen_garden", + description="Peaceful garden with floating stones and soft light", + style="minimalist_ethereal" + ) + print(f"\nRoom created: {result['room_name']}") + print(f"Mood: {result['design']['mood_preset']}") + + # Add lighting + result = add_lighting( + room="zen_garden", + light_type="point", + color=NexusColors.TIMMY_GOLD, + intensity=0.8, + position={"x": 0, "y": 5, "z": 0} + ) + print(f"\nLighting added: {result['light_config']['type']}") + + # Add geometry + result = add_geometry( + room="zen_garden", + shape="sphere", + position={"x": 0, "y": 2, "z": 0}, + material={"color": NexusColors.ALLEGRO_BLUE, "roughness": 0.2}, + name="floating_orb" + ) + print(f"\nGeometry added: {result['geometry_config']['shape']}")