CardioLab-AI / hf_app_v7.py
Saicharan21's picture
Upload hf_app_v7.py
ea36801 verified
import gradio as gr
import os, requests
from groq import Groq
GROQ_KEY = os.environ.get("GROQ_API_KEY","")
client = Groq(api_key=GROQ_KEY)
KNOWHOW = """
SJSU CardioLab Know-How:
MCL: Sylgard 184 PDMS 10:1 ratio 48hr cure, green laser PIV, 70bpm 5L/min flow
TGT: Arduino Uno + Stepper Motor, 150mL blood, sample 0/20/40/60min, TAT PF1.2 hemolysis platelets
uPAD: Jaffe reaction creatinine + picric acid = orange-red, normal 0.6-1.2 mg/dL, CKD above 1.5
FSI: COMSOL ALE mesh, blood 1060 kg/m3, 0.0035 Pa.s, St Jude geometry
MHV: 27mm SJM Regent, bileaflet trileaflet monoleaflet pediatric
CKD Stages: 1 below 1.5, 2 1.5-3.0, 3-4 3.0-6.0, 5 above 6.0 mg/dL
Equipment: Heska HT5, time-resolved PIV, Tygon tubing, Arduino
13 Projects: MCL/PIV, TGT, FSI simulation, uPAD CKD diagnostics
"""
def search_pubmed(query, n=3):
try:
r = requests.get("https://eutils.ncbi.nlm.nih.gov/entrez/eutils/esearch.fcgi",
params={"db":"pubmed","term":query,"retmax":n,"retmode":"json","sort":"date"}, timeout=10)
ids = r.json()["esearchresult"]["idlist"]
if not ids: return [], ""
r2 = requests.get("https://eutils.ncbi.nlm.nih.gov/entrez/eutils/efetch.fcgi",
params={"db":"pubmed","id":",".join(ids),"retmode":"xml","rettype":"abstract"}, timeout=10)
import xmltodict
data = xmltodict.parse(r2.content)
articles = data.get("PubmedArticleSet",{}).get("PubmedArticle",[])
if isinstance(articles, dict): articles = [articles]
real_links = []
context = ""
for a in articles[:n]:
try:
c = a["MedlineCitation"]
title = str(c["Article"]["ArticleTitle"])
abstract = c["Article"].get("Abstract",{}).get("AbstractText","")
if isinstance(abstract, list): abstract = " ".join([str(x) for x in abstract])
if isinstance(abstract, dict): abstract = str(abstract.get("#text",""))
pmid = str(c["PMID"]["#text"] if isinstance(c["PMID"],dict) else c["PMID"])
real_url = "https://pubmed.ncbi.nlm.nih.gov/" + pmid
real_links.append("- " + title[:100] + "\n " + real_url)
context += "[PubMed:" + pmid + "] " + title + ". " + str(abstract)[:300] + "\n"
except: continue
return real_links, context
except: return [], ""
def search_scholar(query, n=3):
try:
r = requests.get("https://api.semanticscholar.org/graph/v1/paper/search",
params={"query":query,"limit":n,"fields":"title,abstract,year,url"}, timeout=10)
papers = r.json().get("data",[])
real_links = []
context = ""
for p in papers:
title = p.get("title","")
abstract = (p.get("abstract") or "")[:300]
year = str(p.get("year",""))
url = p.get("url","")
if url:
real_links.append("- " + title[:100] + " (" + year + ")\n " + url)
context += "[Scholar " + year + "] " + title + ". " + abstract + "\n"
return real_links, context
except: return [], ""
def ask_with_memory(message, history):
if not GROQ_KEY:
return "Error: GROQ_API_KEY not set in Space secrets."
# Build messages with full history for memory
messages = [
{
"role": "system",
"content": """You are CardioLab AI built on Biomni from Stanford SNAP Lab.
Expert in SJSU Biomedical Engineering research.
You remember everything said in this conversation.
NEVER invent paper titles or URLs.
ONLY cite papers from the search results provided.
CARDIOLAB KNOW-HOW:
""" + KNOWHOW
}
]
# Add chat history — new Gradio format uses dicts
for msg in history:
if isinstance(msg, dict):
messages.append({"role": msg["role"], "content": msg["content"]})
else:
# fallback for tuple format
messages.append({"role": "user", "content": str(msg[0])})
messages.append({"role": "assistant", "content": str(msg[1])})
# Search papers
cardio_query = message + " mechanical heart valve OR microfluidic OR CKD creatinine OR PIV OR thrombogenicity"
pubmed_links, pubmed_context = search_pubmed(cardio_query, n=3)
scholar_links, scholar_context = search_scholar(message + " biomedical", n=3)
sources = pubmed_context + scholar_context
messages.append({
"role": "user",
"content": message + "\n\nReal papers (ONLY use these):\n" + sources[:3000]
})
response = client.chat.completions.create(
model="llama-3.3-70b-versatile",
messages=messages,
max_tokens=800
)
answer = response.choices[0].message.content
links = ""
if pubmed_links:
links += "\n\n📚 VERIFIED PUBMED LINKS:\n" + "\n".join(pubmed_links[:3])
if scholar_links:
links += "\n\n🎓 VERIFIED SCHOLAR LINKS:\n" + "\n".join(scholar_links[:3])
return answer + links
def piv_tool(velocity, shear, hr):
v = "HIGH - stenosis risk" if float(velocity)>2.0 else "NORMAL"
s = "HIGH - thrombosis risk" if float(shear)>10 else "ELEVATED - monitor" if float(shear)>5 else "NORMAL"
return "Velocity: "+str(velocity)+" m/s - "+v+"\nShear: "+str(shear)+" Pa - "+s+"\nHeart Rate: "+str(hr)+" bpm"
def tgt_tool(tat, pf12, hemo, platelets, time):
risk = sum([float(tat)>15, float(pf12)>2.0, float(hemo)>50, float(platelets)<150])
overall = "HIGH THROMBOGENIC RISK" if risk>=3 else "MODERATE RISK" if risk>=2 else "LOW RISK"
return "TAT:"+str(tat)+" PF1.2:"+str(pf12)+" Hemo:"+str(hemo)+" Platelets:"+str(platelets)+"\nTime:"+str(time)+"min\nResult: "+overall
def upad_tool(r, g, b):
creatinine = max(0, round(0.02*(float(r)-float(b))-0.5, 2))
stage = "Normal" if creatinine<1.2 else "Borderline" if creatinine<1.5 else "Stage 2 CKD" if creatinine<3.0 else "Stage 3-4 CKD" if creatinine<6.0 else "Stage 5 CKD"
return "Creatinine: "+str(creatinine)+" mg/dL\nStage: "+stage+"\nConfirm with: Heska Element HT5"
with gr.Blocks(title="CardioLab AI - SJSU") as demo:
gr.Markdown("# CardioLab AI Agent")
gr.Markdown("### SJSU Biomedical Engineering | Biomni + Llama 70B + Chat Memory + PubMed")
gr.Markdown("GitHub: github.com/pranatechsol/Cardio-Lab-Ai")
with gr.Tab("Research Chat"):
gr.Markdown("### Chat with memory — remembers your full conversation like ChatGPT")
chatbot = gr.Chatbot(
label="CardioLab AI",
height=500,
type="messages"
)
msg = gr.Textbox(
label="Your message",
placeholder="Ask anything about CardioLab research...",
lines=2
)
with gr.Row():
send = gr.Button("Send", variant="primary")
clear = gr.Button("Clear Chat")
def respond(message, history):
bot_message = ask_with_memory(message, history)
history.append({"role": "user", "content": message})
history.append({"role": "assistant", "content": bot_message})
return "", history
send.click(respond, inputs=[msg, chatbot], outputs=[msg, chatbot])
msg.submit(respond, inputs=[msg, chatbot], outputs=[msg, chatbot])
clear.click(lambda: [], None, chatbot)
with gr.Tab("PIV Analysis"):
gr.Markdown("### Analyze PIV flow data from Mock Circulatory Loop")
v = gr.Number(label="Max Velocity m/s", value=1.8)
s = gr.Number(label="Shear Stress Pa", value=6.5)
h = gr.Number(label="Heart Rate bpm", value=72)
out = gr.Textbox(label="Result", lines=4)
gr.Button("Analyze PIV").click(piv_tool, inputs=[v,s,h], outputs=out)
with gr.Tab("TGT Results"):
gr.Markdown("### Interpret Thrombogenicity Tester blood results")
t1 = gr.Number(label="TAT", value=18)
t2 = gr.Number(label="PF1.2", value=2.5)
t3 = gr.Number(label="Free Hemoglobin mg/L", value=60)
t4 = gr.Number(label="Platelet Count", value=140)
t5 = gr.Number(label="Time minutes", value=40)
out2 = gr.Textbox(label="Result", lines=5)
gr.Button("Analyze TGT").click(tgt_tool, inputs=[t1,t2,t3,t4,t5], outputs=out2)
with gr.Tab("uPAD CKD"):
gr.Markdown("### Analyze uPAD colorimetric result - Jaffe Reaction")
r = gr.Number(label="R value", value=210)
g = gr.Number(label="G value", value=140)
b = gr.Number(label="B value", value=80)
out3 = gr.Textbox(label="Result", lines=4)
gr.Button("Analyze uPAD").click(upad_tool, inputs=[r,g,b], outputs=out3)
demo.launch()