Spaces:
Running on Zero
Running on Zero
File size: 9,000 Bytes
654bfe6 | 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 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 | import React, { useState } from 'react';
import { Code, Terminal, Play, CheckCircle, Copy, FileText, Server } from 'lucide-react';
export const ApiDocsPanel: React.FC = () => {
const [activeEndpoint, setActiveEndpoint] = useState<'/predict' | '/explain' | '/health' | '/organs'>('/predict');
const [testSmiles, setTestSmiles] = useState<string>('CC(=O)NC1=CC=C(O)C=C1');
const [useTissueConditioning, setUseTissueConditioning] = useState<boolean>(true);
const [apiResponse, setApiResponse] = useState<string | null>(null);
const [isLoading, setIsLoading] = useState<boolean>(false);
const [copied, setCopied] = useState<boolean>(false);
const handleRunRequest = async () => {
setIsLoading(true);
setApiResponse(null);
await new Promise((r) => setTimeout(r, 400));
if (activeEndpoint === '/predict') {
setApiResponse(JSON.stringify({
compound: "Acetaminophen",
smiles: testSmiles,
tissueConditioning: useTissueConditioning,
organScores: {
liver: { meanRisk: 0.92, uncertaintySigma: 0.03, riskLevel: "Severe" },
heart: { meanRisk: 0.12, uncertaintySigma: 0.02, riskLevel: "Low" },
kidney: { meanRisk: 0.54, uncertaintySigma: 0.04, riskLevel: "Moderate" },
brain: { meanRisk: 0.15, uncertaintySigma: 0.02, riskLevel: "Low" },
lung: { meanRisk: 0.10, uncertaintySigma: 0.02, riskLevel: "Low" }
},
tanimotoDomain: {
score: 0.88,
status: "High Confidence (In-Domain)"
}
}, null, 2));
} else if (activeEndpoint === '/explain') {
setApiResponse(JSON.stringify({
smiles: testSmiles,
atomicHotspots: [
{ atomIndex: 0, symbol: "C", attentionWeight: 0.15 },
{ atomIndex: 1, symbol: "O", attentionWeight: 0.72, toxicophore: "Reactive Carbonyl" },
{ atomIndex: 2, symbol: "N", attentionWeight: 0.81, toxicophore: "Amide Center" }
],
primaryToxicophores: ["Reactive Carbonyl Center", "Substituted Amine Core"]
}, null, 2));
} else if (activeEndpoint === '/organs') {
setApiResponse(JSON.stringify({
organs: [
{ id: "liver", markerGenes: ["CYP3A4", "ALB"], expressionDim: 128 },
{ id: "heart", markerGenes: ["MYH6", "TNNT2"], expressionDim: 128 },
{ id: "kidney", markerGenes: ["SLC22A2", "UMOD"], expressionDim: 128 }
]
}, null, 2));
} else {
setApiResponse(JSON.stringify({
status: "ok",
version: "2.1.0-EpiADR-Net",
model: "EpiADR-Net Tissue-Conditioned Graph Transformer",
dataset: "SIDER 4.1 & GTEx V8"
}, null, 2));
}
setIsLoading(false);
};
const pythonSnippet = `import requests
url = "http://localhost:8000/predict"
payload = {
"smiles": "${testSmiles}",
"use_tissue_conditioning": ${useTissueConditioning ? "True" : "False"},
"mc_dropout_passes": 30
}
response = requests.post(url, json=payload)
print(response.json())`;
const curlSnippet = `curl -X POST "http://localhost:8000/predict" \\
-H "Content-Type: application/json" \\
-d '{"smiles": "${testSmiles}", "use_tissue_conditioning": ${useTissueConditioning}}'`;
const copyToClipboard = (text: string) => {
navigator.clipboard.writeText(text);
setCopied(true);
setTimeout(() => setCopied(false), 2000);
};
return (
<div className="space-y-6">
{/* Header */}
<div className="bg-slate-900 border border-slate-800 rounded-2xl p-5 shadow-lg space-y-3">
<div className="flex items-center space-x-2 border-b border-slate-800 pb-3">
<Terminal className="w-5 h-5 text-indigo-400" />
<div>
<h2 className="text-base font-bold text-white">FastAPI REST Microservice & Swagger UI Simulator</h2>
<p className="text-xs text-slate-400">Enterprise REST Endpoints for High-Throughput Preclinical Screening</p>
</div>
</div>
{/* Endpoints Nav */}
<div className="flex space-x-2 overflow-x-auto no-scrollbar">
{(['/predict', '/explain', '/organs', '/health'] as const).map((ep) => (
<button
key={ep}
onClick={() => {
setActiveEndpoint(ep);
setApiResponse(null);
}}
className={`px-3.5 py-1.5 rounded-xl text-xs font-mono font-bold transition-all border ${
activeEndpoint === ep
? 'bg-indigo-600 text-white border-indigo-400 shadow-md'
: 'bg-slate-950 border-slate-800 text-slate-400 hover:bg-slate-800'
}`}
>
{ep}
</button>
))}
</div>
</div>
{/* Main Interactive Testing Grid */}
<div className="grid grid-cols-1 lg:grid-cols-12 gap-6">
{/* Left Column: Request Body & Test Form */}
<div className="lg:col-span-5 bg-slate-900 border border-slate-800 rounded-2xl p-5 shadow-lg space-y-4">
<h3 className="text-sm font-bold text-white flex items-center space-x-2">
<Server className="w-4 h-4 text-indigo-400" />
<span>Request Simulator ({activeEndpoint})</span>
</h3>
{(activeEndpoint === '/predict' || activeEndpoint === '/explain') && (
<div className="space-y-3 text-xs">
<div className="space-y-1">
<label className="text-slate-400 font-semibold block">Target SMILES String:</label>
<input
type="text"
value={testSmiles}
onChange={(e) => setTestSmiles(e.target.value)}
className="w-full bg-slate-950 border border-slate-800 rounded-xl px-3 py-2 text-indigo-300 font-mono"
/>
</div>
{activeEndpoint === '/predict' && (
<div className="flex items-center justify-between bg-slate-950 p-2.5 rounded-xl border border-slate-800">
<span className="text-slate-300 font-semibold">use_tissue_conditioning</span>
<input
type="checkbox"
checked={useTissueConditioning}
onChange={(e) => setUseTissueConditioning(e.target.checked)}
className="accent-indigo-500 w-4 h-4 cursor-pointer"
/>
</div>
)}
</div>
)}
<button
onClick={handleRunRequest}
disabled={isLoading}
className="w-full py-2.5 bg-indigo-600 hover:bg-indigo-500 text-white rounded-xl font-bold text-xs shadow-lg flex items-center justify-center space-x-2 transition-all active:scale-95"
>
<Play className="w-4 h-4 fill-white" />
<span>Execute API Request</span>
</button>
{/* Code Snippets */}
<div className="pt-2 border-t border-slate-800 space-y-2">
<div className="flex justify-between items-center text-xs">
<span className="font-bold text-slate-300">Python Integration Snippet</span>
<button
onClick={() => copyToClipboard(pythonSnippet)}
className="text-indigo-400 hover:text-indigo-300 flex items-center space-x-1"
>
<Copy className="w-3.5 h-3.5" />
<span>{copied ? 'Copied!' : 'Copy'}</span>
</button>
</div>
<pre className="bg-slate-950 p-3 rounded-xl border border-slate-800 text-[11px] font-mono text-emerald-300 overflow-x-auto leading-normal">
{pythonSnippet}
</pre>
</div>
</div>
{/* Right Column: JSON Response Output */}
<div className="lg:col-span-7 bg-slate-900 border border-slate-800 rounded-2xl p-5 shadow-lg space-y-3">
<div className="flex items-center justify-between border-b border-slate-800 pb-2">
<span className="text-xs font-bold text-slate-300 font-mono">Response Payload (200 OK)</span>
<span className="text-[11px] text-emerald-400 font-mono">application/json</span>
</div>
<div className="bg-slate-950 p-4 rounded-xl border border-slate-800/80 min-h-[280px]">
{isLoading ? (
<div className="flex items-center justify-center h-48 text-indigo-400 font-mono text-xs animate-pulse">
<span>Executing forward pass...</span>
</div>
) : apiResponse ? (
<pre className="text-xs font-mono text-indigo-200 overflow-x-auto leading-relaxed">
{apiResponse}
</pre>
) : (
<div className="flex flex-col items-center justify-center h-48 text-slate-500 text-xs">
<span>Click "Execute API Request" above to view response payload</span>
</div>
)}
</div>
</div>
</div>
</div>
);
};
|