Spaces:
Running
Running
File size: 3,813 Bytes
3eae4cc | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 | 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);
}
|