Spaces:
Sleeping
Sleeping
Graham Paasch commited on
Commit ·
257a9b6
1
Parent(s): 0d737a7
Add WG bring-up status, runtime log tail, and mermaid image rendering
Browse files- agent/pipeline_engine.py +11 -3
- app.py +22 -1
agent/pipeline_engine.py
CHANGED
|
@@ -224,6 +224,7 @@ class OvergrowthPipeline:
|
|
| 224 |
# Enable deployment by default; set OG_DEPLOY_ENABLED=0 to disable in restricted envs.
|
| 225 |
self.deploy_enabled = strtobool(os.getenv("OG_DEPLOY_ENABLED", "1")) == 1
|
| 226 |
self.ssh_on_seed_fail = strtobool(os.getenv("OG_SSH_ON_SEED_FAIL", "0")) == 1
|
|
|
|
| 227 |
self._ensure_wireguard()
|
| 228 |
|
| 229 |
def _slugify(self, text: str, length: int = 24) -> str:
|
|
@@ -237,6 +238,7 @@ class OvergrowthPipeline:
|
|
| 237 |
"""
|
| 238 |
If WG_CONFIG_B64 is provided, bring up wg0 using that config.
|
| 239 |
"""
|
|
|
|
| 240 |
wg_cfg_b64 = os.getenv("WG_CONFIG_B64")
|
| 241 |
if not wg_cfg_b64:
|
| 242 |
return
|
|
@@ -250,6 +252,7 @@ class OvergrowthPipeline:
|
|
| 250 |
)
|
| 251 |
if already_up.returncode == 0:
|
| 252 |
logger.info("WireGuard wg0 already up; skipping bring-up")
|
|
|
|
| 253 |
return
|
| 254 |
except Exception:
|
| 255 |
pass
|
|
@@ -267,10 +270,13 @@ class OvergrowthPipeline:
|
|
| 267 |
)
|
| 268 |
if result.returncode != 0:
|
| 269 |
logger.error(f"WireGuard bring-up failed: {result.stderr.strip()}")
|
|
|
|
| 270 |
else:
|
| 271 |
logger.info("WireGuard wg0 up")
|
|
|
|
| 272 |
except Exception as e:
|
| 273 |
logger.error(f"WireGuard setup error: {e}")
|
|
|
|
| 274 |
|
| 275 |
def _prepare_lab_info(self, intent: NetworkIntent, create_project: bool = False) -> Dict[str, Any]:
|
| 276 |
"""
|
|
@@ -279,8 +285,8 @@ class OvergrowthPipeline:
|
|
| 279 |
if not self.gns3_server:
|
| 280 |
return {}
|
| 281 |
|
| 282 |
-
project_name =
|
| 283 |
-
requested_project_id =
|
| 284 |
|
| 285 |
lab_info: Dict[str, Any] = {
|
| 286 |
"api": self.gns3_server,
|
|
@@ -551,7 +557,8 @@ class OvergrowthPipeline:
|
|
| 551 |
},
|
| 552 |
"intent": asdict(model.intent),
|
| 553 |
"questions": [],
|
| 554 |
-
"import_summary": import_summary
|
|
|
|
| 555 |
}
|
| 556 |
lab_info = self._prepare_lab_info(model.intent, create_project=False)
|
| 557 |
if lab_info:
|
|
@@ -1636,6 +1643,7 @@ Be specific and practical. Use RFC1918 addressing. Consider scalability and secu
|
|
| 1636 |
results['intent'] = asdict(intent)
|
| 1637 |
results['questions'] = self._generate_clarifying_questions(intent)
|
| 1638 |
lab_info = self._prepare_lab_info(intent, create_project=True)
|
|
|
|
| 1639 |
if lab_info:
|
| 1640 |
results['lab'] = lab_info
|
| 1641 |
|
|
|
|
| 224 |
# Enable deployment by default; set OG_DEPLOY_ENABLED=0 to disable in restricted envs.
|
| 225 |
self.deploy_enabled = strtobool(os.getenv("OG_DEPLOY_ENABLED", "1")) == 1
|
| 226 |
self.ssh_on_seed_fail = strtobool(os.getenv("OG_SSH_ON_SEED_FAIL", "0")) == 1
|
| 227 |
+
self.wg_status = "not_configured"
|
| 228 |
self._ensure_wireguard()
|
| 229 |
|
| 230 |
def _slugify(self, text: str, length: int = 24) -> str:
|
|
|
|
| 238 |
"""
|
| 239 |
If WG_CONFIG_B64 is provided, bring up wg0 using that config.
|
| 240 |
"""
|
| 241 |
+
self.wg_status = "not_configured"
|
| 242 |
wg_cfg_b64 = os.getenv("WG_CONFIG_B64")
|
| 243 |
if not wg_cfg_b64:
|
| 244 |
return
|
|
|
|
| 252 |
)
|
| 253 |
if already_up.returncode == 0:
|
| 254 |
logger.info("WireGuard wg0 already up; skipping bring-up")
|
| 255 |
+
self.wg_status = "up"
|
| 256 |
return
|
| 257 |
except Exception:
|
| 258 |
pass
|
|
|
|
| 270 |
)
|
| 271 |
if result.returncode != 0:
|
| 272 |
logger.error(f"WireGuard bring-up failed: {result.stderr.strip()}")
|
| 273 |
+
self.wg_status = f"error: {result.stderr.strip()}"
|
| 274 |
else:
|
| 275 |
logger.info("WireGuard wg0 up")
|
| 276 |
+
self.wg_status = "up"
|
| 277 |
except Exception as e:
|
| 278 |
logger.error(f"WireGuard setup error: {e}")
|
| 279 |
+
self.wg_status = f"error: {e}"
|
| 280 |
|
| 281 |
def _prepare_lab_info(self, intent: NetworkIntent, create_project: bool = False) -> Dict[str, Any]:
|
| 282 |
"""
|
|
|
|
| 285 |
if not self.gns3_server:
|
| 286 |
return {}
|
| 287 |
|
| 288 |
+
project_name = f"og-{self._slugify(intent.description)}-{int(time.time())}"
|
| 289 |
+
requested_project_id = None
|
| 290 |
|
| 291 |
lab_info: Dict[str, Any] = {
|
| 292 |
"api": self.gns3_server,
|
|
|
|
| 557 |
},
|
| 558 |
"intent": asdict(model.intent),
|
| 559 |
"questions": [],
|
| 560 |
+
"import_summary": import_summary,
|
| 561 |
+
"wg_status": self.wg_status
|
| 562 |
}
|
| 563 |
lab_info = self._prepare_lab_info(model.intent, create_project=False)
|
| 564 |
if lab_info:
|
|
|
|
| 1643 |
results['intent'] = asdict(intent)
|
| 1644 |
results['questions'] = self._generate_clarifying_questions(intent)
|
| 1645 |
lab_info = self._prepare_lab_info(intent, create_project=True)
|
| 1646 |
+
results['wg_status'] = self.wg_status
|
| 1647 |
if lab_info:
|
| 1648 |
results['lab'] = lab_info
|
| 1649 |
|
app.py
CHANGED
|
@@ -26,6 +26,7 @@ from agent.network_ops import (
|
|
| 26 |
from agent.api_monitor import monitor
|
| 27 |
import json
|
| 28 |
import time
|
|
|
|
| 29 |
import html
|
| 30 |
|
| 31 |
VINE_CSS = """
|
|
@@ -247,6 +248,11 @@ def build_ui():
|
|
| 247 |
gr.Markdown("### 📡 Recent API Calls")
|
| 248 |
api_activity = gr.Markdown(value=monitor.format_activity_feed())
|
| 249 |
refresh_activity_btn = gr.Button("🔄 Refresh Activity", size="sm", variant="secondary")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 250 |
|
| 251 |
# Auto-refresh handlers
|
| 252 |
def refresh_api_stats():
|
|
@@ -257,6 +263,14 @@ def build_ui():
|
|
| 257 |
|
| 258 |
refresh_stats_btn.click(fn=refresh_api_stats, outputs=[api_stats])
|
| 259 |
refresh_activity_btn.click(fn=refresh_api_activity, outputs=[api_activity])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 260 |
|
| 261 |
gr.Markdown("---")
|
| 262 |
|
|
@@ -410,6 +424,11 @@ def build_ui():
|
|
| 410 |
status += f"- Nebius key: {llm_env.get('nebius_key', 'missing')}\n"
|
| 411 |
status += f"- HuggingFace key: {llm_env.get('huggingface_key', 'missing')}\n"
|
| 412 |
status += f"- Modal key: {llm_env.get('modal_key', 'missing')}\n\n"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 413 |
|
| 414 |
mode_info = results.get("mode")
|
| 415 |
if mode_info:
|
|
@@ -555,7 +574,9 @@ def build_ui():
|
|
| 555 |
diagrams = results.get('diagrams', {}) or {}
|
| 556 |
mermaid = diagrams.get('mermaid')
|
| 557 |
if mermaid:
|
| 558 |
-
|
|
|
|
|
|
|
| 559 |
else:
|
| 560 |
ascii_diagram = diagrams.get('ascii', 'No diagram available')
|
| 561 |
diagram = f"<pre>{html.escape(ascii_diagram)}</pre>"
|
|
|
|
| 26 |
from agent.api_monitor import monitor
|
| 27 |
import json
|
| 28 |
import time
|
| 29 |
+
import urllib.parse
|
| 30 |
import html
|
| 31 |
|
| 32 |
VINE_CSS = """
|
|
|
|
| 248 |
gr.Markdown("### 📡 Recent API Calls")
|
| 249 |
api_activity = gr.Markdown(value=monitor.format_activity_feed())
|
| 250 |
refresh_activity_btn = gr.Button("🔄 Refresh Activity", size="sm", variant="secondary")
|
| 251 |
+
|
| 252 |
+
with gr.Column(scale=2, elem_classes=["og-panel"]):
|
| 253 |
+
gr.Markdown("### 📜 Runtime Log (tail)")
|
| 254 |
+
log_output = gr.Textbox(value="", lines=12, label="Latest Logs", interactive=False)
|
| 255 |
+
refresh_log_btn = gr.Button("🔄 Refresh Logs", size="sm", variant="secondary")
|
| 256 |
|
| 257 |
# Auto-refresh handlers
|
| 258 |
def refresh_api_stats():
|
|
|
|
| 263 |
|
| 264 |
refresh_stats_btn.click(fn=refresh_api_stats, outputs=[api_stats])
|
| 265 |
refresh_activity_btn.click(fn=refresh_api_activity, outputs=[api_activity])
|
| 266 |
+
def refresh_logs():
|
| 267 |
+
try:
|
| 268 |
+
import subprocess
|
| 269 |
+
tail = subprocess.run(["tail", "-n", "60", "app.log"], capture_output=True, text=True)
|
| 270 |
+
return tail.stdout or "No logs yet."
|
| 271 |
+
except Exception as e:
|
| 272 |
+
return f"Failed to read logs: {e}"
|
| 273 |
+
refresh_log_btn.click(fn=refresh_logs, outputs=[log_output])
|
| 274 |
|
| 275 |
gr.Markdown("---")
|
| 276 |
|
|
|
|
| 424 |
status += f"- Nebius key: {llm_env.get('nebius_key', 'missing')}\n"
|
| 425 |
status += f"- HuggingFace key: {llm_env.get('huggingface_key', 'missing')}\n"
|
| 426 |
status += f"- Modal key: {llm_env.get('modal_key', 'missing')}\n\n"
|
| 427 |
+
|
| 428 |
+
wg_status = results.get("wg_status")
|
| 429 |
+
if wg_status:
|
| 430 |
+
status += "### 🕸️ WireGuard\n"
|
| 431 |
+
status += f"- Status: {wg_status}\n\n"
|
| 432 |
|
| 433 |
mode_info = results.get("mode")
|
| 434 |
if mode_info:
|
|
|
|
| 574 |
diagrams = results.get('diagrams', {}) or {}
|
| 575 |
mermaid = diagrams.get('mermaid')
|
| 576 |
if mermaid:
|
| 577 |
+
encoded = urllib.parse.quote(mermaid)
|
| 578 |
+
img_url = f"https://mermaid.ink/img/{encoded}"
|
| 579 |
+
diagram = f'<img src="{img_url}" alt="Network Diagram" style="max-width:100%;"/>'
|
| 580 |
else:
|
| 581 |
ascii_diagram = diagrams.get('ascii', 'No diagram available')
|
| 582 |
diagram = f"<pre>{html.escape(ascii_diagram)}</pre>"
|