Spaces:
Running
Running
Initial deployment: ClinicalMatch AI v2.0 β FHIR R4 Β· MCP (9 tools) Β· A2A workflow Β· SHARP compliance Β· 100k synthetic patients Β· Neo4j graph Β· GraphRAG chatbot
59abb4f | // Empty string = relative URLs (Docker/HF Spaces: Nginx routes /api/* to FastAPI) | |
| // "http://localhost:8000" = direct for local dev without Docker | |
| const BASE = process.env.NEXT_PUBLIC_API_URL ?? "http://localhost:8000"; | |
| async function req<T>(path: string, options?: RequestInit): Promise<T> { | |
| const res = await fetch(`${BASE}${path}`, { | |
| headers: { "Content-Type": "application/json" }, | |
| ...options, | |
| }); | |
| if (!res.ok) { | |
| const err = await res.text(); | |
| throw new Error(`API ${res.status}: ${err}`); | |
| } | |
| return res.json(); | |
| } | |
| // ββ Patients ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| export const getPatients = () => req<{ patients: any[]; total: number }>("/api/v1/patients"); | |
| export const getPatient = (id: string) => req<any>(`/api/v1/patients/${id}`); | |
| export const getPatientFhir = (id: string) => req<any>(`/api/v1/patients/${id}/fhir`); | |
| // ββ Trials ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| export const searchTrials = (condition: string, phase?: string, pageSize = 20) => { | |
| const params = new URLSearchParams({ condition, page_size: String(pageSize) }); | |
| if (phase) params.set("phase", phase); | |
| return req<{ trials: any[]; total: number; condition: string }>(`/api/v1/trials/search?${params}`); | |
| }; | |
| export const getTrial = (nctId: string) => req<any>(`/api/v1/trials/${nctId}`); | |
| export const getTrialEligiblePatients = (nctId: string) => req<any>(`/api/v1/trials/${nctId}/eligible-patients`); | |
| export const getTrialIntelligence = (nctId: string) => req<any>(`/api/v1/trials/${nctId}/intelligence`); | |
| // ββ Matching ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| export const matchPatientToTrials = (patientId: string, condition?: string, topN = 5) => { | |
| const params = new URLSearchParams({ top_n: String(topN) }); | |
| if (condition) params.set("condition", condition); | |
| return req<{ patient_id: string; matches: any[]; total: number }>(`/api/v1/patients/${patientId}/match-trials?${params}`); | |
| }; | |
| export const screenPatient = (patientId: string, nctId: string) => | |
| req<any>(`/api/v1/patients/${patientId}/screen/${nctId}`, { method: "POST" }); | |
| // ββ A2A Workflow ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| export const runWorkflow = (patientId: string, nctId?: string, condition?: string) => | |
| req<any>("/api/v1/workflow/run", { | |
| method: "POST", | |
| body: JSON.stringify({ patient_id: patientId, nct_id: nctId, condition }), | |
| }); | |
| export const getWorkflowStatus = (workflowId: string) => req<any>(`/api/v1/workflow/${workflowId}/status`); | |
| export const listWorkflows = () => req<{ workflows: any[] }>("/api/v1/workflows"); | |
| // ββ Recruitment βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| export const getKanbanBoard = () => req<Record<string, any[]>>("/api/v1/recruitment/board"); | |
| export const getRecruitmentRecords = () => req<{ records: any[] }>("/api/v1/recruitment/records"); | |
| export const createRecruitmentRecord = (data: { patient_id: string; nct_id: string; trial_title: string; match_score: number }) => | |
| req<any>("/api/v1/recruitment/records", { method: "POST", body: JSON.stringify(data) }); | |
| export const updateRecordStatus = (recordId: string, status: string) => | |
| req<any>(`/api/v1/recruitment/records/${recordId}/status`, { method: "PATCH", body: JSON.stringify({ status }) }); | |
| export const generateOutreach = (data: { patient_id: string; nct_id: string; trial_title: string; channel: string }) => | |
| req<any>("/api/v1/recruitment/outreach", { method: "POST", body: JSON.stringify(data) }); | |
| // ββ Analytics βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| export const getKPIs = () => req<any>("/api/v1/analytics/kpi"); | |
| export const getEnrollmentFunnel = (trialId?: string) => { | |
| const params = trialId ? `?trial_id=${trialId}` : ""; | |
| return req<{ funnel: any[] }>(`/api/v1/analytics/funnel${params}`); | |
| }; | |
| export const getSitePerformance = () => req<{ sites: any[] }>("/api/v1/analytics/sites"); | |
| export const getDemographics = (trialId?: string) => { | |
| const params = trialId ? `?trial_id=${trialId}` : ""; | |
| return req<any>(`/api/v1/analytics/demographics${params}`); | |
| }; | |
| export const getTimeline = (days = 30) => req<{ timeline: any[] }>(`/api/v1/analytics/timeline?days=${days}`); | |
| export const getMapData = () => req<{ sites: any[]; patient_clusters: any[] }>("/api/v1/map/data"); | |
| export const getGraphStats = () => req<any>("/api/v1/graph/stats"); | |
| export const getGraphPatients = (condition?: string, limit = 200) => { | |
| const params = new URLSearchParams({ limit: String(limit) }); | |
| if (condition) params.set("condition", condition); | |
| return req<{ patients: any[]; total: number }>(`/api/v1/graph/patients?${params}`); | |
| }; | |
| // ββ Clinical Intake βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| export interface IntakeLabs { | |
| hemoglobin?: number; // g/dL | |
| wbc?: number; // Γ10βΉ/L | |
| anc?: number; // Γ10βΉ/L | |
| platelets?: number; // Γ10βΉ/L | |
| creatinine?: number; // ΞΌmol/L | |
| egfr?: number; // mL/min/1.73mΒ² | |
| bilirubin?: number; // ΞΌmol/L | |
| alt?: number; // U/L | |
| ast?: number; // U/L | |
| albumin?: number; // g/dL | |
| } | |
| export interface IntakePayload { | |
| condition: string; | |
| age?: number; | |
| sex?: string; | |
| ecog?: number; | |
| stage?: string; | |
| biomarkers?: string[]; | |
| labs?: IntakeLabs; | |
| prior_chemo?: boolean; | |
| prior_radiation?: boolean; | |
| prior_surgery?: boolean; | |
| medications?: string[]; | |
| save_to_graph?: boolean; | |
| } | |
| export const submitIntake = (data: IntakePayload) => | |
| req<{ condition: string; matches: any[]; total: number; patient_id?: string }>( | |
| "/api/v1/intake/match", { method: "POST", body: JSON.stringify(data) } | |
| ); | |
| export const getBiomarkerRegistry = () => | |
| req<{ biomarkers: { id: string; label: string }[] }>("/api/v1/intake/biomarkers"); | |
| // ββ Graph RAG βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| export const graphQuery = (question: string) => | |
| req<{ response: string }>("/api/v1/graph/query", { method: "POST", body: JSON.stringify({ question }) }); | |
| // ββ Streaming A2A Workflow ββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| export const startWorkflow = (patientId: string, nctId?: string, condition?: string) => | |
| req<{ workflow_id: string; status: string; stream_url: string }>("/api/v1/workflow/start", { | |
| method: "POST", | |
| body: JSON.stringify({ patient_id: patientId, nct_id: nctId, condition }), | |
| }); | |
| export const streamWorkflow = (workflowId: string, onEvent: (evt: any) => void, onDone: () => void) => { | |
| const url = `${BASE}/api/v1/workflow/${workflowId}/stream`; | |
| const es = new EventSource(url); | |
| es.onmessage = (e) => { | |
| if (e.data === "[DONE]") { es.close(); onDone(); return; } | |
| try { onEvent(JSON.parse(e.data)); } catch {} | |
| }; | |
| es.onerror = () => { es.close(); onDone(); }; | |
| return () => es.close(); | |
| }; | |
| // ββ Consent & Scheduling βββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| export const getConsents = (patientId?: string) => { | |
| const params = patientId ? `?patient_id=${patientId}` : ""; | |
| return req<{ consents: any[] }>(`/api/v1/consent${params}`); | |
| }; | |
| export const getConsentStats = () => req<any>("/api/v1/consent/stats"); | |
| export const updateConsentStatus = (consentId: string, status: string, notes?: string) => | |
| req<any>(`/api/v1/consent/${consentId}/status`, { | |
| method: "PATCH", | |
| body: JSON.stringify({ status, notes }), | |
| }); | |
| export const getAppointments = (patientId?: string) => { | |
| const params = patientId ? `?patient_id=${patientId}` : ""; | |
| return req<{ appointments: any[] }>(`/api/v1/appointments${params}`); | |
| }; | |
| export const confirmAppointment = (apptId: string) => | |
| req<any>(`/api/v1/appointments/${apptId}/confirm`, { method: "PATCH" }); | |
| // ββ Health ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| export const getHealth = () => req<any>("/health"); | |