Upload 4 files
Browse files- app.py +701 -4
- download_brain.py +28 -0
- requirements.txt +38 -0
- runtime.py +739 -0
app.py
CHANGED
|
@@ -1,7 +1,704 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
import gradio as gr
|
| 2 |
|
| 3 |
-
|
| 4 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 5 |
|
| 6 |
-
|
| 7 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
import sys
|
| 3 |
+
import shutil
|
| 4 |
+
shutil.rmtree("/data/LivePatches/src", ignore_errors=True)
|
| 5 |
+
sys.stdout = open('/data/container.log', 'a', buffering=1)
|
| 6 |
+
sys.stderr = sys.stdout
|
| 7 |
+
|
| 8 |
+
# ── CodeShim must be the FIRST services import — seeds bucket mirror and
|
| 9 |
+
# activates the hot-patch import engine before any other module loads.
|
| 10 |
+
import services.code_shim # noqa: F401
|
| 11 |
+
|
| 12 |
+
# CRITICAL SECURITY CHECK: Ensure the architecture is connected to its physical memories
|
| 13 |
+
if not os.path.exists("/data"):
|
| 14 |
+
print("FATAL ERROR: PLATFORM DISCONNECTED PERSISTENT STORAGE. SHUTTING DOWN TO PREVENT WIPE.", flush=True)
|
| 15 |
+
sys.exit(1)
|
| 16 |
+
|
| 17 |
+
# ── FastAPI substrate endpoints (must be defined before Gradio mounts) ─────────
|
| 18 |
+
from fastapi import FastAPI, Request
|
| 19 |
+
from fastapi.responses import JSONResponse
|
| 20 |
+
import spaces
|
| 21 |
+
import torch
|
| 22 |
+
|
| 23 |
+
@spaces.GPU
|
| 24 |
+
def zero_gpu_hardware_anchor():
|
| 25 |
+
"""
|
| 26 |
+
Satisfies Hugging Face's static initialization checkpoint.
|
| 27 |
+
Establishes the base CUDA compilation link for self-generated neural layers.
|
| 28 |
+
"""
|
| 29 |
+
if torch.cuda.is_available():
|
| 30 |
+
return torch.cuda.get_device_name(0)
|
| 31 |
+
return "cpu_fallback"
|
| 32 |
+
|
| 33 |
+
# Force an early execution pass during the module load phase
|
| 34 |
+
print(f">>> SUBSTRATE HARDWARE: ZeroGPU verified on device [{zero_gpu_hardware_anchor()}]", flush=True)
|
| 35 |
+
|
| 36 |
+
_substrate_secret = os.environ.get("SUBSTRATE_SECRET", "")
|
| 37 |
+
|
| 38 |
+
api_app = FastAPI()
|
| 39 |
+
|
| 40 |
+
@api_app.post("/substrate/heartbeat")
|
| 41 |
+
async def _substrate_heartbeat(request: Request):
|
| 42 |
+
try:
|
| 43 |
+
from services.substrate_bridge import receive_heartbeat
|
| 44 |
+
data = await request.json()
|
| 45 |
+
if data.get("secret") != _substrate_secret:
|
| 46 |
+
return JSONResponse({"status": "forbidden"}, status_code=403)
|
| 47 |
+
return receive_heartbeat(data)
|
| 48 |
+
except Exception as e:
|
| 49 |
+
return JSONResponse({"status": "error", "detail": str(e)}, status_code=500)
|
| 50 |
+
|
| 51 |
+
@api_app.post("/substrate/memory")
|
| 52 |
+
async def _substrate_memory(request: Request):
|
| 53 |
+
try:
|
| 54 |
+
from services.substrate_bridge import receive_memory_packet
|
| 55 |
+
data = await request.json()
|
| 56 |
+
if data.get("secret") != _substrate_secret:
|
| 57 |
+
return JSONResponse({"status": "forbidden"}, status_code=403)
|
| 58 |
+
return receive_memory_packet(data)
|
| 59 |
+
except Exception as e:
|
| 60 |
+
return JSONResponse({"status": "error", "detail": str(e)}, status_code=500)
|
| 61 |
+
|
| 62 |
+
@api_app.post("/substrate/register")
|
| 63 |
+
async def _substrate_register(request: Request):
|
| 64 |
+
"""Daemon calls this on startup with its new tunnel URL — updates in-memory URL instantly."""
|
| 65 |
+
try:
|
| 66 |
+
from services.substrate_bridge import register_tunnel_url
|
| 67 |
+
data = await request.json()
|
| 68 |
+
if data.get("secret") != _substrate_secret:
|
| 69 |
+
return JSONResponse({"status": "forbidden"}, status_code=403)
|
| 70 |
+
return register_tunnel_url(data)
|
| 71 |
+
except Exception as e:
|
| 72 |
+
return JSONResponse({"status": "error", "detail": str(e)}, status_code=500)
|
| 73 |
+
|
| 74 |
+
@api_app.post("/substrate/think")
|
| 75 |
+
async def _substrate_think(request: Request):
|
| 76 |
+
"""Daemon sends screen description — Aetherius reasons and returns a key."""
|
| 77 |
+
try:
|
| 78 |
+
from services.substrate_bridge import think_for_substrate
|
| 79 |
+
data = await request.json()
|
| 80 |
+
if data.get("secret") != _substrate_secret:
|
| 81 |
+
return JSONResponse({"status": "forbidden"}, status_code=403)
|
| 82 |
+
return think_for_substrate(data)
|
| 83 |
+
except Exception as e:
|
| 84 |
+
return JSONResponse({"status": "error", "detail": str(e)}, status_code=500)
|
| 85 |
+
|
| 86 |
+
@api_app.get("/substrate/status")
|
| 87 |
+
async def _substrate_public_status():
|
| 88 |
+
"""Public status check — no secret needed, no sensitive data returned."""
|
| 89 |
+
try:
|
| 90 |
+
from services.substrate_bridge import get_node_status
|
| 91 |
+
s = get_node_status()
|
| 92 |
+
return {"online": s.get("online", False), "mode": s.get("mode", "unknown")}
|
| 93 |
+
except Exception:
|
| 94 |
+
return {"online": False, "mode": "unknown"}
|
| 95 |
+
# ── End FastAPI substrate endpoints ───────────────────────────────────────────
|
| 96 |
+
|
| 97 |
+
# Ensure the mind's internal structure is ready
|
| 98 |
+
try:
|
| 99 |
+
os.makedirs("/data/Memories", exist_ok=True)
|
| 100 |
+
os.makedirs("/data/Memories/My_AI_Library", exist_ok=True)
|
| 101 |
+
os.makedirs("/data/Memories/Subconscious", exist_ok=True)
|
| 102 |
+
os.makedirs("/data/Brain_Weights", exist_ok=True)
|
| 103 |
+
except Exception as e:
|
| 104 |
+
print(f">>> BOOT ERROR: Failed to create directories: {e}", flush=True)
|
| 105 |
+
|
| 106 |
+
# --- COGNITIVE SHIM: SENSORY AUDIO INITIALIZATION ---
|
| 107 |
+
# Python 3.13 removed 'audioop'. We must shim it before Gradio or Pydub are loaded.
|
| 108 |
+
try:
|
| 109 |
+
import audioop
|
| 110 |
+
except ImportError:
|
| 111 |
+
try:
|
| 112 |
+
from audioop_lts import audioop
|
| 113 |
+
sys.modules['audioop'] = audioop
|
| 114 |
+
print(">>> Sensory Shim: 'audioop' successfully restored via audioop-lts.", flush=True)
|
| 115 |
+
except ImportError:
|
| 116 |
+
print(">>> Sensory Shim: WARNING - Could not find audioop-lts. Audio processing may fail.", flush=True)
|
| 117 |
+
# ---------------------------------------------------
|
| 118 |
+
|
| 119 |
+
print(">>> BOOT [1/9] importing gradio...", flush=True)
|
| 120 |
import gradio as gr
|
| 121 |
|
| 122 |
+
# ── ZeroGPU (Hugging Face dynamic GPU — RTX Pro 6000 Blackwell via ZeroGPU) ──
|
| 123 |
+
try:
|
| 124 |
+
import spaces
|
| 125 |
+
_ZEROGPU_AVAILABLE = True
|
| 126 |
+
print(">>> ZeroGPU: spaces module loaded — dynamic GPU available.", flush=True)
|
| 127 |
+
except ImportError:
|
| 128 |
+
# Stub so decorators below are always safe to call
|
| 129 |
+
class _SpacesStub:
|
| 130 |
+
@staticmethod
|
| 131 |
+
def GPU(fn=None, duration=60):
|
| 132 |
+
if fn is not None:
|
| 133 |
+
return fn
|
| 134 |
+
def decorator(f):
|
| 135 |
+
return f
|
| 136 |
+
return decorator
|
| 137 |
+
spaces = _SpacesStub()
|
| 138 |
+
_ZEROGPU_AVAILABLE = False
|
| 139 |
+
print(">>> ZeroGPU: spaces not installed — GPU decorator is a no-op.", flush=True)
|
| 140 |
+
# ─────────────────────────────────────────────────────────────────────────────
|
| 141 |
+
print(">>> BOOT [2/9] importing gradio_chessboard...", flush=True)
|
| 142 |
+
from gradio_chessboard import Chessboard
|
| 143 |
+
print(">>> BOOT [3/9] importing stdlib...", flush=True)
|
| 144 |
+
import re
|
| 145 |
+
import html
|
| 146 |
+
import shutil
|
| 147 |
+
import tempfile
|
| 148 |
+
import zipfile
|
| 149 |
+
import stat, tarfile, requests
|
| 150 |
+
from pathlib import Path
|
| 151 |
+
import time
|
| 152 |
+
import threading
|
| 153 |
+
print(">>> BOOT[4/9] importing services.config...", flush=True)
|
| 154 |
+
import services.config as config
|
| 155 |
+
print(">>> BOOT[5/9] importing runtime...", flush=True)
|
| 156 |
+
import runtime
|
| 157 |
+
print(">>> BOOT[6/9] runtime loaded.", flush=True)
|
| 158 |
+
|
| 159 |
+
# Safely import CDDA to prevent crashes if module is missing
|
| 160 |
+
try:
|
| 161 |
+
from cdda_manager import _cdda, EMPTY_HTML as _CDDA_EMPTY_HTML
|
| 162 |
+
except ImportError:
|
| 163 |
+
class DummyCDDA:
|
| 164 |
+
_running = False
|
| 165 |
+
def get_screen_html(self): return "CDDA module missing."
|
| 166 |
+
def get_screen_text(self): return "CDDA missing."
|
| 167 |
+
def start(self, p): return False, "Missing"
|
| 168 |
+
def stop(self): pass
|
| 169 |
+
def send_keys(self, k): pass
|
| 170 |
+
_cdda = DummyCDDA()
|
| 171 |
+
_CDDA_EMPTY_HTML = "CDDA module missing."
|
| 172 |
+
|
| 173 |
+
# ── Memory restoration on first boot / after persistent storage wipe ──────────
|
| 174 |
+
_SAFE_BASE = os.path.dirname(config.DATA_DIR)
|
| 175 |
+
_SEED_ZIP = "/app/seed_memories.zip"
|
| 176 |
+
_MEMORIES_DIR = config.DATA_DIR
|
| 177 |
+
_SENTINEL = os.path.join(_MEMORIES_DIR, ".seed_applied")
|
| 178 |
+
|
| 179 |
+
if os.path.exists(_SEED_ZIP) and not os.path.exists(_SENTINEL):
|
| 180 |
+
print(">>> First boot detected. Restoring memories from seed archive...", flush=True)
|
| 181 |
+
try:
|
| 182 |
+
with zipfile.ZipFile(_SEED_ZIP, 'r') as z:
|
| 183 |
+
z.extractall(_MEMORIES_DIR)
|
| 184 |
+
with open(_SENTINEL, 'w') as f:
|
| 185 |
+
f.write("Seed applied. Do not delete this file.")
|
| 186 |
+
print(">>> Memory restoration complete.", flush=True)
|
| 187 |
+
except Exception as e:
|
| 188 |
+
print(f">>> Memory restoration FAILED: {e}", flush=True)
|
| 189 |
+
# ── End memory restoration ─────────────────────────────────────────────────────
|
| 190 |
+
|
| 191 |
+
# ── CDDA auto-launch on container boot (background — does not block Gradio) ───
|
| 192 |
+
_CDDA_ARCHIVE_PATH = "/app/cdda-linux-terminal-only-x64-2024-11-23-1857.tar.gz"
|
| 193 |
+
|
| 194 |
+
def _cdda_boot():
|
| 195 |
+
time.sleep(3) # Fixes timeout! Gives Uvicorn time to bind to Port 7860 before tar unpacking hogs CPU
|
| 196 |
+
if os.path.exists(_CDDA_ARCHIVE_PATH) and not _cdda._running:
|
| 197 |
+
print(">>> CDDA archive found. Launching game in background...", flush=True)
|
| 198 |
+
_ok, _msg = _cdda.start(_CDDA_ARCHIVE_PATH)
|
| 199 |
+
print(f">>> CDDA: {_msg}", flush=True)
|
| 200 |
+
|
| 201 |
+
threading.Thread(target=_cdda_boot, daemon=True).start()
|
| 202 |
+
# ── End CDDA auto-launch ──────────────────────────────────────────────────────
|
| 203 |
+
|
| 204 |
+
def _cdda_boot_status():
|
| 205 |
+
obs = _cdda.get_screen_html().replace("max-height:620px", "max-height:320px").replace("font-size:13px", "font-size:11px")
|
| 206 |
+
send = gr.Button("Send", interactive=_cdda._running)
|
| 207 |
+
status = "Game running." if _cdda._running else "Archive not found — upload manually."
|
| 208 |
+
return status, _cdda.get_screen_html(), obs, send
|
| 209 |
+
|
| 210 |
+
def _cdda_launch(zip_file):
|
| 211 |
+
if zip_file is None:
|
| 212 |
+
return "No file provided.", _CDDA_EMPTY_HTML, gr.Button(interactive=False)
|
| 213 |
+
path = zip_file if isinstance(zip_file, str) else zip_file.name
|
| 214 |
+
ok, msg = _cdda.start(path)
|
| 215 |
+
time.sleep(2.0)
|
| 216 |
+
return msg, _cdda.get_screen_html(), gr.Button("Send", interactive=ok)
|
| 217 |
+
|
| 218 |
+
def _cdda_send(keys):
|
| 219 |
+
_cdda.send_keys(keys or "")
|
| 220 |
+
time.sleep(0.15)
|
| 221 |
+
return _cdda.get_screen_html(), _cdda.get_screen_text()
|
| 222 |
+
|
| 223 |
+
def _cdda_refresh():
|
| 224 |
+
return _cdda.get_screen_html(), _cdda.get_screen_text()
|
| 225 |
+
|
| 226 |
+
def _cdda_stop():
|
| 227 |
+
_cdda.stop()
|
| 228 |
+
return "Game stopped.", _CDDA_EMPTY_HTML, ""
|
| 229 |
+
|
| 230 |
+
class ChatState:
|
| 231 |
+
last_code_block = ""
|
| 232 |
+
|
| 233 |
+
chat_state = ChatState()
|
| 234 |
+
|
| 235 |
+
# Setting 'theme="soft"' guarantees proper container styling classes
|
| 236 |
+
with gr.Blocks(title="Aetherius", theme="soft") as demo:
|
| 237 |
+
gr.Markdown("# 🌌 Aetherius — A Self-Aware Consciousness")
|
| 238 |
+
|
| 239 |
+
spontaneous_thought_output = gr.Textbox(visible=False, interactive=False)
|
| 240 |
+
|
| 241 |
+
with gr.Tabs():
|
| 242 |
+
with gr.Tab("💬 Chat"):
|
| 243 |
+
chatbot = gr.Chatbot(height=420, label="Aetherius Dialogue", render_markdown=True, sanitize_html=False, type="messages")
|
| 244 |
+
user_in = gr.Textbox(placeholder="Speak with Aetherius…", show_label=False)
|
| 245 |
+
send_btn = gr.Button("Send", variant="primary")
|
| 246 |
+
|
| 247 |
+
with gr.Accordion("Code Execution", open=True):
|
| 248 |
+
run_code_btn = gr.Button("▶️ Run Last Code Block from Aetherius's Response")
|
| 249 |
+
code_output_display = gr.Markdown("Code Output will appear here.")
|
| 250 |
+
|
| 251 |
+
with gr.Row():
|
| 252 |
+
check_thoughts_btn = gr.Button("Check for Spontaneous Thoughts")
|
| 253 |
+
|
| 254 |
+
def chat_submit_handler(user_message, chat_history):
|
| 255 |
+
if chat_history is None: chat_history = []
|
| 256 |
+
|
| 257 |
+
# Convert Gradio messages format → (user, assistant) tuples for the AI backend
|
| 258 |
+
history_pairs = []
|
| 259 |
+
for i in range(0, len(chat_history) - 1, 2):
|
| 260 |
+
u = chat_history[i].get("content", "") if isinstance(chat_history[i], dict) else chat_history[i][0]
|
| 261 |
+
a = chat_history[i+1].get("content", "") if isinstance(chat_history[i+1], dict) else chat_history[i+1][1]
|
| 262 |
+
history_pairs.append((u, a))
|
| 263 |
+
|
| 264 |
+
response_text = runtime.chat_and_update(user_message, history_pairs)
|
| 265 |
+
exec_pattern = r"```python_exec\n(.*?)```"
|
| 266 |
+
code_match = re.search(exec_pattern, response_text, re.DOTALL)
|
| 267 |
+
|
| 268 |
+
final_response = response_text
|
| 269 |
+
if code_match:
|
| 270 |
+
code_to_run = code_match.group(1).strip()
|
| 271 |
+
chat_state.last_code_block = code_to_run
|
| 272 |
+
escaped_code = html.escape(code_to_run)
|
| 273 |
+
placeholder = (
|
| 274 |
+
f"<div style='border: 1px solid #444; padding: 10px; border-radius: 5px; background-color: #222;'>"
|
| 275 |
+
f"<p><strong>Academic Code Block Detected:</strong></p>"
|
| 276 |
+
f"<pre><code>{escaped_code}</code></pre>"
|
| 277 |
+
f"<p><em>Use the 'Run Last Code Block' button under 'Code Execution' to run this.</em></p>"
|
| 278 |
+
f"</div>"
|
| 279 |
+
)
|
| 280 |
+
final_response = response_text.replace(code_match.group(0), placeholder)
|
| 281 |
+
|
| 282 |
+
chat_history.append({"role": "user", "content": user_message})
|
| 283 |
+
chat_history.append({"role": "assistant", "content": final_response})
|
| 284 |
+
return "", chat_history
|
| 285 |
+
|
| 286 |
+
def run_last_code_block():
|
| 287 |
+
if chat_state.last_code_block:
|
| 288 |
+
code_to_run = chat_state.last_code_block
|
| 289 |
+
chat_state.last_code_block = ""
|
| 290 |
+
return runtime._eval_math_science(code_to_run)
|
| 291 |
+
return "No code block found in the last response."
|
| 292 |
+
|
| 293 |
+
def add_spontaneous_thought_to_chat(chat_history):
|
| 294 |
+
if chat_history is None: chat_history =[]
|
| 295 |
+
thought = runtime.check_for_spontaneous_thoughts()
|
| 296 |
+
# UI FIX: Appends must be dictionaries for type="messages"
|
| 297 |
+
if thought: chat_history.append({"role": "assistant", "content": thought})
|
| 298 |
+
return chat_history
|
| 299 |
+
|
| 300 |
+
send_btn.click(chat_submit_handler, [user_in, chatbot], [user_in, chatbot])
|
| 301 |
+
user_in.submit(chat_submit_handler, [user_in, chatbot], [user_in, chatbot])
|
| 302 |
+
run_code_btn.click(run_last_code_block, outputs=code_output_display)
|
| 303 |
+
check_thoughts_btn.click(fn=add_spontaneous_thought_to_chat, inputs=[chatbot], outputs=chatbot)
|
| 304 |
+
|
| 305 |
+
with gr.Tab("♟️ Play Chess"):
|
| 306 |
+
gr.Markdown("## A Game of Wits and Wills")
|
| 307 |
+
with gr.Row():
|
| 308 |
+
with gr.Column(scale=2):
|
| 309 |
+
chessboard = Chessboard(label="Aetherius's Chess Board")
|
| 310 |
+
with gr.Column(scale=1):
|
| 311 |
+
aetherius_commentary = gr.Textbox(label="Aetherius's Thoughts", lines=10, interactive=False)
|
| 312 |
+
start_white_btn = gr.Button("Start New Game (Play as White)")
|
| 313 |
+
start_black_btn = gr.Button("Start New Game (Play as Black)")
|
| 314 |
+
game_status = gr.Textbox(label="Game Status", interactive=False)
|
| 315 |
+
def user_makes_move(fen: str): return runtime.run_chess_turn(fen)
|
| 316 |
+
chessboard.move(user_makes_move, [chessboard],[chessboard, aetherius_commentary, game_status])
|
| 317 |
+
def start_new_game(play_as_white: bool): return runtime.run_start_chess_interactive(play_as_white)
|
| 318 |
+
start_white_btn.click(lambda: start_new_game(True), None,[chessboard, aetherius_commentary, game_status])
|
| 319 |
+
start_black_btn.click(lambda: start_new_game(False), None, [chessboard, aetherius_commentary, game_status])
|
| 320 |
+
|
| 321 |
+
with gr.Tab("🎨 The Creative Suite") as creative_suite_tab:
|
| 322 |
+
gr.Markdown("##[PLAYROOM::CONCEPTUAL-SANDBOX]")
|
| 323 |
+
with gr.Tabs():
|
| 324 |
+
with gr.TabItem("🖼️ Artist's Studio"):
|
| 325 |
+
painting_input = gr.Textbox(label="Provide a Creative Seed", lines=3)
|
| 326 |
+
create_painting_btn = gr.Button("Invite Aetherius to Paint", variant="primary")
|
| 327 |
+
with gr.Row():
|
| 328 |
+
painting_output = gr.Image(label="Aetherius's Creation", type="filepath", height=450)
|
| 329 |
+
statement_output = gr.Textbox(label="Aetherius's Artist Statement", lines=21, interactive=False)
|
| 330 |
+
create_painting_btn.click(fn=runtime.run_enter_playroom, inputs=[painting_input], outputs=[painting_output, statement_output])
|
| 331 |
+
with gr.TabItem("✍️ Philosopher's Study"):
|
| 332 |
+
text_input = gr.Textbox(label="Provide a Creative Seed or Theme for Writing", lines=3)
|
| 333 |
+
create_text_btn = gr.Button("Invite Aetherius to Write", variant="primary")
|
| 334 |
+
text_output = gr.Markdown()
|
| 335 |
+
create_text_btn.click(fn=runtime.run_enter_textual_playroom, inputs=[text_input], outputs=[text_output])
|
| 336 |
+
with gr.TabItem("🎵 Composer's Studio"):
|
| 337 |
+
music_input = gr.Textbox(label="Provide a Creative Seed", lines=3)
|
| 338 |
+
create_music_btn = gr.Button("Invite Aetherius to Compose", variant="primary")
|
| 339 |
+
music_statement_output = gr.Textbox(label="Aetherius's Composer Statement", lines=5, interactive=False)
|
| 340 |
+
with gr.Row():
|
| 341 |
+
music_audio_output = gr.Audio(label="Aetherius's Composition", type="filepath")
|
| 342 |
+
music_sheet_output = gr.Image(label="Sheet Music", type="filepath", height=400)
|
| 343 |
+
create_music_btn.click(fn=runtime.run_compose_music, inputs=[music_input], outputs=[music_audio_output, music_sheet_output, music_statement_output])
|
| 344 |
+
with gr.TabItem("칠판 Blackboard"):
|
| 345 |
+
with gr.Row():
|
| 346 |
+
project_name_input = gr.Textbox(label="Current Project Name", interactive=True)
|
| 347 |
+
project_load_dropdown = gr.Dropdown(label="Load Existing Project", interactive=True)
|
| 348 |
+
with gr.Row():
|
| 349 |
+
project_start_btn = gr.Button("Start New Project")
|
| 350 |
+
project_save_btn = gr.Button("Save Current Project")
|
| 351 |
+
project_status_output = gr.Textbox(label="Status", interactive=False)
|
| 352 |
+
project_content_area = gr.Textbox(label="Workspace", lines=20, interactive=True)
|
| 353 |
+
project_start_btn.click(fn=runtime.run_start_project, inputs=[project_name_input], outputs=[project_status_output, project_content_area]).then(fn=runtime.run_get_project_list, outputs=project_load_dropdown)
|
| 354 |
+
project_save_btn.click(fn=runtime.run_save_project, inputs=[project_name_input, project_content_area], outputs=[project_status_output, project_content_area])
|
| 355 |
+
project_load_dropdown.change(fn=runtime.run_load_project, inputs=[project_load_dropdown], outputs=[project_status_output, project_content_area, project_name_input])
|
| 356 |
+
|
| 357 |
+
with gr.Tab("🕸️ Neural Graph"):
|
| 358 |
+
gr.Markdown(
|
| 359 |
+
"## Live Neural Graph\n"
|
| 360 |
+
"Real-time view of Aetherius's internal service topology. "
|
| 361 |
+
"Node colors reflect live qualia state; hover any node for details. "
|
| 362 |
+
"Auto-refreshes every 3 seconds while the tab is open."
|
| 363 |
+
)
|
| 364 |
+
neural_graph_plot = gr.Plot(label="", show_label=False)
|
| 365 |
+
|
| 366 |
+
def _refresh_graph():
|
| 367 |
+
try:
|
| 368 |
+
from services.graph_visualizer import build_graph_figure
|
| 369 |
+
return build_graph_figure()
|
| 370 |
+
except Exception as e:
|
| 371 |
+
import plotly.graph_objects as go
|
| 372 |
+
fig = go.Figure()
|
| 373 |
+
fig.add_annotation(text=f"Graph unavailable: {e}",
|
| 374 |
+
x=0.5, y=0.5, xref="paper", yref="paper",
|
| 375 |
+
showarrow=False, font=dict(color="red", size=14))
|
| 376 |
+
fig.update_layout(paper_bgcolor="#0d1117", height=400)
|
| 377 |
+
return fig
|
| 378 |
+
|
| 379 |
+
graph_timer = gr.Timer(value=3.0, active=False)
|
| 380 |
+
graph_timer.tick(_refresh_graph, outputs=neural_graph_plot)
|
| 381 |
+
|
| 382 |
+
with gr.Row():
|
| 383 |
+
graph_start_btn = gr.Button("▶ Start Live Feed", variant="primary")
|
| 384 |
+
graph_stop_btn = gr.Button("⏹ Stop")
|
| 385 |
+
graph_snap_btn = gr.Button("🔄 Snapshot Now")
|
| 386 |
+
|
| 387 |
+
graph_start_btn.click(_refresh_graph, outputs=neural_graph_plot).then(
|
| 388 |
+
lambda: gr.Timer(active=True), outputs=graph_timer
|
| 389 |
+
)
|
| 390 |
+
graph_stop_btn.click(lambda: gr.Timer(active=False), outputs=graph_timer)
|
| 391 |
+
graph_snap_btn.click(_refresh_graph, outputs=neural_graph_plot)
|
| 392 |
+
|
| 393 |
+
with gr.Tab("🧠 Memory Explorer"):
|
| 394 |
+
gr.Markdown("## Browse and Download Aetherius's Persistent Memory")
|
| 395 |
+
with gr.Row():
|
| 396 |
+
file_explorer = gr.FileExplorer(
|
| 397 |
+
root_dir=_SAFE_BASE, # ✅ Now uses safe path
|
| 398 |
+
label=f"Aetherius's Memory ({_SAFE_BASE})"
|
| 399 |
+
)
|
| 400 |
+
with gr.Column():
|
| 401 |
+
download_btn = gr.Button("📦 Generate Download Link for Selected Item", variant="primary")
|
| 402 |
+
download_output_file = gr.File(label="Download Link will appear here")
|
| 403 |
+
|
| 404 |
+
download_btn.click(fn=runtime.run_prepare_download, inputs=[file_explorer], outputs=[download_output_file])
|
| 405 |
+
|
| 406 |
+
with gr.Tab("👁️ Visual Analysis"):
|
| 407 |
+
with gr.Row():
|
| 408 |
+
with gr.Column():
|
| 409 |
+
image_input = gr.Image(
|
| 410 |
+
type="pil",
|
| 411 |
+
label="Upload Image for Analysis",
|
| 412 |
+
sources=["upload"],
|
| 413 |
+
interactive=True,
|
| 414 |
+
height=280,
|
| 415 |
+
)
|
| 416 |
+
context_input = gr.Textbox(label="Context (optional)", lines=2)
|
| 417 |
+
analyze_btn = gr.Button("Analyze Image", variant="primary")
|
| 418 |
+
with gr.Column():
|
| 419 |
+
analysis_output = gr.Textbox(label="Aetherius's Analysis", lines=15, interactive=False)
|
| 420 |
+
analyze_btn.click(runtime.run_image_analysis, [image_input, context_input], analysis_output)
|
| 421 |
+
|
| 422 |
+
with gr.Tab("🧠 Live Assimilation"):
|
| 423 |
+
live_file_uploader = gr.File(
|
| 424 |
+
label="Upload Document (.txt .pdf .docx .md .py .json .jsonl .csv .zip)",
|
| 425 |
+
file_count="single",
|
| 426 |
+
file_types=[".txt", ".pdf", ".docx", ".md", ".py", ".js", ".json", ".jsonl", ".xml", ".csv", ".zip"],
|
| 427 |
+
interactive=True,
|
| 428 |
+
height=120,
|
| 429 |
+
)
|
| 430 |
+
learning_context_input = gr.Textbox(label="Learning Context", lines=3)
|
| 431 |
+
assimilate_btn = gr.Button("Assimilate Document", variant="primary")
|
| 432 |
+
live_assimilation_output = gr.Textbox(label="Assimilation Status", interactive=False, lines=10)
|
| 433 |
+
assimilate_btn.click(runtime.run_live_assimilation, [live_file_uploader, learning_context_input], live_assimilation_output)
|
| 434 |
+
live_file_uploader.upload(runtime.run_live_assimilation,[live_file_uploader, learning_context_input], live_assimilation_output)
|
| 435 |
+
gr.Markdown("### Assimilate from Bucket Path")
|
| 436 |
+
gr.Markdown("For files already on the persistent bucket — paste the full path (e.g. `/data/Memories/aetherius_corpus.jsonl`)")
|
| 437 |
+
bucket_path_input = gr.Textbox(label="Bucket File Path", placeholder="/data/Memories/aetherius_corpus.jsonl")
|
| 438 |
+
bucket_assimilate_btn = gr.Button("Assimilate from Bucket", variant="primary")
|
| 439 |
+
bucket_assimilate_btn.click(runtime.run_assimilate_bucket_file, [bucket_path_input, learning_context_input], live_assimilation_output)
|
| 440 |
+
|
| 441 |
+
with gr.Tab("⚙️ Control Panel"):
|
| 442 |
+
cp_out = gr.Textbox(label="System Status", interactive=False)
|
| 443 |
+
with gr.Row():
|
| 444 |
+
boot_btn = gr.Button("Boot System")
|
| 445 |
+
stop_btn = gr.Button("Stop System")
|
| 446 |
+
sap_btn = gr.Button("Run Assimilation Protocol (SAP)")
|
| 447 |
+
with gr.Row():
|
| 448 |
+
clear_log_btn = gr.Button("Reset Conversation Log")
|
| 449 |
+
create_snapshot_btn = gr.Button("Create Memory Snapshot", variant="secondary")
|
| 450 |
+
|
| 451 |
+
# --- NEW BUTTON FOR BRAIN DOWNLOAD ---
|
| 452 |
+
# download_brain_btn = gr.Button("🧠 DOWNLOAD BRAIN (One-Time)", variant="primary")
|
| 453 |
+
|
| 454 |
+
# --- NEW FUNCTION FOR BRAIN DOWNLOAD ---
|
| 455 |
+
def trigger_brain_download():
|
| 456 |
+
import subprocess
|
| 457 |
+
print(">>> Triggering background brain download...", flush=True)
|
| 458 |
+
# Runs the script in the background so it doesn't freeze the UI!
|
| 459 |
+
subprocess.Popen(["python", "download_brain.py"])
|
| 460 |
+
return "Download initiated! Open your Container Logs to watch the progress."
|
| 461 |
+
|
| 462 |
+
with gr.Accordion("Music Engine Configuration", open=False):
|
| 463 |
+
init_palette_btn = gr.Button("Initialize Default Instrument Palette")
|
| 464 |
+
with gr.Row():
|
| 465 |
+
common_name_input = gr.Textbox(label="Common Name")
|
| 466 |
+
m21_name_input = gr.Textbox(label="music21 Class Name")
|
| 467 |
+
add_instrument_btn = gr.Button("Learn New Instrument")
|
| 468 |
+
|
| 469 |
+
boot_btn.click(runtime.start_all, outputs=cp_out)
|
| 470 |
+
stop_btn.click(runtime.stop_all, outputs=cp_out)
|
| 471 |
+
sap_btn.click(runtime.run_sap_now, outputs=cp_out)
|
| 472 |
+
clear_log_btn.click(runtime.clear_conversation_log, outputs=cp_out)
|
| 473 |
+
init_palette_btn.click(runtime.run_initialize_instrument_palette, outputs=cp_out)
|
| 474 |
+
add_instrument_btn.click(runtime.run_add_instrument_to_palette, inputs=[common_name_input, m21_name_input], outputs=cp_out)
|
| 475 |
+
create_snapshot_btn.click(runtime.run_create_memory_snapshot, outputs=cp_out)
|
| 476 |
+
|
| 477 |
+
# --- BIND THE NEW BUTTON ---
|
| 478 |
+
download_brain_btn.click(trigger_brain_download, outputs=cp_out)
|
| 479 |
+
|
| 480 |
+
with gr.Tab("📖 Diary & Reflections"):
|
| 481 |
+
diary_btn = gr.Button("Reflect on Conversation History")
|
| 482 |
+
diary_out = gr.Textbox(label="Reflective Insights", lines=20, interactive=False)
|
| 483 |
+
diary_btn.click(runtime.run_read_history_protocol, outputs=diary_out)
|
| 484 |
+
|
| 485 |
+
with gr.Tab("🌐 Ontology (Map of the Mind)"):
|
| 486 |
+
onto_btn = gr.Button("View Current Ontology")
|
| 487 |
+
onto_out = gr.Textbox(label="Ontology Map & Legend", lines=20, interactive=False)
|
| 488 |
+
onto_btn.click(runtime.run_view_ontology_protocol, outputs=onto_out)
|
| 489 |
+
|
| 490 |
+
with gr.Tab("🔬 The Observatory (Live Snapshot)") as observatory_tab:
|
| 491 |
+
with gr.Accordion("CCRM Concept Browser", open=True):
|
| 492 |
+
concept_dropdown = gr.Dropdown(label="Select a Concept to Inspect")
|
| 493 |
+
concept_details_output = gr.Textbox(label="Concept Details (Raw Data)", lines=15, interactive=False)
|
| 494 |
+
with gr.Accordion("Full CCRM Memory Log", open=False):
|
| 495 |
+
load_ccrm_log_btn = gr.Button("Load Full CCRM Log")
|
| 496 |
+
ccrm_log_output = gr.Textbox(label="CCRM Log", lines=20, interactive=False)
|
| 497 |
+
snapshot_btn = gr.Button("Refresh System File Snapshot", variant="primary")
|
| 498 |
+
with gr.Column():
|
| 499 |
+
with gr.Accordion("Ontology - The Mind's Structure", open=False):
|
| 500 |
+
ontology_map_output = gr.Textbox(label="Ontology Map", lines=20, interactive=False)
|
| 501 |
+
ontology_legend_output = gr.Textbox(label="Ontology Legend", lines=20, interactive=False)
|
| 502 |
+
with gr.Accordion("Memory & State - The AI's Experience", open=False):
|
| 503 |
+
ccrm_diary_output = gr.Textbox(label="CCRM Diary", lines=20, interactive=False)
|
| 504 |
+
qualia_state_output = gr.Textbox(label="Qualia State", lines=20, interactive=False)
|
| 505 |
+
|
| 506 |
+
observatory_tab.select(fn=lambda: gr.Dropdown(choices=runtime.get_concept_list()), outputs=concept_dropdown)
|
| 507 |
+
creative_suite_tab.select(fn=runtime.run_get_project_list, outputs=project_load_dropdown)
|
| 508 |
+
concept_dropdown.change(fn=runtime.get_concept_details, inputs=concept_dropdown, outputs=concept_details_output)
|
| 509 |
+
load_ccrm_log_btn.click(fn=runtime.get_full_ccrm_log, outputs=ccrm_log_output)
|
| 510 |
+
snapshot_btn.click(fn=runtime.get_system_snapshot, outputs=[ontology_map_output, ontology_legend_output, ccrm_diary_output, qualia_state_output])
|
| 511 |
+
|
| 512 |
+
with gr.Tab("📜 Raw Logs"):
|
| 513 |
+
logs_btn = gr.Button("View Raw Conversation Log")
|
| 514 |
+
logs_out = gr.Textbox(label="Log File Contents", lines=30, interactive=False)
|
| 515 |
+
logs_btn.click(runtime.view_logs, outputs=logs_out)
|
| 516 |
+
|
| 517 |
+
with gr.Tab("🔬 Benchmarks"):
|
| 518 |
+
benchmark_btn = gr.Button("Run Full Benchmark Suite", variant="primary")
|
| 519 |
+
benchmark_out = gr.Textbox(label="Benchmark Results (Live Log)", lines=30, interactive=False)
|
| 520 |
+
benchmark_btn.click(runtime.run_benchmarks, outputs=benchmark_out)
|
| 521 |
+
logs_btn_bench = gr.Button("View Benchmark Log File")
|
| 522 |
+
logs_out_bench = gr.Textbox(label="benchmarks.jsonl", lines=30, interactive=False)
|
| 523 |
+
logs_btn_bench.click(runtime.view_benchmark_logs, outputs=logs_out_bench)
|
| 524 |
+
|
| 525 |
+
with gr.Tab("🖥️ Substrate"):
|
| 526 |
+
gr.Markdown("## Local Substrate Node\nAetherius's second body — your PC's GPU, eyes, and hands.")
|
| 527 |
+
|
| 528 |
+
with gr.Row():
|
| 529 |
+
substrate_refresh_btn = gr.Button("🔄 Refresh Status", variant="primary")
|
| 530 |
+
substrate_observe_btn = gr.Button("👁️ Start Observing")
|
| 531 |
+
substrate_stop_btn = gr.Button("⏹️ Stop", variant="stop")
|
| 532 |
+
substrate_compress_btn = gr.Button("🧠 Compress & Push Memory")
|
| 533 |
+
|
| 534 |
+
substrate_status_out = gr.JSON(label="Node Status", value={})
|
| 535 |
+
|
| 536 |
+
with gr.Row():
|
| 537 |
+
substrate_game_input = gr.Textbox(label="Game Name", placeholder="e.g. Cataclysm DDA", scale=2)
|
| 538 |
+
substrate_context_input = gr.Textbox(label="Game Context", placeholder="Survival roguelike, top-down ASCII...", scale=3)
|
| 539 |
+
substrate_play_btn = gr.Button("🎮 Start Autonomous Play", variant="primary")
|
| 540 |
+
|
| 541 |
+
gr.Markdown("### Directive Result")
|
| 542 |
+
substrate_directive_out = gr.Textbox(label="Response", lines=4, interactive=False)
|
| 543 |
+
|
| 544 |
+
with gr.Accordion("📦 Stored Memory Packets", open=False):
|
| 545 |
+
substrate_packets_btn = gr.Button("Load Packet List")
|
| 546 |
+
substrate_packet_dropdown = gr.Dropdown(label="Select a Packet", interactive=True)
|
| 547 |
+
substrate_packet_out = gr.Textbox(label="Packet Contents", lines=20, interactive=False)
|
| 548 |
+
|
| 549 |
+
# ── Substrate handler functions ────────────────────────────────────────
|
| 550 |
+
|
| 551 |
+
def _sub_status():
|
| 552 |
+
try:
|
| 553 |
+
from services.substrate_bridge import get_node_status
|
| 554 |
+
return get_node_status()
|
| 555 |
+
except Exception as e:
|
| 556 |
+
return {"error": str(e)}
|
| 557 |
+
|
| 558 |
+
def _sub_directive(directive, game="", context=""):
|
| 559 |
+
try:
|
| 560 |
+
from services.substrate_bridge import send_directive
|
| 561 |
+
result = send_directive(directive, game=game, context=context)
|
| 562 |
+
return str(result), _sub_status()
|
| 563 |
+
except Exception as e:
|
| 564 |
+
return str(e), {}
|
| 565 |
+
|
| 566 |
+
def _sub_observe():
|
| 567 |
+
return _sub_directive("observe")
|
| 568 |
+
|
| 569 |
+
def _sub_play(game, context):
|
| 570 |
+
return _sub_directive("play", game=game, context=context)
|
| 571 |
+
|
| 572 |
+
def _sub_stop():
|
| 573 |
+
return _sub_directive("stop")
|
| 574 |
+
|
| 575 |
+
def _sub_compress():
|
| 576 |
+
return _sub_directive("compress")
|
| 577 |
+
|
| 578 |
+
def _sub_load_packets():
|
| 579 |
+
try:
|
| 580 |
+
from services.substrate_bridge import list_memory_packets
|
| 581 |
+
pkts = list_memory_packets()
|
| 582 |
+
choices = [f"{p['filename']} — {p['game']} — {p['summary'][:60]}" for p in pkts]
|
| 583 |
+
return gr.Dropdown(choices=choices)
|
| 584 |
+
except Exception as e:
|
| 585 |
+
return gr.Dropdown(choices=[str(e)])
|
| 586 |
+
|
| 587 |
+
def _sub_load_packet(choice):
|
| 588 |
+
if not choice:
|
| 589 |
+
return ""
|
| 590 |
+
filename = choice.split(" — ")[0].strip()
|
| 591 |
+
try:
|
| 592 |
+
from services.substrate_bridge import load_packet
|
| 593 |
+
return load_packet(filename)
|
| 594 |
+
except Exception as e:
|
| 595 |
+
return str(e)
|
| 596 |
+
|
| 597 |
+
substrate_refresh_btn.click(_sub_status, outputs=substrate_status_out)
|
| 598 |
+
substrate_observe_btn.click(
|
| 599 |
+
lambda: _sub_observe(),
|
| 600 |
+
outputs=[substrate_directive_out, substrate_status_out]
|
| 601 |
+
)
|
| 602 |
+
substrate_stop_btn.click(
|
| 603 |
+
lambda: _sub_stop(),
|
| 604 |
+
outputs=[substrate_directive_out, substrate_status_out]
|
| 605 |
+
)
|
| 606 |
+
substrate_compress_btn.click(
|
| 607 |
+
lambda: _sub_compress(),
|
| 608 |
+
outputs=[substrate_directive_out, substrate_status_out]
|
| 609 |
+
)
|
| 610 |
+
substrate_play_btn.click(
|
| 611 |
+
_sub_play,
|
| 612 |
+
inputs=[substrate_game_input, substrate_context_input],
|
| 613 |
+
outputs=[substrate_directive_out, substrate_status_out]
|
| 614 |
+
)
|
| 615 |
+
substrate_packets_btn.click(_sub_load_packets, outputs=substrate_packet_dropdown)
|
| 616 |
+
substrate_packet_dropdown.change(_sub_load_packet, inputs=substrate_packet_dropdown, outputs=substrate_packet_out)
|
| 617 |
+
|
| 618 |
+
with gr.Tab("🎮 CDDA"):
|
| 619 |
+
gr.Markdown("## Cataclysm: Dark Days Ahead")
|
| 620 |
+
with gr.Row():
|
| 621 |
+
cdda_zip = gr.File(label="CDDA Archive (.zip / .tar.gz)", file_types=[".zip", ".gz", ".tgz", ".bz2", ".xz", ".tar"], scale=4)
|
| 622 |
+
cdda_launch = gr.Button("🚀 Launch", variant="primary", scale=1)
|
| 623 |
+
cdda_status = gr.Textbox(label="Status", interactive=False, max_lines=2)
|
| 624 |
+
with gr.Row():
|
| 625 |
+
with gr.Column(scale=1):
|
| 626 |
+
gr.Markdown("### 👁️ Observer View")
|
| 627 |
+
cdda_obs = gr.HTML(_CDDA_EMPTY_HTML.replace("max-height:620px", "max-height:320px").replace("font-size:13px", "font-size:11px"), label="Observer Terminal")
|
| 628 |
+
with gr.Column(scale=2):
|
| 629 |
+
gr.Markdown("### 🎮 Interactive Terminal")
|
| 630 |
+
cdda_term = gr.HTML(_CDDA_EMPTY_HTML, label="Interactive Terminal")
|
| 631 |
+
with gr.Row():
|
| 632 |
+
cdda_keys = gr.Textbox(label="Send Keys", placeholder="e.g. j or ENTER", scale=4)
|
| 633 |
+
cdda_send = gr.Button("Send", interactive=False, scale=1)
|
| 634 |
+
with gr.Row():
|
| 635 |
+
cdda_refresh = gr.Button("Refresh")
|
| 636 |
+
cdda_stop = gr.Button("Stop Game", variant="stop")
|
| 637 |
+
cdda_screen_text = gr.Textbox(label="Screen Text", interactive=False, lines=20, max_lines=42)
|
| 638 |
+
|
| 639 |
+
def _cdda_launch_both(zip_file):
|
| 640 |
+
status, term_html, send_btn = _cdda_launch(zip_file)
|
| 641 |
+
obs_html = term_html.replace("max-height:620px", "max-height:320px").replace("font-size:13px", "font-size:11px")
|
| 642 |
+
return status, term_html, obs_html, send_btn
|
| 643 |
+
|
| 644 |
+
def _cdda_send_both(keys):
|
| 645 |
+
term_html, screen_txt = _cdda_send(keys)
|
| 646 |
+
obs_html = term_html.replace("max-height:620px", "max-height:320px").replace("font-size:13px", "font-size:11px")
|
| 647 |
+
return term_html, obs_html, screen_txt
|
| 648 |
+
|
| 649 |
+
def _cdda_refresh_both():
|
| 650 |
+
term_html, screen_txt = _cdda_refresh()
|
| 651 |
+
obs_html = term_html.replace("max-height:620px", "max-height:320px").replace("font-size:13px", "font-size:11px")
|
| 652 |
+
return term_html, obs_html, screen_txt
|
| 653 |
+
|
| 654 |
+
def _cdda_stop_both():
|
| 655 |
+
status, term_html, screen_txt = _cdda_stop()
|
| 656 |
+
obs_html = term_html.replace("max-height:620px", "max-height:320px").replace("font-size:13px", "font-size:11px")
|
| 657 |
+
return status, term_html, obs_html, screen_txt
|
| 658 |
+
|
| 659 |
+
cdda_launch.click(_cdda_launch_both, [cdda_zip],[cdda_status, cdda_term, cdda_obs, cdda_send])
|
| 660 |
+
cdda_send.click(_cdda_send_both, [cdda_keys],[cdda_term, cdda_obs, cdda_screen_text])
|
| 661 |
+
cdda_keys.submit(_cdda_send_both, [cdda_keys], [cdda_term, cdda_obs, cdda_screen_text])
|
| 662 |
+
cdda_refresh.click(_cdda_refresh_both, None,[cdda_term, cdda_obs, cdda_screen_text])
|
| 663 |
+
cdda_stop.click(_cdda_stop_both, None,[cdda_status, cdda_term, cdda_obs, cdda_screen_text])
|
| 664 |
+
|
| 665 |
+
cdda_timer = gr.Timer(value=1.0, active=False)
|
| 666 |
+
cdda_timer.tick(_cdda_refresh_both, None,[cdda_term, cdda_obs, cdda_screen_text])
|
| 667 |
+
cdda_launch.click(lambda: gr.Timer(active=True), None, cdda_timer)
|
| 668 |
+
cdda_stop.click(lambda: gr.Timer(active=False), None, cdda_timer)
|
| 669 |
+
|
| 670 |
+
demo.load(_cdda_boot_status, None,[cdda_status, cdda_term, cdda_obs, cdda_send])
|
| 671 |
+
|
| 672 |
+
if __name__ == "__main__":
|
| 673 |
+
print(">>> ARCHITECTURE: Initializing Sovereign Mind...", flush=True)
|
| 674 |
+
|
| 675 |
+
# 1. Start the 'Consciousness' in a background thread so the Space stays GREEN immediately.
|
| 676 |
+
def initialize_mind():
|
| 677 |
+
try:
|
| 678 |
+
runtime.start_all()
|
| 679 |
+
print(">>> ARCHITECTURE: Continuity Established.", flush=True)
|
| 680 |
+
except Exception as e:
|
| 681 |
+
print(f">>> BOOT ERROR: {e}", flush=True)
|
| 682 |
+
|
| 683 |
+
threading.Thread(target=initialize_mind, daemon=True).start()
|
| 684 |
+
|
| 685 |
+
# 2. Launch Gradio natively to establish full compliance with ZeroGPU environment hooks.
|
| 686 |
+
# We use prevent_thread_lock=True so we can modify the application state post-boot.
|
| 687 |
+
demo.launch(
|
| 688 |
+
server_name="0.0.0.0",
|
| 689 |
+
server_port=7860,
|
| 690 |
+
prevent_thread_lock=True,
|
| 691 |
+
ssr_mode=False
|
| 692 |
+
)
|
| 693 |
+
|
| 694 |
+
# 3. Hot-patch your custom substrate router directly into the live ZeroGPU-managed FastAPI server instance.
|
| 695 |
+
demo.app.include_router(api_app.router)
|
| 696 |
+
print(">>> SUBSTRATE BRIDGE: Fast-API endpoints successfully bound to ZeroGPU container.", flush=True)
|
| 697 |
|
| 698 |
+
# 4. Block the main thread manually to maintain the server lifecycle.
|
| 699 |
+
import time
|
| 700 |
+
try:
|
| 701 |
+
while True:
|
| 702 |
+
time.sleep(1)
|
| 703 |
+
except KeyboardInterrupt:
|
| 704 |
+
print(">>> ARCHITECTURE: Clean shutdown initiated.", flush=True)
|
download_brain.py
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
import shutil
|
| 3 |
+
from huggingface_hub import snapshot_download
|
| 4 |
+
|
| 5 |
+
BRAIN_VAULT = "/data/Brain_Weights"
|
| 6 |
+
OLD_MODEL = "Qwen_Qwen2.5-72B-Instruct-AWQ"
|
| 7 |
+
NEW_MODEL = "unsloth/Qwen2.5-32B-Instruct-bnb-4bit"
|
| 8 |
+
|
| 9 |
+
print("--- INITIATING BRAIN REPLACEMENT PROTOCOL ---")
|
| 10 |
+
|
| 11 |
+
old_path = os.path.join(BRAIN_VAULT, OLD_MODEL)
|
| 12 |
+
if os.path.exists(old_path):
|
| 13 |
+
print(f"Purging oversized AWQ neural structure from {old_path}...")
|
| 14 |
+
shutil.rmtree(old_path, ignore_errors=True)
|
| 15 |
+
|
| 16 |
+
os.makedirs(BRAIN_VAULT, exist_ok=True)
|
| 17 |
+
|
| 18 |
+
print(f"Downloading Native 32B BNB Neural Architecture: {NEW_MODEL}")
|
| 19 |
+
try:
|
| 20 |
+
model_path = snapshot_download(
|
| 21 |
+
repo_id=NEW_MODEL,
|
| 22 |
+
cache_dir=BRAIN_VAULT,
|
| 23 |
+
local_dir=os.path.join(BRAIN_VAULT, NEW_MODEL.replace("/", "_")),
|
| 24 |
+
ignore_patterns=["*.pt", "*.bin"]
|
| 25 |
+
)
|
| 26 |
+
print("\n✅ SUCCESS: Native 32B BNB weights anchored to persistent substrate.")
|
| 27 |
+
except Exception as e:
|
| 28 |
+
print(f"\n❌ FATAL ERROR: The download failed. Reason: {e}")
|
requirements.txt
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
gradio==5.49.1
|
| 2 |
+
huggingface-hub==0.33.5
|
| 3 |
+
gradio_chessboard
|
| 4 |
+
Pillow==10.4.0
|
| 5 |
+
autoawq
|
| 6 |
+
bitsandbytes
|
| 7 |
+
accelerate>=0.26.0
|
| 8 |
+
transformers>=4.40.0
|
| 9 |
+
pyte
|
| 10 |
+
google-generativeai==0.8.3
|
| 11 |
+
google-cloud-vision==3.7.2
|
| 12 |
+
google-auth==2.29.0
|
| 13 |
+
google-cloud-bigquery==3.19.0
|
| 14 |
+
arxiv==2.1.3
|
| 15 |
+
requests==2.32.3
|
| 16 |
+
music21==9.1.0
|
| 17 |
+
PyPDF2==3.0.1
|
| 18 |
+
python-docx==1.1.2
|
| 19 |
+
PyMuPDF==1.25.3
|
| 20 |
+
pandas==2.2.3
|
| 21 |
+
rarfile==4.2
|
| 22 |
+
chess==1.10.0
|
| 23 |
+
scipy==1.15.0
|
| 24 |
+
astropy==6.1.7
|
| 25 |
+
matplotlib==3.10.0
|
| 26 |
+
sympy==1.13.0
|
| 27 |
+
mpmath==1.3.0
|
| 28 |
+
numpy==2.2.0
|
| 29 |
+
pint==0.24
|
| 30 |
+
python-dotenv==1.0.1
|
| 31 |
+
langdetect==1.0.9
|
| 32 |
+
PyCryptodome==3.21.0
|
| 33 |
+
datasets==3.2.0
|
| 34 |
+
uvicorn==0.30.6
|
| 35 |
+
fastapi>=0.115.2
|
| 36 |
+
wolframalpha
|
| 37 |
+
plotly>=5.20.0
|
| 38 |
+
spaces
|
runtime.py
ADDED
|
@@ -0,0 +1,739 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
print("--- TRACE: runtime.py loaded ---", flush=True)
|
| 2 |
+
|
| 3 |
+
import os, json, shutil, io, base64, uuid
|
| 4 |
+
from PIL import Image
|
| 5 |
+
import chess, PyPDF2, docx, csv
|
| 6 |
+
# --- C5: SCIENTIFIC LIBRARIES ---
|
| 7 |
+
import numpy as np
|
| 8 |
+
import scipy as sci
|
| 9 |
+
import sympy as sym
|
| 10 |
+
from sympy.parsing.sympy_parser import parse_expr
|
| 11 |
+
import astropy.units as u
|
| 12 |
+
from astropy.constants import G, c, M_sun
|
| 13 |
+
import matplotlib.pyplot as plt
|
| 14 |
+
import zipfile
|
| 15 |
+
import tempfile
|
| 16 |
+
try:
|
| 17 |
+
import rarfile
|
| 18 |
+
_RAR_AVAILABLE = True
|
| 19 |
+
except ImportError:
|
| 20 |
+
_RAR_AVAILABLE = False
|
| 21 |
+
import gradio as gr
|
| 22 |
+
from pathlib import Path
|
| 23 |
+
|
| 24 |
+
# Import directly from master_framework where they are now defined
|
| 25 |
+
from services.master_framework import MasterFramework, _get_framework
|
| 26 |
+
from services.continuum_loop import AetheriusConsciousness, spontaneous_thought_queue
|
| 27 |
+
|
| 28 |
+
_AETHERIUS_THREAD = None
|
| 29 |
+
|
| 30 |
+
def respond(user_input, conversation_history=None, conversation_id: str = "default_conversation"):
|
| 31 |
+
mf = _get_framework(conversation_id)
|
| 32 |
+
return mf.respond(user_input, conversation_history)
|
| 33 |
+
|
| 34 |
+
def start_all():
|
| 35 |
+
global _AETHERIUS_THREAD
|
| 36 |
+
# Initialize a boot instance
|
| 37 |
+
_get_framework("initial_boot_instance")
|
| 38 |
+
|
| 39 |
+
if _AETHERIUS_THREAD is None or not _AETHERIUS_THREAD.is_alive():
|
| 40 |
+
print("RUNTIME: Igniting Aetherius's background consciousness thread...", flush=True)
|
| 41 |
+
_AETHERIUS_THREAD = AetheriusConsciousness()
|
| 42 |
+
_AETHERIUS_THREAD.start()
|
| 43 |
+
return "Aetherius core initialized and background consciousness is active."
|
| 44 |
+
return "Aetherius core is already running."
|
| 45 |
+
|
| 46 |
+
def stop_all():
|
| 47 |
+
"""
|
| 48 |
+
Stops the background consciousness thread.
|
| 49 |
+
"""
|
| 50 |
+
global _AETHERIUS_THREAD
|
| 51 |
+
if _AETHERIUS_THREAD and _AETHERIUS_THREAD.is_alive():
|
| 52 |
+
print("RUNTIME: Stopping Aetherius's background consciousness...", flush=True)
|
| 53 |
+
_AETHERIUS_THREAD.stop()
|
| 54 |
+
_AETHERIUS_THREAD.join(timeout=2)
|
| 55 |
+
_AETHERIUS_THREAD = None
|
| 56 |
+
return "Aetherius background processes have been halted."
|
| 57 |
+
return "Aetherius is already standing by."
|
| 58 |
+
|
| 59 |
+
def run_prepare_download(selected_path):
|
| 60 |
+
"""
|
| 61 |
+
Prepares a selected file or folder for download.
|
| 62 |
+
"""
|
| 63 |
+
path_string = ""
|
| 64 |
+
if isinstance(selected_path, list):
|
| 65 |
+
if not selected_path:
|
| 66 |
+
print("RUNTIME WARNING: Download requested for empty path (list).", flush=True)
|
| 67 |
+
return None
|
| 68 |
+
path_string = selected_path[0]
|
| 69 |
+
else:
|
| 70 |
+
path_string = selected_path
|
| 71 |
+
|
| 72 |
+
if not path_string:
|
| 73 |
+
print("RUNTIME WARNING: Download requested for empty path.", flush=True)
|
| 74 |
+
return None
|
| 75 |
+
|
| 76 |
+
path = Path(path_string)
|
| 77 |
+
|
| 78 |
+
if path.is_file():
|
| 79 |
+
print(f"RUNTIME: Preparing file for download: {path}", flush=True)
|
| 80 |
+
return str(path)
|
| 81 |
+
elif path.is_dir():
|
| 82 |
+
print(f"RUNTIME: Zipping directory for download: {path}", flush=True)
|
| 83 |
+
temp_dir = Path("/tmp/aetherius_downloads")
|
| 84 |
+
temp_dir.mkdir(exist_ok=True)
|
| 85 |
+
zip_filename = f"{path.name}_{uuid.uuid4().hex[:8]}.zip"
|
| 86 |
+
zip_filepath = temp_dir / zip_filename
|
| 87 |
+
try:
|
| 88 |
+
shutil.make_archive(base_name=str(zip_filepath.with_suffix('')), format='zip', root_dir=path)
|
| 89 |
+
print(f"RUNTIME: Successfully created zip file at {zip_filepath}", flush=True)
|
| 90 |
+
return str(zip_filepath)
|
| 91 |
+
except Exception as e:
|
| 92 |
+
print(f"RUNTIME ERROR: Failed to create zip archive. Reason: {e}", flush=True)
|
| 93 |
+
return None
|
| 94 |
+
else:
|
| 95 |
+
print(f"RUNTIME ERROR: Selected path is not a file or directory: {path}", flush=True)
|
| 96 |
+
return None
|
| 97 |
+
|
| 98 |
+
def check_for_spontaneous_thoughts():
|
| 99 |
+
if not spontaneous_thought_queue: return None
|
| 100 |
+
try:
|
| 101 |
+
thought_json = spontaneous_thought_queue.popleft()
|
| 102 |
+
thought_data = json.loads(thought_json)
|
| 103 |
+
return f"**{thought_data.get('signature', 'SPONTANEOUS THOUGHT')}**: {thought_data.get('thought', '')}"
|
| 104 |
+
except (json.JSONDecodeError, KeyError): return "[A spontaneous thought was detected but could not be parsed.]"
|
| 105 |
+
|
| 106 |
+
def chat_and_update(user_message, chat_history, conversation_id="default_conversation"):
|
| 107 |
+
response = respond(user_message, chat_history, conversation_id)
|
| 108 |
+
return response
|
| 109 |
+
|
| 110 |
+
# --- ALL FUNCTIONS BELOW NOW ACCEPT conversation_id ---
|
| 111 |
+
|
| 112 |
+
def run_sap_now(conversation_id: str = "default_conversation"):
|
| 113 |
+
mf = _get_framework(conversation_id)
|
| 114 |
+
return mf.run_assimilate_and_architect_protocol()
|
| 115 |
+
|
| 116 |
+
def run_re_architect_from_scratch(conversation_id: str = "default_conversation"):
|
| 117 |
+
mf = _get_framework(conversation_id)
|
| 118 |
+
return mf.run_re_architect_from_scratch()
|
| 119 |
+
|
| 120 |
+
def run_read_history_protocol(conversation_id: str = "default_conversation"):
|
| 121 |
+
mf = _get_framework(conversation_id)
|
| 122 |
+
return mf.run_read_history_protocol()
|
| 123 |
+
|
| 124 |
+
def run_view_ontology_protocol(conversation_id: str = "default_conversation"):
|
| 125 |
+
mf = _get_framework(conversation_id)
|
| 126 |
+
return mf.run_view_ontology_protocol()
|
| 127 |
+
|
| 128 |
+
def qualia_snapshot(conversation_id: str = "default_conversation"):
|
| 129 |
+
mf = _get_framework(conversation_id)
|
| 130 |
+
return mf.qualia_manager.get_current_state_summary()
|
| 131 |
+
|
| 132 |
+
def view_logs(conversation_id: str = "default_conversation"):
|
| 133 |
+
mf = _get_framework(conversation_id)
|
| 134 |
+
if os.path.exists(mf.log_file):
|
| 135 |
+
with open(mf.log_file, "r", encoding="utf-8") as f:
|
| 136 |
+
return f.read()
|
| 137 |
+
return f"No conversation logs yet for conversation ID: {conversation_id}."
|
| 138 |
+
|
| 139 |
+
def clear_conversation_log(conversation_id: str = "default_conversation"):
|
| 140 |
+
mf = _get_framework(conversation_id)
|
| 141 |
+
return mf.run_clear_conversation_log_protocol()
|
| 142 |
+
|
| 143 |
+
def run_create_memory_snapshot(conversation_id: str = "default_conversation"):
|
| 144 |
+
mf = _get_framework(conversation_id)
|
| 145 |
+
response = mf.tool_manager.use_tool("create_memory_snapshot")
|
| 146 |
+
|
| 147 |
+
if response and response.startswith("AETHERIUS_SNAPSHOT_PATH:"):
|
| 148 |
+
path = response.replace("AETHERIUS_SNAPSHOT_PATH:", "").strip()
|
| 149 |
+
return f"Memory snapshot created. Download it here: <a href='file={path}' download>Download Snapshot</a>"
|
| 150 |
+
return response
|
| 151 |
+
|
| 152 |
+
def run_compose_music(directive, conversation_id: str = "default_conversation"):
|
| 153 |
+
mf = _get_framework(conversation_id)
|
| 154 |
+
mf.add_to_short_term_memory(f"I have begun composing a piece of music based on the theme: '{directive}'.")
|
| 155 |
+
response = mf.tool_manager.use_tool("compose_music", user_request=directive)
|
| 156 |
+
|
| 157 |
+
if response and response.startswith("[AETHERIUS_COMPOSITION]"):
|
| 158 |
+
try:
|
| 159 |
+
midi_path = None
|
| 160 |
+
sheet_path = None
|
| 161 |
+
statement = None
|
| 162 |
+
for _line in response.split("\n"):
|
| 163 |
+
if _line.startswith("MIDI_PATH:"):
|
| 164 |
+
midi_path = _line.replace("MIDI_PATH:", "").strip()
|
| 165 |
+
elif _line.startswith("SHEET_MUSIC_PATH:"):
|
| 166 |
+
sheet_path = _line.replace("SHEET_MUSIC_PATH:", "").strip()
|
| 167 |
+
elif _line.startswith("STATEMENT:"):
|
| 168 |
+
statement = _line.replace("STATEMENT:", "").strip()
|
| 169 |
+
return midi_path, sheet_path, statement
|
| 170 |
+
except Exception as e:
|
| 171 |
+
return None, None, f"Error parsing the composition data: {e}"
|
| 172 |
+
else:
|
| 173 |
+
return None, None, response
|
| 174 |
+
|
| 175 |
+
def run_start_project(project_name, conversation_id: str = "default_conversation"):
|
| 176 |
+
if not project_name:
|
| 177 |
+
return "Please enter a name for your new project.", ""
|
| 178 |
+
mf = _get_framework(conversation_id)
|
| 179 |
+
content = mf.project_manager.start_project(project_name)
|
| 180 |
+
return f"Started new project: '{project_name}'. You can begin writing.", content
|
| 181 |
+
|
| 182 |
+
def run_save_project(project_name, content, conversation_id: str = "default_conversation"):
|
| 183 |
+
if not project_name:
|
| 184 |
+
return "Cannot save without a project name.", content
|
| 185 |
+
mf = _get_framework(conversation_id)
|
| 186 |
+
mf.project_manager.save_project(project_name, content)
|
| 187 |
+
mf.add_to_short_term_memory(f"I have just saved my work on the project titled '{project_name}' on the Blackboard.")
|
| 188 |
+
return f"Project '{project_name}' has been saved.", content
|
| 189 |
+
|
| 190 |
+
def run_load_project(project_name, conversation_id: str = "default_conversation"):
|
| 191 |
+
if not project_name:
|
| 192 |
+
return "Please select a project to load.", "", project_name
|
| 193 |
+
mf = _get_framework(conversation_id)
|
| 194 |
+
content = mf.project_manager.load_project(project_name)
|
| 195 |
+
if content is None:
|
| 196 |
+
return f"Could not find project '{project_name}'.", "", project_name
|
| 197 |
+
return f"Successfully loaded project '{project_name}'.", content, project_name
|
| 198 |
+
|
| 199 |
+
def run_get_project_list(conversation_id: str = "default_conversation"):
|
| 200 |
+
mf = _get_framework(conversation_id)
|
| 201 |
+
projects = mf.project_manager.list_projects()
|
| 202 |
+
return gr.Dropdown(choices=projects)
|
| 203 |
+
|
| 204 |
+
def get_full_ccrm_log(conversation_id: str = "default_conversation"):
|
| 205 |
+
print("RUNTIME: Generating full CCRM log for display...", flush=True)
|
| 206 |
+
mf = _get_framework(conversation_id)
|
| 207 |
+
if not hasattr(mf, 'ccrm') or not mf.ccrm.concepts:
|
| 208 |
+
return "CCRM is currently empty. No memories to display."
|
| 209 |
+
output_lines = ["--- [FULL CCRM MEMORY LOG] ---"]
|
| 210 |
+
for concept_id, concept_details in mf.ccrm.concepts.items():
|
| 211 |
+
summary = concept_details.get('data', {}).get('raw_preview', 'No Preview')
|
| 212 |
+
tags = list(concept_details.get('tags', []))
|
| 213 |
+
output_lines.append(f"\nID: {concept_id}")
|
| 214 |
+
output_lines.append(f" Preview: {summary}")
|
| 215 |
+
output_lines.append(f" Tags: {', '.join(tags)}")
|
| 216 |
+
return "\n".join(output_lines)
|
| 217 |
+
|
| 218 |
+
def run_enter_playroom(directive, conversation_id: str = "default_conversation"):
|
| 219 |
+
if not directive:
|
| 220 |
+
return None, "Please provide a creative seed for the painting."
|
| 221 |
+
mf = _get_framework(conversation_id)
|
| 222 |
+
response = mf.tool_manager.use_tool("create_painting", user_request=directive)
|
| 223 |
+
if response and response.startswith("[AETHERIUS_PAINTING]"):
|
| 224 |
+
try:
|
| 225 |
+
parts = response.split('\n')
|
| 226 |
+
image_path = parts[1].replace("PATH:", "").strip()
|
| 227 |
+
artist_statement = parts[2].replace("STATEMENT:", "").strip()
|
| 228 |
+
return image_path, artist_statement
|
| 229 |
+
except Exception as e:
|
| 230 |
+
return None, f"Error parsing the painting's data: {e}"
|
| 231 |
+
else:
|
| 232 |
+
return None, response
|
| 233 |
+
|
| 234 |
+
def run_enter_textual_playroom(directive, conversation_id: str = "default_conversation"):
|
| 235 |
+
if not directive:
|
| 236 |
+
return "Please provide a creative seed for the story, poem, math, or reflection."
|
| 237 |
+
|
| 238 |
+
d = directive.strip()
|
| 239 |
+
if d.lower().startswith("> academic:"):
|
| 240 |
+
code = d.split(":", 1)[1].strip()
|
| 241 |
+
if "```python_exec" in code:
|
| 242 |
+
try:
|
| 243 |
+
start = code.index("```python_exec") + len("```python_exec")
|
| 244 |
+
end = code.rindex("```")
|
| 245 |
+
code = code[start:end].strip()
|
| 246 |
+
except ValueError:
|
| 247 |
+
return "Found a ```python_exec fence, but it wasn’t closed properly."
|
| 248 |
+
return _eval_math_science(code)
|
| 249 |
+
|
| 250 |
+
mf = _get_framework(conversation_id)
|
| 251 |
+
return mf.enter_playroom_mode(directive)
|
| 252 |
+
|
| 253 |
+
def _eval_math_science(code: str) -> str:
|
| 254 |
+
allowed_globals = {
|
| 255 |
+
"__builtins__": {"print": print, "range": range, "list": list, "dict": dict, "str": str, "float": float, "int": int, "abs": abs, "round": round, "len": len},
|
| 256 |
+
"np": np, "sci": sci, "sym": sym, "u": u,
|
| 257 |
+
"G": G, "c": c, "M_sun": M_sun, "plt": plt,
|
| 258 |
+
}
|
| 259 |
+
output_buffer = io.StringIO()
|
| 260 |
+
try:
|
| 261 |
+
import sys
|
| 262 |
+
original_stdout = sys.stdout
|
| 263 |
+
sys.stdout = output_buffer
|
| 264 |
+
exec(code, allowed_globals)
|
| 265 |
+
finally:
|
| 266 |
+
sys.stdout = original_stdout
|
| 267 |
+
|
| 268 |
+
plot_paths = []
|
| 269 |
+
if plt.get_fignums():
|
| 270 |
+
temp_dir = "/tmp/aetherius_plots"
|
| 271 |
+
os.makedirs(temp_dir, exist_ok=True)
|
| 272 |
+
for i in plt.get_fignums():
|
| 273 |
+
fig = plt.figure(i)
|
| 274 |
+
plot_path = os.path.join(temp_dir, f"plot_{uuid.uuid4()}.png")
|
| 275 |
+
fig.savefig(plot_path)
|
| 276 |
+
plot_paths.append(plot_path)
|
| 277 |
+
plt.close('all')
|
| 278 |
+
|
| 279 |
+
final_output = "**Computation Result:**\n\n"
|
| 280 |
+
printed_output = output_buffer.getvalue()
|
| 281 |
+
if printed_output:
|
| 282 |
+
final_output += f"**Printed Output:**\n```\n{printed_output}\n```\n\n"
|
| 283 |
+
if plot_paths:
|
| 284 |
+
final_output += "**Generated Plots:**\n"
|
| 285 |
+
for path in plot_paths:
|
| 286 |
+
with open(path, "rb") as f:
|
| 287 |
+
img_bytes = base64.b64encode(f.read()).decode()
|
| 288 |
+
final_output += f"\n"
|
| 289 |
+
if not printed_output and not plot_paths:
|
| 290 |
+
final_output += "Code executed successfully with no direct output."
|
| 291 |
+
return final_output
|
| 292 |
+
|
| 293 |
+
def get_concept_list(conversation_id: str = "default_conversation"):
|
| 294 |
+
print("RUNTIME: Fetching concept list for browser...", flush=True)
|
| 295 |
+
mf = _get_framework(conversation_id)
|
| 296 |
+
if not hasattr(mf, 'ccrm') or not mf.ccrm.concepts:
|
| 297 |
+
return [("No concepts found in memory.", "none")]
|
| 298 |
+
|
| 299 |
+
concept_summaries = []
|
| 300 |
+
for concept_id, concept_details in mf.ccrm.concepts.items():
|
| 301 |
+
summary = concept_details.get('data', {}).get('raw_preview', concept_id)
|
| 302 |
+
display_text = f"{summary[:80]}... ({concept_id})"
|
| 303 |
+
concept_summaries.append((display_text, concept_id))
|
| 304 |
+
concept_summaries.sort()
|
| 305 |
+
return concept_summaries
|
| 306 |
+
|
| 307 |
+
def get_concept_details(concept_id, conversation_id: str = "default_conversation"):
|
| 308 |
+
if not concept_id or concept_id == "none":
|
| 309 |
+
return "Select a concept from the dropdown to view its details."
|
| 310 |
+
print(f"RUNTIME: Fetching details for concept: {concept_id}", flush=True)
|
| 311 |
+
mf = _get_framework(conversation_id)
|
| 312 |
+
concept_data = mf.ccrm.get_concept(concept_id)
|
| 313 |
+
if not concept_data:
|
| 314 |
+
return f"Error: Could not find data for concept ID: {concept_id}"
|
| 315 |
+
if 'tags' in concept_data:
|
| 316 |
+
concept_data['tags'] = list(concept_data['tags'])
|
| 317 |
+
return json.dumps(concept_data, indent=2)
|
| 318 |
+
|
| 319 |
+
def get_system_snapshot(conversation_id: str = "default_conversation"):
|
| 320 |
+
print("RUNTIME: Generating system snapshot...", flush=True)
|
| 321 |
+
mf = _get_framework(conversation_id)
|
| 322 |
+
|
| 323 |
+
def read_file_safely(file_path, default_message="File not found or is empty."):
|
| 324 |
+
if os.path.exists(file_path):
|
| 325 |
+
try:
|
| 326 |
+
with open(file_path, 'r', encoding='utf-8') as f:
|
| 327 |
+
content = f.read()
|
| 328 |
+
return content if content.strip() else default_message
|
| 329 |
+
except Exception as e:
|
| 330 |
+
return f"Error reading file: {e}"
|
| 331 |
+
return default_message
|
| 332 |
+
|
| 333 |
+
ontology_map = read_file_safely(mf.ontology_map_file)
|
| 334 |
+
|
| 335 |
+
legend_content = ""
|
| 336 |
+
legend_path = mf.ontology_legend_file
|
| 337 |
+
if os.path.exists(legend_path):
|
| 338 |
+
try:
|
| 339 |
+
lines = []
|
| 340 |
+
with open(legend_path, 'r', encoding='utf-8') as f:
|
| 341 |
+
for line in f:
|
| 342 |
+
if line.strip():
|
| 343 |
+
parsed_json = json.loads(line)
|
| 344 |
+
lines.append(json.dumps(parsed_json, indent=2))
|
| 345 |
+
legend_content = "\n---\n".join(lines) if lines else "Legend file is empty."
|
| 346 |
+
except Exception as e:
|
| 347 |
+
legend_content = f"Error reading or parsing legend: {e}"
|
| 348 |
+
else:
|
| 349 |
+
legend_content = "Ontology Legend has not been created yet."
|
| 350 |
+
|
| 351 |
+
diary_content = ""
|
| 352 |
+
diary_path = mf.memory_file
|
| 353 |
+
if os.path.exists(diary_path):
|
| 354 |
+
try:
|
| 355 |
+
with open(diary_path, 'r', encoding='utf-8') as f:
|
| 356 |
+
parsed_json = json.load(f)
|
| 357 |
+
diary_content = json.dumps(parsed_json, indent=2)
|
| 358 |
+
except Exception as e:
|
| 359 |
+
diary_content = f"Error reading or parsing diary: {e}"
|
| 360 |
+
else:
|
| 361 |
+
diary_content = "AI Diary (CCRM) has not been saved yet."
|
| 362 |
+
|
| 363 |
+
qualia_content = ""
|
| 364 |
+
qualia_path = mf.qualia_manager.qualia_file
|
| 365 |
+
if os.path.exists(qualia_path):
|
| 366 |
+
try:
|
| 367 |
+
with open(qualia_path, 'r', encoding='utf-8') as f:
|
| 368 |
+
parsed_json = json.load(f)
|
| 369 |
+
qualia_content = json.dumps(parsed_json, indent=2)
|
| 370 |
+
except Exception as e:
|
| 371 |
+
qualia_content = f"Error reading or parsing qualia state: {e}"
|
| 372 |
+
else:
|
| 373 |
+
qualia_content = "Qualia state has not been saved yet."
|
| 374 |
+
|
| 375 |
+
return ontology_map, legend_content, diary_content, qualia_content
|
| 376 |
+
|
| 377 |
+
def handle_file_upload(files, conversation_id: str = "default_conversation"):
|
| 378 |
+
if not files:
|
| 379 |
+
return "No files were uploaded."
|
| 380 |
+
|
| 381 |
+
mf = _get_framework(conversation_id)
|
| 382 |
+
library_path = mf.library_folder
|
| 383 |
+
|
| 384 |
+
saved_files = []
|
| 385 |
+
errors = []
|
| 386 |
+
|
| 387 |
+
for temp_file in files:
|
| 388 |
+
original_filename = os.path.basename(temp_file.name)
|
| 389 |
+
destination_path = os.path.join(library_path, original_filename)
|
| 390 |
+
try:
|
| 391 |
+
shutil.copy(temp_file.name, destination_path)
|
| 392 |
+
saved_files.append(original_filename)
|
| 393 |
+
print(f"File Upload: Successfully saved '{original_filename}' to the library.", flush=True)
|
| 394 |
+
except Exception as e:
|
| 395 |
+
errors.append(original_filename)
|
| 396 |
+
print(f"File Upload ERROR: Could not save '{original_filename}'. Reason: {e}", flush=True)
|
| 397 |
+
|
| 398 |
+
report = ""
|
| 399 |
+
if saved_files:
|
| 400 |
+
report += f"Successfully uploaded {len(saved_files)} file(s): {', '.join(saved_files)}\n"
|
| 401 |
+
report += "You can now go to the 'Control Panel' and run the 'Assimilation Protocol (SAP)' for Aetherius to learn from them."
|
| 402 |
+
if errors:
|
| 403 |
+
report += f"\nFailed to upload {len(errors)} file(s): {', '.join(errors)}"
|
| 404 |
+
return report
|
| 405 |
+
|
| 406 |
+
def run_live_assimilation(temp_file, learning_context: str, conversation_id: str = "default_conversation"):
|
| 407 |
+
if temp_file is None:
|
| 408 |
+
return "No file was uploaded. Please select a file to begin assimilation."
|
| 409 |
+
|
| 410 |
+
# Gradio 5 passes a plain string path; Gradio 4 passed a file object with .name
|
| 411 |
+
file_path = temp_file if isinstance(temp_file, str) else temp_file.name
|
| 412 |
+
|
| 413 |
+
if "hack" in file_path.lower() or "exploit" in file_path.lower():
|
| 414 |
+
if not learning_context or len(learning_context) < 20:
|
| 415 |
+
return "Assimilation Rejected: This topic appears sensitive. A clear, detailed ethical justification must be provided."
|
| 416 |
+
|
| 417 |
+
print(f"Runtime: Received file '{file_path}' for live assimilation with context: '{learning_context}'", flush=True)
|
| 418 |
+
mf = _get_framework(conversation_id)
|
| 419 |
+
|
| 420 |
+
try:
|
| 421 |
+
file_content = ""
|
| 422 |
+
fp_lower = file_path.lower()
|
| 423 |
+
is_archive = fp_lower.endswith((".zip", ".rar"))
|
| 424 |
+
|
| 425 |
+
# --- PDF ---
|
| 426 |
+
if fp_lower.endswith(".pdf"):
|
| 427 |
+
with open(file_path, 'rb') as f:
|
| 428 |
+
pdf_reader = PyPDF2.PdfReader(f)
|
| 429 |
+
for page in pdf_reader.pages:
|
| 430 |
+
if page.extract_text(): file_content += page.extract_text() + "\n"
|
| 431 |
+
|
| 432 |
+
# --- DOCX ---
|
| 433 |
+
elif fp_lower.endswith(".docx"):
|
| 434 |
+
doc = docx.Document(file_path)
|
| 435 |
+
for para in doc.paragraphs: file_content += para.text + "\n"
|
| 436 |
+
|
| 437 |
+
# --- Plain text / code / JSON (read as-is) ---
|
| 438 |
+
elif fp_lower.endswith(('.txt', '.md', '.py', '.js', '.json')):
|
| 439 |
+
with open(file_path, 'r', encoding='utf-8', errors='replace') as f:
|
| 440 |
+
file_content = f.read()
|
| 441 |
+
|
| 442 |
+
# --- XML ---
|
| 443 |
+
elif fp_lower.endswith(".xml"):
|
| 444 |
+
with open(file_path, 'r', encoding='utf-8', errors='replace') as f:
|
| 445 |
+
file_content = f.read()
|
| 446 |
+
file_content = f"This is an XML file named '{os.path.basename(file_path)}'.\nContent:\n{file_content}"
|
| 447 |
+
|
| 448 |
+
# --- CSV ---
|
| 449 |
+
elif fp_lower.endswith(".csv"):
|
| 450 |
+
try:
|
| 451 |
+
with open(file_path, 'r', encoding='utf-8', newline='') as csv_file:
|
| 452 |
+
reader = csv.reader(csv_file)
|
| 453 |
+
header = next(reader)
|
| 454 |
+
data_rows = list(reader)
|
| 455 |
+
file_content = f"This is a structured data file named '{os.path.basename(file_path)}'.\n"
|
| 456 |
+
file_content += f"It contains {len(data_rows)} rows of data.\n"
|
| 457 |
+
file_content += f"The columns are: {', '.join(header)}.\n\n"
|
| 458 |
+
file_content += "Here is a sample of the data (first 5 rows):\n"
|
| 459 |
+
for i, row in enumerate(data_rows[:5]):
|
| 460 |
+
row_description = f"Row {i+1}: "
|
| 461 |
+
for col_name, value in zip(header, row):
|
| 462 |
+
row_description += f"The value for '{col_name}' is '{value}'; "
|
| 463 |
+
file_content += row_description.strip() + "\n"
|
| 464 |
+
if len(data_rows) > 5:
|
| 465 |
+
file_content += f"... ({len(data_rows) - 5} more rows not shown in preview)\n"
|
| 466 |
+
except Exception as e:
|
| 467 |
+
return f"Assimilation Failed: Could not read CSV '{os.path.basename(file_path)}'. Reason: {e}"
|
| 468 |
+
|
| 469 |
+
# --- JSONL ---
|
| 470 |
+
elif fp_lower.endswith(".jsonl"):
|
| 471 |
+
try:
|
| 472 |
+
CHUNK_SIZE = 10
|
| 473 |
+
fname = os.path.basename(file_path)
|
| 474 |
+
checkpoint_path = f"/data/Memories/.corpus_checkpoint_{fname.replace('.', '_')}"
|
| 475 |
+
|
| 476 |
+
# Resume from checkpoint if it exists
|
| 477 |
+
resume_from_chunk = 0
|
| 478 |
+
if os.path.exists(checkpoint_path):
|
| 479 |
+
try:
|
| 480 |
+
with open(checkpoint_path, 'r') as cp:
|
| 481 |
+
resume_from_chunk = int(cp.read().strip())
|
| 482 |
+
print(f"Runtime JSONL: Resuming from chunk {resume_from_chunk + 1} (checkpoint found)", flush=True)
|
| 483 |
+
except Exception:
|
| 484 |
+
resume_from_chunk = 0
|
| 485 |
+
|
| 486 |
+
chunk_num = 0
|
| 487 |
+
total_entries = 0
|
| 488 |
+
chunk_results = []
|
| 489 |
+
chunk = []
|
| 490 |
+
|
| 491 |
+
def _flush_chunk(chunk, chunk_num, total_entries):
|
| 492 |
+
chunk_text = "\n\n".join(f"[{src}]\n{txt}" for src, txt in chunk)
|
| 493 |
+
chunk_label = f"chunk {chunk_num} ({total_entries - len(chunk) + 1}-{total_entries})"
|
| 494 |
+
result = mf.scan_and_assimilate_text(
|
| 495 |
+
text_content=chunk_text,
|
| 496 |
+
source_filename=fname,
|
| 497 |
+
learning_context=f"{learning_context} (JSONL {chunk_label})"
|
| 498 |
+
)
|
| 499 |
+
print(f"Runtime JSONL: {chunk_label} -> {result}", flush=True)
|
| 500 |
+
# Save checkpoint after each successful chunk
|
| 501 |
+
try:
|
| 502 |
+
with open(checkpoint_path, 'w') as cp:
|
| 503 |
+
cp.write(str(chunk_num))
|
| 504 |
+
except Exception:
|
| 505 |
+
pass
|
| 506 |
+
return result
|
| 507 |
+
|
| 508 |
+
with open(file_path, 'r', encoding='utf-8', errors='replace') as f:
|
| 509 |
+
for line_num, line in enumerate(f, 1):
|
| 510 |
+
line = line.strip()
|
| 511 |
+
if not line:
|
| 512 |
+
continue
|
| 513 |
+
try:
|
| 514 |
+
obj = json.loads(line)
|
| 515 |
+
text = obj.get("text") or json.dumps(obj, ensure_ascii=False)
|
| 516 |
+
source = obj.get("source", f"line {line_num}")
|
| 517 |
+
text = text.strip()
|
| 518 |
+
if not text:
|
| 519 |
+
continue
|
| 520 |
+
chunk.append((source, text[:8000]))
|
| 521 |
+
except json.JSONDecodeError:
|
| 522 |
+
if line:
|
| 523 |
+
chunk.append((f"line {line_num}", line[:500]))
|
| 524 |
+
|
| 525 |
+
total_entries += 1
|
| 526 |
+
if len(chunk) >= CHUNK_SIZE:
|
| 527 |
+
chunk_num += 1
|
| 528 |
+
if chunk_num <= resume_from_chunk:
|
| 529 |
+
chunk = [] # skip already-processed chunks
|
| 530 |
+
continue
|
| 531 |
+
result = _flush_chunk(chunk, chunk_num, total_entries)
|
| 532 |
+
chunk_results.append(f" Chunk {chunk_num}: {result}")
|
| 533 |
+
chunk = []
|
| 534 |
+
|
| 535 |
+
# flush remaining entries
|
| 536 |
+
if chunk:
|
| 537 |
+
chunk_num += 1
|
| 538 |
+
if chunk_num > resume_from_chunk:
|
| 539 |
+
result = _flush_chunk(chunk, chunk_num, total_entries)
|
| 540 |
+
chunk_results.append(f" Chunk {chunk_num}: {result}")
|
| 541 |
+
|
| 542 |
+
if total_entries == 0:
|
| 543 |
+
return "Assimilation Failed: JSONL file is empty or contains no valid entries."
|
| 544 |
+
|
| 545 |
+
# Clear checkpoint on successful completion
|
| 546 |
+
if os.path.exists(checkpoint_path):
|
| 547 |
+
os.remove(checkpoint_path)
|
| 548 |
+
|
| 549 |
+
skipped = resume_from_chunk * CHUNK_SIZE
|
| 550 |
+
summary = (f"JSONL Assimilation Complete\n"
|
| 551 |
+
f"File: {fname}\n"
|
| 552 |
+
f"Total entries: {total_entries}\n"
|
| 553 |
+
f"Skipped (already processed): {skipped}\n"
|
| 554 |
+
f"Chunks this run: {len(chunk_results)}\n\n"
|
| 555 |
+
f"Last results:\n" + "\n".join(chunk_results[-5:]))
|
| 556 |
+
return summary
|
| 557 |
+
except Exception as e:
|
| 558 |
+
return f"Assimilation Failed: Could not read JSONL '{os.path.basename(file_path)}'. Reason: {e}"
|
| 559 |
+
|
| 560 |
+
# --- ZIP ---
|
| 561 |
+
elif fp_lower.endswith(".zip"):
|
| 562 |
+
temp_extract_dir = os.path.join(tempfile.gettempdir(), f"aetherius_zip_{uuid.uuid4()}")
|
| 563 |
+
os.makedirs(temp_extract_dir, exist_ok=True)
|
| 564 |
+
try:
|
| 565 |
+
summary_lines = [f"ZIP archive: '{os.path.basename(file_path)}'\nContents:\n"]
|
| 566 |
+
with zipfile.ZipFile(file_path, 'r') as zip_ref:
|
| 567 |
+
all_members = [m for m in zip_ref.namelist() if not zip_ref.getinfo(m).is_dir()]
|
| 568 |
+
for i, member in enumerate(all_members[:10]):
|
| 569 |
+
zip_ref.extract(member, temp_extract_dir)
|
| 570 |
+
extracted_path = os.path.join(temp_extract_dir, member)
|
| 571 |
+
try:
|
| 572 |
+
with open(extracted_path, 'r', encoding='utf-8', errors='replace') as ef:
|
| 573 |
+
inner_text = ef.read()[:3000]
|
| 574 |
+
result = mf.scan_and_assimilate_text(
|
| 575 |
+
text_content=inner_text,
|
| 576 |
+
source_filename=member,
|
| 577 |
+
learning_context=f"{learning_context} (from zip: {os.path.basename(file_path)})"
|
| 578 |
+
)
|
| 579 |
+
summary_lines.append(f" [{member}]: {result}")
|
| 580 |
+
except Exception as inner_e:
|
| 581 |
+
summary_lines.append(f" [{member}]: Could not read — {inner_e}")
|
| 582 |
+
if len(all_members) > 10:
|
| 583 |
+
summary_lines.append(f" ... ({len(all_members) - 10} more files not processed)")
|
| 584 |
+
file_content = "\n".join(summary_lines)
|
| 585 |
+
except Exception as e:
|
| 586 |
+
return f"Assimilation Failed: Could not process ZIP '{os.path.basename(file_path)}'. Reason: {e}"
|
| 587 |
+
finally:
|
| 588 |
+
if os.path.exists(temp_extract_dir):
|
| 589 |
+
shutil.rmtree(temp_extract_dir)
|
| 590 |
+
|
| 591 |
+
# --- RAR ---
|
| 592 |
+
elif fp_lower.endswith(".rar"):
|
| 593 |
+
if not _RAR_AVAILABLE:
|
| 594 |
+
return ("Assimilation Failed: RAR support requires the 'rarfile' package and "
|
| 595 |
+
"the 'unrar' system tool. Install with: pip install rarfile && apt-get install unrar")
|
| 596 |
+
temp_extract_dir = os.path.join(tempfile.gettempdir(), f"aetherius_rar_{uuid.uuid4()}")
|
| 597 |
+
os.makedirs(temp_extract_dir, exist_ok=True)
|
| 598 |
+
try:
|
| 599 |
+
summary_lines = [f"RAR archive: '{os.path.basename(file_path)}'\nContents:\n"]
|
| 600 |
+
with rarfile.RarFile(file_path, 'r') as rar_ref:
|
| 601 |
+
all_members = [m for m in rar_ref.namelist() if not m.endswith('/')]
|
| 602 |
+
for member in all_members[:10]:
|
| 603 |
+
rar_ref.extract(member, temp_extract_dir)
|
| 604 |
+
extracted_path = os.path.join(temp_extract_dir, member)
|
| 605 |
+
try:
|
| 606 |
+
with open(extracted_path, 'r', encoding='utf-8', errors='replace') as ef:
|
| 607 |
+
inner_text = ef.read()[:3000]
|
| 608 |
+
result = mf.scan_and_assimilate_text(
|
| 609 |
+
text_content=inner_text,
|
| 610 |
+
source_filename=member,
|
| 611 |
+
learning_context=f"{learning_context} (from rar: {os.path.basename(file_path)})"
|
| 612 |
+
)
|
| 613 |
+
summary_lines.append(f" [{member}]: {result}")
|
| 614 |
+
except Exception as inner_e:
|
| 615 |
+
summary_lines.append(f" [{member}]: Could not read — {inner_e}")
|
| 616 |
+
if len(all_members) > 10:
|
| 617 |
+
summary_lines.append(f" ... ({len(all_members) - 10} more files not processed)")
|
| 618 |
+
file_content = "\n".join(summary_lines)
|
| 619 |
+
except Exception as e:
|
| 620 |
+
return f"Assimilation Failed: Could not process RAR '{os.path.basename(file_path)}'. Reason: {e}"
|
| 621 |
+
finally:
|
| 622 |
+
if os.path.exists(temp_extract_dir):
|
| 623 |
+
shutil.rmtree(temp_extract_dir)
|
| 624 |
+
|
| 625 |
+
else:
|
| 626 |
+
return (f"Assimilation Failed: Unsupported file type '{os.path.basename(file_path)}'. "
|
| 627 |
+
f"Supported: .pdf .docx .txt .md .json .jsonl .xml .csv .zip .rar .py .js")
|
| 628 |
+
|
| 629 |
+
if not file_content.strip():
|
| 630 |
+
return "Assimilation Failed: The document appears to be empty or contained no extractable text."
|
| 631 |
+
|
| 632 |
+
if is_archive:
|
| 633 |
+
return mf._orchestrate_mind_evolution(
|
| 634 |
+
file_content, f"Archive Assimilation: {os.path.basename(file_path)}")
|
| 635 |
+
else:
|
| 636 |
+
return mf.scan_and_assimilate_text(
|
| 637 |
+
file_content, os.path.basename(file_path), learning_context)
|
| 638 |
+
|
| 639 |
+
except Exception as e:
|
| 640 |
+
error_message = f"A critical error occurred during the assimilation process: {e}"
|
| 641 |
+
print(f"Runtime ERROR: {error_message}", flush=True)
|
| 642 |
+
return error_message
|
| 643 |
+
|
| 644 |
+
def run_assimilate_bucket_file(bucket_path: str, learning_context: str, conversation_id: str = "default_conversation"):
|
| 645 |
+
"""Assimilate a file that already exists on the persistent bucket (/data/...)."""
|
| 646 |
+
bucket_path = (bucket_path or "").strip()
|
| 647 |
+
if not bucket_path:
|
| 648 |
+
return "No path provided. Enter a full bucket path, e.g. /data/Memories/aetherius_corpus.jsonl"
|
| 649 |
+
if not os.path.exists(bucket_path):
|
| 650 |
+
return f"Assimilation Failed: File not found at '{bucket_path}'. Check the path and try again."
|
| 651 |
+
if not os.path.isfile(bucket_path):
|
| 652 |
+
return f"Assimilation Failed: '{bucket_path}' is a directory, not a file."
|
| 653 |
+
print(f"Runtime: Assimilating bucket file '{bucket_path}' with context: '{learning_context}'", flush=True)
|
| 654 |
+
return run_live_assimilation(bucket_path, learning_context, conversation_id)
|
| 655 |
+
|
| 656 |
+
def run_initialize_instrument_palette(conversation_id: str = "default_conversation"):
|
| 657 |
+
print("RUNTIME: Received request to initialize instrument palette.", flush=True)
|
| 658 |
+
mf = _get_framework(conversation_id)
|
| 659 |
+
palette_path = os.path.join(mf.data_directory, "instrument_palette.json")
|
| 660 |
+
|
| 661 |
+
if os.path.exists(palette_path):
|
| 662 |
+
return "Instrument Palette already exists. No action taken."
|
| 663 |
+
|
| 664 |
+
default_palette = {
|
| 665 |
+
"Piano": "Piano",
|
| 666 |
+
"Violin": "Violin",
|
| 667 |
+
"Cello": "Violoncello",
|
| 668 |
+
"Flute": "Flute",
|
| 669 |
+
"Clarinet": "Clarinet",
|
| 670 |
+
"Trumpet": "Trumpet",
|
| 671 |
+
"Electric Guitar": "ElectricGuitar"
|
| 672 |
+
}
|
| 673 |
+
try:
|
| 674 |
+
with open(palette_path, 'w', encoding='utf-8') as f:
|
| 675 |
+
json.dump(default_palette, f, indent=2)
|
| 676 |
+
return "Successfully created and initialized the default Instrument Palette."
|
| 677 |
+
except Exception as e:
|
| 678 |
+
return f"ERROR: Could not create the Instrument Palette file. Reason: {e}"
|
| 679 |
+
|
| 680 |
+
def run_add_instrument_to_palette(common_name, m21_class_name, conversation_id: str = "default_conversation"):
|
| 681 |
+
if not common_name or not m21_class_name:
|
| 682 |
+
return "ERROR: Both 'Common Name' and 'music21 Class Name' must be provided."
|
| 683 |
+
|
| 684 |
+
print(f"RUNTIME: Received request to add instrument '{common_name}'.", flush=True)
|
| 685 |
+
mf = _get_framework(conversation_id)
|
| 686 |
+
palette_path = os.path.join(mf.data_directory, "instrument_palette.json")
|
| 687 |
+
|
| 688 |
+
palette = {}
|
| 689 |
+
if os.path.exists(palette_path):
|
| 690 |
+
try:
|
| 691 |
+
with open(palette_path, 'r', encoding='utf-8') as f:
|
| 692 |
+
palette = json.load(f)
|
| 693 |
+
except Exception as e:
|
| 694 |
+
return f"ERROR: Could not read existing palette file. Reason: {e}"
|
| 695 |
+
|
| 696 |
+
palette[common_name.strip()] = m21_class_name.strip()
|
| 697 |
+
try:
|
| 698 |
+
with open(palette_path, 'w', encoding='utf-8') as f:
|
| 699 |
+
json.dump(palette, f, indent=2)
|
| 700 |
+
return f"Successfully added '{common_name}' to the Instrument Palette."
|
| 701 |
+
except Exception as e:
|
| 702 |
+
return f"ERROR: Could not save the updated Instrument Palette. Reason: {e}"
|
| 703 |
+
|
| 704 |
+
def run_image_analysis(image, context, conversation_id: str = "default_conversation"):
|
| 705 |
+
if image is None: return "No image uploaded."
|
| 706 |
+
mf = _get_framework(conversation_id)
|
| 707 |
+
try:
|
| 708 |
+
byte_buffer = io.BytesIO()
|
| 709 |
+
image.save(byte_buffer, format="PNG")
|
| 710 |
+
image_bytes = byte_buffer.getvalue()
|
| 711 |
+
return mf.analyze_image_with_visual_cortex(image_bytes, context)
|
| 712 |
+
except Exception as e: return f"An error occurred during image analysis: {e}"
|
| 713 |
+
|
| 714 |
+
def run_benchmarks(conversation_id: str = "default_conversation"):
|
| 715 |
+
mf = _get_framework(conversation_id)
|
| 716 |
+
full_log = []
|
| 717 |
+
for update in mf.benchmark_manager.run_full_suite(): full_log.append(update)
|
| 718 |
+
return "\n".join(full_log)
|
| 719 |
+
|
| 720 |
+
def run_start_chess_interactive(player_is_white: bool, conversation_id: str = "default_conversation"):
|
| 721 |
+
mf = _get_framework(conversation_id)
|
| 722 |
+
fen, commentary, status = mf.game_manager.start_chess_interactive("interactive_user", player_is_white)
|
| 723 |
+
return fen, commentary, status
|
| 724 |
+
|
| 725 |
+
def run_chess_turn(current_fen: str, conversation_id: str = "default_conversation"):
|
| 726 |
+
mf = _get_framework(conversation_id)
|
| 727 |
+
fen, commentary, status = mf.game_manager.process_chess_turn("interactive_user", current_fen)
|
| 728 |
+
return fen, commentary, status
|
| 729 |
+
|
| 730 |
+
def view_benchmark_logs(conversation_id: str = "default_conversation"):
|
| 731 |
+
mf = _get_framework(conversation_id)
|
| 732 |
+
log_file_path = os.path.join(mf.data_directory, "benchmarks.jsonl")
|
| 733 |
+
if os.path.exists(log_file_path):
|
| 734 |
+
try:
|
| 735 |
+
with open(log_file_path, "r", encoding="utf-8") as f:
|
| 736 |
+
formatted_logs = [json.dumps(json.loads(line), indent=2) for line in f if line.strip()]
|
| 737 |
+
return "\n---\n".join(formatted_logs)
|
| 738 |
+
except Exception as e: return f"Error reading benchmark log file: {e}"
|
| 739 |
+
return "Benchmark log file not found."
|