Spaces:
Running
Running
Upload app.py with huggingface_hub
Browse files
app.py
CHANGED
|
@@ -13,8 +13,13 @@ KNOWHOW = ('SJSU CardioLab: '
|
|
| 13 |
'Equipment: Heska HT5 hematology analyzer time-resolved PIV Tygon tubing Arduino Uno.')
|
| 14 |
|
| 15 |
CSS = (
|
|
|
|
| 16 |
'body { background: #0a0f1e !important; }'
|
| 17 |
-
'.
|
|
|
|
|
|
|
|
|
|
|
|
|
| 18 |
)
|
| 19 |
|
| 20 |
def get_pubmed(query, n=5):
|
|
@@ -24,8 +29,7 @@ def get_pubmed(query, n=5):
|
|
| 24 |
'retmax':n,'retmode':'json','sort':'date'},timeout=10)
|
| 25 |
ids = r.json()['esearchresult']['idlist']
|
| 26 |
if not ids: return ''
|
| 27 |
-
|
| 28 |
-
return 'PubMed: ' + ' | '.join(links)
|
| 29 |
except: return ''
|
| 30 |
|
| 31 |
def get_scholar(query, n=5):
|
|
@@ -40,17 +44,17 @@ def get_scholar(query, n=5):
|
|
| 40 |
url = p.get('url','')
|
| 41 |
citations = str(p.get('citationCount',0))
|
| 42 |
if url:
|
| 43 |
-
out.append(title[:80]+' ('+year+') '+citations+' citations
|
| 44 |
-
return chr(10).join(out)
|
| 45 |
except: return ''
|
| 46 |
|
| 47 |
def quick_search(query):
|
| 48 |
if not query.strip(): return 'Please enter a research topic.'
|
| 49 |
pubmed = get_pubmed(query, n=8)
|
| 50 |
scholar = get_scholar(query, n=5)
|
| 51 |
-
result = '
|
| 52 |
-
result += 'PUBMED (
|
| 53 |
-
result += 'SEMANTIC SCHOLAR:'
|
| 54 |
return result
|
| 55 |
|
| 56 |
def research_chat(message, history):
|
|
@@ -61,8 +65,7 @@ def research_chat(message, history):
|
|
| 61 |
try:
|
| 62 |
client = Groq(api_key=GROQ_KEY)
|
| 63 |
pubmed = get_pubmed(message, n=3)
|
| 64 |
-
|
| 65 |
-
system_msg = 'You are CardioLab AI from SJSU Biomedical Engineering built on Biomni Stanford SNAP Lab. Expert in MHV MCL PIV TGT uPAD CKD FSI. Remember full conversation. Never invent URLs. ' + KNOWHOW
|
| 66 |
msgs = [{'role':'system','content':system_msg}]
|
| 67 |
for item in history:
|
| 68 |
if isinstance(item, dict):
|
|
@@ -70,8 +73,7 @@ def research_chat(message, history):
|
|
| 70 |
msgs.append({'role':'user','content':message})
|
| 71 |
resp = client.chat.completions.create(model='llama-3.3-70b-versatile',messages=msgs,max_tokens=700)
|
| 72 |
answer = resp.choices[0].message.content
|
| 73 |
-
if pubmed: answer += chr(10)+chr(10)+'
|
| 74 |
-
if scholar: answer += chr(10)+chr(10)+'SEMANTIC SCHOLAR: '+chr(10)+scholar
|
| 75 |
history.append({'role':'user','content':message})
|
| 76 |
history.append({'role':'assistant','content':answer})
|
| 77 |
return '', history
|
|
@@ -82,37 +84,30 @@ def research_chat(message, history):
|
|
| 82 |
|
| 83 |
def piv_tool(velocity, shear, hr):
|
| 84 |
v = 'HIGH - stenosis risk' if float(velocity)>2.0 else 'NORMAL'
|
| 85 |
-
s = 'HIGH - thrombosis risk' if float(shear)>10 else 'ELEVATED
|
| 86 |
hr_s = 'ABNORMAL' if float(hr)<60 or float(hr)>100 else 'NORMAL'
|
| 87 |
-
return '
|
| 88 |
|
| 89 |
def tgt_tool(tat,pf12,hemo,platelets,time):
|
| 90 |
risk=sum([float(tat)>15,float(pf12)>2.0,float(hemo)>50,float(platelets)<150])
|
| 91 |
r='HIGH THROMBOGENIC RISK' if risk>=3 else 'MODERATE RISK' if risk>=2 else 'LOW RISK'
|
| 92 |
-
|
| 93 |
-
p_s='HIGH' if float(pf12)>2.0 else 'NORMAL'
|
| 94 |
-
h_s='HIGH' if float(hemo)>50 else 'NORMAL'
|
| 95 |
-
pl_s='LOW' if float(platelets)<150 else 'NORMAL'
|
| 96 |
-
return 'TGT BLOOD ANALYSIS'+chr(10)+'Time: '+str(time)+' min'+chr(10)+'TAT: '+str(tat)+' - '+t_s+chr(10)+'PF1.2: '+str(pf12)+' - '+p_s+chr(10)+'Hemoglobin: '+str(hemo)+' - '+h_s+chr(10)+'Platelets: '+str(platelets)+' - '+pl_s+chr(10)+'Overall: '+r
|
| 97 |
|
| 98 |
def upad_tool(r,g,b):
|
| 99 |
c=max(0,round(0.02*(float(r)-float(b))-0.5,2))
|
| 100 |
-
if c<1.2: s='Normal'
|
| 101 |
-
elif c<1.5: s='Borderline'
|
| 102 |
elif c<3.0: s='Stage 2 CKD'
|
| 103 |
elif c<6.0: s='Stage 3-4 CKD'
|
| 104 |
-
else: s='Stage 5 CKD'
|
| 105 |
-
return '
|
| 106 |
|
| 107 |
-
with gr.Blocks(title='CardioLab AI
|
| 108 |
-
gr.Markdown('# CardioLab AI
|
| 109 |
-
gr.Markdown('### SJSU Biomedical Engineering | Biomni Stanford + Llama 70B + PubMed Live Search')
|
| 110 |
-
gr.Markdown('github.com/pranatechsol/Cardio-Lab-Ai | huggingface.co/Saicharan21')
|
| 111 |
|
| 112 |
with gr.Tabs():
|
| 113 |
-
with gr.Tab('
|
| 114 |
-
gr.
|
| 115 |
-
chatbot = gr.Chatbot(label='CardioLab AI', height=500)
|
| 116 |
with gr.Row():
|
| 117 |
msg_box = gr.Textbox(placeholder='Ask anything about CardioLab research...', label='', lines=2, scale=4)
|
| 118 |
with gr.Column(scale=1):
|
|
@@ -123,47 +118,35 @@ with gr.Blocks(title='CardioLab AI SJSU', css=CSS) as demo:
|
|
| 123 |
clear_btn.click(lambda: ([], ''), outputs=[chatbot, msg_box])
|
| 124 |
|
| 125 |
with gr.Tab('Paper Search'):
|
| 126 |
-
gr.
|
| 127 |
-
gr.Markdown('Searches PubMed + Semantic Scholar - only real verified URLs sorted by most recent')
|
| 128 |
-
search_input = gr.Textbox(placeholder='e.g. mechanical heart valve thrombogenicity 2024', label='Research topic')
|
| 129 |
search_btn = gr.Button('Search Papers', variant='primary')
|
| 130 |
-
search_output = gr.Textbox(label='
|
| 131 |
search_btn.click(quick_search, inputs=search_input, outputs=search_output)
|
| 132 |
search_input.submit(quick_search, inputs=search_input, outputs=search_output)
|
| 133 |
-
gr.Markdown('Suggested: mechanical heart valve PIV | creatinine uPAD microfluidic | bileaflet MHV thrombogenicity | CKD point-of-care')
|
| 134 |
|
| 135 |
with gr.Tab('PIV Analysis'):
|
| 136 |
-
gr.Markdown('### Analyze PIV flow data from Mock Circulatory Loop')
|
| 137 |
with gr.Row():
|
| 138 |
with gr.Column():
|
| 139 |
-
v=gr.Number(label='Max Velocity m/s', value=1.8
|
| 140 |
-
s=gr.Number(label='Wall Shear Stress Pa', value=6.5
|
| 141 |
-
h=gr.Number(label='Heart Rate bpm', value=72
|
| 142 |
-
|
|
|
|
| 143 |
|
| 144 |
with gr.Tab('TGT Results'):
|
| 145 |
-
gr.
|
| 146 |
-
|
| 147 |
-
|
| 148 |
-
|
| 149 |
-
t4=gr.Number(label='Platelet Count', value=140, info='Normal: >150')
|
| 150 |
t5=gr.Number(label='Time minutes', value=40)
|
| 151 |
-
out2=gr.Textbox(label='Result',lines=8)
|
| 152 |
gr.Button('Analyze TGT', variant='primary').click(tgt_tool,inputs=[t1,t2,t3,t4,t5],outputs=out2)
|
| 153 |
|
| 154 |
with gr.Tab('uPAD CKD'):
|
| 155 |
-
gr.
|
| 156 |
-
|
| 157 |
-
|
| 158 |
-
|
| 159 |
-
out3=gr.Textbox(label='CKD Result',lines=6)
|
| 160 |
gr.Button('Analyze uPAD', variant='primary').click(upad_tool,inputs=[r,g,b],outputs=out3)
|
| 161 |
|
| 162 |
-
with gr.Tab('About'):
|
| 163 |
-
gr.Markdown('## CardioLab AI Agent - SJSU Biomedical Engineering')
|
| 164 |
-
gr.Markdown('Built on Biomni Stanford SNAP Lab | Apache 2.0')
|
| 165 |
-
gr.Markdown('Live: https://huggingface.co/spaces/Saicharan21/CardioLab-AI')
|
| 166 |
-
gr.Markdown('Code: https://github.com/pranatechsol/Cardio-Lab-Ai')
|
| 167 |
-
gr.Markdown('Brain: Llama 3.3 70B via Groq free | Search: PubMed + Semantic Scholar free')
|
| 168 |
-
|
| 169 |
demo.launch()
|
|
|
|
| 13 |
'Equipment: Heska HT5 hematology analyzer time-resolved PIV Tygon tubing Arduino Uno.')
|
| 14 |
|
| 15 |
CSS = (
|
| 16 |
+
'.gradio-container { background: #0a0f1e !important; color: white !important; max-width: 100% !important; }'
|
| 17 |
'body { background: #0a0f1e !important; }'
|
| 18 |
+
'.gr-button-primary { background: linear-gradient(135deg, #e63946, #c1121f) !important; border: none !important; color: white !important; border-radius: 8px !important; }'
|
| 19 |
+
'.gr-button-secondary { background: #1d3461 !important; border: 1px solid #4361ee !important; color: white !important; border-radius: 8px !important; }'
|
| 20 |
+
'textarea, input { background: #1a2744 !important; color: white !important; border: 1px solid #2d4a8a !important; border-radius: 8px !important; }'
|
| 21 |
+
'.gr-box { background: #0d1b3e !important; border: 1px solid #2d4a8a !important; border-radius: 12px !important; }'
|
| 22 |
+
'h1 { color: #e63946 !important; font-size: 2.5em !important; font-weight: 900 !important; text-align: center !important; padding: 20px !important; }'
|
| 23 |
)
|
| 24 |
|
| 25 |
def get_pubmed(query, n=5):
|
|
|
|
| 29 |
'retmax':n,'retmode':'json','sort':'date'},timeout=10)
|
| 30 |
ids = r.json()['esearchresult']['idlist']
|
| 31 |
if not ids: return ''
|
| 32 |
+
return chr(10).join(['https://pubmed.ncbi.nlm.nih.gov/'+i for i in ids])
|
|
|
|
| 33 |
except: return ''
|
| 34 |
|
| 35 |
def get_scholar(query, n=5):
|
|
|
|
| 44 |
url = p.get('url','')
|
| 45 |
citations = str(p.get('citationCount',0))
|
| 46 |
if url:
|
| 47 |
+
out.append(title[:80]+' ('+year+') - '+citations+' citations'+chr(10)+' '+url)
|
| 48 |
+
return chr(10)+chr(10).join(out)
|
| 49 |
except: return ''
|
| 50 |
|
| 51 |
def quick_search(query):
|
| 52 |
if not query.strip(): return 'Please enter a research topic.'
|
| 53 |
pubmed = get_pubmed(query, n=8)
|
| 54 |
scholar = get_scholar(query, n=5)
|
| 55 |
+
result = 'PAPERS FOR: ' + query + chr(10)+chr(10)
|
| 56 |
+
result += 'PUBMED (click links to open):'+chr(10)+pubmed+chr(10)+chr(10)
|
| 57 |
+
result += 'SEMANTIC SCHOLAR:'+chr(10)+scholar
|
| 58 |
return result
|
| 59 |
|
| 60 |
def research_chat(message, history):
|
|
|
|
| 65 |
try:
|
| 66 |
client = Groq(api_key=GROQ_KEY)
|
| 67 |
pubmed = get_pubmed(message, n=3)
|
| 68 |
+
system_msg = 'You are CardioLab AI assistant. Expert in MHV MCL PIV TGT uPAD CKD FSI. Remember full conversation. Never invent URLs. ' + KNOWHOW
|
|
|
|
| 69 |
msgs = [{'role':'system','content':system_msg}]
|
| 70 |
for item in history:
|
| 71 |
if isinstance(item, dict):
|
|
|
|
| 73 |
msgs.append({'role':'user','content':message})
|
| 74 |
resp = client.chat.completions.create(model='llama-3.3-70b-versatile',messages=msgs,max_tokens=700)
|
| 75 |
answer = resp.choices[0].message.content
|
| 76 |
+
if pubmed: answer += chr(10)+chr(10)+'PUBMED LINKS:'+chr(10)+pubmed
|
|
|
|
| 77 |
history.append({'role':'user','content':message})
|
| 78 |
history.append({'role':'assistant','content':answer})
|
| 79 |
return '', history
|
|
|
|
| 84 |
|
| 85 |
def piv_tool(velocity, shear, hr):
|
| 86 |
v = 'HIGH - stenosis risk' if float(velocity)>2.0 else 'NORMAL'
|
| 87 |
+
s = 'HIGH - thrombosis risk' if float(shear)>10 else 'ELEVATED' if float(shear)>5 else 'NORMAL'
|
| 88 |
hr_s = 'ABNORMAL' if float(hr)<60 or float(hr)>100 else 'NORMAL'
|
| 89 |
+
return 'VELOCITY: '+str(velocity)+' m/s - '+v+chr(10)+'SHEAR: '+str(shear)+' Pa - '+s+chr(10)+'HEART RATE: '+str(hr)+' bpm - '+hr_s
|
| 90 |
|
| 91 |
def tgt_tool(tat,pf12,hemo,platelets,time):
|
| 92 |
risk=sum([float(tat)>15,float(pf12)>2.0,float(hemo)>50,float(platelets)<150])
|
| 93 |
r='HIGH THROMBOGENIC RISK' if risk>=3 else 'MODERATE RISK' if risk>=2 else 'LOW RISK'
|
| 94 |
+
return 'TIME: '+str(time)+' min'+chr(10)+'TAT: '+str(tat)+(' - HIGH' if float(tat)>15 else ' - NORMAL')+chr(10)+'PF1.2: '+str(pf12)+(' - HIGH' if float(pf12)>2.0 else ' - NORMAL')+chr(10)+'HEMOGLOBIN: '+str(hemo)+(' - HIGH' if float(hemo)>50 else ' - NORMAL')+chr(10)+'PLATELETS: '+str(platelets)+(' - LOW' if float(platelets)<150 else ' - NORMAL')+chr(10)+chr(10)+'OVERALL: '+r
|
|
|
|
|
|
|
|
|
|
|
|
|
| 95 |
|
| 96 |
def upad_tool(r,g,b):
|
| 97 |
c=max(0,round(0.02*(float(r)-float(b))-0.5,2))
|
| 98 |
+
if c<1.2: s='Normal - No CKD'
|
| 99 |
+
elif c<1.5: s='Borderline - Monitor'
|
| 100 |
elif c<3.0: s='Stage 2 CKD'
|
| 101 |
elif c<6.0: s='Stage 3-4 CKD'
|
| 102 |
+
else: s='Stage 5 CKD - Kidney Failure'
|
| 103 |
+
return 'RGB: R='+str(r)+' G='+str(g)+' B='+str(b)+chr(10)+'CREATININE: '+str(c)+' mg/dL'+chr(10)+'CKD STAGE: '+s+chr(10)+'Confirm with: Heska Element HT5'
|
| 104 |
|
| 105 |
+
with gr.Blocks(title='CardioLab AI', css=CSS) as demo:
|
| 106 |
+
gr.Markdown('# CardioLab AI')
|
|
|
|
|
|
|
| 107 |
|
| 108 |
with gr.Tabs():
|
| 109 |
+
with gr.Tab('Chat'):
|
| 110 |
+
chatbot = gr.Chatbot(label='', height=500)
|
|
|
|
| 111 |
with gr.Row():
|
| 112 |
msg_box = gr.Textbox(placeholder='Ask anything about CardioLab research...', label='', lines=2, scale=4)
|
| 113 |
with gr.Column(scale=1):
|
|
|
|
| 118 |
clear_btn.click(lambda: ([], ''), outputs=[chatbot, msg_box])
|
| 119 |
|
| 120 |
with gr.Tab('Paper Search'):
|
| 121 |
+
search_input = gr.Textbox(placeholder='e.g. mechanical heart valve thrombogenicity', label='Research topic')
|
|
|
|
|
|
|
| 122 |
search_btn = gr.Button('Search Papers', variant='primary')
|
| 123 |
+
search_output = gr.Textbox(label='Results', lines=20)
|
| 124 |
search_btn.click(quick_search, inputs=search_input, outputs=search_output)
|
| 125 |
search_input.submit(quick_search, inputs=search_input, outputs=search_output)
|
|
|
|
| 126 |
|
| 127 |
with gr.Tab('PIV Analysis'):
|
|
|
|
| 128 |
with gr.Row():
|
| 129 |
with gr.Column():
|
| 130 |
+
v=gr.Number(label='Max Velocity m/s', value=1.8)
|
| 131 |
+
s=gr.Number(label='Wall Shear Stress Pa', value=6.5)
|
| 132 |
+
h=gr.Number(label='Heart Rate bpm', value=72)
|
| 133 |
+
piv_out=gr.Textbox(label='Result', lines=5)
|
| 134 |
+
gr.Button('Analyze PIV', variant='primary').click(piv_tool,inputs=[v,s,h],outputs=piv_out)
|
| 135 |
|
| 136 |
with gr.Tab('TGT Results'):
|
| 137 |
+
t1=gr.Number(label='TAT ng/mL', value=18)
|
| 138 |
+
t2=gr.Number(label='PF1.2 nmol/L', value=2.5)
|
| 139 |
+
t3=gr.Number(label='Free Hemoglobin mg/L', value=60)
|
| 140 |
+
t4=gr.Number(label='Platelet Count', value=140)
|
|
|
|
| 141 |
t5=gr.Number(label='Time minutes', value=40)
|
| 142 |
+
out2=gr.Textbox(label='Result', lines=8)
|
| 143 |
gr.Button('Analyze TGT', variant='primary').click(tgt_tool,inputs=[t1,t2,t3,t4,t5],outputs=out2)
|
| 144 |
|
| 145 |
with gr.Tab('uPAD CKD'):
|
| 146 |
+
r=gr.Number(label='R value', value=210)
|
| 147 |
+
g=gr.Number(label='G value', value=140)
|
| 148 |
+
b=gr.Number(label='B value', value=80)
|
| 149 |
+
out3=gr.Textbox(label='Result', lines=6)
|
|
|
|
| 150 |
gr.Button('Analyze uPAD', variant='primary').click(upad_tool,inputs=[r,g,b],outputs=out3)
|
| 151 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 152 |
demo.launch()
|