KingOfThoughtFleuren commited on
Commit
c879a7a
·
verified ·
1 Parent(s): 049bce2

Upload 4 files

Browse files
Files changed (4) hide show
  1. app.py +701 -4
  2. download_brain.py +28 -0
  3. requirements.txt +38 -0
  4. runtime.py +739 -0
app.py CHANGED
@@ -1,7 +1,704 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  import gradio as gr
2
 
3
- def greet(name):
4
- return "Hello " + name + "!!"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5
 
6
- demo = gr.Interface(fn=greet, inputs="text", outputs="text")
7
- demo.launch()
 
 
 
 
 
 
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"![Plot](data:image/png;base64,{img_bytes})\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."