Initial commit: 2125_GCE project
This commit is contained in:
Executable
+371
@@ -0,0 +1,371 @@
|
||||
"""VRAM + Resonance Manager.
|
||||
|
||||
Combines computational VRAM limits with symbolic resonance:
|
||||
- Monitors GPU VRAM (8GB GTX1080)
|
||||
- Adjusts model loading based on glyph resonance
|
||||
- Prevents crashes from simultaneous Forge + Janus
|
||||
- Dynamic VRAM budgeting from glyph activation
|
||||
|
||||
Usage:
|
||||
from dual_layer.vram_manager import VRAMManager
|
||||
|
||||
manager = VRAMManager()
|
||||
if manager.can_activate_glyph(glyph_routing_result):
|
||||
manager.activate(glyph_routing_result)
|
||||
"""
|
||||
|
||||
import logging
|
||||
from typing import Dict, List, Any, Optional, Tuple
|
||||
from dataclasses import dataclass
|
||||
from datetime import datetime
|
||||
import asyncio
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# VRAM constants (GTX 1080: 8GB)
|
||||
MAX_VRAM = 8.0
|
||||
WARNING_THRESHOLD = 6.5
|
||||
CRITICAL_THRESHOLD = 7.5
|
||||
VRAM_WARNING_GB = 6.5
|
||||
VRAM_CRITICAL_GB = 7.5
|
||||
VRAM_TOTAL_GB = 8.0
|
||||
|
||||
# Model VRAM estimates
|
||||
MODEL_VRAM_ESTIMATES: Dict[str, float] = {
|
||||
"llama": 2.0, # Llama 7B ~2GB
|
||||
"forge": 4.5, # Stable Diffusion XL ~4.5GB
|
||||
"janus": 5.0, # Janus-Pro-7B ~5GB
|
||||
"google_ai": 1.5, # Google AI API (minimal local)
|
||||
}
|
||||
|
||||
|
||||
@dataclass
|
||||
class GlyphActivation:
|
||||
"""Active glyph reservation."""
|
||||
glyph_id: str
|
||||
specialized_type: str
|
||||
model: str
|
||||
vram_budget: float
|
||||
resonance_score: float
|
||||
power_boost: float
|
||||
activated_at: datetime
|
||||
priority: float
|
||||
|
||||
|
||||
class VRAMManager:
|
||||
"""Manages VRAM + resonance for dual-layer system."""
|
||||
|
||||
def __init__(self, total_vram: float = VRAM_TOTAL_GB):
|
||||
self.total_vram = total_vram
|
||||
self.active_glyphs: Dict[str, GlyphActivation] = {}
|
||||
self.vram_usage: float = 0.0
|
||||
self._lock = asyncio.Lock() # Async lock for concurrent safety
|
||||
|
||||
# Model state tracking
|
||||
self.loaded_models: Dict[str, bool] = {
|
||||
"llama": False,
|
||||
"forge": False,
|
||||
"janus": False,
|
||||
"google_ai": False,
|
||||
}
|
||||
|
||||
# Critical rule: NEVER run Forge + Janus simultaneously
|
||||
self._forge_active = False
|
||||
self._janus_active = False
|
||||
|
||||
async def get_vram_status(self) -> Dict[str, Any]:
|
||||
"""Get current VRAM status."""
|
||||
async with self._lock:
|
||||
return {
|
||||
"total_vram_gb": self.total_vram,
|
||||
"used_vram_gb": self.vram_usage,
|
||||
"available_vram_gb": self.total_vram - self.vram_usage,
|
||||
"usage_percent": (self.vram_usage / self.total_vram) * 100,
|
||||
"active_glyphs": len(self.active_glyphs),
|
||||
"warning": self.vram_usage >= VRAM_WARNING_GB,
|
||||
"critical": self.vram_usage >= VRAM_CRITICAL_GB,
|
||||
"loaded_models": self.loaded_models,
|
||||
"forge_active": self._forge_active,
|
||||
"janus_active": self._janus_active,
|
||||
}
|
||||
|
||||
def can_activate_glyph(
|
||||
self,
|
||||
glyph_id: str,
|
||||
model: str,
|
||||
vram_budget: float,
|
||||
priority: float
|
||||
) -> Tuple[bool, str]:
|
||||
"""Check if glyph can be activated without VRAM crash.
|
||||
|
||||
Args:
|
||||
glyph_id: Glyph identifier
|
||||
model: Model to use (llama, forge, janus, google_ai)
|
||||
vram_budget: Requested VRAM budget
|
||||
priority: Glyph priority (higher = more authority)
|
||||
|
||||
Returns:
|
||||
(can_activate, reason)
|
||||
"""
|
||||
# Check critical VRAM
|
||||
if self.vram_usage >= VRAM_CRITICAL_GB:
|
||||
return False, f"Critical VRAM: {self.vram_usage:.2f}GB used"
|
||||
|
||||
# Check Forge + Janus mutex
|
||||
if model == "forge" and self._janus_active:
|
||||
return False, "Forge cannot run while Janus is active (VRAM crash risk)"
|
||||
|
||||
if model == "janus" and self._forge_active:
|
||||
return False, "Janus cannot run while Forge is active (VRAM crash risk)"
|
||||
|
||||
# Check available VRAM
|
||||
projected_usage = self.vram_usage + vram_budget
|
||||
if projected_usage > self.total_vram:
|
||||
# Check if we can deactivate lower-priority glyphs
|
||||
can_free = self._can_free_vram_for(
|
||||
vram_budget,
|
||||
priority,
|
||||
model
|
||||
)
|
||||
if not can_free:
|
||||
return False, f"Insufficient VRAM: need {vram_budget:.2f}GB, have {self.total_vram - self.vram_usage:.2f}GB available"
|
||||
|
||||
# Check warning threshold
|
||||
if projected_usage >= VRAM_WARNING_GB:
|
||||
logger.warning(
|
||||
f"Glyph {glyph_id} activation will trigger VRAM warning "
|
||||
f"({projected_usage:.2f}GB >= {VRAM_WARNING_GB}GB)"
|
||||
)
|
||||
|
||||
return True, "OK"
|
||||
|
||||
def _can_free_vram_for(
|
||||
self,
|
||||
needed_vram: float,
|
||||
priority: float,
|
||||
model: str
|
||||
) -> bool:
|
||||
"""Check if we can free VRAM by deactivating lower-priority glyphs."""
|
||||
available = self.total_vram - self.vram_usage
|
||||
|
||||
# Find lower-priority glyphs
|
||||
lower_priority_glyphs = [
|
||||
(gid, activation)
|
||||
for gid, activation in self.active_glyphs.items()
|
||||
if activation.priority < priority
|
||||
]
|
||||
|
||||
# Sort by priority (lowest first)
|
||||
lower_priority_glyphs.sort(key=lambda x: x[1].priority)
|
||||
|
||||
# Calculate if deactivating would free enough
|
||||
potential_free = available
|
||||
for _, activation in lower_priority_glyphs:
|
||||
potential_free += activation.vram_budget
|
||||
if potential_free >= needed_vram:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
async def activate_glyph(
|
||||
self,
|
||||
glyph_id: str,
|
||||
specialized_type: str,
|
||||
model: str,
|
||||
vram_budget: float,
|
||||
resonance_score: float,
|
||||
power_boost: float,
|
||||
priority: float
|
||||
) -> bool:
|
||||
"""Activate a glyph (reserve VRAM).
|
||||
|
||||
Args:
|
||||
glyph_id: Glyph identifier
|
||||
specialized_type: Glyph specialized type
|
||||
model: Model to use
|
||||
vram_budget: VRAM budget
|
||||
resonance_score: Resonance score (0-100)
|
||||
power_boost: Power boost multiplier
|
||||
priority: Priority level
|
||||
|
||||
Returns:
|
||||
True if activated, False if failed
|
||||
"""
|
||||
async with self._lock:
|
||||
# Check again under lock
|
||||
can_activate, reason = self.can_activate_glyph(
|
||||
glyph_id, model, vram_budget, priority
|
||||
)
|
||||
|
||||
if not can_activate:
|
||||
logger.error(f"Cannot activate {glyph_id}: {reason}")
|
||||
return False
|
||||
|
||||
# Deactivate lower-priority glyphs if needed
|
||||
self._deactivate_lower_priority(priority, vram_budget)
|
||||
|
||||
# Create activation record
|
||||
activation = GlyphActivation(
|
||||
glyph_id=glyph_id,
|
||||
specialized_type=specialized_type,
|
||||
model=model,
|
||||
vram_budget=vram_budget,
|
||||
resonance_score=resonance_score,
|
||||
power_boost=power_boost,
|
||||
activated_at=datetime.now(),
|
||||
priority=priority
|
||||
)
|
||||
|
||||
# Track model loading
|
||||
if not self.loaded_models.get(model, False):
|
||||
logger.info(f"Loading model: {model} (estimated {MODEL_VRAM_ESTIMATES.get(model, 0):.1f}GB)")
|
||||
self.loaded_models[model] = True
|
||||
|
||||
# Track Forge/Janus mutex
|
||||
if model == "forge":
|
||||
self._forge_active = True
|
||||
elif model == "janus":
|
||||
self._janus_active = True
|
||||
|
||||
# Reserve VRAM
|
||||
self.active_glyphs[glyph_id] = activation
|
||||
self.vram_usage += vram_budget
|
||||
|
||||
logger.info(
|
||||
f"✅ Activated glyph {glyph_id} ({specialized_type}) "
|
||||
f"→ {model} model, {vram_budget:.2f}GB VRAM, "
|
||||
f"resonance={resonance_score:.1f}, boost={power_boost:.2f}x"
|
||||
)
|
||||
|
||||
return True
|
||||
|
||||
async def deactivate_glyph(self, glyph_id: str) -> bool:
|
||||
"""Deactivate a glyph (release VRAM).
|
||||
|
||||
Args:
|
||||
glyph_id: Glyph identifier
|
||||
|
||||
Returns:
|
||||
True if deactivated, False if not found
|
||||
"""
|
||||
async with self._lock:
|
||||
if glyph_id not in self.active_glyphs:
|
||||
return False
|
||||
|
||||
activation = self.active_glyphs.pop(glyph_id)
|
||||
self.vram_usage -= activation.vram_budget
|
||||
|
||||
# Track model unloading
|
||||
model = activation.model
|
||||
if self.loaded_models.get(model, False):
|
||||
# Check if any other glyphs use this model
|
||||
model_users = sum(
|
||||
1 for a in self.active_glyphs.values()
|
||||
if a.model == model
|
||||
)
|
||||
if model_users == 0:
|
||||
logger.info(f"Unloading model: {model}")
|
||||
self.loaded_models[model] = False
|
||||
|
||||
# Track Forge/Janus mutex
|
||||
if model == "forge":
|
||||
self._forge_active = False
|
||||
elif model == "janus":
|
||||
self._janus_active = False
|
||||
|
||||
logger.info(
|
||||
f"❌ Deactivated glyph {glyph_id} "
|
||||
f"(released {activation.vram_budget:.2f}GB VRAM)"
|
||||
)
|
||||
|
||||
return True
|
||||
|
||||
def _deactivate_lower_priority(
|
||||
self,
|
||||
priority: float,
|
||||
needed_vram: float
|
||||
):
|
||||
"""Deactivate lower-priority glyphs to free VRAM."""
|
||||
available = self.total_vram - self.vram_usage
|
||||
|
||||
if available >= needed_vram:
|
||||
return # No need to deactivate
|
||||
|
||||
# Find and sort lower-priority glyphs
|
||||
lower_priority_glyphs = [
|
||||
(gid, activation)
|
||||
for gid, activation in self.active_glyphs.items()
|
||||
if activation.priority < priority
|
||||
]
|
||||
lower_priority_glyphs.sort(key=lambda x: x[1].priority)
|
||||
|
||||
# Deactivate until enough VRAM is freed
|
||||
for glyph_id, activation in lower_priority_glyphs:
|
||||
self.deactivate_glyph(glyph_id)
|
||||
available += activation.vram_budget
|
||||
|
||||
if available >= needed_vram:
|
||||
logger.info(
|
||||
f"Deactivated {len(lower_priority_glyphs)} lower-priority "
|
||||
f"glyphs to free {needed_vram - (self.total_vram - available):.2f}GB"
|
||||
)
|
||||
break
|
||||
|
||||
def get_active_glyphs(self) -> List[Dict[str, Any]]:
|
||||
"""Get list of active glyphs."""
|
||||
return [
|
||||
{
|
||||
"glyph_id": a.glyph_id,
|
||||
"specialized_type": a.specialized_type,
|
||||
"model": a.model,
|
||||
"vram_budget": a.vram_budget,
|
||||
"resonance_score": a.resonance_score,
|
||||
"power_boost": a.power_boost,
|
||||
"priority": a.priority,
|
||||
"activated_at": a.activated_at.isoformat(),
|
||||
}
|
||||
for a in self.active_glyphs.values()
|
||||
]
|
||||
|
||||
def get_resonance_summary(self) -> Dict[str, Any]:
|
||||
"""Get resonance-based VRAM summary."""
|
||||
if not self.active_glyphs:
|
||||
return {
|
||||
"total_resonance": 0,
|
||||
"average_resonance": 0,
|
||||
"highest_priority_glyph": None,
|
||||
"model_distribution": {},
|
||||
}
|
||||
|
||||
# Calculate resonance metrics
|
||||
total_resonance = sum(a.resonance_score for a in self.active_glyphs.values())
|
||||
avg_resonance = total_resonance / len(self.active_glyphs)
|
||||
|
||||
# Find highest priority
|
||||
highest = max(self.active_glyphs.values(), key=lambda a: a.priority)
|
||||
|
||||
# Model distribution
|
||||
model_counts = {}
|
||||
for a in self.active_glyphs.values():
|
||||
model_counts[a.model] = model_counts.get(a.model, 0) + 1
|
||||
|
||||
return {
|
||||
"total_resonance": total_resonance,
|
||||
"average_resonance": avg_resonance,
|
||||
"highest_priority_glyph": highest.glyph_id,
|
||||
"highest_priority_type": highest.specialized_type,
|
||||
"model_distribution": model_counts,
|
||||
"vram_efficiency": total_resonance / self.vram_usage if self.vram_usage > 0 else 0,
|
||||
}
|
||||
|
||||
|
||||
# Global singleton instance
|
||||
_vram_manager: Optional[VRAMManager] = None
|
||||
|
||||
|
||||
def get_vram_manager() -> VRAMManager:
|
||||
"""Get global VRAM manager instance."""
|
||||
global _vram_manager
|
||||
if _vram_manager is None:
|
||||
_vram_manager = VRAMManager()
|
||||
return _vram_manager
|
||||
Reference in New Issue
Block a user