Siddharaj Shirke
deploy: fresh snapshot to Hugging Face Space
3eae4cc
const DEFAULT_LOCAL_API = "http://127.0.0.1:7860";
const LOCAL_PORTS = ["7860"];
const LOCAL_HOSTS = ["127.0.0.1", "localhost"];
function candidates(path) {
const urls = [];
const rootOnlyPaths = path === "/rl/models";
const compatNoApiPaths =
path.startsWith("/simulation/") ||
path.startsWith("/training/") ||
path.startsWith("/rl/") ||
path.startsWith("/openenv/") ||
path.startsWith("/benchmark") ||
path.startsWith("/history/");
let isLocalDev5173 = false;
if (typeof window !== "undefined") {
const host = window.location.hostname;
const isLocal = host === "localhost" || host === "127.0.0.1";
isLocalDev5173 = isLocal && window.location.port === "5173";
}
// Training story endpoints are mounted at /training/* (not /api/training/*).
// Avoid known-bad prefixes first to prevent noisy 404 logs in browser console.
if (path.startsWith("/training/")) {
if (isLocalDev5173) {
for (const port of LOCAL_PORTS) {
for (const lh of LOCAL_HOSTS) {
urls.push(`http://${lh}:${port}${path}`);
}
}
} else {
urls.push(path);
}
return [...new Set(urls)];
}
if (isLocalDev5173) {
// For local dev, prefer direct backend URLs first to avoid noisy Vite proxy
// connection-refused spam when backend is temporarily down.
for (const port of LOCAL_PORTS) {
for (const lh of LOCAL_HOSTS) {
if (rootOnlyPaths) {
urls.push(`http://${lh}:${port}${path}`);
} else {
urls.push(`http://${lh}:${port}/api${path}`);
urls.push(`http://${lh}:${port}/api/v1${path}`);
if (compatNoApiPaths) {
urls.push(`http://${lh}:${port}${path}`);
}
}
}
}
}
if (rootOnlyPaths) {
urls.push(path);
} else {
urls.push(`/api${path}`, `/api/v1${path}`);
if (compatNoApiPaths) {
urls.push(path);
}
}
if (isLocalDev5173 && !rootOnlyPaths) {
for (const port of LOCAL_PORTS) {
for (const lh of LOCAL_HOSTS) {
// keep original ordering as fallback candidates
urls.push(`http://${lh}:${port}/api${path}`);
urls.push(`http://${lh}:${port}/api/v1${path}`);
}
}
}
return [...new Set(urls)];
}
export async function api(path, options = {}) {
const method = String(options.method || "GET").toUpperCase();
const headers = { ...(options.headers || {}) };
if (method !== "GET" && method !== "HEAD" && !("Content-Type" in headers)) {
headers["Content-Type"] = "application/json";
}
const requestOptions = {
...options,
method,
headers,
};
if (method === "GET" || method === "HEAD") {
delete requestOptions.body;
}
const errors = [];
for (const url of candidates(path)) {
try {
const res = await fetch(url, requestOptions);
let payload = null;
try {
payload = await res.json();
} catch (err) {
payload = null;
}
if (!res.ok) {
const detail = payload?.detail || `${res.status}`;
throw new Error(`API ${path} failed on ${url}: ${detail}`);
}
return payload;
} catch (err) {
errors.push(err);
}
}
const firstApiError = errors.find(
(e) => e instanceof Error && e.message.startsWith(`API ${path} failed`)
);
if (firstApiError) {
throw firstApiError;
}
const lastError = errors.length ? errors[errors.length - 1] : new Error("Unknown request failure.");
throw new Error(
`API ${path} connection failed. Start backend on ${DEFAULT_LOCAL_API}. Last error: ${
lastError instanceof Error ? lastError.message : String(lastError)
}`
);
}
export function fmt(value, digits = 2) {
if (value == null || Number.isNaN(Number(value))) return "-";
return Number(value).toFixed(digits);
}