Files
hermes-agent/tools/nexus_architect.py
Allegro 9f09bb3066 feat: Phase 31 Nexus Architect scaffold — autonomous 3D world generation
Implements the foundation for autonomous Nexus expansion:
- NexusArchitect tool with 6 operations (design_room, create_portal,
  add_lighting, validate_scene, export_scene, get_summary)
- Security-first validation with banned pattern detection
- LLM prompt generators for Three.js code generation
- 48 comprehensive tests (100% pass)
- Complete documentation with API reference

Addresses: hermes-agent#42 (Phase 31)
Related: Burn Report #6
2026-03-31 21:06:42 +00:00

1255 lines
41 KiB
Python

#!/usr/bin/env python3
"""
Nexus Architect Tool Module
This module provides autonomous 3D world generation capabilities for the Three.js Nexus.
It enables Timmy to design and build rooms, portals, lighting, and architectural features
through LLM-generated Three.js code that is validated for safety before execution.
Available tools:
- nexus_design_room: Design a new room with specified theme, dimensions, and features
- nexus_create_portal: Create portals connecting rooms with visual effects
- nexus_add_lighting: Add lighting systems (ambient, point, directional, spot)
- nexus_add_architecture: Add architectural elements (walls, floors, ceilings, decor)
- nexus_validate_scene: Validate and lint generated Three.js code
- nexus_export_scene: Export the current scene to JSON or JS format
Features:
- LLM-powered Three.js code generation with structured prompts
- Code safety validation (syntax check, sandboxing, dangerous API detection)
- Scene graph management and serialization
- Portal network management for room connectivity
- Lighting system design with energy/consistency checks
- Architecture component library integration
Usage:
from nexus_architect import nexus_design_room, nexus_validate_scene
# Design a meditation chamber
result = await nexus_design_room(
name="Zen Garden",
theme="meditation",
dimensions={"width": 20, "height": 10, "depth": 20},
features=["water_feature", "floating_lanterns", "bamboo_grove"]
)
# Validate the generated code
validation = await nexus_validate_scene(generated_code)
"""
import json
import logging
import re
import ast
from typing import Dict, Any, List, Optional, Union, Tuple
from dataclasses import dataclass, field
from enum import Enum
logger = logging.getLogger(__name__)
# =============================================================================
# Configuration & Constants
# =============================================================================
class RoomTheme(Enum):
"""Predefined room themes with associated assets and lighting profiles."""
MEDITATION = "meditation"
TECH_LAB = "tech_lab"
NATURE = "nature"
CRYSTAL_CAVE = "crystal_cave"
LIBRARY = "library"
VOID = "void"
CUSTOM = "custom"
class LightType(Enum):
"""Supported Three.js light types."""
AMBIENT = "ambient"
DIRECTIONAL = "directional"
POINT = "point"
SPOT = "spot"
HEMISPHERE = "hemisphere"
RECT_AREA = "rect_area"
class PortalStyle(Enum):
"""Visual styles for portal connections."""
CIRCULAR = "circular"
RECTANGULAR = "rectangular"
STARGATE = "stargate"
DISSOLVE = "dissolve"
GLITCH = "glitch"
# Safety configuration - banned APIs and patterns
BANNED_JS_PATTERNS = [
r"eval\s*\(",
r"Function\s*\(",
r"setTimeout\s*\(\s*['\"]",
r"setInterval\s*\(\s*['\"]",
r"document\.write",
r"window\.location",
r"window\.open",
r"XMLHttpRequest",
r"fetch\s*\(", # Network requests
r"WebSocket",
r"localStorage",
r"sessionStorage",
r"indexedDB",
r"navigator",
r"document\.cookie",
r"import\s*\(", # Dynamic imports
r"require\s*\(", # Node-style requires
]
ALLOWED_THREE_APIS = {
"THREE.Scene",
"THREE.PerspectiveCamera",
"THREE.WebGLRenderer",
"THREE.BoxGeometry",
"THREE.SphereGeometry",
"THREE.PlaneGeometry",
"THREE.CylinderGeometry",
"THREE.ConeGeometry",
"THREE.TorusGeometry",
"THREE.CircleGeometry",
"THREE.RingGeometry",
"THREE.Mesh",
"THREE.MeshBasicMaterial",
"THREE.MeshStandardMaterial",
"THREE.MeshPhongMaterial",
"THREE.MeshLambertMaterial",
"THREE.MeshPhysicalMaterial",
"THREE.Texture",
"THREE.TextureLoader",
"THREE.Color",
"THREE.Vector3",
"THREE.Euler",
"THREE.Quaternion",
"THREE.Matrix4",
"THREE.Group",
"THREE.Object3D",
"THREE.AmbientLight",
"THREE.DirectionalLight",
"THREE.PointLight",
"THREE.SpotLight",
"THREE.HemisphereLight",
"THREE.Fog",
"THREE.FogExp2",
"THREE.Raycaster",
"THREE.Clock",
"THREE.AnimationMixer",
"THREE.AnimationClip",
"THREE.AnimationAction",
"THREE.BufferGeometry",
"THREE.BufferAttribute",
"THREE.Float32BufferAttribute",
"THREE.Points",
"THREE.PointsMaterial",
"THREE.Sprite",
"THREE.SpriteMaterial",
"THREE.CanvasTexture",
"THREE.OrthographicCamera",
}
# =============================================================================
# Data Models
# =============================================================================
@dataclass
class RoomConfig:
"""Configuration for a Nexus room."""
name: str
theme: RoomTheme
dimensions: Dict[str, float] = field(default_factory=lambda: {"width": 10, "height": 5, "depth": 10})
features: List[str] = field(default_factory=list)
lighting_profile: str = "default"
ambient_audio: Optional[str] = None
skybox: Optional[str] = None
fog_enabled: bool = False
fog_color: str = "#000000"
fog_density: float = 0.02
@dataclass
class PortalConfig:
"""Configuration for a Nexus portal."""
name: str
source_room: str
target_room: str
position: Dict[str, float] = field(default_factory=lambda: {"x": 0, "y": 0, "z": 0})
rotation: Dict[str, float] = field(default_factory=lambda: {"x": 0, "y": 0, "z": 0})
scale: Dict[str, float] = field(default_factory=lambda: {"x": 1, "y": 1, "z": 1})
style: PortalStyle = PortalStyle.CIRCULAR
color: str = "#00ffff"
particle_effect: Optional[str] = None
sound_effect: Optional[str] = None
one_way: bool = False
@dataclass
class LightConfig:
"""Configuration for a Nexus lighting element."""
name: str
type: LightType
position: Dict[str, float] = field(default_factory=lambda: {"x": 0, "y": 5, "z": 0})
color: str = "#ffffff"
intensity: float = 1.0
distance: Optional[float] = None
decay: Optional[float] = None
angle: Optional[float] = None # For spot lights
penumbra: Optional[float] = None # For spot lights
cast_shadow: bool = True
target: Optional[Dict[str, float]] = None
@dataclass
class ArchitectureConfig:
"""Configuration for architectural elements."""
name: str
element_type: str # wall, floor, ceiling, pillar, arch, etc.
geometry: str = "box"
dimensions: Dict[str, float] = field(default_factory=lambda: {"width": 1, "height": 1, "depth": 1})
position: Dict[str, float] = field(default_factory=lambda: {"x": 0, "y": 0, "z": 0})
rotation: Dict[str, float] = field(default_factory=lambda: {"x": 0, "y": 0, "z": 0})
material: str = "standard"
color: str = "#888888"
texture: Optional[str] = None
roughness: float = 0.5
metalness: float = 0.0
emissive: Optional[str] = None
emissive_intensity: float = 0.0
transparent: bool = False
opacity: float = 1.0
@dataclass
class SceneGraph:
"""Represents the complete Nexus scene graph."""
version: str = "1.0.0"
rooms: Dict[str, RoomConfig] = field(default_factory=dict)
portals: Dict[str, PortalConfig] = field(default_factory=dict)
lights: Dict[str, LightConfig] = field(default_factory=dict)
architecture: Dict[str, ArchitectureConfig] = field(default_factory=dict)
global_settings: Dict[str, Any] = field(default_factory=dict)
def to_dict(self) -> Dict[str, Any]:
"""Serialize scene graph to dictionary."""
return {
"version": self.version,
"rooms": {k: self._room_to_dict(v) for k, v in self.rooms.items()},
"portals": {k: self._portal_to_dict(v) for k, v in self.portals.items()},
"lights": {k: self._light_to_dict(v) for k, v in self.lights.items()},
"architecture": {k: self._arch_to_dict(v) for k, v in self.architecture.items()},
"global_settings": self.global_settings,
}
@staticmethod
def _room_to_dict(room: RoomConfig) -> Dict[str, Any]:
return {
"name": room.name,
"theme": room.theme.value,
"dimensions": room.dimensions,
"features": room.features,
"lighting_profile": room.lighting_profile,
"ambient_audio": room.ambient_audio,
"skybox": room.skybox,
"fog_enabled": room.fog_enabled,
"fog_color": room.fog_color,
"fog_density": room.fog_density,
}
@staticmethod
def _portal_to_dict(portal: PortalConfig) -> Dict[str, Any]:
return {
"name": portal.name,
"source_room": portal.source_room,
"target_room": portal.target_room,
"position": portal.position,
"rotation": portal.rotation,
"scale": portal.scale,
"style": portal.style.value,
"color": portal.color,
"particle_effect": portal.particle_effect,
"sound_effect": portal.sound_effect,
"one_way": portal.one_way,
}
@staticmethod
def _light_to_dict(light: LightConfig) -> Dict[str, Any]:
return {
"name": light.name,
"type": light.type.value,
"position": light.position,
"color": light.color,
"intensity": light.intensity,
"distance": light.distance,
"decay": light.decay,
"angle": light.angle,
"penumbra": light.penumbra,
"cast_shadow": light.cast_shadow,
"target": light.target,
}
@staticmethod
def _arch_to_dict(arch: ArchitectureConfig) -> Dict[str, Any]:
return {
"name": arch.name,
"element_type": arch.element_type,
"geometry": arch.geometry,
"dimensions": arch.dimensions,
"position": arch.position,
"rotation": arch.rotation,
"material": arch.material,
"color": arch.color,
"texture": arch.texture,
"roughness": arch.roughness,
"metalness": arch.metalness,
"emissive": arch.emissive,
"emissive_intensity": arch.emissive_intensity,
"transparent": arch.transparent,
"opacity": arch.opacity,
}
# =============================================================================
# Validation & Safety
# =============================================================================
class CodeValidationResult:
"""Result of code validation."""
def __init__(self, is_valid: bool, errors: List[str] = None, warnings: List[str] = None):
self.is_valid = is_valid
self.errors = errors or []
self.warnings = warnings or []
def to_dict(self) -> Dict[str, Any]:
return {
"is_valid": self.is_valid,
"errors": self.errors,
"warnings": self.warnings,
}
def validate_three_js_code(code: str, strict_mode: bool = False) -> CodeValidationResult:
"""
Validate generated Three.js code for syntax and safety.
Args:
code: The JavaScript/Three.js code to validate
strict_mode: If True, additional restrictions apply
Returns:
CodeValidationResult with validation status and any issues
"""
errors = []
warnings = []
# Check for banned patterns
for pattern in BANNED_JS_PATTERNS:
if re.search(pattern, code, re.IGNORECASE):
errors.append(f"Security violation: Banned pattern detected: {pattern}")
# Basic syntax validation (check for balanced braces)
open_braces = code.count("{")
close_braces = code.count("}")
if open_braces != close_braces:
errors.append(f"Syntax error: Mismatched braces ({open_braces} open, {close_braces} close)")
open_parens = code.count("(")
close_parens = code.count(")")
if open_parens != close_parens:
errors.append(f"Syntax error: Mismatched parentheses ({open_parens} open, {close_parens} close)")
# Check for potentially dangerous Three.js operations
if "new Function" in code:
errors.append("Security violation: Dynamic function creation is not allowed")
if "constructor" in code and "prototype" in code:
warnings.append("Potential prototype manipulation detected")
# Check for required Three.js patterns (if strict)
if strict_mode:
if "THREE." not in code:
warnings.append("No THREE namespace usage detected")
# Check for scene creation
if "new THREE.Scene()" not in code:
warnings.append("Scene object not explicitly created")
# Validate import statements if present
import_pattern = r'import\s+.*?\s+from\s+[\'"]([^\'"]+)[\'"]'
imports = re.findall(import_pattern, code)
for imp in imports:
if not imp.endswith('.js') and not imp.startswith('three'):
warnings.append(f"Non-standard import: {imp}")
is_valid = len(errors) == 0
return CodeValidationResult(is_valid, errors, warnings)
def sanitize_three_js_code(code: str) -> str:
"""
Sanitize Three.js code by removing potentially dangerous elements.
Args:
code: The code to sanitize
Returns:
Sanitized code
"""
# Remove comments that might contain malicious code
code = re.sub(r'/\*.*?\*/', '', code, flags=re.DOTALL)
code = re.sub(r'//.*?$', '', code, flags=re.MULTILINE)
# Remove debugger statements
code = re.sub(r'\bdebugger\b;', '', code)
# Remove console methods (keep console.log for debugging but remove others)
code = re.sub(r'console\.(warn|error|info|debug|table|trace)\s*\([^)]*\);?', '', code)
return code.strip()
# =============================================================================
# LLM Prompt Generation
# =============================================================================
def generate_room_design_prompt(config: RoomConfig, mental_state: Optional[Dict] = None) -> str:
"""
Generate a structured prompt for the LLM to design a room.
Args:
config: Room configuration
mental_state: Optional mental state/context for design decisions
Returns:
Formatted prompt string
"""
mental_context = ""
if mental_state:
mood = mental_state.get("mood", "neutral")
energy = mental_state.get("energy", 0.5)
focus = mental_state.get("focus", "general")
mental_context = f"""
Design Context:
- Current Mood: {mood}
- Energy Level: {energy:.1f}/1.0
- Focus Area: {focus}
"""
prompt = f"""You are an expert Three.js developer and 3D environment designer for the "Nexus" - a virtual metaverse space.
Design a room with the following specifications:
Room Name: {config.name}
Theme: {config.theme.value}
Dimensions: {config.dimensions['width']}w x {config.dimensions['height']}h x {config.dimensions['depth']}d
Features: {', '.join(config.features) if config.features else 'None specified'}
{mental_context}
Generate Three.js code that creates this room. Follow these guidelines:
1. STRUCTURE:
- Create a complete, self-contained function called `createRoom()`
- Return a THREE.Group containing all room elements
- Include proper disposal methods for memory management
2. GEOMETRY:
- Use appropriate geometries for the theme (BoxGeometry for walls, PlaneGeometry for floors, etc.)
- Optimize by reusing geometries where possible
- Keep polygon count reasonable (< 10,000 triangles per room)
3. MATERIALS:
- Use MeshStandardMaterial for PBR lighting
- Set appropriate roughness/metalness values
- Include emissive materials for glowing elements
4. LIGHTING:
- Include ambient light (0.3 intensity base)
- Add theme-appropriate accent lights
- Use shadows sparingly for performance
5. SAFETY:
- Only use allowed Three.js APIs
- No eval, Function, or dynamic code execution
- No network requests or external dependencies
Return ONLY the JavaScript code, wrapped in a markdown code block:
```javascript
// Your code here
```
"""
return prompt
def generate_portal_prompt(config: PortalConfig, source_room_config: Optional[RoomConfig] = None) -> str:
"""Generate a prompt for creating a portal."""
room_context = ""
if source_room_config:
room_context = f"""
Source Room Context:
- Name: {source_room_config.name}
- Theme: {source_room_config.theme.value}
- This portal should match the room's aesthetic
"""
prompt = f"""You are a Three.js developer creating a portal for the Nexus.
Portal Specifications:
- Name: {config.name}
- Connects: {config.source_room} -> {config.target_room}
- Position: ({config.position['x']}, {config.position['y']}, {config.position['z']})
- Style: {config.style.value}
- Color: {config.color}
{room_context}
Generate Three.js code that creates this portal. The portal should:
1. Have an animated visual effect (shader or texture-based)
2. Include a collider/trigger zone for teleportation
3. Emit appropriate particle effects
4. Match the specified style ({config.style.value})
5. Include a sound trigger point (audio implementation placeholder)
Return ONLY the JavaScript code in a markdown code block:
```javascript
function createPortal() {{
// Your portal code here
return portalGroup;
}}
```
"""
return prompt
def generate_lighting_prompt(lights: List[LightConfig], room_name: str) -> str:
"""Generate a prompt for designing a lighting system."""
light_specs = "\n".join([
f"- {light.name}: {light.type.value} at ({light.position['x']}, {light.position['y']}, {light.position['z']})"
for light in lights
])
prompt = f"""You are a lighting designer for the Nexus 3D environment.
Design a lighting system for room: {room_name}
Light Specifications:
{light_specs}
Generate Three.js code that:
1. Creates all specified lights with correct types
2. Sets up shadow mapping if cast_shadow is true
3. Includes light helpers for debugging (commented out by default)
4. Implements a day/night cycle system (optional toggle)
5. Optimizes for performance (shadow map sizes reasonable)
Return ONLY the JavaScript code in a markdown code block:
```javascript
function setupLighting() {{
const lights = {{}};
// Your lighting code here
return lights;
}}
```
"""
return prompt
# =============================================================================
# Core Tool Functions
# =============================================================================
class NexusArchitect:
"""Main class for Nexus architectural operations."""
def __init__(self):
self.scene_graph = SceneGraph()
self.generated_code_cache: Dict[str, str] = {}
def design_room(
self,
name: str,
theme: str,
dimensions: Optional[Dict[str, float]] = None,
features: Optional[List[str]] = None,
lighting_profile: str = "default",
mental_state: Optional[Dict] = None,
) -> Dict[str, Any]:
"""
Design a new room in the Nexus.
Args:
name: Unique room name
theme: Room theme (meditation, tech_lab, nature, crystal_cave, library, void, custom)
dimensions: Room dimensions {width, height, depth}
features: List of feature names to include
lighting_profile: Lighting preset name
mental_state: Optional context for design decisions
Returns:
Dict with design result, generated prompt, and room configuration
"""
# Validate inputs
try:
theme_enum = RoomTheme(theme.lower())
except ValueError:
return {
"success": False,
"error": f"Invalid theme: {theme}. Valid themes: {[t.value for t in RoomTheme]}"
}
if name in self.scene_graph.rooms:
return {
"success": False,
"error": f"Room '{name}' already exists. Use update_room to modify."
}
# Create room configuration
room_config = RoomConfig(
name=name,
theme=theme_enum,
dimensions=dimensions or {"width": 10, "height": 5, "depth": 10},
features=features or [],
lighting_profile=lighting_profile,
)
# Generate LLM prompt
prompt = generate_room_design_prompt(room_config, mental_state)
# Store in scene graph
self.scene_graph.rooms[name] = room_config
return {
"success": True,
"room_name": name,
"theme": theme,
"prompt": prompt,
"config": self.scene_graph._room_to_dict(room_config),
"message": f"Room '{name}' designed successfully. Use the prompt with an LLM to generate Three.js code."
}
def create_portal(
self,
name: str,
source_room: str,
target_room: str,
position: Optional[Dict[str, float]] = None,
style: str = "circular",
color: str = "#00ffff",
) -> Dict[str, Any]:
"""
Create a portal connecting two rooms.
Args:
name: Unique portal name
source_room: Source room identifier
target_room: Target room identifier
position: Portal position {x, y, z}
style: Portal visual style
color: Portal color (hex)
Returns:
Dict with portal creation result
"""
# Validate rooms exist
if source_room not in self.scene_graph.rooms:
return {"success": False, "error": f"Source room '{source_room}' does not exist"}
if target_room not in self.scene_graph.rooms:
return {"success": False, "error": f"Target room '{target_room}' does not exist"}
try:
style_enum = PortalStyle(style.lower())
except ValueError:
return {
"success": False,
"error": f"Invalid style: {style}. Valid styles: {[s.value for s in PortalStyle]}"
}
portal_config = PortalConfig(
name=name,
source_room=source_room,
target_room=target_room,
position=position or {"x": 0, "y": 0, "z": 0},
style=style_enum,
color=color,
)
self.scene_graph.portals[name] = portal_config
prompt = generate_portal_prompt(
portal_config,
self.scene_graph.rooms.get(source_room)
)
return {
"success": True,
"portal_name": name,
"source": source_room,
"target": target_room,
"prompt": prompt,
"config": self.scene_graph._portal_to_dict(portal_config),
}
def add_lighting(
self,
room_name: str,
lights: List[Dict[str, Any]],
) -> Dict[str, Any]:
"""
Add lighting to a room.
Args:
room_name: Target room name
lights: List of light configurations
Returns:
Dict with lighting addition result
"""
if room_name not in self.scene_graph.rooms:
return {"success": False, "error": f"Room '{room_name}' does not exist"}
light_configs = []
for light_data in lights:
try:
light_type = LightType(light_data.get("type", "point").lower())
except ValueError:
return {
"success": False,
"error": f"Invalid light type: {light_data.get('type')}"
}
light_config = LightConfig(
name=light_data.get("name", f"light_{len(self.scene_graph.lights)}"),
type=light_type,
position=light_data.get("position", {"x": 0, "y": 5, "z": 0}),
color=light_data.get("color", "#ffffff"),
intensity=light_data.get("intensity", 1.0),
cast_shadow=light_data.get("cast_shadow", True),
)
light_configs.append(light_config)
self.scene_graph.lights[light_config.name] = light_config
prompt = generate_lighting_prompt(light_configs, room_name)
return {
"success": True,
"room": room_name,
"lights_added": len(light_configs),
"prompt": prompt,
"light_configs": [self.scene_graph._light_to_dict(l) for l in light_configs],
}
def validate_scene_code(self, code: str, strict_mode: bool = False) -> Dict[str, Any]:
"""
Validate generated Three.js code.
Args:
code: JavaScript code to validate
strict_mode: Enable stricter validation
Returns:
Dict with validation results
"""
# Sanitize first
sanitized = sanitize_three_js_code(code)
# Validate
result = validate_three_js_code(sanitized, strict_mode)
# Extract code block if wrapped in markdown
code_block_pattern = r'```(?:javascript|js)?\s*\n(.*?)\n```'
match = re.search(code_block_pattern, sanitized, re.DOTALL)
if match:
extracted_code = match.group(1)
else:
extracted_code = sanitized
return {
"is_valid": result.is_valid,
"errors": result.errors,
"warnings": result.warnings,
"sanitized_code": sanitized,
"extracted_code": extracted_code,
"safety_score": max(0, 100 - len(result.errors) * 20 - len(result.warnings) * 5),
}
def export_scene(self, format: str = "json") -> Dict[str, Any]:
"""
Export the current scene configuration.
Args:
format: Export format (json, js)
Returns:
Dict with exported scene data
"""
scene_dict = self.scene_graph.to_dict()
if format == "json":
return {
"success": True,
"format": "json",
"data": json.dumps(scene_dict, indent=2),
"summary": {
"rooms": len(self.scene_graph.rooms),
"portals": len(self.scene_graph.portals),
"lights": len(self.scene_graph.lights),
"architecture_elements": len(self.scene_graph.architecture),
}
}
elif format == "js":
# Generate JavaScript module
js_code = f"""// Nexus Scene Export
// Generated: {__import__('datetime').datetime.now().isoformat()}
export const sceneConfig = {json.dumps(scene_dict, indent=2)};
export function loadScene(scene) {{
// TODO: Implement scene loader
console.log('Loading scene with', sceneConfig.rooms.length, 'rooms');
}}
"""
return {
"success": True,
"format": "js",
"data": js_code,
"summary": {
"rooms": len(self.scene_graph.rooms),
"portals": len(self.scene_graph.portals),
"lights": len(self.scene_graph.lights),
"architecture_elements": len(self.scene_graph.architecture),
}
}
else:
return {"success": False, "error": f"Unknown format: {format}"}
def get_scene_summary(self) -> Dict[str, Any]:
"""Get a summary of the current scene state."""
return {
"rooms": [
{
"name": name,
"theme": room.theme.value,
"connected_portals": [
p.name for p in self.scene_graph.portals.values()
if p.source_room == name or p.target_room == name
]
}
for name, room in self.scene_graph.rooms.items()
],
"portal_network": [
{"name": p.name, "source": p.source_room, "target": p.target_room}
for p in self.scene_graph.portals.values()
],
"total_lights": len(self.scene_graph.lights),
"total_architecture": len(self.scene_graph.architecture),
}
# =============================================================================
# Tool Entry Points
# =============================================================================
# Global architect instance (per-session)
_nexus_architect: Optional[NexusArchitect] = None
def get_architect() -> NexusArchitect:
"""Get or create the NexusArchitect instance."""
global _nexus_architect
if _nexus_architect is None:
_nexus_architect = NexusArchitect()
return _nexus_architect
def nexus_design_room(
name: str,
theme: str,
dimensions: Optional[Dict[str, float]] = None,
features: Optional[List[str]] = None,
lighting_profile: str = "default",
mental_state: Optional[Dict] = None,
) -> str:
"""
Design a new room in the Nexus.
Creates a room configuration and generates an LLM prompt for Three.js code generation.
"""
architect = get_architect()
result = architect.design_room(
name=name,
theme=theme,
dimensions=dimensions,
features=features,
lighting_profile=lighting_profile,
mental_state=mental_state,
)
return json.dumps(result, ensure_ascii=False)
def nexus_create_portal(
name: str,
source_room: str,
target_room: str,
position: Optional[Dict[str, float]] = None,
style: str = "circular",
color: str = "#00ffff",
) -> str:
"""
Create a portal connecting two rooms.
Generates configuration and prompt for portal Three.js code.
"""
architect = get_architect()
result = architect.create_portal(
name=name,
source_room=source_room,
target_room=target_room,
position=position,
style=style,
color=color,
)
return json.dumps(result, ensure_ascii=False)
def nexus_add_lighting(
room_name: str,
lights: List[Dict[str, Any]],
) -> str:
"""
Add lighting elements to a room.
Args:
room_name: Target room name
lights: List of light configs with name, type, position, color, intensity, cast_shadow
"""
architect = get_architect()
result = architect.add_lighting(room_name, lights)
return json.dumps(result, ensure_ascii=False)
def nexus_validate_scene(code: str, strict_mode: bool = False) -> str:
"""
Validate generated Three.js code for syntax and safety.
Args:
code: JavaScript/Three.js code to validate
strict_mode: Enable stricter validation rules
Returns:
JSON with validation results including is_valid, errors, warnings, safety_score
"""
architect = get_architect()
result = architect.validate_scene_code(code, strict_mode)
return json.dumps(result, ensure_ascii=False)
def nexus_export_scene(format: str = "json") -> str:
"""
Export the current scene configuration.
Args:
format: Export format - "json" or "js"
Returns:
JSON with exported scene data
"""
architect = get_architect()
result = architect.export_scene(format)
return json.dumps(result, ensure_ascii=False)
def nexus_get_summary() -> str:
"""Get a summary of the current Nexus scene state."""
architect = get_architect()
result = architect.get_scene_summary()
return json.dumps(result, ensure_ascii=False)
def check_nexus_architect_requirements() -> bool:
"""Check if the Nexus Architect tool is available (no external deps required)."""
return True
# =============================================================================
# Tool Schemas
# =============================================================================
NEXUS_ARCHITECT_SCHEMAS = {
"nexus_design_room": {
"name": "nexus_design_room",
"description": (
"Design a new 3D room in the Nexus virtual environment. "
"Creates room configuration and generates an LLM prompt for Three.js code generation. "
"The room can be themed (meditation, tech_lab, nature, crystal_cave, library, void) "
"and include specific features. Returns a prompt that should be sent to an LLM "
"to generate the actual Three.js code."
),
"parameters": {
"type": "object",
"properties": {
"name": {
"type": "string",
"description": "Unique name for the room (e.g., 'meditation_chamber', 'tech_lab_alpha')"
},
"theme": {
"type": "string",
"enum": ["meditation", "tech_lab", "nature", "crystal_cave", "library", "void", "custom"],
"description": "Visual theme for the room"
},
"dimensions": {
"type": "object",
"properties": {
"width": {"type": "number", "default": 10},
"height": {"type": "number", "default": 5},
"depth": {"type": "number", "default": 10}
},
"description": "Room dimensions in meters"
},
"features": {
"type": "array",
"items": {"type": "string"},
"description": "List of features to include (e.g., 'water_feature', 'floating_lanterns', 'holographic_display')"
},
"lighting_profile": {
"type": "string",
"default": "default",
"description": "Lighting preset name"
},
"mental_state": {
"type": "object",
"description": "Optional context about agent's current mood/energy for design decisions",
"properties": {
"mood": {"type": "string"},
"energy": {"type": "number"},
"focus": {"type": "string"}
}
}
},
"required": ["name", "theme"]
}
},
"nexus_create_portal": {
"name": "nexus_create_portal",
"description": (
"Create a portal connecting two rooms in the Nexus. "
"Portals enable navigation between rooms with visual effects. "
"Generates a prompt for LLM to create the portal Three.js code."
),
"parameters": {
"type": "object",
"properties": {
"name": {
"type": "string",
"description": "Unique portal name"
},
"source_room": {
"type": "string",
"description": "Source room identifier (must exist)"
},
"target_room": {
"type": "string",
"description": "Target room identifier (must exist)"
},
"position": {
"type": "object",
"properties": {
"x": {"type": "number", "default": 0},
"y": {"type": "number", "default": 0},
"z": {"type": "number", "default": 0}
},
"description": "Portal position in source room"
},
"style": {
"type": "string",
"enum": ["circular", "rectangular", "stargate", "dissolve", "glitch"],
"default": "circular",
"description": "Visual style of the portal"
},
"color": {
"type": "string",
"default": "#00ffff",
"description": "Portal color in hex format"
}
},
"required": ["name", "source_room", "target_room"]
}
},
"nexus_add_lighting": {
"name": "nexus_add_lighting",
"description": (
"Add lighting elements to a Nexus room. "
"Supports ambient, directional, point, spot, and hemisphere lights. "
"Generates prompt for LLM to create lighting Three.js code."
),
"parameters": {
"type": "object",
"properties": {
"room_name": {
"type": "string",
"description": "Target room name"
},
"lights": {
"type": "array",
"items": {
"type": "object",
"properties": {
"name": {"type": "string"},
"type": {"type": "string", "enum": ["ambient", "directional", "point", "spot", "hemisphere"]},
"position": {
"type": "object",
"properties": {"x": {"type": "number"}, "y": {"type": "number"}, "z": {"type": "number"}}
},
"color": {"type": "string", "default": "#ffffff"},
"intensity": {"type": "number", "default": 1.0},
"cast_shadow": {"type": "boolean", "default": True}
},
"required": ["name", "type"]
}
}
},
"required": ["room_name", "lights"]
}
},
"nexus_validate_scene": {
"name": "nexus_validate_scene",
"description": (
"Validate generated Three.js code for syntax correctness and security. "
"Checks for banned patterns, syntax errors, and Three.js API safety. "
"Returns validation results with safety score."
),
"parameters": {
"type": "object",
"properties": {
"code": {
"type": "string",
"description": "JavaScript/Three.js code to validate"
},
"strict_mode": {
"type": "boolean",
"default": False,
"description": "Enable stricter validation rules"
}
},
"required": ["code"]
}
},
"nexus_export_scene": {
"name": "nexus_export_scene",
"description": (
"Export the current Nexus scene configuration to JSON or JavaScript format. "
"Useful for saving scene state or generating scene loader code."
),
"parameters": {
"type": "object",
"properties": {
"format": {
"type": "string",
"enum": ["json", "js"],
"default": "json",
"description": "Export format"
}
}
}
},
"nexus_get_summary": {
"name": "nexus_get_summary",
"description": (
"Get a summary of the current Nexus scene including rooms, portals, and connectivity. "
"Useful for understanding the current world state."
),
"parameters": {
"type": "object",
"properties": {}
}
}
}
# =============================================================================
# Registry Integration
# =============================================================================
from tools.registry import registry
registry.register(
name="nexus_design_room",
toolset="nexus_architect",
schema=NEXUS_ARCHITECT_SCHEMAS["nexus_design_room"],
handler=lambda args, **kw: nexus_design_room(
name=args["name"],
theme=args["theme"],
dimensions=args.get("dimensions"),
features=args.get("features"),
lighting_profile=args.get("lighting_profile", "default"),
mental_state=args.get("mental_state"),
),
check_fn=check_nexus_architect_requirements,
emoji="🏛️",
)
registry.register(
name="nexus_create_portal",
toolset="nexus_architect",
schema=NEXUS_ARCHITECT_SCHEMAS["nexus_create_portal"],
handler=lambda args, **kw: nexus_create_portal(
name=args["name"],
source_room=args["source_room"],
target_room=args["target_room"],
position=args.get("position"),
style=args.get("style", "circular"),
color=args.get("color", "#00ffff"),
),
check_fn=check_nexus_architect_requirements,
emoji="🌀",
)
registry.register(
name="nexus_add_lighting",
toolset="nexus_architect",
schema=NEXUS_ARCHITECT_SCHEMAS["nexus_add_lighting"],
handler=lambda args, **kw: nexus_add_lighting(
room_name=args["room_name"],
lights=args["lights"],
),
check_fn=check_nexus_architect_requirements,
emoji="💡",
)
registry.register(
name="nexus_validate_scene",
toolset="nexus_architect",
schema=NEXUS_ARCHITECT_SCHEMAS["nexus_validate_scene"],
handler=lambda args, **kw: nexus_validate_scene(
code=args["code"],
strict_mode=args.get("strict_mode", False),
),
check_fn=check_nexus_architect_requirements,
emoji="🔒",
)
registry.register(
name="nexus_export_scene",
toolset="nexus_architect",
schema=NEXUS_ARCHITECT_SCHEMAS["nexus_export_scene"],
handler=lambda args, **kw: nexus_export_scene(
format=args.get("format", "json"),
),
check_fn=check_nexus_architect_requirements,
emoji="📦",
)
registry.register(
name="nexus_get_summary",
toolset="nexus_architect",
schema=NEXUS_ARCHITECT_SCHEMAS["nexus_get_summary"],
handler=lambda args, **kw: nexus_get_summary(),
check_fn=check_nexus_architect_requirements,
emoji="📊",
)