feat: Issue #42 - Nexus Architect for autonomous Three.js world building
Implement Phase 31: Autonomous 'Nexus' Expansion & Architecture DELIVERABLES: - agent/nexus_architect.py: AI agent for natural language to Three.js conversion * Prompt engineering for LLM-driven immersive environment generation * Mental state integration for dynamic aesthetic tuning * Mood preset system (contemplative, energetic, mysterious, etc.) * Room and portal design generation - tools/nexus_build_tool.py: Build tool interface with functions: * create_room(name, description, style) - Generate room modules * create_portal(from_room, to_room, style) - Generate portal connections * add_lighting(room, type, color, intensity) - Add Three.js 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 - agent/nexus_deployment.py: Real-time deployment system * Hot-reload Three.js modules without page refresh * Validation (syntax check, Three.js API compliance) * Rollback on error with version history * Module versioning and status tracking - config/nexus-templates/: Template library * base_room.js - Base room template (Three.js r128+) * portal_template.js - Portal template (circular, rectangular, stargate) * lighting_presets.json - Warm, cool, dramatic, serene, crystalline presets * material_presets.json - 15 material presets including Timmy's gold, Allegro blue - tests/test_nexus_architect.py: Comprehensive test coverage * Unit tests for all components * Integration tests for full workflow * Template file validation DESIGN PRINCIPLES: - Modular architecture (each room = separate JS module) - Valid Three.js code (r128+ compatible) - Hot-reloadable (no page refresh needed) - Mental state integration (SOUL.md values influence aesthetic) NEXUS AESTHETIC GUIDELINES: - Timmy's color: warm gold (#D4AF37) - Allegro's color: motion blue (#4A90E2) - Sovereignty theme: crystalline structures, clean lines - Service theme: open spaces, welcoming lighting - Default mood: contemplative, expansive, hopeful
This commit is contained in:
813
agent/nexus_architect.py
Normal file
813
agent/nexus_architect.py
Normal file
@@ -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']}")
|
||||||
752
agent/nexus_deployment.py
Normal file
752
agent/nexus_deployment.py
Normal file
@@ -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']}")
|
||||||
200
config/nexus-templates/base_room.js
Normal file
200
config/nexus-templates/base_room.js
Normal file
@@ -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 };
|
||||||
|
})();
|
||||||
221
config/nexus-templates/lighting_presets.json
Normal file
221
config/nexus-templates/lighting_presets.json
Normal file
@@ -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"
|
||||||
|
}
|
||||||
154
config/nexus-templates/material_presets.json
Normal file
154
config/nexus-templates/material_presets.json
Normal file
@@ -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"
|
||||||
|
}
|
||||||
|
}
|
||||||
339
config/nexus-templates/portal_template.js
Normal file
339
config/nexus-templates/portal_template.js
Normal file
@@ -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 };
|
||||||
|
})();
|
||||||
666
tests/test_nexus_architect.py
Normal file
666
tests/test_nexus_architect.py
Normal file
@@ -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)
|
||||||
721
tools/nexus_build_tool.py
Normal file
721
tools/nexus_build_tool.py
Normal file
@@ -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']}")
|
||||||
Reference in New Issue
Block a user