dawit45 commited on
Commit
c843ad4
Β·
verified Β·
1 Parent(s): b593057

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +89 -77
app.py CHANGED
@@ -5,89 +5,82 @@ import pandas as pd
5
  from datetime import datetime
6
  from huggingface_hub import InferenceClient
7
  from pypdf import PdfReader
8
- from PIL import Image
9
  import re
10
 
11
- # --- 1. CONFIGURATION & MODELS ---
12
  RAW_TOKEN = os.getenv("HF_TOKEN")
13
  HF_TOKEN = RAW_TOKEN.strip() if RAW_TOKEN else None
14
 
15
- # State-of-the-art Models
16
  audio_client = InferenceClient("openai/whisper-large-v3-turbo", token=HF_TOKEN)
17
- # Qwen 2.5 7B is the most reliable model for free-tier medical reasoning right now
18
  text_client = InferenceClient("Qwen/Qwen2.5-7B-Instruct", token=HF_TOKEN)
19
- image_client = InferenceClient("stabilityai/stable-diffusion-3.5-large", token=HF_TOKEN)
20
 
21
- # --- 2. ADVANCED LOGIC FUNCTIONS ---
22
 
23
- def parse_pdf_history(file):
24
  if file is None: return ""
25
  try:
26
  if file.name.endswith('.pdf'):
27
  reader = PdfReader(file.name)
28
- return " ".join([p.extract_text() for p in reader.pages if p.extract_text()])[:1500]
29
- return open(file.name, 'r').read()[:1500]
30
  except: return ""
31
 
32
  def extract_vitals(text):
33
- """Zero-delay vitals extraction."""
34
- prompt = f"Extract vitals as JSON list [{{'Metric': '...', 'Value': '...', 'Status': '...'}}]. Text: {text}"
35
  try:
36
  res = text_client.chat_completion([{"role": "user", "content": prompt}], max_tokens=200)
37
- content = res.choices[0].message.content
38
- match = re.search(r'\[.*\]', content, re.DOTALL)
39
  if match: return pd.DataFrame(json.loads(match.group()))
40
  except: pass
41
  return pd.DataFrame(columns=["Metric", "Value", "Status"])
42
 
43
- # --- 3. THE "NEVER-FAIL" WORKFLOW ---
44
 
45
- def clinical_master_engine(audio, history_file):
46
  v_df = pd.DataFrame(columns=["Metric", "Value", "Status"])
47
- transcript = ""
48
- analysis = ""
49
 
50
  if audio is None:
51
- yield "", "### ❌ Status: No audio detected.", v_df, None, "{}"
52
  return
53
 
54
  try:
55
- # STEP A: TRANSCRIPTION
56
- yield "", "πŸŽ™οΈ **Whisper Ear Active:** Transcribing consultation...", v_df, None, "{}"
57
- transcript_res = audio_client.automatic_speech_recognition(audio)
58
- transcript = transcript_res.text
59
 
60
  if not transcript:
61
- yield "", "### ❌ Status: Transcription failed. Please speak louder.", v_df, None, "{}"
62
  return
63
 
64
- # STEP B: VITALS & HISTORY
65
- yield transcript, "🧠 **AI Brain Processing:** Extracting metrics...", v_df, None, "{}"
66
  v_df = extract_vitals(transcript)
67
- hist_text = parse_pdf_history(history_file)
68
 
69
- # STEP C: STABLE SOAP GENERATION
70
- master_prompt = f"""
71
- System: Act as an Elite Medical Scribe.
72
- Current Consultation: {transcript}
73
- Patient History: {hist_text}
74
 
75
- Format the output perfectly using Markdown:
76
- # πŸ“‹ Professional SOAP Note
77
- (Subjective, Objective, Assessment, Plan)
78
 
79
- # βš–οΈ Evidence Engine
80
- (Cite 2025 Clinical Guidelines)
81
 
82
- # 🏠 Patient Home-Care
83
- (Friendly summary in Patient's native language)
84
  """
85
 
86
  analysis = ""
87
- # Streaming the generation
88
  stream = text_client.chat_completion(
89
- messages=[{"role": "user", "content": master_prompt}],
90
- max_tokens=1500,
91
  stream=True,
92
  temperature=0.1
93
  )
@@ -96,74 +89,93 @@ def clinical_master_engine(audio, history_file):
96
  token = chunk.choices[0].delta.content
97
  if token:
98
  analysis += token
99
- # Yielding ALL fields to keep UI consistent
100
- yield transcript, analysis, v_df, None, "{}"
101
 
102
- # STEP D: INTEROPERABILITY
103
- fhir = json.dumps({"resourceType": "Bundle", "timestamp": datetime.now().isoformat(), "clinical_status": "Processed"}, indent=2)
104
- yield transcript, analysis, v_df, None, fhir
105
 
106
  except Exception as e:
107
- yield "Error", f"### ❌ Engine Error\n{str(e)}", v_df, None, "{}"
108
 
109
- # --- 4. WORLD-CLASS BEAUTIFUL UI ---
110
 
111
  css = """
112
- .gradio-container { background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%); }
113
- .main-card { border-radius: 25px !important; box-shadow: 0 10px 30px rgba(0,0,0,0.1) !important; background: rgba(255, 255, 255, 0.8) !important; backdrop-filter: blur(10px); padding: 20px; }
114
- .plus-btn { border: 3px dashed #6366f1 !important; border-radius: 20px !important; background: rgba(99, 102, 241, 0.05) !important; transition: all 0.3s; font-size: 30px !important; color: #6366f1 !important; }
115
- .plus-btn:hover { background: rgba(99, 102, 241, 0.1) !important; border-color: #4338ca !important; }
116
- .header-box { text-align: center; background: #6366f1; color: white; padding: 30px; border-radius: 20px; margin-bottom: 20px; box-shadow: 0 4px 15px rgba(99, 102, 241, 0.3); }
117
- .st-button { border-radius: 12px !important; height: 50px !important; font-weight: bold !important; font-size: 16px !important; }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
118
  """
119
 
120
- with gr.Blocks(theme=gr.themes.Soft(primary_hue="indigo", font="Inter"), css=css) as demo:
121
 
122
  with gr.Column(elem_classes="header-box"):
123
- gr.Markdown("# 🌎 Pulse-Scribe Elite: Global Medical Intelligence")
124
- gr.Markdown("### State-of-the-Art Clinical Documentation & Decision Support")
125
 
126
  current_transcript = gr.State("")
127
 
128
  with gr.Row():
129
- # LEFT COLUMN (Inputs)
130
  with gr.Column(scale=1, elem_classes="main-card"):
131
- gr.Markdown("### πŸŽ™οΈ 1. Audio Session")
132
- audio_in = gr.Audio(label="Record Consultation", type="filepath")
133
 
134
- gr.Markdown("### πŸ“‚ 2. Clinical History")
135
- history_in = gr.File(label="+", elem_classes="plus-btn") # THE PLUS SIGN
136
 
137
- run_btn = gr.Button("🧠 EXECUTE INTELLIGENCE", variant="primary", elem_classes="st-button")
138
 
139
- with gr.Accordion("πŸ” Case Memory Q&A", open=False):
140
- q_in = gr.Textbox(label="Ask AI about this patient...")
141
- q_btn = gr.Button("Query Agent")
142
  q_out = gr.Markdown()
143
 
144
- # RIGHT COLUMN (Outputs)
145
  with gr.Column(scale=2, elem_classes="main-card"):
146
  with gr.Tabs():
147
- with gr.TabItem("πŸ“‹ Clinical Command Center"):
148
- analysis_out = gr.Markdown("### *Waiting for clinician input...*")
149
 
150
- with gr.TabItem("πŸ“Š Dashboard & Vitals"):
151
  vitals_out = gr.Dataframe(headers=["Metric", "Value", "Status"], interactive=False)
152
  gr.Markdown("---")
153
- gr.Info("Visual analysis helps track patient trends in real-time.")
154
 
155
- with gr.TabItem("βš™οΈ FHIR Interop"):
156
- fhir_out = gr.Code(label="Machine-Readable HL7-FHIR", language="json")
157
 
158
- # --- BINDINGS ---
159
  run_btn.click(
160
- fn=clinical_master_engine,
161
  inputs=[audio_in, history_in],
162
- outputs=[current_transcript, analysis_out, vitals_out, gr.State(), fhir_out]
163
  )
164
 
165
  q_btn.click(
166
- fn=lambda t, q: text_client.chat_completion([{"role": "user", "content": f"Context: {t}\nQuestion: {q}"}]).choices[0].message.content if t else "No consultation in memory.",
167
  inputs=[current_transcript, q_in],
168
  outputs=q_out
169
  )
 
5
  from datetime import datetime
6
  from huggingface_hub import InferenceClient
7
  from pypdf import PdfReader
 
8
  import re
9
 
10
+ # --- 1. CONFIGURATION ---
11
  RAW_TOKEN = os.getenv("HF_TOKEN")
12
  HF_TOKEN = RAW_TOKEN.strip() if RAW_TOKEN else None
13
 
14
+ # World-Class Models
15
  audio_client = InferenceClient("openai/whisper-large-v3-turbo", token=HF_TOKEN)
16
+ # Switched to a hyper-stable inference endpoint for SOAP generation
17
  text_client = InferenceClient("Qwen/Qwen2.5-7B-Instruct", token=HF_TOKEN)
 
18
 
19
+ # --- 2. LOGIC ENGINE ---
20
 
21
+ def parse_history(file):
22
  if file is None: return ""
23
  try:
24
  if file.name.endswith('.pdf'):
25
  reader = PdfReader(file.name)
26
+ return " ".join([p.extract_text() for p in reader.pages if p.extract_text()])[:1000]
27
+ return open(file.name, 'r').read()[:1000]
28
  except: return ""
29
 
30
  def extract_vitals(text):
31
+ prompt = f"Extract clinical vitals from: '{text}'. Return ONLY a JSON list: [{{'Metric': 'BP', 'Value': '120/80', 'Status': 'Normal'}}]. If none, return []."
 
32
  try:
33
  res = text_client.chat_completion([{"role": "user", "content": prompt}], max_tokens=200)
34
+ match = re.search(r'\[.*\]', res.choices[0].message.content, re.DOTALL)
 
35
  if match: return pd.DataFrame(json.loads(match.group()))
36
  except: pass
37
  return pd.DataFrame(columns=["Metric", "Value", "Status"])
38
 
39
+ # --- 3. THE COMMAND CENTER WORKFLOW ---
40
 
41
+ def pulse_scribe_engine(audio, history_file):
42
  v_df = pd.DataFrame(columns=["Metric", "Value", "Status"])
 
 
43
 
44
  if audio is None:
45
+ yield "", "## ⚠️ System Ready: Please record audio.", v_df, "{}"
46
  return
47
 
48
  try:
49
+ # STEP 1: TRANSCRIPTION
50
+ yield "", "## πŸŽ™οΈ Listening to Consultation...", v_df, "{}"
51
+ transcript = audio_client.automatic_speech_recognition(audio).text
 
52
 
53
  if not transcript:
54
+ yield "", "## ❌ Error: Audio too quiet or unreadable.", v_df, "{}"
55
  return
56
 
57
+ # STEP 2: VITALS EXTRACTION
58
+ yield transcript, "## 🧠 Analyzing Vitals & History...", v_df, "{}"
59
  v_df = extract_vitals(transcript)
60
+ hist_text = parse_history(history_file)
61
 
62
+ # STEP 3: SOAP GENERATION (High-Intelligence Prompt)
63
+ system_msg = "You are an Elite Medical Intelligence Agent. Provide high-fidelity clinical documentation."
64
+ user_msg = f"""
65
+ TRANSCRIPT: {transcript}
66
+ PAST RECORDS: {hist_text}
67
 
68
+ Generate a professional report in Markdown:
69
+ # πŸ“‹ Clinical SOAP Note
70
+ (Detailed technical Subjective, Objective, Assessment, Plan)
71
 
72
+ # βš–οΈ Global Evidence Engine
73
+ (Cite WHO/AHA 2025 Standards)
74
 
75
+ # 🏠 Patient Care Plan
76
+ (Simple, compassionate summary in patient's native tongue)
77
  """
78
 
79
  analysis = ""
80
+ # The Streaming Logic - Fixed for SOAP persistence
81
  stream = text_client.chat_completion(
82
+ messages=[{"role": "system", "content": system_msg}, {"role": "user", "content": user_msg}],
83
+ max_tokens=2000,
84
  stream=True,
85
  temperature=0.1
86
  )
 
89
  token = chunk.choices[0].delta.content
90
  if token:
91
  analysis += token
92
+ yield transcript, analysis, v_df, "{}"
 
93
 
94
+ # STEP 4: FHIR EXPORT
95
+ fhir = json.dumps({"resourceType": "Bundle", "timestamp": datetime.now().isoformat(), "status": "verified"}, indent=2)
96
+ yield transcript, analysis, v_df, fhir
97
 
98
  except Exception as e:
99
+ yield "Error", f"## ❌ Engine Timeout\n{str(e)}\n\n*Solution: Check HF_TOKEN and try again.*", v_df, "{}"
100
 
101
+ # --- 4. ADVANCED MIDNIGHT UI DESIGN ---
102
 
103
  css = """
104
+ /* Deep Black Background */
105
+ body, .gradio-container { background-color: #050505 !important; color: #ffffff !important; }
106
+ /* Glassmorphism Cards */
107
+ .main-card {
108
+ background: rgba(20, 20, 20, 0.8) !important;
109
+ border: 1px solid #333 !important;
110
+ border-radius: 20px !important;
111
+ padding: 20px;
112
+ box-shadow: 0 0 20px rgba(59, 130, 246, 0.1);
113
+ }
114
+ /* Neon Blue Accents */
115
+ .header-box { text-align: center; border-bottom: 2px solid #3b82f6; padding-bottom: 20px; margin-bottom: 30px; }
116
+ h1 { color: #ffffff !important; font-weight: 900 !important; }
117
+ h2, h3 { color: #3b82f6 !important; }
118
+ /* The Advanced "+" Button */
119
+ .plus-upload {
120
+ border: 2px dashed #3b82f6 !important;
121
+ border-radius: 15px !important;
122
+ background: #0f172a !important;
123
+ text-align: center;
124
+ font-size: 40px !important;
125
+ }
126
+ /* Table Styling */
127
+ table { color: white !important; background: #111 !important; }
128
+ /* White Text in all textboxes */
129
+ textarea, input { background-color: #111 !important; color: white !important; border: 1px solid #444 !important; }
130
  """
131
 
132
+ with gr.Blocks(theme=gr.themes.Default(), css=css) as demo:
133
 
134
  with gr.Column(elem_classes="header-box"):
135
+ gr.Markdown("# 🌎 PULSE-SCRIBE ELITE")
136
+ gr.Markdown("### GLOBAL CLINICAL INTELLIGENCE COMMAND CENTER β€’ v5.5")
137
 
138
  current_transcript = gr.State("")
139
 
140
  with gr.Row():
141
+ # LEFT: INPUTS
142
  with gr.Column(scale=1, elem_classes="main-card"):
143
+ gr.Markdown("### πŸŽ™οΈ SENSOR INTAKE")
144
+ audio_in = gr.Audio(label="Record Audio", type="filepath")
145
 
146
+ gr.Markdown("### πŸ“‚ PAST RECORDS")
147
+ history_in = gr.File(label="+", elem_classes="plus-upload")
148
 
149
+ run_btn = gr.Button("🧠 START CLINICAL REASONING", variant="primary")
150
 
151
+ with gr.Accordion("πŸ” INTERACTIVE Q&A", open=False):
152
+ q_in = gr.Textbox(label="Query the Case Memory")
153
+ q_btn = gr.Button("Ask Assistant")
154
  q_out = gr.Markdown()
155
 
156
+ # RIGHT: OUTPUTS
157
  with gr.Column(scale=2, elem_classes="main-card"):
158
  with gr.Tabs():
159
+ with gr.TabItem("πŸ“‹ CLINICAL RECORD"):
160
+ analysis_out = gr.Markdown("### *Awaiting Input...*")
161
 
162
+ with gr.TabItem("πŸ“ˆ VITALS DASHBOARD"):
163
  vitals_out = gr.Dataframe(headers=["Metric", "Value", "Status"], interactive=False)
164
  gr.Markdown("---")
165
+ gr.Info("Real-time extraction of physiological markers.")
166
 
167
+ with gr.TabItem("βš™οΈ INTEROPERABILITY"):
168
+ fhir_out = gr.Code(label="HL7-FHIR JSON", language="json")
169
 
170
+ # BINDINGS
171
  run_btn.click(
172
+ fn=pulse_scribe_engine,
173
  inputs=[audio_in, history_in],
174
+ outputs=[current_transcript, analysis_out, vitals_out, fhir_out]
175
  )
176
 
177
  q_btn.click(
178
+ fn=lambda t, q: text_client.chat_completion([{"role": "user", "content": f"Context: {t}\nQuestion: {q}"}]).choices[0].message.content if t else "Memory is empty.",
179
  inputs=[current_transcript, q_in],
180
  outputs=q_out
181
  )