Files
ModelForge-Public/frontend/src/components/CapabilityWorkspace.tsx
T

343 lines
28 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { FormEvent, useCallback, useEffect, useRef, useState } from "react";
import type { ReactNode } from "react";
import { CapabilityDetails } from "./CapabilityDetails";
import { PromotionGate } from "./PromotionGate";
import type { OperatorIdentity } from "../lib/api";
import { CircleGauge, Clock3, KeyRound, Play, RefreshCw, ShieldCheck } from "lucide-react";
import { api, ApiError, operatorCredentialReference, operatorSessionVersion } from "../lib/api";
import { SafetyDialog } from "./SafetyDialog";
import { TabList, TabPanel } from "./TabList";
import type { TabDescriptor } from "./TabList";
import type {
CapabilityDeployment, CapabilityContractRecord, CapabilityEstate, DeploymentCandidate, GatewayInvokeResponse,
GatewayRequest, OCRInvokeResponse, PlacementPlan, ProductionApproval, RuntimeProfile, SchedulerBudget, SchedulerPolicy, ServiceClient,
ServiceClientCreated, SpeechTranscriptionResponse, VisionEmbeddingResponse,
} from "../types";
type View = "overview" | "scheduler" | "requests" | "clients" | "promotion" | "playground";
type CapabilityReadFailure = {
kind: "offline" | "error" | "unknown";
message: string;
code: string;
correlationId: string | null;
retryable: boolean;
nextAction: string;
};
type CapabilityReadState =
| { kind: "loading" }
| { kind: "fresh"; lastUpdatedAt: number }
| { kind: "unavailable"; failure: CapabilityReadFailure }
| { kind: "stale"; failure: CapabilityReadFailure; lastUpdatedAt: number };
const capabilityViews: TabDescriptor<View>[] = [
{ key: "overview", label: "overview" }, { key: "scheduler", label: "scheduler" },
{ key: "requests", label: "requests" }, { key: "clients", label: "clients" },
{ key: "promotion", label: "promotion" }, { key: "playground", label: "playground" },
];
export function CapabilityWorkspace({ onError, role = "viewer", targetDeploymentId }: { onError: (message: string | null) => void; role?: OperatorIdentity["role"]; targetDeploymentId?: string | null }) {
const sessionReference = operatorCredentialReference();
const sessionActive = Boolean(sessionReference) && role === "admin";
const generation = operatorSessionVersion();
const [view, setView] = useState<View>("overview");
const [deployments, setDeployments] = useState<CapabilityDeployment[]>([]);
const [contracts, setContracts] = useState<CapabilityContractRecord[]>([]);
const [estate, setEstate] = useState<CapabilityEstate[]>([]);
const [budgets, setBudgets] = useState<SchedulerBudget[]>([]);
const [profiles, setProfiles] = useState<RuntimeProfile[]>([]);
const [candidates, setCandidates] = useState<DeploymentCandidate[]>([]);
const [requests, setRequests] = useState<GatewayRequest[]>([]);
const [clients, setClients] = useState<ServiceClient[]>([]);
const [approvals, setApprovals] = useState<ProductionApproval[]>([]);
const [plans, setPlans] = useState<PlacementPlan[]>([]);
const [policy, setPolicy] = useState<SchedulerPolicy | null>(null);
const adminToken = sessionReference;
const adminUnlocked = sessionActive;
const [loading, setLoading] = useState(true);
const [refreshing, setRefreshing] = useState(false);
const [publicRead, setPublicRead] = useState<CapabilityReadState>({ kind: "loading" });
const publicRequest = useRef(0);
const publicSnapshotAt = useRef<number | null>(null);
const mounted = useRef(false);
useEffect(() => {
mounted.current = true;
return () => { mounted.current = false; publicRequest.current += 1; };
}, []);
const loadPublic = useCallback(async () => {
const request = ++publicRequest.current;
const session = operatorSessionVersion();
const current = () => mounted.current && request === publicRequest.current && session === operatorSessionVersion();
try {
const [nextDeployments, nextBudgets, nextCandidates, nextEstate, nextProfiles, nextContracts] = await Promise.all([
api.capabilityDeployments(), api.scheduler(), api.deploymentCandidates(), api.capabilityEstate(),
api.runtimeProfiles(), api.capabilityContracts(),
]);
if (!current()) return;
setContracts(nextContracts);
setDeployments(nextDeployments);
setBudgets(nextBudgets);
setCandidates(nextCandidates);
setEstate(nextEstate);
setProfiles(nextProfiles);
const lastUpdatedAt = Date.now();
publicSnapshotAt.current = lastUpdatedAt;
setPublicRead({ kind: "fresh", lastUpdatedAt });
} catch (cause: unknown) {
if (!current()) return;
const failure = capabilityReadFailure(cause);
const lastUpdatedAt = publicSnapshotAt.current;
setPublicRead(lastUpdatedAt === null
? { kind: "unavailable", failure }
: { kind: "stale", failure, lastUpdatedAt });
}
}, []);
const loadAdmin = useCallback(async (token: string) => {
if (!sessionActive || generation !== operatorSessionVersion()) return;
const [nextRequests, nextClients, nextApprovals, nextPlans, nextPolicy] = await Promise.all([
api.gatewayRequests(token), api.serviceClients(token), api.productionApprovals(token),
api.placementHistory(token), api.schedulerPolicy(token),
]);
if (generation !== operatorSessionVersion()) return;
setRequests(nextRequests);
setClients(nextClients);
setApprovals(nextApprovals);
setPlans(nextPlans);
setPolicy(nextPolicy);
}, [sessionActive, generation]);
useEffect(() => {
Promise.all([loadPublic(), sessionActive ? loadAdmin(sessionReference) : Promise.resolve()])
.catch((cause: unknown) => onError(errorMessage(cause)))
.finally(() => setLoading(false));
}, [loadAdmin, loadPublic, onError, sessionActive, sessionReference]);
useEffect(() => {
const interval = window.setInterval(() => {
void loadPublic();
if (adminUnlocked) loadAdmin(adminToken).catch((cause: unknown) => onError(errorMessage(cause)));
}, 5000);
return () => window.clearInterval(interval);
}, [adminToken, adminUnlocked, loadAdmin, loadPublic, onError]);
const refresh = async () => {
onError(null);
setRefreshing(true);
try { await Promise.all([loadPublic(), sessionActive ? loadAdmin(adminToken) : Promise.resolve()]); }
catch (cause: unknown) { onError(errorMessage(cause)); }
finally { setRefreshing(false); }
};
if (loading || publicRead.kind === "loading") return <div className="state-card"><span className="state-spinner" />Loading capability serving state…</div>;
if (publicRead.kind === "unavailable") return <CapabilityReadNotice failure={publicRead.failure} onRetry={refresh} refreshing={refreshing} />;
return <div className="capability-stack">
{publicRead.kind === "stale" ? <CapabilityReadNotice failure={publicRead.failure} lastUpdatedAt={publicRead.lastUpdatedAt} onRetry={refresh} refreshing={refreshing} /> : null}
<section className="capability-hero panel">
<div><span className="eyebrow">MULTI-CAPABILITY ESTATE</span><h2>{contracts.length} declared contracts</h2><p>Capability-first routing across RAG, document, vision and audio · no client-visible model names</p></div>
<div className="capability-health"><strong>{deployments.length} deployment records</strong><span>Inspect health per exact binding below.</span></div>
</section>
<div className="capability-toolbar">
<TabList idPrefix="capability" label="Capability operations" className="capability-tabs" tabs={capabilityViews} active={view} onSelect={setView} />
<button className="icon-action" aria-label="Refresh capability state" onClick={refresh}><RefreshCw size={15} /></button>
</div>
<TabPanel idPrefix="capability" active={view} className="capability-tabpanel">
{view === "overview" ? <CapabilityDetails contracts={contracts} deployments={deployments} estate={estate} targetDeploymentId={targetDeploymentId} /> : null}
{view === "scheduler" ? <><SchedulerView budgets={budgets} deployments={deployments} plans={plans} policy={policy} token={adminToken} unlocked={adminUnlocked} onChanged={refresh} onError={onError} /><AdminBoundary unlocked={adminUnlocked}><></></AdminBoundary></> : null}
{view === "requests" ? <AdminBoundary unlocked={adminUnlocked}><RequestHistory requests={requests} /></AdminBoundary> : null}
{view === "clients" ? <AdminBoundary unlocked={adminUnlocked}><Clients clients={clients} token={adminToken} onChanged={() => loadAdmin(adminToken)} onError={onError} /></AdminBoundary> : null}
{view === "promotion" ? <AdminBoundary unlocked={adminUnlocked}><PromotionGate key={generation} deployments={deployments} candidates={candidates} profiles={profiles} approvals={approvals} token={adminToken} onChanged={refresh} /></AdminBoundary> : null}
{view === "playground" ? <Playground /> : null}
</TabPanel>
</div>;
}
function CapabilityReadNotice({ failure, lastUpdatedAt, onRetry, refreshing }: {
failure: CapabilityReadFailure;
lastUpdatedAt?: number;
onRetry: () => Promise<void>;
refreshing: boolean;
}) {
const hasSnapshot = lastUpdatedAt !== undefined;
const stateLabel = failure.kind === "offline" ? "OFFLINE" : hasSnapshot ? "STALE" : failure.kind.toUpperCase();
const stateClass = failure.kind === "offline" ? "offline" : hasSnapshot ? "stale" : failure.kind === "unknown" ? "unknown" : "error";
return <section className={hasSnapshot ? "panel capability-read-notice" : "error-banner capability-read-notice"} role={hasSnapshot ? "status" : "alert"} aria-labelledby="capability-read-notice-title">
<div><span className={`status-chip ${stateClass}`}>{stateLabel}</span><h2 id="capability-read-notice-title">{hasSnapshot ? "Capability data may be out of date" : "Capability serving state is unavailable"}</h2></div>
{hasSnapshot
? <p>The last successful snapshot was read {formatTime(new Date(lastUpdatedAt).toISOString())}. Counts below show that snapshot; the latest read failed.</p>
: <p>No authoritative snapshot was loaded, so contract and deployment totals are unknown. A failed read does not mean the estate is empty.</p>}
<div className="evidence-list">
<Evidence label="Read failure" value={failure.message} />
<Evidence label="Failure code" value={failure.code} />
<Evidence label="Correlation ID" value={failure.correlationId ?? "Not provided"} />
<Evidence label="Retryable" value={failure.retryable ? "Yes" : "No"} />
</div>
<p><strong>Next action:</strong> {failure.nextAction}</p>
<button type="button" onClick={() => void onRetry()} disabled={refreshing}>{refreshing ? "Retrying capability state…" : "Retry capability state"}</button>
</section>;
}
function SchedulerView({ budgets, deployments, plans, policy, token, unlocked, onChanged, onError }: { budgets: SchedulerBudget[]; deployments: CapabilityDeployment[]; plans: PlacementPlan[]; policy: SchedulerPolicy | null; token: string; unlocked: boolean; onChanged: () => Promise<void>; onError: (message: string | null) => void }) {
const [latest, setLatest] = useState<Record<string, PlacementPlan>>({});
const [pendingUnload, setPendingUnload] = useState<CapabilityDeployment | null>(null);
const [unloadAcknowledged, setUnloadAcknowledged] = useState(false);
const [unloadPending, setUnloadPending] = useState(false);
const [unloadError, setUnloadError] = useState<string | null>(null);
const [unloadOutcome, setUnloadOutcome] = useState<string | null>(null);
if (!budgets.length) return <div className="state-card">No accelerator telemetry is available for scheduling.</div>;
const simulate = async (deploymentId: string) => {
try { const plan = await api.dryRunPlacement(deploymentId, "interactive", token); setLatest((current) => ({ ...current, [deploymentId]: plan })); }
catch (cause: unknown) { onError(errorMessage(cause)); }
};
const previewUnload = (deployment: CapabilityDeployment) => {
setUnloadAcknowledged(false); setUnloadError(null); setUnloadOutcome(null); setPendingUnload(deployment);
};
const confirmUnload = async (deployment: CapabilityDeployment) => {
// Guarded against a replayed handler so one acknowledgement can never evict residency twice.
if (unloadPending) return;
setUnloadPending(true); setUnloadError(null);
try {
await api.unloadDeployment(deployment.id, token);
setPendingUnload(null); setUnloadAcknowledged(false);
setUnloadOutcome(`Residency for ${deployment.capability}@${deployment.contract_version} was unloaded.`);
await onChanged();
} catch (cause: unknown) { setUnloadError(errorMessage(cause)); }
finally { setUnloadPending(false); }
};
const changePolicy = async (deployment: CapabilityDeployment, value: string) => {
try { await api.updateResidencyPolicy(deployment.id, value, token); await onChanged(); }
catch (cause: unknown) { onError(errorMessage(cause)); }
};
const pauseLab = async () => {
try { await api.updateSchedulerPolicy(!Boolean(policy?.configuration.lab_paused), token); await onChanged(); }
catch (cause: unknown) { onError(errorMessage(cause)); }
};
return <div className="capability-stack"><section className="scheduler-grid">{budgets.map((budget) => {
const segments = [
["External / unmanaged", budget.external_vram_bytes], ["Safety reserve", budget.safety_reserve_bytes],
["ModelForge resident", budget.resident_vram_bytes], ["Active leases", budget.leased_vram_bytes],
["Schedulable free", budget.schedulable_free_vram_bytes],
] as const;
return <article className="panel scheduler-card" key={budget.accelerator_id}><div className="panel-header"><div><span className="eyebrow">GPU SCHEDULING COCKPIT · {budget.node_name}</span><h2>{budget.accelerator_name}</h2></div><span className={`status-chip ${budget.pressure_state === "NORMAL" ? "active" : "degraded"}`}>{budget.pressure_state}</span></div><div className="budget-total"><strong>{formatBytes(budget.total_vram_bytes)}</strong><span>Total VRAM · attribution {budget.attribution_confidence}</span></div><div className="budget-bar" aria-label="GPU capacity distribution">{segments.map(([label, bytes], index) => <span key={label} className={`budget-segment segment-${index}`} style={{ width: `${Math.min(100, (bytes / budget.total_vram_bytes) * 100)}%` }} title={`${label}: ${formatBytes(bytes)}`} />)}</div><div className="budget-legend">{segments.map(([label, bytes]) => <Evidence key={label} label={label} value={formatBytes(bytes)} />)}</div><small>Observed {formatBytes(budget.observed_used_vram_bytes)} · invariant delta {formatBytes(budget.invariant_delta_bytes)} · policy {budget.policy_revision} · telemetry {formatTime(budget.observed_at)}. External usage is observation-only and has no controls.</small></article>;
})}</section>{unlocked ? <><article className="panel evidence-list"><div className="panel-header"><div><span className="eyebrow">RESIDENCY MAP · WHAT CAN RUN NOW?</span><h2>Managed capability placements</h2></div><button onClick={pauseLab}>{Boolean(policy?.configuration.lab_paused) ? "Resume LAB" : "Pause LAB"}</button></div>{deployments.map((item) => { const plan = latest[item.id]; return <div className="client-row" key={item.id}><div><strong>{item.capability}@{item.contract_version} · {item.residency.state.toUpperCase()}</strong><span>{item.production ? "production" : "lab"} · {formatBytes(item.residency.measured_resident_vram_bytes)} · {item.residency.active_requests} active · cold load {formatMs(item.resource_envelope.cold_load_time_ms)}</span>{plan ? <span>{plan.verdict} · {plan.reason_codes.join(", ")} · after {formatBytes(plan.headroom_after_bytes)}{plan.evictions.length ? ` · evict ${plan.evictions.map((entry) => entry.capability).join(", ")}` : ""}</span> : null}</div><div className="promotion-actions"><button onClick={() => simulate(item.id)}>Dry run</button><select aria-label={`Residency policy for ${item.capability}`} value={item.residency_policy} onChange={(event) => changePolicy(item, event.target.value)}><option value="always_warm">ALWAYS_WARM</option><option value="keep_warm">KEEP_WARM</option><option value="load_on_demand">LOAD_ON_DEMAND</option><option value="lab_only">LAB_ONLY</option></select><button className="danger" disabled={item.residency.active_requests > 0 || item.residency.state === "cold"} onClick={() => previewUnload(item)}>Unload idle</button></div></div>; })}</article><article className="panel evidence-list"><div className="panel-header"><div><span className="eyebrow">PLACEMENT HISTORY</span><h2>Recent bounded decisions</h2></div><CircleGauge size={19}/></div>{plans.slice(0, 20).map((plan) => <Evidence key={plan.id ?? plan.decision_fingerprint} label={`${plan.capability} · ${plan.verdict}`} value={`${plan.reason_codes.join(", ")} · ${formatBytes(plan.headroom_before_bytes)} → ${formatBytes(plan.headroom_after_bytes)} · ${formatTime(plan.created_at)}`}/>)}</article></> : null}
{unloadOutcome ? <p className="field-success" role="status">{unloadOutcome}</p> : null}
{pendingUnload ? <SafetyDialog
idPrefix="residency-unload"
eyebrow="DESTRUCTIVE ACTION PREVIEW"
title="Unload idle residency?"
subject={`${pendingUnload.capability}@${pendingUnload.contract_version}`}
description="ModelForge will evict this deployment from accelerator memory. Serving is not withdrawn: the next request reloads the model and pays the cold-load cost. Residency is unloaded only while no request is active."
facts={[
{ label: "Channel", value: pendingUnload.production ? "production" : "lab" },
{ label: "Residency state", value: pendingUnload.residency.state },
{ label: "Active requests", value: String(pendingUnload.residency.active_requests) },
{ label: "Resident VRAM released", value: formatBytes(pendingUnload.residency.measured_resident_vram_bytes) },
{ label: "Next cold load", value: formatMs(pendingUnload.resource_envelope.cold_load_time_ms) },
]}
acknowledgement="I understand the next request for this capability will pay the cold-load cost."
confirmLabel="Unload residency"
acknowledged={unloadAcknowledged}
onAcknowledgedChange={setUnloadAcknowledged}
pending={unloadPending}
errorTitle="Unload blocked"
error={unloadError}
closeLabel="Close residency unload preview"
onCancel={() => setPendingUnload(null)}
onConfirm={() => void confirmUnload(pendingUnload)}
/> : null}</div>;
}
function RequestHistory({ requests }: { requests: GatewayRequest[] }) {
if (!requests.length) return <div className="state-card">No gateway request metadata exists yet. Input text is never displayed or persisted.</div>;
return <article className="panel table-panel"><div className="panel-header"><div><span className="eyebrow">PRIVACY-PRESERVING HISTORY</span><h2>Gateway requests</h2></div><Clock3 size={19}/></div><div className="operation-table"><div className="table-head"><span>Request</span><span>Client</span><span>State</span><span>Path</span><span>Queue</span><span>Total</span><span>Time</span></div>{requests.map((request) => <div className="table-row" key={request.request_id}><code>{request.request_id.slice(0, 8)}…</code><span>{request.client ?? "rejected identity"}</span><span className={`registry-status ${request.status}`}>{request.failure_code ?? request.status}</span><span>{request.cold == null ? "—" : request.cold ? "cold" : "warm"}</span><span>{formatMs(request.queue_time_ms)}</span><span>{formatMs(request.total_latency_ms)}</span><span>{formatTime(request.created_at)}</span></div>)}</div></article>;
}
function Clients({ clients, token, onChanged, onError }: { clients: ServiceClient[]; token: string; onChanged: () => Promise<void>; onError: (message: string | null) => void }) {
const [name, setName] = useState("modelforge-m5-acceptance");
const [rpm, setRpm] = useState(60);
const [created, setCreated] = useState<ServiceClientCreated | null>(null);
const submit = async (event: FormEvent) => {
event.preventDefault(); onError(null);
try { setCreated(await api.createServiceClient({ name, allowed_capabilities: ["rag.embedding@1"], requests_per_minute: rpm, max_concurrent_requests: 1 }, token)); await onChanged(); }
catch (cause: unknown) { onError(errorMessage(cause)); }
};
const revoke = async (clientId: string) => {
onError(null);
try { await api.revokeServiceCredential(clientId, token); await onChanged(); }
catch (cause: unknown) { onError(errorMessage(cause)); }
};
return <section className="grid-two"><form className="panel client-form" onSubmit={submit}><span className="eyebrow">SCOPED IDENTITY</span><h2>Create service client</h2><label>Name<input required pattern="[a-z][a-z0-9-]*" value={name} onChange={(event) => setName(event.target.value)} /></label><label>Requests per minute<input type="number" min="1" max="10000" value={rpm} onChange={(event) => setRpm(Number(event.target.value))} /></label><p>Scope is fixed to <code>rag.embedding@1</code>; concurrency starts conservatively at one.</p><button type="submit"><KeyRound size={15}/>Create one-time credential</button>{created ? <div className="one-time-secret"><strong>Copy this credential now</strong><code>{created.credential}</code><small>Only its hash is stored. It cannot be retrieved again.</small></div> : null}</form><article className="panel evidence-list"><div className="panel-header"><div><span className="eyebrow">CLIENTS</span><h2>Authorized consumers</h2></div><KeyRound size={19}/></div>{clients.length ? clients.map((client) => <div className="client-row" key={client.id}><div><strong>{client.name}</strong><span>{client.allowed_capabilities.join(", ")} · {client.requests_per_minute}/min · last used {formatTime(client.last_used_at)}</span></div><button className="danger" disabled={client.status !== "active"} onClick={() => revoke(client.id)}>{client.status === "active" ? "Revoke" : "Revoked"}</button></div>) : <p>No service clients created.</p>}</article></section>;
}
type ModalityResult = GatewayInvokeResponse | OCRInvokeResponse | VisionEmbeddingResponse | SpeechTranscriptionResponse;
async function fileBase64(file: File): Promise<string> {
const bytes = new Uint8Array(await file.arrayBuffer());
let binary = "";
for (const byte of bytes) binary += String.fromCharCode(byte);
return btoa(binary);
}
function Playground() {
const [capability, setCapability] = useState<"rag.embedding" | "document.ocr" | "vision.embedding" | "speech.transcription">("rag.embedding");
const [credential, setCredential] = useState("");
const [input, setInput] = useState("ModelForge capability gateway test");
const [file, setFile] = useState<File | null>(null);
const [result, setResult] = useState<ModalityResult | null>(null);
const [failure, setFailure] = useState<string | null>(null);
const [running, setRunning] = useState(false);
const invoke = async (event: FormEvent) => {
event.preventDefault(); setFailure(null); setRunning(true);
try {
if (capability === "rag.embedding") setResult(await api.invokeEmbedding(input, credential));
else {
if (!file) throw new Error("Choose a local fixture first");
const encoded = await fileBase64(file);
if (capability === "document.ocr") {
if (file.type !== "image/png" && file.type !== "image/jpeg") throw new Error("OCR accepts PNG or JPEG");
setResult(await api.invokeOcr(encoded, file.type, credential));
} else if (capability === "vision.embedding") {
if (file.type !== "image/png" && file.type !== "image/jpeg") throw new Error("Vision accepts PNG or JPEG");
setResult(await api.invokeVisionEmbedding(encoded, file.type, credential));
} else {
if (file.type !== "audio/wav" && !file.name.toLowerCase().endsWith(".wav")) throw new Error("Transcription accepts WAV");
setResult(await api.invokeTranscription(encoded, credential));
}
}
}
catch (cause: unknown) { setFailure(errorMessage(cause)); }
finally { setRunning(false); }
};
const output = result ? ("data" in result ? `${result.data.length} × ${result.dimension} finite values` : "text" in result ? result.text || "valid empty text" : "typed output") : "";
return <form className="panel playground" onSubmit={invoke}><div className="panel-header"><div><span className="eyebrow">REAL GATEWAY PATH</span><h2>Capability playground</h2></div><Play size={20}/></div><p>The client selects a capability contract. No model name, path, or runtime engine is accepted here.</p><label>Capability<select value={capability} onChange={(event) => { setCapability(event.target.value as typeof capability); setResult(null); }}><option value="rag.embedding">rag.embedding@1</option><option value="document.ocr">document.ocr@1</option><option value="vision.embedding">vision.embedding@1</option><option value="speech.transcription">speech.transcription@1</option></select></label><label>Service credential<input required type="password" autoComplete="off" value={credential} onChange={(event) => setCredential(event.target.value)} /></label>{capability === "rag.embedding" ? <label>Input text<textarea required maxLength={8192} value={input} onChange={(event) => setInput(event.target.value)} /></label> : <label>{capability === "speech.transcription" ? "WAV audio" : "PNG or JPEG image"}<input required type="file" accept={capability === "speech.transcription" ? ".wav,audio/wav" : "image/png,image/jpeg"} onChange={(event) => setFile(event.target.files?.[0] ?? null)} /></label>}<button disabled={running}>{running ? "Invoking…" : "Invoke capability"}</button>{failure ? <div className="error-banner"><strong>Typed gateway failure</strong><span>{failure}</span></div> : null}{result ? <div className="playground-result"><span className={`registry-status ${result.execution.cold ? "candidate" : "ready"}`}>{result.execution.cold ? "cold" : "warm"}</span><Evidence label="Request ID" value={result.request_id}/><Evidence label="Output" value={output}/>{"embedding_space_id" in result ? <Evidence label="Embedding space" value={result.embedding_space_id}/> : null}<Evidence label="Total / inference" value={`${formatMs(result.execution.timings.total_ms)} / ${formatMs(result.execution.timings.inference_ms)}`}/></div> : null}</form>;
}
function AdminBoundary({ unlocked, children }: { unlocked: boolean; children: ReactNode }) {
return unlocked ? <>{children}</> : <div className="state-card">Read-only access. Administrative operations require an active admin Console session.</div>;
}
function Evidence({ label, value }: { label: string; value: string }) { return <div className="evidence-row"><span>{label}</span><strong title={value}>{value}</strong></div>; }
function formatBytes(value: number): string { return `${(value / 1024 ** 3).toFixed(2)} GiB`; }
function formatMs(value?: number | null): string { return value == null ? "—" : `${value.toFixed(1)} ms`; }
function formatTime(value?: string | null): string { return value ? new Date(value).toLocaleString() : "never"; }
function capabilityReadFailure(cause: unknown): CapabilityReadFailure {
const apiFailure = cause instanceof ApiError ? cause : null;
const offline = apiFailure === null && ((cause instanceof TypeError) || (typeof navigator !== "undefined" && navigator.onLine === false));
const kind: CapabilityReadFailure["kind"] = offline ? "offline" : apiFailure ? "error" : "unknown";
const failureCode = apiFailure?.details.failure_code;
const code = typeof failureCode === "string" ? failureCode : apiFailure?.code ?? (apiFailure ? `HTTP_${apiFailure.status}` : offline ? "NETWORK_UNAVAILABLE" : "UNKNOWN_ERROR");
const retryable = apiFailure ? apiFailure.status === 408 || apiFailure.status === 429 || apiFailure.status >= 500 : offline;
const rawMessage = cause instanceof Error ? cause.message : "Capability state could not be read";
const message = rawMessage.replace(/\s+\(correlation [^)]+\)$/, "");
const nextAction = offline
? "Restore the connection to the ModelForge API, then retry this read."
: apiFailure?.status === 401 || apiFailure?.status === 403
? "Check the active Console identity and its read access, then retry."
: retryable
? "Retry this read. If it continues to fail, share the failure code and correlation ID with the operator team."
: "Review the API response and Console access, then retry after addressing the reported failure.";
return { kind, message, code, correlationId: apiFailure?.correlationId ?? null, retryable, nextAction };
}
function errorMessage(cause: unknown): string { return cause instanceof ApiError ? `${cause.message}${typeof cause.details.failure_code === "string" ? ` (${cause.details.failure_code})` : ""}` : cause instanceof Error ? cause.message : "Capability operation failed"; }