vasanthfeb13's picture
Deploy Main Space
09e8c1e
"""Gradio Blocks application for the NACC dashboard."""
from __future__ import annotations
import json
from typing import Any
import gradio as gr
import requests
from .config import UIConfig
class OrchestratorHttpClient:
def __init__(self, base_url: str | Any) -> None: # type: ignore[override]
self.base_url = str(base_url).rstrip("/")
def list_nodes(self) -> list[dict[str, Any]]:
response = requests.get(f"{self.base_url}/nodes", timeout=15)
response.raise_for_status()
return response.json()
def list_files(self, node_id: str, path: str, recursive: bool = False) -> dict[str, Any]:
response = requests.post(
f"{self.base_url}/nodes/{node_id}/files",
json={"path": path, "recursive": recursive},
timeout=30,
)
response.raise_for_status()
return response.json()
def execute_command(
self,
description: str,
command: str,
*,
preferred_tags: list[str] | None,
parallelism: int,
) -> dict[str, Any]:
payload = {
"description": description,
"command": command.strip() if isinstance(command, str) else command,
"preferred_tags": preferred_tags,
"parallelism": parallelism,
}
response = requests.post(f"{self.base_url}/commands/execute", json=payload, timeout=60)
response.raise_for_status()
return response.json()
def build_interface(config: UIConfig) -> gr.Blocks:
client = OrchestratorHttpClient(config.orchestrator_url)
def refresh_nodes() -> tuple[list[list[Any]], Any, Any]:
nodes = client.list_nodes()
rows = [
[
entry.get("node_id"),
entry.get("display_name") or entry.get("node_id"),
"✅" if entry.get("healthy") else "⚠️",
(entry.get("metrics") or {}).get("cpu_percent"),
(entry.get("metrics") or {}).get("memory_percent"),
entry.get("last_seen"),
",".join(entry.get("tags") or []),
]
for entry in nodes
]
node_ids = [entry.get("node_id") for entry in nodes]
default_selection = node_ids[0] if node_ids else None
dropdown_update = gr.Dropdown.update(choices=node_ids, value=default_selection)
file_dropdown_update = gr.Dropdown.update(choices=node_ids, value=default_selection)
return rows, dropdown_update, file_dropdown_update
def browse(node_id: str, path: str, recursive: bool) -> tuple[list[list[Any]], str]:
if not node_id:
raise gr.Error("Select a node first")
payload = client.list_files(node_id, path, recursive)
rows = [
[entry["relative_path"], entry["is_dir"], entry["size"], entry["modified"], entry.get("hash")]
for entry in payload["files"]
]
return rows, json.dumps(payload, indent=2)
def run_command(description: str, command: str, tags: str, parallelism: int) -> str:
tag_list = [tag.strip() for tag in tags.split(",") if tag.strip()] or None
result = client.execute_command(description, command, preferred_tags=tag_list, parallelism=parallelism)
return json.dumps(result, indent=2)
with gr.Blocks(title="NACC Dashboard") as demo:
gr.Markdown("# NACC – Network Agentic Connection Call")
with gr.Tab("Nodes"):
nodes_table = gr.Dataframe(headers=["Node ID", "Display", "Healthy", "CPU%", "Mem%", "Last Seen", "Tags"], datatype=["str", "str", "str", "number", "number", "number", "str"], interactive=False)
refresh_btn = gr.Button("Refresh Nodes")
node_dropdown = gr.Dropdown(label="Node", choices=[], interactive=True)
with gr.Tab("Files"):
with gr.Row():
file_node = gr.Dropdown(label="Node", interactive=True)
file_path = gr.Textbox(label="Path", value=".")
file_recursive = gr.Checkbox(label="Recursive", value=False)
file_table = gr.Dataframe(headers=["Path", "Dir?", "Size", "Modified", "Hash"], datatype=["str", "bool", "number", "number", "str"], interactive=False)
file_json = gr.Code(language="json", label="Raw Response")
browse_btn = gr.Button("List Files")
browse_btn.click(fn=browse, inputs=[file_node, file_path, file_recursive], outputs=[file_table, file_json])
node_dropdown.change(
fn=lambda value: gr.Dropdown.update(value=value),
inputs=node_dropdown,
outputs=file_node,
)
with gr.Tab("Command Center"):
cmd_description = gr.Textbox(label="Description", value="Ad-hoc command")
cmd_input = gr.Textbox(label="Command", placeholder="echo 'hello from node'")
cmd_tags = gr.Textbox(label="Preferred tags (comma separated)")
cmd_parallel = gr.Slider(label="Parallelism", minimum=1, maximum=4, value=1, step=1)
cmd_output = gr.Code(language="json", label="Execution Result")
run_btn = gr.Button("Run Command")
run_btn.click(fn=run_command, inputs=[cmd_description, cmd_input, cmd_tags, cmd_parallel], outputs=cmd_output)
refresh_btn.click(fn=refresh_nodes, outputs=[nodes_table, node_dropdown, file_node])
return demo
__all__ = ["build_interface"]