Spaces:
Paused
Paused
| """ | |
| Operon Regenerative Swarm -- Interactive Gradio Demo | |
| ==================================================== | |
| Simulate a swarm of workers that detect entropy collapse, self-terminate | |
| (apoptosis), and respawn with summarized learnings from predecessors. | |
| Run locally: | |
| pip install gradio | |
| python space-swarm/app.py | |
| Deploy to HuggingFace Spaces: | |
| Copy this directory to a new HF Space with sdk=gradio. | |
| """ | |
| import sys | |
| from pathlib import Path | |
| import gradio as gr | |
| # Allow importing operon_ai from the repo root when running locally | |
| _repo_root = Path(__file__).resolve().parent.parent | |
| if str(_repo_root) not in sys.path: | |
| sys.path.insert(0, str(_repo_root)) | |
| from operon_ai import ( | |
| RegenerativeSwarm, | |
| SwarmResult, | |
| SimpleWorker, | |
| WorkerMemory, | |
| ) | |
| from operon_ai.healing import create_default_summarizer | |
| # ββ Trace instrumentation βββββββββββββββββββββββββββββββββββββββββββββββββ | |
| _trace_log: list[dict] = [] | |
| def _make_worker_factory(behavior: str): | |
| """Create a worker factory with the specified behavior pattern. | |
| Each factory returns a SimpleWorker whose work_function follows | |
| a scripted behavior for demonstration purposes. | |
| """ | |
| worker_counter = [0] | |
| def factory(task: str, hints: list[str]) -> SimpleWorker: | |
| worker_counter[0] += 1 | |
| worker_id = f"worker-{worker_counter[0]}" | |
| step_counter = [0] | |
| def work(t: str, memory: WorkerMemory) -> str: | |
| step_counter[0] += 1 | |
| step = step_counter[0] | |
| if behavior == "stuck": | |
| # Worker repeats same output β entropy collapse | |
| # If hints were injected (successor), solve it | |
| if hints: | |
| output = f"[{worker_id}] SOLVED using hint: {hints[0]}" | |
| _trace_log.append({ | |
| "worker": worker_id, "step": step, | |
| "output": output, "event": "solved_with_hint", | |
| }) | |
| return output | |
| output = f"[{worker_id}] Trying approach A..." | |
| _trace_log.append({ | |
| "worker": worker_id, "step": step, | |
| "output": output, "event": "repeated", | |
| }) | |
| return output | |
| elif behavior == "quick": | |
| output = f"[{worker_id}] SOLVED: Solution found immediately!" | |
| _trace_log.append({ | |
| "worker": worker_id, "step": step, | |
| "output": output, "event": "solved", | |
| }) | |
| return output | |
| elif behavior == "gradual": | |
| if step < 4: | |
| output = f"[{worker_id}] Exploring angle {step}..." | |
| _trace_log.append({ | |
| "worker": worker_id, "step": step, | |
| "output": output, "event": "exploring", | |
| }) | |
| return output | |
| output = f"[{worker_id}] SOLVED: Converged on solution at step {step}!" | |
| _trace_log.append({ | |
| "worker": worker_id, "step": step, | |
| "output": output, "event": "solved", | |
| }) | |
| return output | |
| elif behavior == "error_prone": | |
| if hints: | |
| output = f"[{worker_id}] SOLVED: Fixed with predecessor knowledge" | |
| _trace_log.append({ | |
| "worker": worker_id, "step": step, | |
| "output": output, "event": "solved_with_hint", | |
| }) | |
| return output | |
| if step % 2 == 0: | |
| # Return error string (don't raise β let entropy collapse handle it) | |
| output = f"[{worker_id}] ERROR at step {step}: computation failed" | |
| _trace_log.append({ | |
| "worker": worker_id, "step": step, | |
| "output": output, "event": "error", | |
| }) | |
| return output | |
| output = f"[{worker_id}] Partial progress step {step}" | |
| _trace_log.append({ | |
| "worker": worker_id, "step": step, | |
| "output": output, "event": "partial", | |
| }) | |
| return output | |
| else: # always_stuck | |
| output = f"[{worker_id}] Still stuck..." | |
| _trace_log.append({ | |
| "worker": worker_id, "step": step, | |
| "output": output, "event": "repeated", | |
| }) | |
| return output | |
| return SimpleWorker(id=worker_id, work_function=work) | |
| return factory | |
| # ββ Presets ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| PRESETS: dict[str, dict] = { | |
| "(custom)": { | |
| "description": "Configure your own swarm parameters.", | |
| "behavior": "stuck", | |
| "entropy_threshold": 0.9, | |
| "max_steps": 10, | |
| "max_regenerations": 3, | |
| }, | |
| "Stuck worker (entropy collapse)": { | |
| "description": "Worker repeats same output β entropy collapse β apoptosis β successor with hints solves it.", | |
| "behavior": "stuck", | |
| "entropy_threshold": 0.9, | |
| "max_steps": 10, | |
| "max_regenerations": 3, | |
| }, | |
| "Quick solver": { | |
| "description": "Worker finds the solution immediately on the first step.", | |
| "behavior": "quick", | |
| "entropy_threshold": 0.9, | |
| "max_steps": 10, | |
| "max_regenerations": 3, | |
| }, | |
| "Gradual convergence": { | |
| "description": "Worker explores multiple angles before converging after 4-5 steps.", | |
| "behavior": "gradual", | |
| "entropy_threshold": 0.9, | |
| "max_steps": 10, | |
| "max_regenerations": 3, | |
| }, | |
| "Error-prone recovery": { | |
| "description": "Errors accumulate β worker killed β next worker succeeds with predecessor knowledge.", | |
| "behavior": "error_prone", | |
| "entropy_threshold": 0.9, | |
| "max_steps": 10, | |
| "max_regenerations": 3, | |
| }, | |
| "Max regenerations exhausted": { | |
| "description": "All workers get stuck β regeneration limit reached, swarm fails.", | |
| "behavior": "always_stuck", | |
| "entropy_threshold": 0.9, | |
| "max_steps": 5, | |
| "max_regenerations": 2, | |
| }, | |
| } | |
| def _load_preset(name: str) -> tuple[float, int, int]: | |
| p = PRESETS.get(name, PRESETS["(custom)"]) | |
| return p["entropy_threshold"], p["max_steps"], p["max_regenerations"] | |
| # ββ Core simulation βββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| # Apoptosis reason styling | |
| _REASON_COLORS: dict[str, str] = { | |
| "entropy_collapse": "#f97316", | |
| "error_accumulation": "#ef4444", | |
| "timeout": "#eab308", | |
| "resource_exhaustion": "#a855f7", | |
| "manual": "#6b7280", | |
| } | |
| def run_swarm( | |
| preset_name: str, | |
| entropy_threshold: float, | |
| max_steps: int, | |
| max_regenerations: int, | |
| ) -> tuple[str, str, str, str]: | |
| """Run the regenerative swarm simulation. | |
| Returns (result_banner, worker_timeline_html, apoptosis_md, regeneration_md). | |
| """ | |
| global _trace_log | |
| _trace_log = [] | |
| p = PRESETS.get(preset_name, PRESETS["(custom)"]) | |
| behavior = p["behavior"] | |
| factory = _make_worker_factory(behavior) | |
| swarm = RegenerativeSwarm( | |
| worker_factory=factory, | |
| summarizer=create_default_summarizer(), | |
| entropy_threshold=entropy_threshold, | |
| max_steps_per_worker=int(max_steps), | |
| max_regenerations=int(max_regenerations), | |
| silent=True, | |
| ) | |
| result: SwarmResult = swarm.supervise("Solve the task") | |
| # ββ Result banner βββββββββββββββββββββββββββββββββββββββββββββββββ | |
| if result.success: | |
| color, label = "#22c55e", "SUCCESS" | |
| detail = f"Output: {result.output}" | |
| else: | |
| color, label = "#ef4444", "FAILURE" | |
| detail = f"Swarm exhausted {result.total_workers_spawned} workers without solving the task." | |
| banner = ( | |
| f'<div style="padding:12px 16px;border-radius:8px;' | |
| f"background:{color}20;border:2px solid {color};margin-bottom:8px\">" | |
| f'<span style="font-size:1.3em;font-weight:700;color:{color}">' | |
| f"{label}</span>" | |
| f'<span style="color:#888;margin-left:12px">' | |
| f"Workers spawned: {result.total_workers_spawned}</span><br>" | |
| f'<span style="font-size:0.9em">{detail}</span></div>' | |
| ) | |
| # ββ Worker timeline (from trace log) ββββββββββββββββββββββββββββββ | |
| event_colors = { | |
| "solved": "#22c55e", | |
| "solved_with_hint": "#10b981", | |
| "exploring": "#3b82f6", | |
| "partial": "#eab308", | |
| "repeated": "#f97316", | |
| "error": "#ef4444", | |
| } | |
| timeline_rows = [] | |
| for entry in _trace_log: | |
| color = event_colors.get(entry["event"], "#888") | |
| timeline_rows.append( | |
| f'<tr>' | |
| f'<td style="padding:4px 8px;font-family:monospace">{entry["worker"]}</td>' | |
| f'<td style="padding:4px 8px;text-align:center">{entry["step"]}</td>' | |
| f'<td style="padding:4px 8px">{entry["output"]}</td>' | |
| f'<td style="padding:4px 8px">' | |
| f'<span style="background:{color}20;color:{color};padding:1px 6px;' | |
| f'border-radius:3px;font-size:0.85em">{entry["event"]}</span></td>' | |
| f'</tr>' | |
| ) | |
| timeline_html = ( | |
| '<table style="width:100%;border-collapse:collapse;font-size:0.9em">' | |
| '<tr style="background:#f0f0f0">' | |
| '<th style="padding:6px 8px;text-align:left">Worker</th>' | |
| '<th style="padding:6px 8px;text-align:center">Step</th>' | |
| '<th style="padding:6px 8px;text-align:left">Output</th>' | |
| '<th style="padding:6px 8px;text-align:left">Event</th></tr>' | |
| + "".join(timeline_rows) | |
| + "</table>" | |
| ) | |
| if not timeline_rows: | |
| timeline_html = '<p style="color:#888">No trace data captured.</p>' | |
| # ββ Apoptosis events ββββββββββββββββββββββββββββββββββββββββββββββ | |
| if result.apoptosis_events: | |
| apo_lines = ["### Apoptosis Events\n"] | |
| for evt in result.apoptosis_events: | |
| reason_color = _REASON_COLORS.get(evt.reason.value, "#888") | |
| apo_lines.append( | |
| f"**Worker `{evt.worker_id}`** β " | |
| f'<span style="color:{reason_color}">{evt.reason.value}</span>\n' | |
| ) | |
| if evt.memory_summary: | |
| apo_lines.append("Memory summary passed to successor:") | |
| for hint in evt.memory_summary: | |
| apo_lines.append(f"- {hint}") | |
| if evt.details: | |
| apo_lines.append(f"\n> {evt.details}") | |
| apo_lines.append("") | |
| apoptosis_md = "\n".join(apo_lines) | |
| else: | |
| apoptosis_md = "*No apoptosis events β worker solved it without getting stuck.*" | |
| # ββ Regeneration events βββββββββββββββββββββββββββββββββββββββββββ | |
| if result.regeneration_events: | |
| regen_lines = ["### Regeneration Events\n"] | |
| for evt in result.regeneration_events: | |
| regen_lines.append( | |
| f"**`{evt.old_worker_id}`** β **`{evt.new_worker_id}`**\n" | |
| ) | |
| if evt.injected_summary: | |
| regen_lines.append("Injected hints:") | |
| for hint in evt.injected_summary: | |
| regen_lines.append(f"- _{hint}_") | |
| regen_lines.append("") | |
| regeneration_md = "\n".join(regen_lines) | |
| else: | |
| regeneration_md = "*No regeneration events β first worker succeeded.*" | |
| return banner, timeline_html, apoptosis_md, regeneration_md | |
| # ββ Gradio UI ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| def build_app() -> gr.Blocks: | |
| with gr.Blocks(title="Regenerative Swarm") as app: | |
| gr.Markdown( | |
| "# 𧬠Regenerative Swarm\n" | |
| "Simulate workers that detect entropy collapse, self-terminate " | |
| "(apoptosis), and respawn with summarized learnings." | |
| ) | |
| with gr.Row(): | |
| preset_dd = gr.Dropdown( | |
| choices=list(PRESETS.keys()), | |
| value="Stuck worker (entropy collapse)", | |
| label="Preset", | |
| scale=2, | |
| ) | |
| run_btn = gr.Button("Run Swarm", variant="primary", scale=1) | |
| with gr.Row(): | |
| entropy_sl = gr.Slider(0.1, 1.0, value=0.9, step=0.05, label="Entropy threshold") | |
| steps_sl = gr.Slider(1, 20, value=10, step=1, label="Max steps per worker") | |
| regens_sl = gr.Slider(1, 5, value=3, step=1, label="Max regenerations") | |
| banner_html = gr.HTML(label="Result") | |
| gr.Markdown("### Worker Timeline") | |
| timeline_html = gr.HTML(label="Timeline") | |
| with gr.Row(): | |
| with gr.Column(): | |
| apoptosis_md = gr.Markdown(label="Apoptosis Events") | |
| with gr.Column(): | |
| regeneration_md = gr.Markdown(label="Regeneration Events") | |
| # ββ Event wiring ββββββββββββββββββββββββββββββββββββββββββββββ | |
| preset_dd.change( | |
| fn=_load_preset, | |
| inputs=[preset_dd], | |
| outputs=[entropy_sl, steps_sl, regens_sl], | |
| ) | |
| run_btn.click( | |
| fn=run_swarm, | |
| inputs=[preset_dd, entropy_sl, steps_sl, regens_sl], | |
| outputs=[banner_html, timeline_html, apoptosis_md, regeneration_md], | |
| ) | |
| return app | |
| if __name__ == "__main__": | |
| app = build_app() | |
| app.launch(theme=gr.themes.Soft()) | |