Packages

A Fair Source multi-agent runtime with deterministic agent scoring and replayable run history.

Retired package: Deprecated - superseded — operator console moved to the Syntropy app

Current section

Files

Jump to
syntropy lib syntropy_web live tasks_live.ex
Raw

lib/syntropy_web/live/tasks_live.ex

defmodule SyntropyWeb.TasksLive do
use SyntropyWeb, :live_view
import SyntropyWeb.RuntimeViewHelpers
alias Syntropy.{HistoryStore, RecommendationStore, TaskScheduler}
@history_limit 20
@impl true
def mount(_params, _session, socket) do
if connected?(socket) do
Phoenix.PubSub.subscribe(Syntropy.PubSub, "lattice:events")
end
{:ok,
assign(socket,
task_entries: build_task_entries(),
active_runs: build_active_runs(),
cancel_error: nil
)}
end
@impl true
def handle_info({:lattice_event, _event}, socket) do
{:noreply,
assign(socket, task_entries: build_task_entries(), active_runs: build_active_runs())}
end
@impl true
def handle_event("cancel_run", %{"id" => run_id}, socket) do
case TaskScheduler.cancel_run(run_id) do
{:ok, _cancelled_run} ->
{:noreply,
assign(socket,
cancel_error: nil,
task_entries: build_task_entries(),
active_runs: build_active_runs()
)}
{:error, reason} ->
{:noreply,
assign(socket,
cancel_error: format_cancel_error(run_id, reason),
active_runs: build_active_runs()
)}
end
end
@impl true
def render(assigns) do
~H"""
<main class="dashboard">
<section class="dashboard-header glass-panel p-4">
<div>
<h1>Task history</h1>
<p class="body-copy">Active and retained runs with selection evidence.</p>
</div>
<nav class="dashboard-nav">
<.link navigate={~p"/"} class="text-link">Lattice</.link>
<span> · </span>
<.link navigate={~p"/history"} class="text-link">History</.link>
<span> · </span>
<.link navigate={~p"/operations"} class="text-link">Operations</.link>
</nav>
</section>
<%= if @active_runs != [] or @cancel_error do %>
<section class="glass-panel detail-panel mt-4">
<header class="section-heading section-heading--flush">
<div>
<h2>Active runs</h2>
</div>
</header>
<%= if @cancel_error do %>
<p id="cancel-error" class="meta-label text-danger">
<%= @cancel_error %>
</p>
<% end %>
<ul id="active-runs" class="task-list">
<%= for run <- @active_runs do %>
<li id={"active-run-#{run.id}"} class="task-list-item">
<div class="section-heading section-heading--flush">
<div>
<p class="task-title"><%= run.id %></p>
<p class="meta-copy">
node <%= node_id_for(run) %>
· <%= run.strategy %> resolved <%= run.resolved_mode %>
· <%= length(run.selected_agent_ids) %> agents
</p>
</div>
<div class="flex-row" style="gap: 8px; align-items: center;">
<span class="status-chip status-chip--warn"><%= run.status %></span>
<button
type="button"
class="ds-button--destructive"
phx-click="cancel_run"
phx-value-id={run.id}
data-confirm={"Cancel run #{run.id}? In-flight agent calls will be aborted."}
>
Cancel
</button>
</div>
</div>
<p class="body-copy synthesis-copy"><%= run.prompt %></p>
</li>
<% end %>
</ul>
</section>
<% end %>
<section class="glass-panel detail-panel mt-4">
<header class="section-heading section-heading--flush">
<div>
<h2>Task runs</h2>
</div>
</header>
<%= if @task_entries == [] do %>
<div class="empty-panel">
<p id="tasks-empty" class="body-copy">No completed tasks yet.</p>
</div>
<% else %>
<ul id="task-history" class="task-list">
<%= for entry <- @task_entries do %>
<li id={"task-#{entry.task.task_id}"} class="task-list-item">
<div class="section-heading section-heading--flush">
<div>
<p class="task-title"><%= entry.task.task_id %></p>
<p class="meta-copy">
node <%= node_id_for(entry.task) %>
· <%= entry.task.strategy %> resolved <%= entry.task.resolved_mode %>
</p>
</div>
<span class="status-chip status-chip--muted">
<%= length(entry.task.selected_agent_ids) %> agents
</span>
</div>
<div>
<span class="meta-label">Synthesis</span>
<p class="body-copy synthesis-copy"><%= entry.task.synthesis %></p>
</div>
<div>
<span class="meta-label">Selected agents</span>
<div class="task-list-meta mt-2">
<%= for agent_id <- entry.task.selected_agent_ids do %>
<.link
navigate={graph_path(entry.snapshot && entry.snapshot.id, agent_id)}
class="status-chip status-chip--muted"
>
<%= agent_id %>
</.link>
<% end %>
</div>
</div>
<%= if entry.snapshot || entry.recommendations != [] do %>
<div>
<span class="meta-label">Evidence</span>
<div class="task-list-meta mt-2">
<%= if entry.snapshot do %>
<.link navigate={~p"/history?snapshot=#{entry.snapshot.id}"} class="status-chip status-chip--ok">
<%= entry.snapshot.id %>
</.link>
<% end %>
<%= for recommendation <- entry.recommendations do %>
<.link
navigate={
graph_path(
entry.snapshot && entry.snapshot.id,
List.first(recommendation.candidate_agent_ids)
)
}
class="status-chip status-chip--warn"
>
<%= recommendation.id %> · <%= recommendation.status %>
</.link>
<% end %>
</div>
</div>
<% end %>
<details class="progressive-detail">
<summary>Selection trace (<%= length(entry.task.selection_trace) %>)</summary>
<div class="trace-table">
<%= for trace <- entry.task.selection_trace do %>
<div class="trace-row">
<strong><%= trace.agent_name %></strong>
<span>rank <%= trace.rank %></span>
<span>rel <%= format_score(trace.relevance) %></span>
<span>pos <%= format_score(trace.position) %></span>
<span>cap <%= format_score(trace.capacity) %></span>
<span class={if trace.selected, do: "trace-selected", else: "trace-considered"}>
<%= format_score(trace.composite) %> <%= if trace.selected, do: "sel", else: "—" %>
</span>
</div>
<% end %>
</div>
</details>
</li>
<% end %>
</ul>
<% end %>
</section>
</main>
"""
end
defp build_active_runs do
TaskScheduler.recent_runs(@history_limit)
|> Enum.filter(&(&1.status in ["queued", "running"]))
end
defp format_cancel_error(run_id, :not_found), do: "Run #{run_id} was not found."
defp format_cancel_error(run_id, :not_cancellable),
do: "Run #{run_id} already reached a terminal state."
defp build_task_entries do
snapshots = HistoryStore.recent(@history_limit)
snapshots_by_task_id =
Map.new(snapshots, fn snapshot ->
{snapshot.task_id, snapshot}
end)
recommendations = RecommendationStore.recent(@history_limit)
recommendations_by_id = Map.new(recommendations, &{&1.id, &1})
Enum.map(TaskScheduler.recent(@history_limit), fn task ->
%{
task: task,
snapshot: Map.get(snapshots_by_task_id, task.task_id),
recommendations:
task.recommendation_ids
|> Enum.map(&Map.get(recommendations_by_id, &1))
|> Enum.reject(&is_nil/1)
}
end)
end
defp graph_path(snapshot_id, agent_id) do
params =
%{}
|> maybe_put("snapshot", snapshot_id)
|> maybe_put("agent", agent_id)
~p"/?#{params}"
end
defp maybe_put(params, _key, nil), do: params
defp maybe_put(params, key, value), do: Map.put(params, key, value)
end