Graham Paasch commited on
Commit
257a9b6
·
1 Parent(s): 0d737a7

Add WG bring-up status, runtime log tail, and mermaid image rendering

Browse files
Files changed (2) hide show
  1. agent/pipeline_engine.py +11 -3
  2. 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 = self.gns3_project_name_default or f"og-{self._slugify(intent.description)}-{int(time.time())}"
283
- requested_project_id = self.gns3_project_id_default
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
- diagram = f'<div class="mermaid og-mermaid">{mermaid}</div><script>window.renderMermaid && window.renderMermaid();</script>'
 
 
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>"