proteinea / src /app /chat /_components /ComputeProviderSelector.tsx
Mahmoud Eljendy
feat: Antibody Studio β€” AI-native antibody design workspace by Proteinea
30cc31a
"use client";
import { useCallback, useEffect, useState } from "react";
const PHYLO_BACKEND =
process.env.NEXT_PUBLIC_PHYLO_BACKEND_URL || "http://127.0.0.1:8601";
type Provider = "hub-runpod" | "hub-slurm" | "runpod-direct";
const LABELS: Record<Provider, string> = {
"hub-runpod": "Hub (RunPod)",
"hub-slurm": "Hub (SLURM)",
"runpod-direct": "RunPod Direct",
};
export default function ComputeProviderSelector() {
const [provider, setProvider] = useState<Provider>("hub-runpod");
const [runpodConfigured, setRunpodConfigured] = useState(false);
const [loading, setLoading] = useState(true);
useEffect(() => {
(async () => {
try {
const res = await fetch(`${PHYLO_BACKEND}/api/settings`);
if (res.ok) {
const data = await res.json();
setProvider(data.compute_provider);
setRunpodConfigured(data.runpod_configured);
}
} catch {
// Backend unreachable β€” keep default
} finally {
setLoading(false);
}
})();
}, []);
const onChange = useCallback(async (newProvider: Provider) => {
setProvider(newProvider);
try {
await fetch(`${PHYLO_BACKEND}/api/settings`, {
method: "PATCH",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ compute_provider: newProvider }),
});
} catch {
// Silently fail β€” setting will be stale until next restart
}
}, []);
if (loading) return null;
return (
<div className="flex items-center gap-1.5">
<svg
className="w-3 h-3 text-muted-fg"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth={2}
>
<rect x="4" y="4" width="16" height="16" rx="2" />
<path d="M9 9h6v6H9z" />
</svg>
<select
value={provider}
onChange={(e) => onChange(e.target.value as Provider)}
className="text-[10px] bg-transparent border border-border rounded-md px-1.5 py-0.5 text-foreground-dim focus:outline-none focus:border-accent cursor-pointer"
title="Compute provider β€” where GPU jobs run"
>
{(Object.keys(LABELS) as Provider[]).map((p) => (
<option key={p} value={p} disabled={p === "runpod-direct" && !runpodConfigured}>
{LABELS[p]}{p === "runpod-direct" && !runpodConfigured ? " (no key)" : ""}
</option>
))}
</select>
</div>
);
}