Download frontend/src/lib/api.ts from Hattie/PharmAI: direct link, hf CLI and curl.
- Browser
- Download file 7 kB
-
https://huggingface.co/spaces/Hattie/PharmAI/resolve/main/frontend/src/lib/api.ts
- Command line
-
hf download hf://spaces/Hattie/PharmAI/frontend/src/lib/api.ts
-
curl -L -o api.ts https://huggingface.co/spaces/Hattie/PharmAI/resolve/main/frontend/src/lib/api.ts
7 kB
| 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<string, EndpointResult>; | |
| meta: { model_version: string }; | |
| } | |
| export interface PBPKSimulateResponse { | |
| status: string; | |
| message: string; | |
| drug_name: string; | |
| drug_data: Record<string, unknown>; | |
| 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<ApiHealthResponse | null> { | |
| 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<ApiHealthResponse | null> { | |
| 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<ApiPredictResponse> { | |
| 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<ADMETFeatures> { | |
| 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<ClearanceResult> { | |
| 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<string, number | null> | null; | |
| dose: number; | |
| dose_unit: string; | |
| body_weight?: number; | |
| }): Promise<PBPKSimulateResponse> { | |
| 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<Record<string, number>> { | |
| 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<string, number> = {}; | |
| 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" }; | |
| } |