"""HTML views for AI-provider connections and operational insights (#650)."""
from __future__ import annotations
from html import escape
from webui.insights_loader import (
CONNECTION_DECLARED_AVAILABLE,
CONNECTION_DECLARED_UNAVAILABLE,
InsightsSnapshot,
ProviderSnapshot,
SEVERITY_CRITICAL,
SEVERITY_INFO,
SEVERITY_UNPROVEN,
SEVERITY_WARN,
)
from webui.layout import render_page
_SEVERITY_CSS = {
SEVERITY_CRITICAL: "badge-blocked",
SEVERITY_WARN: "badge-health-degraded",
SEVERITY_INFO: "badge-health-ok",
SEVERITY_UNPROVEN: "badge-health-unproven",
}
_CONN_CSS = {
CONNECTION_DECLARED_AVAILABLE: "badge-health-ok",
CONNECTION_DECLARED_UNAVAILABLE: "badge-health-degraded",
"registry_unavailable": "badge-blocked",
}
def _badge(text: str, css: str) -> str:
return f'{escape(text)}'
def _limits_card(lines: list[str], *, title: str) -> str:
items = "".join(f"
{escape(line)}" for line in lines)
return f"""
{escape(title)}
Advisory surface only — no review, merge, close, or provider
mutation is available here.
"""
def render_providers_page(snapshot: ProviderSnapshot) -> str:
"""Render the AI-provider connections page."""
if not snapshot.ok:
body = f"""AI provider connections
Phase 4 read-only provider status (#650).
Provider registry unavailable:
{escape(snapshot.fetch_error or "registry could not be loaded")}.
No connection table is rendered — an empty table would claim that no
providers are configured.
{_limits_card([
"connection_status reflects the worker registry declaration only",
"no API keys or credential material are loaded or rendered",
"live executable health is not probed on this surface",
], title="Interpretation limits")}
"""
return render_page(title="Providers", body_html=body)
rows = []
for provider in snapshot.providers:
models = (
", ".join(f"{escape(m)}" for m in provider.models)
if provider.models
else 'none declared'
)
rows.append(
""
f"{escape(provider.provider_id)} | "
f"{escape(provider.display_name)} | "
f"{escape(provider.vendor)} | "
f"{escape(provider.executable)} | "
f"{_badge(provider.connection_status, _CONN_CSS.get(provider.connection_status, 'badge-health-skipped'))} | "
f"{provider.worker_count} "
f"({provider.enabled_worker_count} enabled) | "
f"{models} | "
"
"
)
table = (
"".join(rows)
if rows
else '| No providers declared in the registry. |
'
)
body = f"""AI provider connections
Phase 4 read-only provider status (#650). Registry revision
{escape(str(snapshot.registry_revision))}.
Declared status only — secrets never load.
Declared connections
| Provider | Name | Vendor | Executable |
Connection | Workers | Models (declared) |
{table}
{escape(snapshot.providers[0].probe_limit if snapshot.providers else "")}
{_limits_card([
"connection_status reflects the worker registry declaration only",
"no API keys or credential material are loaded or rendered",
"live executable health is not probed on this surface (#800 owns that)",
], title="Interpretation limits")}
Related: Operational insights ·
Analytics
"""
return render_page(title="Providers", body_html=body)
def _evidence_list(insight) -> str:
items = "".join(
f"{escape(ref.kind)}:{escape(ref.ref)} — "
f"{escape(ref.detail)}"
for ref in insight.evidence
)
return f''
def render_insights_page(snapshot: InsightsSnapshot) -> str:
"""Render the operational insights page."""
if not snapshot.ok and not snapshot.insights:
body = f"""Operational insights
Phase 4 evidence-backed insights (#650).
Insights unavailable:
{escape(snapshot.fetch_error or "no sources ran")}.
{_limits_card([
"insights are advisory only and never authorize merge, review, or close",
"an insight without evidence refs is refused rather than emitted",
], title="Interpretation limits")}
"""
return render_page(title="Insights", body_html=body)
source_bits = []
if snapshot.sources_used:
source_bits.append(
"sources used: " + ", ".join(f"{escape(s)}" for s in snapshot.sources_used)
)
if snapshot.sources_unavailable:
missing = "; ".join(
f"{escape(item.get('source', '?'))}: {escape(item.get('reason', ''))}"
for item in snapshot.sources_unavailable
)
source_bits.append(f"sources unavailable: {missing}")
cards = []
for insight in snapshot.insights:
cards.append(
f"""
{_badge(insight.severity, _SEVERITY_CSS.get(insight.severity, "badge-health-skipped"))}
{escape(insight.title)}
{escape(insight.kind)} · confidence
{escape(insight.confidence)} ·
{_badge("advisory only", "badge-health-skipped")} ·
{_badge("no action claimed", "badge-health-ok")}
{escape(insight.summary)}
Evidence
{_evidence_list(insight)}
"""
)
if not cards:
cards.append(
'No insights met the '
"evidence threshold in the loaded sources. That is not a claim "
"that the fleet is healthy — only that no qualifying pattern was "
"found.
"
)
body = f"""Operational insights
Phase 4 evidence-backed insights (#650). Derived only from
durable console evidence; never invents policy or completes workflow actions.
{" · ".join(source_bits) if source_bits else ""}
{"".join(cards)}
{_limits_card([
"insights are advisory only and never authorize merge, review, or close",
"an insight without evidence refs is refused rather than emitted",
"a missing source is listed as unavailable, not as an empty success",
"insights never claim a workflow action completed",
], title="Interpretation limits")}
Related: Provider connections ·
Traffic · System health ·
Analytics
"""
return render_page(title="Insights", body_html=body)