Current section
Files
Jump to
Current section
Files
lib/superintelligence_web/live/dashboard_live.ex
defmodule SuperintelligenceWeb.DashboardLive do
use SuperintelligenceWeb, :live_view
alias Superintelligence.{TelemetryReporter, WorkerPool, ResourceManager, HealthMonitor}
@refresh_interval 1000
def mount(_params, _session, socket) do
if connected?(socket) do
:timer.send_interval(@refresh_interval, self(), :tick)
Phoenix.PubSub.subscribe(Superintelligence.PubSub, "telemetry:updates")
Phoenix.PubSub.subscribe(Superintelligence.PubSub, "worker:updates")
Phoenix.PubSub.subscribe(Superintelligence.PubSub, "health:updates")
end
socket =
socket
|> assign(:page_title, "Superintelligence Dashboard")
|> assign(:metrics, get_metrics())
|> assign(:workers, get_workers())
|> assign(:health_checks, get_health_checks())
|> assign(:resource_usage, get_resource_usage())
|> assign(:system_info, get_system_info())
|> assign(:performance_data, get_performance_data())
|> assign(:ai_models, get_ai_models())
|> assign(:active_tasks, get_active_tasks())
{:ok, socket}
end
def handle_info(:tick, socket) do
socket =
socket
|> assign(:metrics, get_metrics())
|> assign(:workers, get_workers())
|> assign(:health_checks, get_health_checks())
|> assign(:resource_usage, get_resource_usage())
|> assign(:performance_data, get_performance_data())
|> assign(:active_tasks, get_active_tasks())
{:noreply, socket}
end
def handle_info({:telemetry_update, data}, socket) do
{:noreply, update(socket, :metrics, fn metrics ->
Map.merge(metrics, data)
end)}
end
def handle_info({:worker_update, data}, socket) do
{:noreply, assign(socket, :workers, data)}
end
def handle_info({:health_update, data}, socket) do
{:noreply, assign(socket, :health_checks, data)}
end
def handle_event("deploy_model", %{"model_id" => model_id}, socket) do
# Handle model deployment
send(self(), {:deploy_model, model_id})
{:noreply, put_flash(socket, :info, "Deploying model #{model_id}...")}
end
def handle_event("scale_workers", %{"count" => count}, socket) do
# Handle worker scaling
WorkerPool.scale_workers(String.to_integer(count))
{:noreply, put_flash(socket, :info, "Scaling workers to #{count}")}
end
def handle_event("execute_task", %{"task_type" => task_type}, socket) do
# Handle task execution
task_id = UUID.uuid4()
WorkerPool.submit_task(%{id: task_id, type: task_type, priority: :high})
{:noreply, put_flash(socket, :info, "Task #{task_id} submitted")}
end
def render(assigns) do
~H"""
<div class="min-h-screen bg-gray-900 text-gray-100">
<div class="px-4 sm:px-6 lg:px-8 py-8">
<div class="mb-8">
<h1 class="text-4xl font-bold text-transparent bg-clip-text bg-gradient-to-r from-blue-400 to-purple-600">
Superintelligence Control Center
</h1>
<p class="mt-2 text-gray-400">Real-time monitoring and control</p>
</div>
<!-- System Overview Cards -->
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6 mb-8">
<.metric_card title="Active Workers" value={@workers.active_count} icon="hero-cpu-chip" color="blue" />
<.metric_card title="Tasks/Second" value={@metrics[:tasks_per_second] || 0} icon="hero-bolt" color="green" />
<.metric_card title="System Health" value={@health_checks.overall_health} icon="hero-heart" color="red" />
<.metric_card title="Memory Usage" value={format_bytes(@resource_usage.memory)} icon="hero-chart-bar" color="purple" />
</div>
<!-- Main Dashboard Grid -->
<div class="grid grid-cols-1 lg:grid-cols-3 gap-6">
<!-- Worker Pool Status -->
<div class="lg:col-span-2">
<.panel title="Worker Pool Status">
<div class="space-y-4">
<div class="flex justify-between items-center">
<span class="text-sm text-gray-400">Total Workers</span>
<span class="text-2xl font-bold">{@workers.total_count}</span>
</div>
<div class="grid grid-cols-2 gap-4">
<.worker_status_chart workers={@workers} />
<.performance_graph data={@performance_data} />
</div>
<div class="mt-4">
<h4 class="text-sm font-medium text-gray-400 mb-2">Worker Details</h4>
<div class="space-y-2">
<%= for worker <- @workers.workers do %>
<.worker_row worker={worker} />
<% end %>
</div>
</div>
</div>
</.panel>
</div>
<!-- AI Models Status -->
<div>
<.panel title="AI Models">
<div class="space-y-4">
<%= for model <- @ai_models do %>
<.model_card model={model} />
<% end %>
</div>
<button
phx-click="deploy_model"
phx-value-model_id="new-model"
class="mt-4 w-full bg-blue-600 hover:bg-blue-700 text-white font-medium py-2 px-4 rounded-lg transition"
>
Deploy New Model
</button>
</.panel>
</div>
</div>
<!-- Health Monitoring -->
<div class="mt-6 grid grid-cols-1 lg:grid-cols-2 gap-6">
<.panel title="Health Checks">
<div class="space-y-3">
<%= for {name, check} <- @health_checks.checks do %>
<.health_check_row name={name} check={check} />
<% end %>
</div>
</.panel>
<.panel title="Active Tasks">
<div class="space-y-3">
<%= for task <- @active_tasks do %>
<.task_row task={task} />
<% end %>
</div>
<div class="mt-4 flex gap-2">
<button
phx-click="execute_task"
phx-value-task_type="inference"
class="flex-1 bg-purple-600 hover:bg-purple-700 text-white font-medium py-2 px-4 rounded-lg transition"
>
Run Inference
</button>
<button
phx-click="execute_task"
phx-value-task_type="training"
class="flex-1 bg-green-600 hover:bg-green-700 text-white font-medium py-2 px-4 rounded-lg transition"
>
Start Training
</button>
</div>
</.panel>
</div>
<!-- Resource Usage -->
<div class="mt-6">
<.panel title="Resource Usage">
<div class="grid grid-cols-1 md:grid-cols-3 gap-6">
<.resource_meter title="CPU" value={@resource_usage.cpu_percent} max={100} unit="%" />
<.resource_meter title="Memory" value={@resource_usage.memory_percent} max={100} unit="%" />
<.resource_meter title="GPU" value={@resource_usage.gpu_percent || 0} max={100} unit="%" />
</div>
</.panel>
</div>
<!-- System Information -->
<div class="mt-6">
<.panel title="System Information">
<div class="grid grid-cols-2 md:grid-cols-4 gap-4 text-sm">
<div>
<span class="text-gray-400">Uptime</span>
<p class="font-medium">{format_uptime(@system_info.uptime)}</p>
</div>
<div>
<span class="text-gray-400">Nodes</span>
<p class="font-medium">{@system_info.node_count}</p>
</div>
<div>
<span class="text-gray-400">Version</span>
<p class="font-medium">{@system_info.version}</p>
</div>
<div>
<span class="text-gray-400">Environment</span>
<p class="font-medium">{@system_info.env}</p>
</div>
</div>
</.panel>
</div>
</div>
</div>
"""
end
# Component Functions
defp metric_card(assigns) do
~H"""
<div class={"bg-gray-800 rounded-lg p-6 border border-gray-700 hover:border-#{@color}-500 transition"}>
<div class="flex items-center justify-between">
<div>
<p class="text-sm text-gray-400">{@title}</p>
<p class="text-2xl font-bold mt-1">{@value}</p>
</div>
<div class={"text-#{@color}-400"}>
<.icon name={@icon} class="w-8 h-8" />
</div>
</div>
</div>
"""
end
defp panel(assigns) do
~H"""
<div class="bg-gray-800 rounded-lg p-6 border border-gray-700">
<h3 class="text-lg font-semibold mb-4">{@title}</h3>
<%= render_slot(@inner_block) %>
</div>
"""
end
defp worker_row(assigns) do
~H"""
<div class="flex items-center justify-between p-3 bg-gray-700 rounded-lg">
<div class="flex items-center gap-3">
<div class={"w-3 h-3 rounded-full #{worker_status_color(@worker.status)}"} />
<span class="font-medium">Worker-{@worker.id}</span>
</div>
<div class="flex items-center gap-4 text-sm">
<span class="text-gray-400">Tasks: {@worker.tasks_completed}</span>
<span class="text-gray-400">CPU: {@worker.cpu_usage}%</span>
<span class={"px-2 py-1 rounded text-xs #{worker_status_badge(@worker.status)}"}>
{@worker.status}
</span>
</div>
</div>
"""
end
defp worker_status_chart(assigns) do
~H"""
<div class="bg-gray-700 rounded-lg p-4">
<h4 class="text-sm font-medium text-gray-400 mb-3">Status Distribution</h4>
<div class="space-y-2">
<.status_bar label="Active" count={@workers.active_count} total={@workers.total_count} color="green" />
<.status_bar label="Idle" count={@workers.idle_count} total={@workers.total_count} color="yellow" />
<.status_bar label="Error" count={@workers.error_count} total={@workers.total_count} color="red" />
</div>
</div>
"""
end
defp status_bar(assigns) do
percentage = if assigns.total > 0, do: assigns.count / assigns.total * 100, else: 0
assigns = assign(assigns, :percentage, percentage)
~H"""
<div>
<div class="flex justify-between text-sm mb-1">
<span>{@label}</span>
<span>{@count}</span>
</div>
<div class="w-full bg-gray-600 rounded-full h-2">
<div class={"bg-#{@color}-500 h-2 rounded-full"} style={"width: #{@percentage}%"} />
</div>
</div>
"""
end
defp performance_graph(assigns) do
~H"""
<div class="bg-gray-700 rounded-lg p-4">
<h4 class="text-sm font-medium text-gray-400 mb-3">Performance</h4>
<div class="h-32 flex items-end justify-between gap-1">
<%= for point <- @data do %>
<div
class="bg-blue-500 w-full rounded-t"
style={"height: #{point}%"}
/>
<% end %>
</div>
</div>
"""
end
defp model_card(assigns) do
~H"""
<div class="p-4 bg-gray-700 rounded-lg">
<div class="flex items-center justify-between mb-2">
<h4 class="font-medium">{@model.name}</h4>
<span class={"text-xs px-2 py-1 rounded #{model_status_badge(@model.status)}"}>
{@model.status}
</span>
</div>
<div class="text-sm text-gray-400 space-y-1">
<p>Type: {@model.type}</p>
<p>Accuracy: {@model.accuracy}%</p>
<p>Latency: {@model.latency}ms</p>
</div>
</div>
"""
end
defp health_check_row(assigns) do
~H"""
<div class="flex items-center justify-between p-3 bg-gray-700 rounded-lg">
<span class="font-medium">{@name}</span>
<div class="flex items-center gap-2">
<span class="text-sm text-gray-400">Last: {@check.last_check_ago}s ago</span>
<div class={"w-3 h-3 rounded-full #{health_status_color(@check.status)}"} />
</div>
</div>
"""
end
defp task_row(assigns) do
~H"""
<div class="flex items-center justify-between p-3 bg-gray-700 rounded-lg">
<div>
<p class="font-medium">{@task.type}</p>
<p class="text-xs text-gray-400">ID: {@task.id}</p>
</div>
<div class="flex items-center gap-3">
<div class="text-sm text-gray-400">
<span>Worker {@task.worker_id}</span>
</div>
<.progress_ring progress={@task.progress} />
</div>
</div>
"""
end
defp progress_ring(assigns) do
circumference = 2 * :math.pi() * 16
stroke_dashoffset = circumference - (assigns.progress / 100 * circumference)
assigns = assign(assigns, :circumference, circumference)
assigns = assign(assigns, :stroke_dashoffset, stroke_dashoffset)
~H"""
<div class="relative w-10 h-10">
<svg class="transform -rotate-90 w-10 h-10">
<circle cx="20" cy="20" r="16" stroke="currentColor" stroke-width="4" fill="none" class="text-gray-600" />
<circle
cx="20" cy="20" r="16"
stroke="currentColor"
stroke-width="4"
fill="none"
class="text-blue-500"
stroke-dasharray={@circumference}
stroke-dashoffset={@stroke_dashoffset}
/>
</svg>
<span class="absolute inset-0 flex items-center justify-center text-xs">
{@progress}%
</span>
</div>
"""
end
defp resource_meter(assigns) do
~H"""
<div>
<div class="flex justify-between mb-2">
<span class="text-sm text-gray-400">{@title}</span>
<span class="text-sm font-medium">{@value}{@unit}</span>
</div>
<div class="w-full bg-gray-700 rounded-full h-3">
<div
class={"h-3 rounded-full transition-all duration-300 #{resource_color(@value, @max)}"}
style={"width: #{@value/@max * 100}%"}
/>
</div>
</div>
"""
end
# Helper Functions
defp get_metrics do
case TelemetryReporter.get_metrics() do
{:ok, metrics} -> metrics
_ -> %{}
end
end
defp get_workers do
case WorkerPool.get_status() do
status when is_map(status) ->
workers = status[:workers] || []
%{
workers: workers,
total_count: length(workers),
active_count: Enum.count(workers, & &1.status == :busy),
idle_count: Enum.count(workers, & &1.status == :idle),
error_count: Enum.count(workers, & &1.status == :error)
}
_ ->
%{workers: [], total_count: 0, active_count: 0, idle_count: 0, error_count: 0}
end
end
defp get_health_checks do
try do
status = HealthMonitor.get_health_status()
%{
checks: status.checks || %{},
overall_health: status.overall || "Unknown"
}
rescue
_ ->
%{checks: %{}, overall_health: "Unknown"}
end
end
defp get_resource_usage do
case ResourceManager.get_usage() do
{:ok, usage} ->
%{
memory: usage[:memory] || 0,
memory_percent: (usage[:memory] || 0) / :erlang.memory(:total) * 100,
cpu_percent: usage[:cpu] || 0,
gpu_percent: usage[:gpu]
}
_ ->
%{memory: 0, memory_percent: 0, cpu_percent: 0, gpu_percent: 0}
end
end
defp get_system_info do
%{
uptime: :erlang.system_info(:uptime) |> elem(0),
node_count: length(Node.list()) + 1,
version: Application.spec(:superintelligence, :vsn) |> to_string(),
env: Mix.env() |> to_string()
}
end
defp get_performance_data do
# Generate sample performance data
Enum.map(1..20, fn _ -> :rand.uniform(100) end)
end
defp get_ai_models do
[
%{id: 1, name: "GPT-4 Turbo", type: "LLM", status: :active, accuracy: 98.5, latency: 120},
%{id: 2, name: "BERT-Large", type: "NLP", status: :active, accuracy: 95.2, latency: 45},
%{id: 3, name: "Vision Transformer", type: "CV", status: :loading, accuracy: 94.8, latency: 80},
%{id: 4, name: "Whisper", type: "ASR", status: :inactive, accuracy: 96.1, latency: 150}
]
end
defp get_active_tasks do
[
%{id: "task-1", type: "Inference", worker_id: 3, progress: 75},
%{id: "task-2", type: "Training", worker_id: 5, progress: 30},
%{id: "task-3", type: "Preprocessing", worker_id: 1, progress: 90}
]
end
defp format_bytes(bytes) when is_number(bytes) do
cond do
bytes >= 1_073_741_824 -> "#{Float.round(bytes / 1_073_741_824, 2)} GB"
bytes >= 1_048_576 -> "#{Float.round(bytes / 1_048_576, 2)} MB"
bytes >= 1024 -> "#{Float.round(bytes / 1024, 2)} KB"
true -> "#{bytes} B"
end
end
defp format_bytes(_), do: "0 B"
defp format_uptime(seconds) do
days = div(seconds, 86400)
hours = div(rem(seconds, 86400), 3600)
minutes = div(rem(seconds, 3600), 60)
"#{days}d #{hours}h #{minutes}m"
end
defp calculate_overall_health(checks) do
if Enum.all?(checks, fn {_, check} -> check.status == :healthy end) do
"Healthy"
else
"Degraded"
end
end
defp worker_status_color(:busy), do: "bg-green-500"
defp worker_status_color(:idle), do: "bg-yellow-500"
defp worker_status_color(:error), do: "bg-red-500"
defp worker_status_color(_), do: "bg-gray-500"
defp worker_status_badge(:busy), do: "bg-green-900 text-green-300"
defp worker_status_badge(:idle), do: "bg-yellow-900 text-yellow-300"
defp worker_status_badge(:error), do: "bg-red-900 text-red-300"
defp worker_status_badge(_), do: "bg-gray-900 text-gray-300"
defp model_status_badge(:active), do: "bg-green-900 text-green-300"
defp model_status_badge(:loading), do: "bg-yellow-900 text-yellow-300"
defp model_status_badge(:inactive), do: "bg-gray-900 text-gray-300"
defp model_status_badge(_), do: "bg-gray-900 text-gray-300"
defp health_status_color(:healthy), do: "bg-green-500"
defp health_status_color(:warning), do: "bg-yellow-500"
defp health_status_color(:critical), do: "bg-red-500"
defp health_status_color(_), do: "bg-gray-500"
defp resource_color(value, max) do
percentage = value / max * 100
cond do
percentage >= 90 -> "bg-red-500"
percentage >= 70 -> "bg-yellow-500"
true -> "bg-green-500"
end
end
end