tamilprabaharan commited on
Commit
c7077c5
·
1 Parent(s): 29e1dd1

Initial commit of AI Doctor App

Browse files
Files changed (6) hide show
  1. README.md +7 -11
  2. analyze.py +96 -0
  3. app.py +538 -0
  4. pdfhandle.py +157 -0
  5. requirements.txt +14 -0
  6. voice.py +374 -0
README.md CHANGED
@@ -1,12 +1,8 @@
1
- ---
2
- title: AI Doctor
3
- emoji: 👀
4
- colorFrom: pink
5
- colorTo: indigo
6
- sdk: streamlit
7
- sdk_version: 1.45.0
8
- app_file: app.py
9
- pinned: false
10
- ---
11
 
12
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
1
+ # AI Doctor
 
 
 
 
 
 
 
 
 
2
 
3
+ AI-powered health insights in your native language. Upload medical reports for analysis and get voice assistance in Tamil.
4
+
5
+ ## Features
6
+ - Medical report PDF analysis
7
+ - Parameter categorization
8
+ - Tamil voice assistant
analyze.py ADDED
@@ -0,0 +1,96 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ from openai import AzureOpenAI
3
+ import json
4
+ from dotenv import load_dotenv
5
+
6
+ # ✅ Load the .env file
7
+ load_dotenv()
8
+
9
+ # Access environment variables (works in both local + Hugging Face Spaces)
10
+ AZURE_API_KEY = os.getenv("AZURE_OPENAI_API_KEY")
11
+ AZURE_ENDPOINT = os.getenv("AZURE_OPENAI_ENDPOINT")
12
+ MODEL_NAME = os.getenv("AZURE_OPENAI_DEPLOYMENT_NAME")
13
+ API_VERSION = os.getenv("AZURE_OPENAI_API_VERSION", "2024-02-15-preview") # Default if not set
14
+
15
+ # Initialize AzureOpenAI client
16
+ client = AzureOpenAI(
17
+ api_key=AZURE_API_KEY,
18
+ azure_endpoint=AZURE_ENDPOINT,
19
+ api_version=API_VERSION
20
+ )
21
+
22
+ def analyze_parameter(test_name, value, reference):
23
+ """Get AI analysis with strict output control"""
24
+ prompt = f"""Analyze this medical parameter:
25
+ Test: {test_name}
26
+ Value: {value}
27
+ Reference: {reference}
28
+
29
+ Return JSON with:
30
+ - status: "Good"/"Moderate"/"Immediate Attention"
31
+ - reason: 20-word explanation
32
+ - food: 3 specific food items
33
+ - exercise: 1 measurable activity
34
+
35
+ Example: {{
36
+ "status": "Immediate Attention",
37
+ "reason": "High LDL increases cardiovascular risk",
38
+ "food": "Oats, walnuts, olive oil",
39
+ "exercise": "45-min daily brisk walking"
40
+ }}"""
41
+
42
+ try:
43
+ response = client.chat.completions.create(
44
+ model=MODEL_NAME,
45
+ messages=[{"role": "user", "content": prompt}],
46
+ temperature=0.1,
47
+ response_format={"type": "json_object"}
48
+ )
49
+
50
+ print(json.dumps(response.choices[0].message.content, indent=4))
51
+ return json.loads(response.choices[0].message.content)
52
+ except Exception as e:
53
+ print(f"API Error: {str(e)}")
54
+ return {
55
+ "status": "Immediate Attention",
56
+ "reason": "Requires professional evaluation",
57
+ "food": "Maintain balanced diet",
58
+ "exercise": "Consult doctor"
59
+ }
60
+
61
+ def generate_report_summary(raw_data):
62
+ """Generate an overall summary of the medical report"""
63
+ if not raw_data:
64
+ return "No medical data found in the report."
65
+
66
+ # Create a simplified list of parameters for the summary
67
+ parameters = []
68
+ for item in raw_data:
69
+ parameters.append(f"{item['test']}: {item['value']} ({item['reference']})")
70
+
71
+ parameters_text = "\n".join(parameters)
72
+
73
+ prompt = f"""Generate a concise summary of this medical report:
74
+
75
+ {parameters_text}
76
+
77
+ Focus on:
78
+ 1. Overall health status
79
+ 2. Key areas of concern (if any)
80
+ 3. General health advice
81
+
82
+ Keep it under 150 words, use simple language, and be honest but reassuring.
83
+ """
84
+
85
+ try:
86
+ response = client.chat.completions.create(
87
+ model=MODEL_NAME,
88
+ messages=[{"role": "user", "content": prompt}],
89
+ temperature=0.3,
90
+ max_tokens=300
91
+ )
92
+
93
+ return response.choices[0].message.content
94
+ except Exception as e:
95
+ print(f"Summary generation error: {str(e)}")
96
+ return "Unable to generate summary. Please review the detailed analysis of each parameter."
app.py ADDED
@@ -0,0 +1,538 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ from pdfhandle import parse_medical_pdf
3
+ from analyze import analyze_parameter, generate_report_summary
4
+ from voice import get_medical_report_answer, play_audio_response
5
+ import os
6
+ import tempfile
7
+ import base64
8
+ import pandas as pd
9
+
10
+ st.set_page_config(
11
+ page_title="AI Doctor",
12
+ layout="wide",
13
+ page_icon="🩺"
14
+ )
15
+
16
+ # Custom CSS for enhanced styling
17
+ st.markdown("""
18
+ <style>
19
+ .main-header {
20
+ text-align: center;
21
+ color: #1e4d8c;
22
+ font-size: 3em;
23
+ margin-bottom: 5px;
24
+ padding-top: 10px;
25
+ font-weight: 700;
26
+ font-family: 'Arial', sans-serif;
27
+ }
28
+
29
+ .tagline {
30
+ text-align: center;
31
+ color: #4a7bb7;
32
+ font-size: 1.2em;
33
+ margin-bottom: 30px;
34
+ font-style: italic;
35
+ font-weight: 400;
36
+ }
37
+
38
+ .icon-header {
39
+ text-align: center;
40
+ font-size: 2.5em;
41
+ margin-bottom: 0;
42
+ }
43
+
44
+ .report-summary {
45
+ background-color: #f8f9fa;
46
+ border-left: 5px solid #1e4d8c;
47
+ padding: 25px;
48
+ margin-bottom: 20px;
49
+ border-radius: 8px;
50
+ box-shadow: 0 2px 5px rgba(0,0,0,0.1);
51
+ }
52
+
53
+ .subheader {
54
+ color: #1e4d8c;
55
+ border-bottom: 2px solid #eee;
56
+ padding-bottom: 10px;
57
+ margin-top: 30px;
58
+ font-weight: 600;
59
+ }
60
+
61
+ .good {
62
+ background-color: #d4edda;
63
+ border-radius: 8px;
64
+ padding: 10px;
65
+ margin-bottom: 10px;
66
+ box-shadow: 0 1px 3px rgba(0,0,0,0.1);
67
+ }
68
+
69
+ .moderate {
70
+ background-color: #fff3cd;
71
+ border-radius: 8px;
72
+ padding: 10px;
73
+ margin-bottom: 10px;
74
+ box-shadow: 0 1px 3px rgba(0,0,0,0.1);
75
+ }
76
+
77
+ .attention {
78
+ background-color: #f8d7da;
79
+ border-radius: 8px;
80
+ padding: 10px;
81
+ margin-bottom: 10px;
82
+ box-shadow: 0 1px 3px rgba(0,0,0,0.1);
83
+ }
84
+
85
+ .status-badge {
86
+ padding: 5px 10px;
87
+ border-radius: 15px;
88
+ font-weight: bold;
89
+ font-size: 0.85em;
90
+ }
91
+
92
+ .tab-content {
93
+ padding: 25px 0;
94
+ }
95
+
96
+ .audio-player {
97
+ margin-top: 20px;
98
+ width: 100%;
99
+ border-radius: 8px;
100
+ }
101
+
102
+ .dataframe {
103
+ font-size: 0.9em;
104
+ }
105
+
106
+ .dataframe th {
107
+ background-color: #e6f2ff;
108
+ padding: 10px !important;
109
+ text-align: left;
110
+ }
111
+
112
+ .dataframe td {
113
+ padding: 10px !important;
114
+ }
115
+
116
+ .stButton > button {
117
+ background-color: #1e4d8c;
118
+ color: white;
119
+ font-weight: 500;
120
+ border-radius: 8px;
121
+ padding: 10px 15px;
122
+ border: none;
123
+ box-shadow: 0 2px 5px rgba(0,0,0,0.2);
124
+ transition: all 0.3s ease;
125
+ }
126
+
127
+ .stButton > button:hover {
128
+ background-color: #0d3b76;
129
+ box-shadow: 0 4px 8px rgba(0,0,0,0.3);
130
+ }
131
+
132
+ .upload-section {
133
+ background-color: #f0f7ff;
134
+ padding: 25px;
135
+ border-radius: 10px;
136
+ margin-bottom: 25px;
137
+ text-align: center;
138
+ }
139
+
140
+ .query-box {
141
+ background-color: #f0f7ff;
142
+ border-left: 5px solid #1e4d8c;
143
+ padding: 15px;
144
+ margin-bottom: 15px;
145
+ border-radius: 8px;
146
+ box-shadow: 0 2px 5px rgba(0,0,0,0.1);
147
+ }
148
+
149
+ .response-box {
150
+ background-color: #f4f9f4;
151
+ border-left: 5px solid #389738;
152
+ padding: 15px;
153
+ margin-bottom: 15px;
154
+ border-radius: 8px;
155
+ box-shadow: 0 2px 5px rgba(0,0,0,0.1);
156
+ }
157
+
158
+ .stTextInput > div > div > input {
159
+ border-radius: 8px;
160
+ border: 1px solid #bbd0e6;
161
+ padding: 10px 15px;
162
+ }
163
+
164
+ .metric-card {
165
+ background-color: #ffffff;
166
+ padding: 15px;
167
+ border-radius: 10px;
168
+ box-shadow: 0 3px 10px rgba(0,0,0,0.1);
169
+ text-align: center;
170
+ transition: transform 0.3s ease;
171
+ }
172
+
173
+ .metric-card:hover {
174
+ transform: translateY(-5px);
175
+ }
176
+
177
+ .metric-good {
178
+ border-top: 5px solid #28a745;
179
+ }
180
+
181
+ .metric-moderate {
182
+ border-top: 5px solid #ffc107;
183
+ }
184
+
185
+ .metric-attention {
186
+ border-top: 5px solid #dc3545;
187
+ }
188
+
189
+ .st-expander {
190
+ border-radius: 8px;
191
+ box-shadow: 0 2px 5px rgba(0,0,0,0.05);
192
+ }
193
+
194
+ footer {
195
+ text-align: center;
196
+ padding: 20px 0;
197
+ color: #6c757d;
198
+ font-size: 0.9em;
199
+ }
200
+
201
+ .stTabs [data-baseweb="tab-list"] {
202
+ gap: 20px;
203
+ }
204
+
205
+ .stTabs [data-baseweb="tab"] {
206
+ height: 50px;
207
+ white-space: pre-wrap;
208
+ background-color: #f8f9fa;
209
+ border-radius: 4px 4px 0 0;
210
+ gap: 1px;
211
+ padding-top: 10px;
212
+ padding-bottom: 10px;
213
+ }
214
+
215
+ .stTabs [aria-selected="true"] {
216
+ background-color: #1e4d8c;
217
+ color: white;
218
+ }
219
+ </style>
220
+ """, unsafe_allow_html=True)
221
+
222
+ # Header with icons and tagline
223
+ st.markdown('<div class="icon-header">👨‍⚕️ 🩺</div>', unsafe_allow_html=True)
224
+ st.markdown('<h1 class="main-header">AI Doctor</h1>', unsafe_allow_html=True)
225
+ st.markdown('<p class="tagline">Empowering people through AI-powered health insights in their native language</p>', unsafe_allow_html=True)
226
+
227
+ # Initialize session state for storing analysis results
228
+ if 'raw_data' not in st.session_state:
229
+ st.session_state.raw_data = None
230
+ if 'categorized' not in st.session_state:
231
+ st.session_state.categorized = None
232
+ if 'summary' not in st.session_state:
233
+ st.session_state.summary = None
234
+ if 'voice_response' not in st.session_state:
235
+ st.session_state.voice_response = None
236
+ # Add active tab tracking to session state
237
+ if 'active_tab' not in st.session_state:
238
+ st.session_state.active_tab = 0
239
+
240
+ # Styled file upload section
241
+ st.markdown('<div class="upload-section">', unsafe_allow_html=True)
242
+ uploaded_file = st.file_uploader(
243
+ "Upload Medical Report (PDF, max 10MB)",
244
+ type="pdf",
245
+ help="We never store your medical data. All processing happens on-demand.",
246
+ accept_multiple_files=False
247
+ )
248
+ st.markdown('</div>', unsafe_allow_html=True)
249
+
250
+ def get_binary_file_downloader_html(bin_file, file_label='File'):
251
+ with open(bin_file, 'rb') as f:
252
+ data = f.read()
253
+ b64 = base64.b64encode(data).decode()
254
+ href = f'<a href="data:audio/mp3;base64,{b64}" download="{file_label}.mp3" class="download-button">Download {file_label} 📥</a>'
255
+ return href
256
+
257
+ # Function to update active tab in session state
258
+ def set_active_tab(tab_idx):
259
+ st.session_state.active_tab = tab_idx
260
+
261
+ # Main application flow
262
+ if uploaded_file:
263
+ if uploaded_file.size > 10 * 1024 * 1024:
264
+ st.error("❌ File size exceeds 10MB limit")
265
+ st.stop()
266
+
267
+ # Only process the PDF if it hasn't been processed yet or a new file was uploaded
268
+ file_hash = hash(uploaded_file.getvalue())
269
+ if 'file_hash' not in st.session_state or file_hash != st.session_state.file_hash:
270
+ with st.spinner("Analyzing your medical report..."):
271
+ try:
272
+ # Process PDF
273
+ st.session_state.raw_data = parse_medical_pdf(uploaded_file)
274
+ st.session_state.file_hash = file_hash
275
+
276
+ if not st.session_state.raw_data:
277
+ st.error("No parameters found in document. Please ensure this is a standard medical report.")
278
+ st.stop()
279
+
280
+ # Generate summary
281
+ st.session_state.summary = generate_report_summary(st.session_state.raw_data)
282
+
283
+ # Process analysis
284
+ categorized = {
285
+ "Good": [],
286
+ "Moderate": [],
287
+ "Immediate Attention": []
288
+ }
289
+
290
+ for item in st.session_state.raw_data:
291
+ analysis = analyze_parameter(
292
+ item["test"],
293
+ item["value"],
294
+ item["reference"]
295
+ )
296
+ row = {
297
+ "Parameter": item["test"],
298
+ "Value": f"{item['value']} (Ref: {item['reference']})",
299
+ "Clinical Significance": analysis["reason"],
300
+ "Dietary Recommendation": analysis["food"],
301
+ "Activity Guidance": analysis["exercise"],
302
+ "Status": analysis["status"]
303
+ }
304
+ categorized[analysis["status"]].append(row)
305
+
306
+ st.session_state.categorized = categorized
307
+
308
+ except Exception as e:
309
+ st.error(f"Analysis failed: {str(e)}")
310
+ st.stop()
311
+
312
+ # Create tabs with specified active tab from session state and improved icons
313
+ tab_titles = ["📊 Summary", "🔍 Detailed Analysis", "🗣️ Voice Assistant"]
314
+
315
+ # Create tab containers with the active tab selected
316
+ active_tab_index = st.session_state.active_tab
317
+ tabs = st.tabs(tab_titles)
318
+
319
+ # Tab 1: Summary with enhanced cards
320
+ with tabs[0]:
321
+ st.markdown("<h2 class='subheader'>Report Summary</h2>", unsafe_allow_html=True)
322
+ st.markdown(f"<div class='report-summary'>{st.session_state.summary}</div>", unsafe_allow_html=True)
323
+
324
+ # Summary stats with improved metric cards
325
+ st.markdown("<h3>Health Parameters Overview</h3>", unsafe_allow_html=True)
326
+ col1, col2, col3 = st.columns(3)
327
+
328
+ with col1:
329
+ good_count = len(st.session_state.categorized["Good"])
330
+ st.markdown(f"""
331
+ <div class="metric-card metric-good">
332
+ <h4>Good Parameters</h4>
333
+ <h2>{good_count}</h2>
334
+ <p>Normal range values</p>
335
+ </div>
336
+ """, unsafe_allow_html=True)
337
+
338
+ with col2:
339
+ moderate_count = len(st.session_state.categorized["Moderate"])
340
+ st.markdown(f"""
341
+ <div class="metric-card metric-moderate">
342
+ <h4>Moderate Parameters</h4>
343
+ <h2>{moderate_count}</h2>
344
+ <p>Borderline values</p>
345
+ </div>
346
+ """, unsafe_allow_html=True)
347
+
348
+ with col3:
349
+ attention_count = len(st.session_state.categorized["Immediate Attention"])
350
+ st.markdown(f"""
351
+ <div class="metric-card metric-attention">
352
+ <h4>Needs Attention</h4>
353
+ <h2>{attention_count}</h2>
354
+ <p>Critical values</p>
355
+ </div>
356
+ """, unsafe_allow_html=True)
357
+
358
+ # Tab 2: Detailed Analysis with improved styling
359
+ with tabs[1]:
360
+ st.markdown("<h2 class='subheader'>Detailed Analysis</h2>", unsafe_allow_html=True)
361
+ st.warning("❗ This tool provides general insights only. Always consult a healthcare professional.")
362
+
363
+ # Create tables for each status category with improved styling
364
+ for status in ["Immediate Attention", "Moderate", "Good"]:
365
+ if data := st.session_state.categorized[status]:
366
+ status_color = "attention" if status == "Immediate Attention" else "moderate" if status == "Moderate" else "good"
367
+ status_icon = "⚠️" if status == "Immediate Attention" else "⚠️" if status == "Moderate" else "✅"
368
+
369
+ with st.expander(f"{status_icon} {status} Parameters ({len(data)})", expanded=(status == "Immediate Attention")):
370
+ # Convert list of dictionaries to DataFrame for tabular display
371
+ df = pd.DataFrame(data)
372
+
373
+ # Apply styling based on status
374
+ st.markdown(f"<div class='{status_color}'>", unsafe_allow_html=True)
375
+ st.dataframe(
376
+ df,
377
+ hide_index=True,
378
+ use_container_width=True
379
+ )
380
+ st.markdown("</div>", unsafe_allow_html=True)
381
+
382
+ # Tab 3: Voice Assistant with improved layout
383
+ with tabs[2]:
384
+ st.markdown("<h2 class='subheader'>Voice Assistant (Tamil)</h2>", unsafe_allow_html=True)
385
+ st.info("You can ask questions about your medical report in Tamil. The assistant will respond in Tamil.")
386
+
387
+ # Create a placeholder for status messages
388
+ status_placeholder = st.empty()
389
+
390
+ # Remove doctor icon and adjust spacing
391
+
392
+ col1, col2 = st.columns(2)
393
+
394
+ with col1:
395
+ # Button for voice input
396
+ if st.button("🎤 Ask Questions (you may speak in Tamil)", type="primary", key="listen_button"):
397
+ # Update the active tab in session state
398
+ st.session_state.active_tab = 2
399
+
400
+ # Process voice input
401
+ st.session_state.voice_response = get_medical_report_answer(st.session_state.summary)
402
+
403
+ # Use JavaScript to ensure we stay on Voice Assistant tab
404
+ st.components.v1.html("""
405
+ <script>
406
+ // Wait a moment for the UI to update
407
+ setTimeout(function() {
408
+ // Select the Voice Assistant tab (index 2)
409
+ window.parent.document.querySelectorAll('[data-baseweb="tab"]')[2].click();
410
+ }, 100);
411
+ </script>
412
+ """, height=0)
413
+
414
+ with col2:
415
+ # Text input as an alternative with better styling
416
+ tamil_text = st.text_input("💬 Or type your question in Tamil:", placeholder="என் இரத்த அழுத்தம் எப்படி உள்ளது?")
417
+ if tamil_text and st.button("✓ Submit", key="submit_button"):
418
+ # Update the active tab in session state
419
+ st.session_state.active_tab = 2
420
+
421
+ with st.spinner("Processing your query..."):
422
+ st.session_state.voice_response = get_medical_report_answer(st.session_state.summary, tamil_text)
423
+
424
+ # Use JavaScript to ensure we stay on Voice Assistant tab
425
+ st.components.v1.html("""
426
+ <script>
427
+ // Wait a moment for the UI to update
428
+ setTimeout(function() {
429
+ // Select the Voice Assistant tab (index 2)
430
+ window.parent.document.querySelectorAll('[data-baseweb="tab"]')[2].click();
431
+ }, 100);
432
+ </script>
433
+ """, height=0)
434
+
435
+ # Display voice response in a more visually appealing way
436
+ if 'voice_response' in st.session_state and st.session_state.voice_response:
437
+ response = st.session_state.voice_response
438
+
439
+ # Clear any status messages
440
+ status_placeholder.empty()
441
+
442
+ # Original query display with improved styling
443
+ if response["original_query"]:
444
+ st.markdown("<h3>Your Question</h3>", unsafe_allow_html=True)
445
+
446
+ st.markdown(f"""
447
+ <div class="query-box">
448
+ <strong>Tamil:</strong> {response['original_query']}<br>
449
+ <strong>English:</strong> {response['translated_query']}
450
+ </div>
451
+ """, unsafe_allow_html=True)
452
+
453
+ # Response display with improved styling
454
+ st.markdown("<h3>Response</h3>", unsafe_allow_html=True)
455
+
456
+ with st.expander("🇺🇸 English Response", expanded=False):
457
+ st.markdown(f"<div class='response-box'>{response['english_response']}</div>", unsafe_allow_html=True)
458
+
459
+ with st.expander("🇮🇳 Tamil Response", expanded=True):
460
+ st.markdown(f"<div class='response-box'>{response['tamil_response']}</div>", unsafe_allow_html=True)
461
+
462
+ # Audio playback with auto-play and improved styling
463
+ if response["audio_file"] and os.path.exists(response["audio_file"]):
464
+ st.markdown("<h3>🔊 Voice Response</h3>", unsafe_allow_html=True)
465
+
466
+ # Auto-play the audio
467
+ play_audio_response(response["audio_file"])
468
+
469
+ # Display audio controls for manual replay
470
+ st.audio(response["audio_file"])
471
+
472
+ # Download button with better styling
473
+ st.markdown(get_binary_file_downloader_html(response["audio_file"], 'Audio Response'), unsafe_allow_html=True)
474
+
475
+ # We don't need complex JavaScript for tabs anymore since we're using direct click events
476
+ # This is much simpler and more reliable
477
+
478
+ else:
479
+ # Show info when no file is uploaded with more attractive layout
480
+ st.info("Upload your medical report PDF to get started with your personalized health analysis")
481
+
482
+ # Sample information about the app with better formatting
483
+ col1, col2 = st.columns(2)
484
+
485
+ with col1:
486
+ st.markdown("""
487
+ <div style="background-color: #f0f7ff; padding: 20px; border-radius: 10px; height: 100%;">
488
+ <h3>How it works</h3>
489
+
490
+ <ol style="margin-top: 15px;">
491
+ <li><strong>Upload your medical report</strong> in PDF format</li>
492
+ <li>Our AI analyzes each parameter and provides:
493
+ <ul>
494
+ <li>Status classification</li>
495
+ <li>Clinical significance</li>
496
+ <li>Dietary recommendations</li>
497
+ <li>Activity guidance</li>
498
+ </ul>
499
+ </li>
500
+ <li><strong>Ask questions in Tamil</strong> about your report using voice or text</li>
501
+ </ol>
502
+ </div>
503
+ """, unsafe_allow_html=True)
504
+
505
+ with col2:
506
+ st.markdown("""
507
+ <div style="background-color: #f0f7ff; padding: 20px; border-radius: 10px; height: 100%;">
508
+ <h3>Privacy & Security</h3>
509
+
510
+ <ul style="margin-top: 15px;">
511
+ <li>Your medical data is processed securely and never stored</li>
512
+ <li>All analysis happens on-demand</li>
513
+ <li>Voice data is only used for processing your queries</li>
514
+ <li>We prioritize your data privacy and security</li>
515
+ </ul>
516
+ </div>
517
+ """, unsafe_allow_html=True)
518
+
519
+ # Footer with improved styling
520
+ st.markdown("""
521
+ <footer>
522
+ <hr style="margin: 20px 0;">
523
+ <div>
524
+ <p>AI Doctor © 2025 | Empowering people through AI-powered health insights</p>
525
+ <p style="font-size: 0.8em; color: #999;">For educational purposes only. Always consult a healthcare professional for medical advice.</p>
526
+ </div>
527
+ </footer>
528
+ """, unsafe_allow_html=True)
529
+
530
+ # Display LinkedIn profile
531
+ st.markdown(
532
+ """
533
+ <div style="text-align: center; font-size: 15px;">
534
+ <a href="https://www.linkedin.com/in/tamilprabaharan/" target="_blank">Visit my LinkedIn Profile</a>
535
+ </div>
536
+ """,
537
+ unsafe_allow_html=True
538
+ )
pdfhandle.py ADDED
@@ -0,0 +1,157 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # pdfhandle.py (Enhanced with AI fallback)
2
+ import pdfplumber
3
+ import re
4
+ import logging
5
+ import os
6
+ from langchain_community.chat_models import AzureChatOpenAI
7
+ #from langchain.chat_models import AzureChatOpenAI
8
+ from langchain.schema import HumanMessage
9
+ from langchain.output_parsers import PydanticOutputParser
10
+ from pydantic import BaseModel, Field
11
+ from typing import List
12
+
13
+ logging.basicConfig(level=logging.INFO)
14
+ logger = logging.getLogger(__name__)
15
+
16
+ class MedicalParameter(BaseModel):
17
+ test: str = Field(description="Name of the medical test")
18
+ value: str = Field(description="Observed value of the test")
19
+ reference: str = Field(description="Reference range with units if available")
20
+
21
+ class MedicalReport(BaseModel):
22
+ parameters: List[MedicalParameter] = Field(description="List of medical parameters from the report")
23
+
24
+ def parse_medical_pdf(pdf_file):
25
+ """Enhanced PDF parser with AI fallback for medical reports"""
26
+ # First attempt with regex-based parsing
27
+ results = standard_parse(pdf_file)
28
+
29
+ # If standard parsing yields no results, try AI-based parsing
30
+ if not results:
31
+ logger.info("Standard parsing yielded no results. Trying AI-based parsing...")
32
+ results = ai_based_parse(pdf_file)
33
+
34
+ return results
35
+
36
+ def standard_parse(pdf_file):
37
+ """Standard regex-based parsing method"""
38
+ results = []
39
+ header_found = False
40
+ header_pattern = re.compile(
41
+ r'TEST\s+NAME\s+OBSERVED\s+VALUE\s+UNITS\s+BIO\.?\s+REF\.?\s*INTERVAL',
42
+ re.IGNORECASE
43
+ )
44
+
45
+ # Extended pattern to handle common variations in medical reports
46
+ data_pattern = re.compile(
47
+ r'^(?P<test>.+?)\s+' # Test name (non-greedy match)
48
+ r'(?P<value>\d+\.?\d*)\s+' # Numeric value
49
+ r'(?P<units>[^\s]+)\s+' # Units (no spaces)
50
+ r'(?P<ref>.+)$' # Reference range
51
+ )
52
+
53
+ with pdfplumber.open(pdf_file) as pdf:
54
+ for page in pdf.pages:
55
+ text = page.extract_text()
56
+ lines = [line.strip() for line in text.split('\n') if line.strip()]
57
+
58
+ for line in lines:
59
+ # Skip disclaimers and empty lines
60
+ if not line or line.startswith('Disclaimer'):
61
+ continue
62
+
63
+ # Detect header row
64
+ if header_pattern.search(line):
65
+ header_found = True
66
+ logger.info(f"Header found: {line}")
67
+ continue
68
+
69
+ if header_found:
70
+ # Skip section headers (all caps without numbers)
71
+ if re.match(r'^[A-Z\s/]+$', line) and not re.search(r'\d', line):
72
+ logger.debug(f"Skipping section: {line}")
73
+ continue
74
+
75
+ # Extract data using regex
76
+ if match := data_pattern.match(line):
77
+ data = match.groupdict()
78
+ results.append({
79
+ "test": data['test'].strip(),
80
+ "value": data['value'],
81
+ "reference": f"{data['ref']} {data['units']}".strip()
82
+ })
83
+ logger.info(f"Valid row: {data}")
84
+ else:
85
+ logger.debug(f"Skipped line: {line}")
86
+
87
+ return results
88
+
89
+ def ai_based_parse(pdf_file):
90
+ """AI-based parsing using LangChain and Azure OpenAI"""
91
+ try:
92
+ # Configure Azure OpenAI client
93
+ llm = AzureChatOpenAI(
94
+ openai_api_version=os.getenv("AZURE_OPENAI_API_VERSION", "2024-02-15-preview"),
95
+ azure_deployment=os.getenv("AZURE_OPENAI_DEPLOYMENT_NAME"),
96
+ openai_api_key=os.getenv("AZURE_OPENAI_API_KEY"),
97
+ azure_endpoint=os.getenv("AZURE_OPENAI_ENDPOINT")
98
+ )
99
+
100
+ # Extract text from PDF
101
+ full_text = ""
102
+ with pdfplumber.open(pdf_file) as pdf:
103
+ for page in pdf.pages:
104
+ full_text += page.extract_text() + "\n"
105
+
106
+ # Define the output parser
107
+ parser = PydanticOutputParser(pydantic_object=MedicalReport)
108
+
109
+ # Create the prompt
110
+ prompt = f"""
111
+ You are a medical data extraction expert. Extract all medical test parameters from this report.
112
+
113
+ Medical Report Text:
114
+ {full_text}
115
+
116
+ Extract each test with its observed value and reference range. Format your response exactly as in this example:
117
+ {{
118
+ "parameters": [
119
+ {{
120
+ "test": "Hemoglobin",
121
+ "value": "14.5",
122
+ "reference": "13.0 - 17.0 g/dL"
123
+ }},
124
+ {{
125
+ "test": "Total Cholesterol",
126
+ "value": "198",
127
+ "reference": "<200 mg/dL"
128
+ }}
129
+ ]
130
+ }}
131
+
132
+ Extract only actual test parameters. Include units in the reference field.
133
+ {parser.get_format_instructions()}
134
+ """
135
+
136
+ # Get response from the LLM
137
+ messages = [HumanMessage(content=prompt)]
138
+ response = llm.predict_messages(messages)
139
+
140
+ # Parse the response
141
+ report = parser.parse(response.content)
142
+
143
+ # Convert to the expected format
144
+ results = []
145
+ for param in report.parameters:
146
+ results.append({
147
+ "test": param.test,
148
+ "value": param.value,
149
+ "reference": param.reference
150
+ })
151
+
152
+ logger.info(f"AI parsing successful. Extracted {len(results)} parameters.")
153
+ return results
154
+
155
+ except Exception as e:
156
+ logger.error(f"AI-based parsing failed: {str(e)}")
157
+ return []
requirements.txt ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ streamlit>=1.22
2
+ pdfplumber>=0.9
3
+ openai>=1.0.0
4
+ python-dotenv>=1.0
5
+ pyyaml>=6.0
6
+ langchain-community>=0.0.13
7
+ langchain>=0.1.0
8
+ pydantic>=2.5.0
9
+ typing-extensions>=4.8.0
10
+ SpeechRecognition>=3.10.0
11
+ google-generativeai>=0.3.0
12
+ pygame>=2.5.0
13
+ translate>=3.6.1
14
+ google-cloud-texttospeech>=2.14.1
voice.py ADDED
@@ -0,0 +1,374 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # voice.py (Updated with Azure OpenAI integration)
2
+ import os
3
+ import speech_recognition as sr
4
+ import google.generativeai as genai
5
+ import tempfile
6
+ import logging
7
+ from io import BytesIO
8
+ import re
9
+ import pygame
10
+ from translate import Translator
11
+ import base64
12
+ import streamlit as st
13
+ from google.cloud import texttospeech
14
+ import json
15
+ from openai import AzureOpenAI
16
+
17
+ # Set up logging
18
+ logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
19
+ logger = logging.getLogger(__name__)
20
+
21
+ # Get API keys from environment variables
22
+ gemini_api_key = os.getenv('GEMINI_API_KEY', "AIzaSyCZL29aqWTmP_NTzkGILK4Kujx_MuyRAs4")
23
+ google_tts_credentials = os.getenv('GOOGLE_TTS_CREDENTIALS', "D:/AI and Data Science/Projects/AI DoctorV2/tamiltextspeech-458116-147b3efcaf84.json")
24
+
25
+ # Azure OpenAI configuration
26
+ AZURE_API_KEY = os.getenv("AZURE_OPENAI_API_KEY")
27
+ AZURE_ENDPOINT = os.getenv("AZURE_OPENAI_ENDPOINT")
28
+ MODEL_NAME = os.getenv("AZURE_OPENAI_DEPLOYMENT_NAME")
29
+ API_VERSION = os.getenv("AZURE_OPENAI_API_VERSION", "2024-02-15-preview")
30
+
31
+ # Initialize Azure OpenAI client
32
+ try:
33
+ azure_client = AzureOpenAI(
34
+ api_key=AZURE_API_KEY,
35
+ azure_endpoint=AZURE_ENDPOINT,
36
+ api_version=API_VERSION
37
+ )
38
+ logger.info("Azure OpenAI client initialized successfully")
39
+ except Exception as e:
40
+ logger.error(f"Failed to initialize Azure OpenAI client: {str(e)}")
41
+ azure_client = None
42
+
43
+ # Initialize Google TTS client
44
+ try:
45
+ # Set credentials from JSON file
46
+ if os.path.exists(google_tts_credentials):
47
+ os.environ["GOOGLE_APPLICATION_CREDENTIALS"] = google_tts_credentials
48
+ tts_client = texttospeech.TextToSpeechClient()
49
+ logger.info("Google Text-to-Speech client initialized successfully")
50
+ else:
51
+ logger.warning(f"Google TTS credentials file not found: {google_tts_credentials}")
52
+ tts_client = None
53
+ except Exception as e:
54
+ logger.error(f"Failed to initialize Google TTS: {str(e)}")
55
+ tts_client = None
56
+
57
+ # Configure Gemini for translations only
58
+ genai.configure(api_key=gemini_api_key)
59
+ model = genai.GenerativeModel('gemini-1.5-pro')
60
+
61
+ def listen_tamil():
62
+ """Listen to Tamil speech with improved end detection and error handling"""
63
+ recognizer = sr.Recognizer()
64
+ with sr.Microphone() as source:
65
+ logger.info("Listening for Tamil speech...")
66
+ # Adjust for ambient noise
67
+ recognizer.adjust_for_ambient_noise(source, duration=1.5) # Increased duration
68
+
69
+ # Improve speech detection with better pause threshold
70
+ recognizer.pause_threshold = 1.0 # Increased pause threshold for better recognition
71
+ recognizer.energy_threshold = 300 # Adjust sensitivity
72
+
73
+ try:
74
+ st.info("🎤 Listening... Please speak in Tamil")
75
+ audio = recognizer.listen(source, timeout=15, phrase_time_limit=30) # Extended timeout
76
+ logger.info("Speech detected, processing...")
77
+ st.success("✅ Speech recorded! Processing...")
78
+ except sr.WaitTimeoutError:
79
+ logger.error("No speech detected")
80
+ st.error("❌ No speech detected. Please try again.")
81
+ return None
82
+
83
+ try:
84
+ # Using Google's speech recognition with Tamil language
85
+ tamil_text = recognizer.recognize_google(audio, language='ta-IN')
86
+ logger.info(f"Recognized Tamil text: {tamil_text}")
87
+ return tamil_text
88
+ except sr.UnknownValueError:
89
+ logger.error("Could not understand audio")
90
+ st.error("❌ Could not understand the speech. Please try again more clearly.")
91
+ return None
92
+ except sr.RequestError as e:
93
+ logger.error(f"Speech recognition service error: {e}")
94
+ st.error("❌ Speech recognition service error. Please try again later.")
95
+ return None
96
+
97
+ def translate_tamil_to_english(tamil_text):
98
+ """Translate Tamil text to English while preserving numbers"""
99
+ if not tamil_text:
100
+ return ""
101
+
102
+ # Extract numbers from the text
103
+ numbers = re.findall(r'\d+\.?\d*', tamil_text)
104
+
105
+ # Replace numbers with placeholders
106
+ for i, num in enumerate(numbers):
107
+ tamil_text = tamil_text.replace(num, f'NUM{i}PLACEHOLDER')
108
+
109
+ try:
110
+ # Use Gemini for more accurate translation
111
+ prompt = f"""Translate this Tamil text to English accurately, preserving the exact meaning:
112
+
113
+ {tamil_text}
114
+
115
+ Return only the translation, nothing else."""
116
+
117
+ response = model.generate_content(prompt)
118
+ translation = response.text
119
+
120
+ # Fallback to basic translator if Gemini fails
121
+ if not translation or len(translation) < 5:
122
+ translator = Translator(to_lang="en", from_lang="ta")
123
+ translation = translator.translate(tamil_text)
124
+
125
+ # Restore numbers
126
+ for i, num in enumerate(numbers):
127
+ translation = translation.replace(f'NUM{i}PLACEHOLDER', num)
128
+
129
+ # Clean up any artifacts
130
+ translation = re.sub(r'\s+', ' ', translation).strip()
131
+ logger.info(f"Translation result: {translation}")
132
+
133
+ return translation
134
+
135
+ except Exception as e:
136
+ logger.error(f"Translation error: {e}")
137
+ # Try fallback translator
138
+ try:
139
+ translator = Translator(to_lang="en", from_lang="ta")
140
+ return translator.translate(tamil_text)
141
+ except:
142
+ return tamil_text # Return original if translation fails
143
+
144
+ def translate_english_to_tamil(english_text):
145
+ """Translate English text to Tamil while preserving numbers"""
146
+ if not english_text:
147
+ return ""
148
+
149
+ # Extract numbers from the text
150
+ numbers = re.findall(r'\d+\.?\d*', english_text)
151
+
152
+ # Replace numbers with placeholders
153
+ for i, num in enumerate(numbers):
154
+ english_text = english_text.replace(num, f'NUM{i}PLACEHOLDER')
155
+
156
+ try:
157
+ # Use Gemini for more accurate translation
158
+ prompt = f"""Translate this English text to Tamil accurately, preserving the exact meaning:
159
+
160
+ {english_text}
161
+
162
+ Return only the translation, nothing else."""
163
+
164
+ response = model.generate_content(prompt)
165
+ translation = response.text
166
+
167
+ # Fallback to basic translator if Gemini fails
168
+ if not translation or len(translation) < 5:
169
+ translator = Translator(to_lang="ta", from_lang="en")
170
+ translation = translator.translate(english_text)
171
+
172
+ # Restore numbers
173
+ for i, num in enumerate(numbers):
174
+ translation = translation.replace(f'NUM{i}PLACEHOLDER', num)
175
+
176
+ # Clean up any artifacts
177
+ translation = re.sub(r'\s+', ' ', translation).strip()
178
+ logger.info(f"Translation to Tamil: {translation}")
179
+
180
+ return translation
181
+
182
+ except Exception as e:
183
+ logger.error(f"Translation error: {e}")
184
+ # Try fallback translator
185
+ try:
186
+ translator = Translator(to_lang="ta", from_lang="en")
187
+ return translator.translate(english_text)
188
+ except:
189
+ return english_text # Return original if translation fails
190
+
191
+ def process_with_azure_openai(english_text, medical_summary):
192
+ """Process medical report with Azure OpenAI using empathetic approach"""
193
+ if not english_text or not medical_summary:
194
+ return "No data available to process."
195
+
196
+ if not azure_client:
197
+ logger.error("Azure OpenAI client not initialized")
198
+ return "Sorry, the AI service is currently unavailable."
199
+
200
+ try:
201
+ prompt = f"""You are a compassionate medical assistant. Analyze the medical report and respond to the user's question.
202
+
203
+ User's question: {english_text}
204
+
205
+ Requirements:
206
+ 1. Respond only if the question relates to the medical report
207
+ 2. Keep the response under 100 words
208
+ 3. Use simple, non-medical language when possible
209
+ 4. Focus on answering the specific question
210
+ 5. Be empathetic and reassuring (avoid causing panic)
211
+ 6. Include positive, actionable health improvement suggestions
212
+ 7. Use phrases like "Don't worry", "You can improve this by", "This is manageable"
213
+
214
+ Medical Report:
215
+ {medical_summary}
216
+ """
217
+
218
+ response = azure_client.chat.completions.create(
219
+ model=MODEL_NAME,
220
+ messages=[{"role": "user", "content": prompt}],
221
+ temperature=0.3,
222
+ max_tokens=400
223
+ )
224
+
225
+ processed_text = response.choices[0].message.content
226
+ logger.info("Successfully processed query with Azure OpenAI")
227
+ return processed_text
228
+
229
+ except Exception as e:
230
+ logger.error(f"Error processing with Azure OpenAI: {str(e)}")
231
+ return "I apologize, but I couldn't process your question about the medical report."
232
+
233
+ def text_to_speech(text, output_file="output.mp3"):
234
+ """Convert text to speech using Google TTS"""
235
+ if not text:
236
+ logger.warning("No text provided for speech synthesis")
237
+ return None
238
+
239
+ try:
240
+ if tts_client:
241
+ # Configure the synthesis input
242
+ synthesis_input = texttospeech.SynthesisInput(text=text)
243
+
244
+ # Build the voice request, selecting Tamil language and female voice
245
+ voice = texttospeech.VoiceSelectionParams(
246
+ language_code="ta-IN",
247
+ ssml_gender=texttospeech.SsmlVoiceGender.FEMALE
248
+ )
249
+
250
+ # Select the audio file type with improved settings
251
+ audio_config = texttospeech.AudioConfig(
252
+ audio_encoding=texttospeech.AudioEncoding.MP3,
253
+ speaking_rate=0.9, # Slightly slower for better comprehension
254
+ pitch=0.0, # Normal pitch
255
+ volume_gain_db=1.0 # Slightly louder
256
+ )
257
+
258
+ # Perform the text-to-speech request
259
+ response = tts_client.synthesize_speech(
260
+ input=synthesis_input,
261
+ voice=voice,
262
+ audio_config=audio_config
263
+ )
264
+
265
+ # Save the response to a file
266
+ with open(output_file, "wb") as out:
267
+ out.write(response.audio_content)
268
+ logger.info(f"Audio content written to file {output_file}")
269
+
270
+ # Return audio bytes for streaming
271
+ audio_bytes = BytesIO(response.audio_content)
272
+ return audio_bytes
273
+ else:
274
+ logger.warning("Google TTS client not available")
275
+ return None
276
+
277
+ except Exception as e:
278
+ logger.error(f"Error in text-to-speech: {e}")
279
+ return None
280
+
281
+ def play_audio(audio_file):
282
+ """Play audio file using pygame"""
283
+ try:
284
+ pygame.mixer.init()
285
+ pygame.mixer.music.load(audio_file)
286
+ pygame.mixer.music.play()
287
+ while pygame.mixer.music.get_busy():
288
+ pygame.time.Clock().tick(10)
289
+ except Exception as e:
290
+ logger.error(f"Error playing audio: {e}")
291
+
292
+ def get_base64_audio(audio_file):
293
+ """Convert audio file to base64 for embedding"""
294
+ with open(audio_file, "rb") as f:
295
+ data = f.read()
296
+ return base64.b64encode(data).decode()
297
+
298
+ def play_audio_response(audio_file):
299
+ """Play audio file automatically in browser"""
300
+ if audio_file and os.path.exists(audio_file):
301
+ try:
302
+ # Create HTML with autoplay audio element
303
+ audio_html = f"""
304
+ <audio id="response_audio" autoplay="true">
305
+ <source src="data:audio/mp3;base64,{get_base64_audio(audio_file)}" type="audio/mp3">
306
+ </audio>
307
+ <script>
308
+ // Ensure audio plays automatically
309
+ var audio = document.getElementById("response_audio");
310
+ audio.play().catch(function(error) {{
311
+ console.error("Audio playback failed:", error);
312
+ }});
313
+ </script>
314
+ """
315
+ st.components.v1.html(audio_html, height=0)
316
+ logger.info("Audio playback triggered")
317
+ except Exception as e:
318
+ logger.error(f"Error in auto-play: {e}")
319
+
320
+ def get_medical_report_answer(medical_summary, tamil_text=None):
321
+ """Process a voice query about the medical report"""
322
+ # If tamil_text is not provided, listen for it
323
+ if not tamil_text:
324
+ tamil_text = listen_tamil()
325
+
326
+ if not tamil_text:
327
+ return {
328
+ "original_query": None,
329
+ "translated_query": None,
330
+ "english_response": "No speech detected. Please try again.",
331
+ "tamil_response": "பேச்சு இல்லை. மீண்டும் முயற்சிக்கவும்.",
332
+ "audio_file": None
333
+ }
334
+
335
+ # Step 2: Translate Tamil to English
336
+ english_query = translate_tamil_to_english(tamil_text)
337
+
338
+ # Step 3: Process with Azure OpenAI instead of Gemini
339
+ english_response = process_with_azure_openai(english_query, medical_summary)
340
+
341
+ # Step 4: Translate response back to Tamil
342
+ tamil_response = translate_english_to_tamil(english_response)
343
+
344
+ # Add empathetic phrases in Tamil if they're not already present
345
+ empathetic_phrases = [
346
+ "கவலைப்பட வேண்டாம்", # Don't worry
347
+ "இது கையாளக்கூடியது", # This is manageable
348
+ "இதை மேம்படுத்த முடியும்" # You can improve this
349
+ ]
350
+
351
+ # Check if at least one empathetic phrase is present
352
+ has_empathetic_phrase = any(phrase in tamil_response for phrase in empathetic_phrases)
353
+
354
+ # Add an empathetic phrase at the beginning if none found
355
+ if not has_empathetic_phrase:
356
+ tamil_response = f"{empathetic_phrases[0]}. {tamil_response}"
357
+
358
+ # Step 5: Convert to speech
359
+ audio_file = "response_audio.mp3"
360
+ audio_data = text_to_speech(tamil_response, audio_file)
361
+
362
+ # Log success or failure of audio generation
363
+ if audio_data:
364
+ logger.info("Audio response generated successfully")
365
+ else:
366
+ logger.warning("Failed to generate audio response")
367
+
368
+ return {
369
+ "original_query": tamil_text,
370
+ "translated_query": english_query,
371
+ "english_response": english_response,
372
+ "tamil_response": tamil_response,
373
+ "audio_file": audio_file if audio_data else None
374
+ }