feat(awooop): surface degraded ai route lanes
This commit is contained in:
@@ -428,6 +428,23 @@ interface AiRouteHealthItem {
|
||||
checked?: boolean;
|
||||
}
|
||||
|
||||
interface AiRouteLaneItem {
|
||||
priority?: number | null;
|
||||
provider_name?: string | null;
|
||||
role?: string | null;
|
||||
runtime?: string | null;
|
||||
url?: string | null;
|
||||
health_status?: string | null;
|
||||
reason?: string | null;
|
||||
action_required?: boolean;
|
||||
}
|
||||
|
||||
interface AiRouteOperatorAction {
|
||||
human_required?: boolean;
|
||||
action?: string | null;
|
||||
reason?: string | null;
|
||||
}
|
||||
|
||||
interface AiRouteStatusResponse {
|
||||
schema_version: string;
|
||||
workload_type: string;
|
||||
@@ -440,6 +457,10 @@ interface AiRouteStatusResponse {
|
||||
route_source: string;
|
||||
route_error?: string | null;
|
||||
health: Record<string, AiRouteHealthItem>;
|
||||
lane_mode?: string | null;
|
||||
active_lane?: AiRouteLaneItem | null;
|
||||
skipped_lanes?: AiRouteLaneItem[];
|
||||
operator_action?: AiRouteOperatorAction | null;
|
||||
checked_at: string;
|
||||
}
|
||||
|
||||
@@ -2005,6 +2026,30 @@ function aiRouteRoleLabelKey(role?: string | null) {
|
||||
return "roles.ollama";
|
||||
}
|
||||
|
||||
function aiRouteLaneModeLabelKey(mode?: string | null) {
|
||||
if (
|
||||
mode === "primary" ||
|
||||
mode === "degraded_failover" ||
|
||||
mode === "cloud_fallback" ||
|
||||
mode === "unavailable"
|
||||
) {
|
||||
return `laneModes.${mode}`;
|
||||
}
|
||||
return "laneModes.unknown";
|
||||
}
|
||||
|
||||
function aiRouteOperatorActionLabelKey(action?: string | null) {
|
||||
if (
|
||||
action === "monitor" ||
|
||||
action === "repair_skipped_primary_lane" ||
|
||||
action === "restore_ollama_lanes" ||
|
||||
action === "inspect_ai_router"
|
||||
) {
|
||||
return `operatorActions.${action}`;
|
||||
}
|
||||
return "operatorActions.unknown";
|
||||
}
|
||||
|
||||
function AiRouteStatusPanel({
|
||||
status,
|
||||
error,
|
||||
@@ -2016,6 +2061,15 @@ function AiRouteStatusPanel({
|
||||
const policy = status?.policy_order ?? [];
|
||||
const selectedProvider = status?.selected_provider ?? null;
|
||||
const selectedModel = status?.selected_model ?? null;
|
||||
const laneMode = status?.lane_mode ?? null;
|
||||
const laneModeKey = aiRouteLaneModeLabelKey(laneMode);
|
||||
const operatorActionKey = aiRouteOperatorActionLabelKey(status?.operator_action?.action);
|
||||
const skippedLanes = status?.skipped_lanes ?? [];
|
||||
const skippedProviderSet = new Set(
|
||||
skippedLanes
|
||||
.map((lane) => lane.provider_name)
|
||||
.filter((provider): provider is string => Boolean(provider))
|
||||
);
|
||||
const checkedAt = status?.checked_at
|
||||
? new Date(status.checked_at).toLocaleTimeString("zh-TW", {
|
||||
hour: "2-digit",
|
||||
@@ -2033,7 +2087,14 @@ function AiRouteStatusPanel({
|
||||
<p className="text-xs text-[#77736a]">{t("subtitle")}</p>
|
||||
</div>
|
||||
</div>
|
||||
<span className="border border-[#9bb6d9] bg-[#eef5ff] px-2 py-0.5 text-xs font-semibold text-[#1f5b9b]">
|
||||
<span
|
||||
className={cn(
|
||||
"border px-2 py-0.5 text-xs font-semibold",
|
||||
laneMode === "primary"
|
||||
? "border-[#9bc7a4] bg-[#f0faf2] text-[#17602a]"
|
||||
: "border-[#d9b36f] bg-[#fff7e8] text-[#8a5a08]"
|
||||
)}
|
||||
>
|
||||
{selectedProvider
|
||||
? t("selected", { provider: selectedProvider })
|
||||
: t("selectedEmpty")}
|
||||
@@ -2050,7 +2111,28 @@ function AiRouteStatusPanel({
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
<div className="grid gap-px bg-[#e0ddd4] md:grid-cols-3">
|
||||
{laneMode && laneMode !== "primary" && (
|
||||
<div className="flex items-start gap-3 border-b border-[#e0ddd4] bg-[#fff7e8] px-4 py-3 text-sm text-[#6d4707]">
|
||||
<TriangleAlert className="mt-0.5 h-4 w-4 shrink-0" aria-hidden="true" />
|
||||
<div className="min-w-0">
|
||||
<p className="font-semibold text-[#141413]">
|
||||
{t(laneModeKey as never)}
|
||||
</p>
|
||||
<p className="mt-1 leading-5">
|
||||
{t("degradedSummary", {
|
||||
active: selectedProvider ?? "--",
|
||||
skipped: skippedLanes
|
||||
.map((lane) => lane.provider_name)
|
||||
.filter(Boolean)
|
||||
.join(" -> ") || "--",
|
||||
action: t(operatorActionKey as never),
|
||||
})}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="grid gap-px bg-[#e0ddd4] md:grid-cols-4">
|
||||
<div className="bg-white px-4 py-3">
|
||||
<p className="text-xs font-semibold text-[#77736a]">{t("fields.workload")}</p>
|
||||
<p className="mt-2 font-mono text-sm font-semibold text-[#141413]">
|
||||
@@ -2060,6 +2142,15 @@ function AiRouteStatusPanel({
|
||||
{t("fields.checkedAt", { time: checkedAt })}
|
||||
</p>
|
||||
</div>
|
||||
<div className="bg-white px-4 py-3">
|
||||
<p className="text-xs font-semibold text-[#77736a]">{t("fields.laneMode")}</p>
|
||||
<p className="mt-2 text-sm font-semibold text-[#141413]">
|
||||
{t(laneModeKey as never)}
|
||||
</p>
|
||||
<p className="mt-2 text-xs leading-5 text-[#5f5b52]">
|
||||
{t(operatorActionKey as never)}
|
||||
</p>
|
||||
</div>
|
||||
<div className="bg-white px-4 py-3">
|
||||
<p className="text-xs font-semibold text-[#77736a]">{t("fields.primary")}</p>
|
||||
<p className="mt-2 truncate font-mono text-sm font-semibold text-[#141413]">
|
||||
@@ -2090,6 +2181,7 @@ function AiRouteStatusPanel({
|
||||
const healthKey = aiRouteHealthLabelKey(health?.status);
|
||||
const roleKey = aiRouteRoleLabelKey(item.role);
|
||||
const isSelected = selectedProvider === item.provider_name;
|
||||
const isSkipped = skippedProviderSet.has(item.provider_name);
|
||||
const latency = typeof health?.latency_ms === "number"
|
||||
? `${health.latency_ms.toFixed(1)}ms`
|
||||
: "--";
|
||||
@@ -2110,12 +2202,18 @@ function AiRouteStatusPanel({
|
||||
"shrink-0 border px-2 py-0.5 text-xs font-semibold",
|
||||
isSelected
|
||||
? "border-[#9bc7a4] bg-[#f0faf2] text-[#17602a]"
|
||||
: isSkipped
|
||||
? "border-[#d9b36f] bg-[#fff7e8] text-[#8a5a08]"
|
||||
: item.runtime === "cloud"
|
||||
? "border-[#d9b36f] bg-[#fff7e8] text-[#8a5a08]"
|
||||
: "border-[#d8d3c7] bg-[#faf9f3] text-[#5f5b52]"
|
||||
)}
|
||||
>
|
||||
{isSelected ? t("badges.active") : t("badges.standby")}
|
||||
{isSelected
|
||||
? t("badges.active")
|
||||
: isSkipped
|
||||
? t("badges.skipped")
|
||||
: t("badges.standby")}
|
||||
</span>
|
||||
</div>
|
||||
<div className="mt-3 space-y-1 text-xs leading-5 text-[#5f5b52]">
|
||||
@@ -2124,6 +2222,9 @@ function AiRouteStatusPanel({
|
||||
<p className="truncate font-mono text-[#77736a]">
|
||||
{item.url || t("fields.noUrl")}
|
||||
</p>
|
||||
<p className="line-clamp-2 text-[#77736a]">
|
||||
{health?.reason || item.reason || "--"}
|
||||
</p>
|
||||
</div>
|
||||
</article>
|
||||
);
|
||||
|
||||
@@ -120,6 +120,12 @@ interface AiRouteStatusResponse {
|
||||
route_reason?: string | null
|
||||
route_error?: string | null
|
||||
health?: Record<string, AiRouteHealthItem>
|
||||
lane_mode?: string | null
|
||||
skipped_lanes?: Array<{ provider_name?: string | null }>
|
||||
operator_action?: {
|
||||
action?: string | null
|
||||
human_required?: boolean
|
||||
} | null
|
||||
}
|
||||
|
||||
interface EvidenceSnapshot {
|
||||
@@ -185,6 +191,18 @@ function routeHealthLabelKey(status?: string | null) {
|
||||
return 'routeHealth.unknown'
|
||||
}
|
||||
|
||||
function routeLaneModeLabelKey(mode?: string | null) {
|
||||
if (
|
||||
mode === 'primary' ||
|
||||
mode === 'degraded_failover' ||
|
||||
mode === 'cloud_fallback' ||
|
||||
mode === 'unavailable'
|
||||
) {
|
||||
return `routeLaneMode.${mode}`
|
||||
}
|
||||
return 'routeLaneMode.unknown'
|
||||
}
|
||||
|
||||
function providerDisplayName(provider?: string | null) {
|
||||
switch (provider) {
|
||||
case 'ollama_gcp_a':
|
||||
@@ -353,6 +371,10 @@ export function AutomationEvidenceCard() {
|
||||
const primaryProvider = route?.policy_order?.[0]?.provider_name ?? null
|
||||
const primaryStatus = primaryProvider ? route?.health?.[primaryProvider]?.status : null
|
||||
const selectedProvider = providerDisplayName(route?.selected_provider)
|
||||
const laneMode = route?.lane_mode ?? null
|
||||
const skippedLanes = route?.skipped_lanes
|
||||
?.map((lane) => providerDisplayName(lane.provider_name))
|
||||
.join(' -> ')
|
||||
const fallback = route?.fallback_chain
|
||||
?.map((item) => item.provider_name)
|
||||
.map(providerDisplayName)
|
||||
@@ -366,8 +388,14 @@ export function AutomationEvidenceCard() {
|
||||
primaryStatus: t(routeHealthLabelKey(primaryStatus) as never),
|
||||
fallback: fallback || t('routeNoFallback'),
|
||||
})
|
||||
const laneDetail = laneMode && laneMode !== 'primary'
|
||||
? t('routeLaneDetail', {
|
||||
mode: t(routeLaneModeLabelKey(laneMode) as never),
|
||||
skipped: skippedLanes || '--',
|
||||
})
|
||||
: null
|
||||
const routeDetail = route?.route_reason && !route.route_error
|
||||
? `${routeSummary}${t('routeReasonSeparator')}${t('routeReason', { reason: route.route_reason })}`
|
||||
? `${routeSummary}${laneDetail ? t('routeReasonSeparator') + laneDetail : ''}${t('routeReasonSeparator')}${t('routeReason', { reason: route.route_reason })}`
|
||||
: routeSummary
|
||||
|
||||
return {
|
||||
|
||||
Reference in New Issue
Block a user