import { PredictionResult, EndpointResult, SAMPLE_PREDICTIONS, } from "@/data/sampleData"; const API_BASE_URL = import.meta.env.VITE_API_BASE_URL || "http://localhost:8000"; export type ConnectionStatus = "connected" | "disconnected" | "checking"; export interface ApiHealthResponse { status: string; model_loaded?: boolean; model_version?: string | null; num_targets?: number; admet_loaded?: boolean; clearance_loaded?: boolean; } export interface ApiPredictResponse { smiles: string; predictions: Record; meta: { model_version: string }; } export interface PBPKSimulateResponse { status: string; message: string; drug_name: string; drug_data: Record; result_paths: { nca_csv: string; conc_csv: string; pbpk_plot: string; acat_plot: string; }; } export interface ADMETFeatures { molecular_weight: number; pKa: number; logP: number; solubility: number; permeability: number; fu_in_vitro: number; } export interface ClearanceResult { SMILES: string; Clint: number; } /** Check ADMET CYP backend health */ export async function checkAdmetHealth(): Promise { try { const res = await fetch(`${API_BASE_URL}/api/health`, { method: "GET", signal: AbortSignal.timeout(3000), }); if (!res.ok) return null; return await res.json(); } catch { return null; } } /** Check PBPK backend health (same server, different endpoint) */ export async function checkPbpkHealth(): Promise { try { const res = await fetch(`${API_BASE_URL}/api/status`, { method: "GET", signal: AbortSignal.timeout(3000), }); if (!res.ok) return null; return await res.json(); } catch { return null; } } /** Predict ADMET via API */ export async function predictAdmetFromApi( smiles: string, threshold = 0.5 ): Promise { const res = await fetch(`${API_BASE_URL}/api/predict`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ smiles, threshold }), }); if (!res.ok) { const err = await res.json().catch(() => ({ detail: "Unknown error" })); throw new Error(err.detail || `API error: ${res.status}`); } return await res.json(); } /** Predict ADMET properties only */ export async function predictAdmetProperties( smiles: string ): Promise { const res = await fetch(`${API_BASE_URL}/api/predict/admet`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ smiles }), }); if (!res.ok) { const err = await res.json().catch(() => ({ detail: "Unknown error" })); throw new Error(err.detail || `ADMET prediction error: ${res.status}`); } return await res.json(); } /** Predict Clearance */ export async function predictClearance( smiles: string ): Promise { const res = await fetch(`${API_BASE_URL}/api/predict/clearance`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ smiles }), }); if (!res.ok) { const err = await res.json().catch(() => ({ detail: "Unknown error" })); throw new Error(err.detail || `Clearance prediction error: ${res.status}`); } return await res.json(); } /** Run full PBPK simulation */ export async function runPbpkSimulation(params: { smiles: string; drug_name: string; use_admet: boolean; use_clearance: boolean; db_features: Record | null; dose: number; dose_unit: string; body_weight?: number; }): Promise { const res = await fetch(`${API_BASE_URL}/api/pbpk/simulate`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(params), }); if (!res.ok) { const err = await res.json().catch(() => ({ detail: "Unknown error" })); throw new Error(err.detail || `PBPK simulation error: ${res.status}`); } return await res.json(); } /** Load NCA CSV results */ export async function loadNCAResults( filePath: string ): Promise> { const res = await fetch(`${API_BASE_URL}/${filePath}`); if (!res.ok) throw new Error("Failed to load NCA results"); const csvText = await res.text(); const lines = csvText.split("\n"); const headers = lines[0].split(",").map((h) => h.replace(/"/g, "").trim()); if (lines.length < 2) return {}; const values = lines[1].split(",").map((v) => v.trim()); const result: Record = {}; headers.forEach((h, i) => { result[h] = parseFloat(values[i]) || 0; }); return result; } /** Load concentration CSV results */ export async function loadConcentrationResults(filePath: string): Promise<{ time: number[]; plasma_conc: number[]; liver_cell_conc: number[]; kidney_cell_conc: number[]; brain_cell_conc: number[]; muscle_cell_conc: number[]; fat_cell_conc: number[]; }> { const res = await fetch(`${API_BASE_URL}/${filePath}`); if (!res.ok) throw new Error("Failed to load concentration results"); const csvText = await res.text(); const lines = csvText.split("\n"); const headers = lines[0].split(",").map((h) => h.replace(/"/g, "").trim()); const data = { time: [] as number[], plasma_conc: [] as number[], liver_cell_conc: [] as number[], kidney_cell_conc: [] as number[], brain_cell_conc: [] as number[], muscle_cell_conc: [] as number[], fat_cell_conc: [] as number[], }; const getIdx = (name: string) => headers.indexOf(name); for (let i = 1; i < lines.length; i++) { const line = lines[i].trim(); if (!line) continue; const vals = line.split(",").map((v) => parseFloat(v.trim()) || 0); data.time.push(vals[0]); data.plasma_conc.push(vals[getIdx("plasma_conc")] || 0); data.liver_cell_conc.push(vals[getIdx("liver_cell_conc")] || 0); data.kidney_cell_conc.push(vals[getIdx("kidneys_cell_conc")] || 0); data.brain_cell_conc.push(vals[getIdx("brain_cell_conc")] || 0); data.muscle_cell_conc.push(vals[getIdx("muscle_cell_conc")] || 0); data.fat_cell_conc.push(vals[getIdx("fat_cell_conc")] || 0); } return data; } /** Try API first, fallback to sample data for ADMET */ export async function predictAdmet(smiles: string): Promise<{ result: PredictionResult | null; source: "api" | "sample" | "not_found"; error?: string; }> { try { const apiResult = await predictAdmetFromApi(smiles); return { result: { smiles: apiResult.smiles, name: apiResult.smiles, pred: apiResult.predictions, meta: apiResult.meta, }, source: "api", }; } catch { // fallback to sample } const trimmed = smiles.trim().toLowerCase(); const found = SAMPLE_PREDICTIONS.find( (p) => p.smiles.toLowerCase() === trimmed ); if (found) return { result: found, source: "sample" }; return { result: null, source: "not_found" }; }