export type RankedCandidate = { rank: number; canonical_smiles: string; structure_svg: string; reference_structure_svg: string; candidate_change_svg: string; change_summary: string; score: number; similarity: number; metrics: Record; recommendation: string; warnings: string[]; }; type ApiErrorBody = { detail?: unknown; error?: unknown; message?: unknown; }; export type DrugLookupResult = { drug_name: string; structure_input: string; structure_format: "smiles" | "inchi"; structure_svg: string; target_name: string; indication: string; mechanism: string; source: string; }; export async function lookupDrug(name: string): Promise { const response = await fetch(`/api/drugs/lookup?name=${encodeURIComponent(name)}`); if (!response.ok) throw new Error(await buildApiError("Drug lookup failed", response)); return readJsonResponse("Drug lookup failed", response); } export async function createProjectAndRun(payload: Record) { const projectResponse = await fetch("/api/projects", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(payload), }); if (!projectResponse.ok) throw new Error(await buildApiError("Project creation failed", projectResponse)); const project = await readJsonResponse<{ id: number }>("Project creation failed", projectResponse); const runResponse = await fetch(`/api/projects/${project.id}/run`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(payload), }); if (!runResponse.ok) throw new Error(await buildApiError("Pipeline run failed", runResponse)); return readJsonResponse("Pipeline run failed", runResponse); } async function buildApiError(prefix: string, response: Response) { const text = await response.text(); if (looksLikeHtml(response, text)) { return `${prefix}: API returned HTML instead of JSON. Check that the backend server is running and the /api proxy points to FastAPI.`; } const detail = readErrorDetail(text); return detail ? `${prefix}: ${detail}` : prefix; } async function readJsonResponse(prefix: string, response: Response): Promise { const text = await response.text(); if (looksLikeHtml(response, text)) { throw new Error( `${prefix}: API returned HTML instead of JSON. Check that the backend server is running and the /api proxy points to FastAPI.`, ); } try { return JSON.parse(text) as T; } catch { throw new Error(`${prefix}: API returned invalid JSON.`); } } function readErrorDetail(text: string) { try { const body = JSON.parse(text) as ApiErrorBody; return stringifyErrorValue(body.detail ?? body.error ?? body.message); } catch { return ""; } } function looksLikeHtml(response: Response, text: string) { const contentType = response.headers.get("Content-Type") ?? ""; const trimmed = text.trim().toLowerCase(); return contentType.includes("text/html") || trimmed.startsWith("