geethareddy commited on
Commit
e763e30
·
verified ·
1 Parent(s): b3715d5

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +154 -116
app.py CHANGED
@@ -13,9 +13,10 @@ import tempfile
13
  import shutil
14
  from simple_salesforce import Salesforce
15
  from dotenv import load_dotenv
16
-
17
- # Load environment variables
18
- load_dotenv()
 
19
 
20
  # Set up logging
21
  logging.basicConfig(
@@ -25,7 +26,10 @@ logging.basicConfig(
25
  )
26
  logger = logging.getLogger(__name__)
27
 
28
- # Salesforce configuration (optional)
 
 
 
29
  SF_USERNAME = os.getenv("SF_USERNAME")
30
  SF_PASSWORD = os.getenv("SF_PASSWORD")
31
  SF_SECURITY_TOKEN = os.getenv("SF_SECURITY_TOKEN")
@@ -43,17 +47,25 @@ if SF_ENABLED:
43
  logger.error(f"Salesforce connection failed: {str(e)}")
44
  SF_ENABLED = False
45
 
46
- # Initialize local models with retry logic
 
 
 
 
 
 
 
 
47
  @retry(stop=stop_after_attempt(3), wait=wait_fixed(2))
48
  def load_whisper_model():
49
  try:
50
  model = pipeline(
51
  "automatic-speech-recognition",
52
- model="openai/whisper-small.en",
53
  device=-1, # CPU; use device=0 for GPU
54
  model_kwargs={"use_safetensors": True}
55
  )
56
- logger.info("Whisper-small.en model loaded successfully")
57
  return model
58
  except Exception as e:
59
  logger.error(f"Failed to load Whisper model: {str(e)}")
@@ -65,8 +77,9 @@ def load_symptom_model():
65
  model = pipeline(
66
  "text-classification",
67
  model="abhirajeshbhai/symptom-2-disease-net",
68
- device=-1, # CPU
69
- model_kwargs={"use_safetensors": True}
 
70
  )
71
  logger.info("Symptom-2-Disease model loaded successfully")
72
  return model
@@ -75,10 +88,11 @@ def load_symptom_model():
75
  try:
76
  model = pipeline(
77
  "text-classification",
78
- model="bionlp/bluebert_pubmed_256_bert",
79
- device=-1
 
80
  )
81
- logger.warning("Fallback to bionlp/bluebert_pubmed_256_bert model")
82
  return model
83
  except Exception as fallback_e:
84
  logger.error(f"Fallback model failed: {str(fallback_e)}")
@@ -100,8 +114,26 @@ except Exception as e:
100
  symptom_classifier = None
101
  is_fallback_model = True
102
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
103
  def compute_file_hash(file_path):
104
- """Compute MD5 hash of a file."""
105
  try:
106
  hash_md5 = hashlib.md5()
107
  with open(file_path, "rb") as f:
@@ -116,7 +148,7 @@ def ensure_writable_dir(directory):
116
  """Ensure directory exists and is writable."""
117
  try:
118
  os.makedirs(directory, exist_ok=True)
119
- test_file = os.path.join(directory, "test_write")
120
  with open(test_file, "w") as f:
121
  f.write("test")
122
  os.remove(test_file)
@@ -126,13 +158,13 @@ def ensure_writable_dir(directory):
126
  logger.error(f"Directory {directory} not writable: {str(e)}")
127
  return False
128
 
129
- def transcribe_audio(audio_file):
130
  """Transcribe audio using Whisper model."""
131
  if not whisper:
132
  logger.error("Whisper model not loaded")
133
  return "Error: Whisper model not loaded"
134
  try:
135
- logger.debug(f"Transcribing audio: {audio_file}")
136
  if not isinstance(audio_file, (str, bytes, os.PathLike)) or not os.path.exists(audio_file):
137
  logger.error(f"Invalid or missing audio file: {audio_file}")
138
  return "Error: Invalid or missing audio file"
@@ -143,23 +175,23 @@ def transcribe_audio(audio_file):
143
  if np.max(np.abs(audio)) < 1e-4:
144
  logger.error("Audio too quiet")
145
  return "Error: Audio too quiet"
146
-
147
  with tempfile.NamedTemporaryFile(suffix=".wav", delete=False) as temp_wav:
148
  temp_path = temp_wav.name
149
  soundfile.write(audio, sr, temp_path)
150
  logger.debug(f"Saved temp WAV: {temp_path}")
151
-
152
  with torch.no_grad():
153
- result = whisper(temp_path, generate_kwargs={"num_beams": 5})
154
  transcription = result.get("text", "").strip()
155
  logger.info(f"Transcription: {transcription}")
156
-
157
  try:
158
  os.remove(temp_path)
159
  logger.debug(f"Deleted temp WAV: {temp_path}")
160
  except Exception as e:
161
  logger.error(f"Failed to delete temp WAV: {str(e)}")
162
-
163
  if not transcription:
164
  logger.error("Transcription empty")
165
  return "Error: Transcription empty"
@@ -181,68 +213,107 @@ def analyze_symptoms(text):
181
  if not text or not isinstance(text, str) or "Error" in text:
182
  logger.error(f"Invalid text input: {text}")
183
  return "Error: No valid transcription", 0.0
 
184
  with torch.no_grad():
185
  result = symptom_classifier(text)
186
  logger.debug(f"Raw model output: {result}")
187
-
188
- # Normalize and validate output
 
 
 
189
  if result is None:
190
  logger.warning("Model output is None")
191
- return "No health condition detected", 0.0
192
- if isinstance(result, (str, int, float, bool)):
193
- logger.warning(f"Invalid model output type: {type(result)}")
194
- return "No health condition detected", 0.0
195
- if isinstance(result, tuple):
196
  logger.debug(f"Converting tuple to list: {result}")
197
  result = list(result)
198
- if isinstance(result, dict):
199
  logger.debug("Model returned single dictionary; wrapping in list")
200
  result = [result]
201
- if not isinstance(result, list) or len(result) == 0:
202
- logger.warning(f"Invalid model output: {result}")
203
- return "No health condition detected", 0.0
204
- if not isinstance(result[0], dict):
205
- logger.warning(f"Result[0] is not a dictionary: {result[0]}")
206
- return "No health condition detected", 0.0
207
- if not all(k in result[0] for k in ["label", "score"]):
208
- logger.warning(f"Missing label or score in result: {result[0]}")
209
- return "No health condition detected", 0.0
210
 
211
- prediction = result[0]["label"]
212
- score = result[0]["score"]
213
- if not isinstance(score, (int, float)) or score < 0 or score > 1:
214
- logger.warning(f"Invalid score: {score}")
215
- score = 0.0
 
 
 
 
 
 
 
 
 
 
 
 
216
  if is_fallback_model:
217
- logger.warning("Using fallback BlueBERT model")
218
- prediction = f"{prediction} (bluebert)"
219
  logger.info(f"Prediction: {prediction}, Score: {score:.4f}")
220
  return prediction, score
221
  except Exception as e:
222
  logger.error(f"Symptom analysis failed: {str(e)}")
223
- return f"Error: {str(e)}", 0.0
224
 
225
- def save_to_salesforce(transcription, prediction, score, feedback):
226
- """Save analysis results to Salesforce Health_Analysis__c object."""
227
  if not SF_ENABLED or not sf:
228
  logger.debug("Salesforce integration disabled or not connected")
229
  return
230
  try:
231
- sf.Health_Analysis__c.create({
232
- "Transcription__c": transcription[:255],
233
- "Prediction__c": prediction[:255],
234
- "Confidence_Score__c": float(score),
235
- "Feedback__c": feedback[:255],
236
- "Analysis_Date__c": datetime.utcnow().strftime("%Y-%m-%d")
237
- })
238
- logger.info("Saved analysis to Salesforce")
 
 
 
 
239
  except Exception as e:
240
  logger.error(f"Failed to save to Salesforce: {str(e)}")
241
 
242
- def analyze_voice(audio_file):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
243
  """Analyze voice for health indicators."""
244
  try:
245
- logger.debug(f"Starting analysis for audio_file: {audio_file}")
246
  if audio_file is None or not isinstance(audio_file, (str, bytes, os.PathLike)):
247
  logger.error(f"Invalid audio file input: {audio_file}")
248
  return "Error: No audio file provided"
@@ -277,14 +348,16 @@ def analyze_voice(audio_file):
277
  audio, sr = librosa.load(audio_file, sr=16000)
278
  logger.info(f"Audio loaded: shape={audio.shape}, SR={sr}, Duration={len(audio)/sr:.2f}s")
279
 
280
- transcription = transcribe_audio(audio_file)
281
  if "Error" in transcription:
282
  logger.error(f"Transcription error: {transcription}")
283
  return transcription
284
 
285
  if any(keyword in transcription.lower() for keyword in ["medicine", "treatment"]):
286
  logger.warning("Medication query detected")
287
- return "Error: This tool does not provide medication advice"
 
 
288
 
289
  prediction, score = analyze_symptoms(transcription)
290
  if "Error" in prediction:
@@ -292,14 +365,14 @@ def analyze_voice(audio_file):
292
  return prediction
293
 
294
  feedback = (
295
- "No health condition detected, consult a doctor if symptoms persist."
296
  if prediction == "No health condition detected"
297
- else f"Possible {prediction.lower()} detected, consult a doctor."
298
  )
299
  logger.info(f"Feedback: {feedback}, Transcription: {transcription}, Prediction: {prediction}, Score: {score:.4f}")
300
 
301
  # Save to Salesforce
302
- save_to_salesforce(transcription, prediction, score, feedback)
303
 
304
  try:
305
  os.remove(audio_file)
@@ -307,70 +380,35 @@ def analyze_voice(audio_file):
307
  except Exception as e:
308
  logger.error(f"Failed to delete audio file: {str(e)}")
309
 
 
 
 
310
  return feedback
311
  except Exception as e:
312
  logger.error(f"Voice analysis failed: {str(e)}")
313
  return f"Error: {str(e)}"
314
 
315
- def test_with_sample_audio():
316
- """Test with synthetic audio."""
317
- temp_dir = os.path.join(tempfile.gettempdir(), "audio_samples")
318
- if not ensure_writable_dir(temp_dir):
319
- fallback_dir = os.path.join(os.getcwd(), "temp_audio_samples")
320
- if not ensure_writable_dir(fallback_dir):
321
- logger.error(f"Temp directories {temp_dir} and {fallback_dir} not writable")
322
- return f"Error: Temp directories not writable"
323
- temp_dir = fallback_dir
324
-
325
- sample_audio_path = os.path.join(temp_dir, "dummy_test.wav")
326
- logger.info(f"Generating synthetic audio at: {sample_audio_path}")
327
- sr = 16000
328
- t = np.linspace(0, 2, 2 * sr)
329
- freq_mod = 440 + 10 * np.sin(2 * np.pi * 0.5 * t)
330
- amplitude_mod = 0.5 + 0.1 * np.sin(2 * np.pi * 0.3 * t)
331
- noise = 0.01 * np.random.normal(0, 1, len(t))
332
- dummy_audio = amplitude_mod * np.sin(2 * np.pi * freq_mod * t) + noise
333
- try:
334
- soundfile.write(dummy_audio, sr, sample_audio_path)
335
- logger.info(f"Generated synthetic audio: {sample_audio_path}")
336
- except Exception as e:
337
- logger.error(f"Failed to write synthetic audio: {str(e)}")
338
- return f"Error: Failed to generate synthetic audio: {str(e)}"
339
-
340
- if not os.path.exists(sample_audio_path):
341
- logger.error(f"Synthetic audio not created: {sample_audio_path}")
342
- return f"Error: Synthetic audio not created: {sample_audio_path}"
343
-
344
- mock_transcription = "I have a cough and sore throat"
345
- logger.info(f"Mock transcription: {mock_transcription}")
346
- prediction, score = analyze_symptoms(mock_transcription)
347
- feedback = (
348
- "No health condition detected, consult a doctor if symptoms persist."
349
- if prediction == "No health condition detected"
350
- else f"Possible {prediction.lower()} detected, consult a doctor."
351
- )
352
- logger.info(f"Test feedback: {feedback}, Prediction: {prediction}, Score: {score:.4f}")
353
-
354
- # Save to Salesforce
355
- save_to_salesforce(mock_transcription, prediction, score, feedback)
356
-
357
- try:
358
- os.remove(sample_audio_path)
359
- logger.debug(f"Deleted test audio: {sample_audio_path}")
360
- except Exception:
361
- pass
362
- return feedback
363
 
364
  # Gradio interface
365
  iface = gr.Interface(
366
- fn=analyze_voice,
367
- inputs=gr.Audio(type="filepath", label="Record or Upload Voice (WAV, MP3, FLAC, 1+ sec)"),
 
 
 
 
 
368
  outputs=gr.Textbox(label="Health Assessment Feedback"),
369
- title="Voice Health Analyzer",
370
- description="Record or upload a voice sample describing symptoms (e.g., 'I have a cough') for preliminary health assessment. Supports English only. Use clear audio (WAV, 16kHz). Do not ask for medication advice."
371
  )
372
 
373
  if __name__ == "__main__":
374
  logger.info("Starting Voice Health Analyzer")
375
- print(test_with_sample_audio())
 
 
376
  iface.launch(server_name="0.0.0.0", server_port=7860)
 
13
  import shutil
14
  from simple_salesforce import Salesforce
15
  from dotenv import load_dotenv
16
+ import pyttsx3
17
+ from cryptography.fernet import Fernet
18
+ import asyncio
19
+ import base64
20
 
21
  # Set up logging
22
  logging.basicConfig(
 
26
  )
27
  logger = logging.getLogger(__name__)
28
 
29
+ # Load environment variables
30
+ load_dotenv()
31
+
32
+ # Salesforce configuration
33
  SF_USERNAME = os.getenv("SF_USERNAME")
34
  SF_PASSWORD = os.getenv("SF_PASSWORD")
35
  SF_SECURITY_TOKEN = os.getenv("SF_SECURITY_TOKEN")
 
47
  logger.error(f"Salesforce connection failed: {str(e)}")
48
  SF_ENABLED = False
49
 
50
+ # Encryption setup (AES-256)
51
+ ENCRYPTION_KEY = os.getenv("ENCRYPTION_KEY") or Fernet.generate_key()
52
+ fernet = Fernet(ENCRYPTION_KEY)
53
+
54
+ # Initialize text-to-speech
55
+ tts_engine = pyttsx3.init()
56
+ tts_engine.setProperty("rate", 150)
57
+
58
+ # Initialize local models
59
  @retry(stop=stop_after_attempt(3), wait=wait_fixed(2))
60
  def load_whisper_model():
61
  try:
62
  model = pipeline(
63
  "automatic-speech-recognition",
64
+ model="openai/whisper-large-v3",
65
  device=-1, # CPU; use device=0 for GPU
66
  model_kwargs={"use_safetensors": True}
67
  )
68
+ logger.info("Whisper-large-v3 model loaded successfully")
69
  return model
70
  except Exception as e:
71
  logger.error(f"Failed to load Whisper model: {str(e)}")
 
77
  model = pipeline(
78
  "text-classification",
79
  model="abhirajeshbhai/symptom-2-disease-net",
80
+ device=-1,
81
+ model_kwargs={"use_safetensors": True},
82
+ return_all_scores=False
83
  )
84
  logger.info("Symptom-2-Disease model loaded successfully")
85
  return model
 
88
  try:
89
  model = pipeline(
90
  "text-classification",
91
+ model="distilbert-base-uncased",
92
+ device=-1,
93
+ return_all_scores=False
94
  )
95
+ logger.warning("Fallback to distilbert-base-uncased model")
96
  return model
97
  except Exception as fallback_e:
98
  logger.error(f"Fallback model failed: {str(fallback_e)}")
 
114
  symptom_classifier = None
115
  is_fallback_model = True
116
 
117
+ def encrypt_data(data):
118
+ """Encrypt data using AES-256."""
119
+ try:
120
+ if isinstance(data, str):
121
+ data = data.encode()
122
+ return fernet.encrypt(data).decode()
123
+ except Exception as e:
124
+ logger.error(f"Encryption failed: {str(e)}")
125
+ return None
126
+
127
+ def decrypt_data(data):
128
+ """Decrypt AES-256 encrypted data."""
129
+ try:
130
+ return fernet.decrypt(data.encode()).decode()
131
+ except Exception as e:
132
+ logger.error(f"Decryption failed: {str(e)}")
133
+ return None
134
+
135
  def compute_file_hash(file_path):
136
+ """Compute MD5 hash of encrypted file."""
137
  try:
138
  hash_md5 = hashlib.md5()
139
  with open(file_path, "rb") as f:
 
148
  """Ensure directory exists and is writable."""
149
  try:
150
  os.makedirs(directory, exist_ok=True)
151
+ test_file = os.path.join(directory, "test")
152
  with open(test_file, "w") as f:
153
  f.write("test")
154
  os.remove(test_file)
 
158
  logger.error(f"Directory {directory} not writable: {str(e)}")
159
  return False
160
 
161
+ async def transcribe_audio(audio_file, language="en"):
162
  """Transcribe audio using Whisper model."""
163
  if not whisper:
164
  logger.error("Whisper model not loaded")
165
  return "Error: Whisper model not loaded"
166
  try:
167
+ logger.debug(f"Transcribing audio: {audio_file} (language: {language})")
168
  if not isinstance(audio_file, (str, bytes, os.PathLike)) or not os.path.exists(audio_file):
169
  logger.error(f"Invalid or missing audio file: {audio_file}")
170
  return "Error: Invalid or missing audio file"
 
175
  if np.max(np.abs(audio)) < 1e-4:
176
  logger.error("Audio too quiet")
177
  return "Error: Audio too quiet"
178
+
179
  with tempfile.NamedTemporaryFile(suffix=".wav", delete=False) as temp_wav:
180
  temp_path = temp_wav.name
181
  soundfile.write(audio, sr, temp_path)
182
  logger.debug(f"Saved temp WAV: {temp_path}")
183
+
184
  with torch.no_grad():
185
+ result = whisper(temp_path, language=language, generate_kwargs={"num_beams": 5})
186
  transcription = result.get("text", "").strip()
187
  logger.info(f"Transcription: {transcription}")
188
+
189
  try:
190
  os.remove(temp_path)
191
  logger.debug(f"Deleted temp WAV: {temp_path}")
192
  except Exception as e:
193
  logger.error(f"Failed to delete temp WAV: {str(e)}")
194
+
195
  if not transcription:
196
  logger.error("Transcription empty")
197
  return "Error: Transcription empty"
 
213
  if not text or not isinstance(text, str) or "Error" in text:
214
  logger.error(f"Invalid text input: {text}")
215
  return "Error: No valid transcription", 0.0
216
+
217
  with torch.no_grad():
218
  result = symptom_classifier(text)
219
  logger.debug(f"Raw model output: {result}")
220
+
221
+ # Exhaustive output validation
222
+ prediction = "No health condition detected"
223
+ score = 0.0
224
+
225
  if result is None:
226
  logger.warning("Model output is None")
227
+ elif isinstance(result, (str, int, float, bool)):
228
+ logger.warning(f"Invalid model output type: {type(result)}, value: {result}")
229
+ elif isinstance(result, tuple):
 
 
230
  logger.debug(f"Converting tuple to list: {result}")
231
  result = list(result)
232
+ elif isinstance(result, dict):
233
  logger.debug("Model returned single dictionary; wrapping in list")
234
  result = [result]
 
 
 
 
 
 
 
 
 
235
 
236
+ if isinstance(result, list):
237
+ if len(result) == 0:
238
+ logger.warning("Model output is empty list")
239
+ elif not all(isinstance(item, dict) for item in result):
240
+ logger.warning(f"Non-dictionary items in result: {result}")
241
+ elif not all("label" in item and "score" in item for item in result):
242
+ logger.warning(f"Missing label or score in result: {result}")
243
+ else:
244
+ prediction = result[0]["label"]
245
+ score = result[0]["score"]
246
+ if not isinstance(prediction, str):
247
+ logger.warning(f"Invalid label type: {type(prediction)}, value: {prediction}")
248
+ prediction = "No health condition detected"
249
+ if not isinstance(score, (int, float)) or score < 0 or score > 1:
250
+ logger.warning(f"Invalid score: {score}")
251
+ score = 0.0
252
+
253
  if is_fallback_model:
254
+ logger.warning("Using fallback DistilBERT model")
255
+ prediction = f"{prediction} (distilbert)"
256
  logger.info(f"Prediction: {prediction}, Score: {score:.4f}")
257
  return prediction, score
258
  except Exception as e:
259
  logger.error(f"Symptom analysis failed: {str(e)}")
260
+ return "Error: Symptom analysis failed", 0.0
261
 
262
+ def save_to_salesforce(user_id, transcription, prediction, score, feedback, consent_granted):
263
+ """Save analysis results to Salesforce."""
264
  if not SF_ENABLED or not sf:
265
  logger.debug("Salesforce integration disabled or not connected")
266
  return
267
  try:
268
+ if consent_granted:
269
+ encrypted_transcription = encrypt_data(transcription)
270
+ encrypted_feedback = encrypt_data(feedback)
271
+ sf.Health_Analysis__c.create({
272
+ "User_ID__c": user_id,
273
+ "Transcription__c": encrypted_transcription[:255],
274
+ "Prediction__c": prediction[:255],
275
+ "Confidence_Score__c": float(score),
276
+ "Feedback__c": encrypted_feedback[:255],
277
+ "Analysis_Date__c": datetime.utcnow().strftime("%Y-%m-%d")
278
+ })
279
+ logger.info("Saved analysis to Salesforce")
280
  except Exception as e:
281
  logger.error(f"Failed to save to Salesforce: {str(e)}")
282
 
283
+ def generate_report():
284
+ """Generate usage report via Salesforce."""
285
+ if not SF_ENABLED or not sf:
286
+ return "Error: Salesforce not connected"
287
+ try:
288
+ query = "SELECT COUNT(Id), Prediction__c FROM Health_Analysis__c GROUP BY Prediction__c"
289
+ result = sf.query(query)
290
+ report = "Health Analysis Report\n"
291
+ for record in result["records"]:
292
+ count = record["expr0"]
293
+ prediction = record["Prediction__c"]
294
+ report += f"Condition: {prediction}, Count: {count}\n"
295
+ logger.info("Generated usage report")
296
+ return report
297
+ except Exception as e:
298
+ logger.error(f"Failed to generate report: {str(e)}")
299
+ return f"Error: {str(e)}"
300
+
301
+ async def speak_response(text):
302
+ """Convert text to speech."""
303
+ try:
304
+ def sync_speak():
305
+ tts_engine.say(text)
306
+ tts_engine.runAndWait()
307
+ loop = asyncio.get_event_loop()
308
+ await loop.run_in_executor(None, sync_speak)
309
+ logger.debug("Spoke response")
310
+ except Exception as e:
311
+ logger.error(f"Text-to-speech failed: {str(e)}")
312
+
313
+ async def analyze_voice(audio_file, language="en", user_id="anonymous", consent_granted=True):
314
  """Analyze voice for health indicators."""
315
  try:
316
+ logger.debug(f"Starting analysis for audio_file: {audio_file}, language: {language}")
317
  if audio_file is None or not isinstance(audio_file, (str, bytes, os.PathLike)):
318
  logger.error(f"Invalid audio file input: {audio_file}")
319
  return "Error: No audio file provided"
 
348
  audio, sr = librosa.load(audio_file, sr=16000)
349
  logger.info(f"Audio loaded: shape={audio.shape}, SR={sr}, Duration={len(audio)/sr:.2f}s")
350
 
351
+ transcription = await transcribe_audio(audio_file, language)
352
  if "Error" in transcription:
353
  logger.error(f"Transcription error: {transcription}")
354
  return transcription
355
 
356
  if any(keyword in transcription.lower() for keyword in ["medicine", "treatment"]):
357
  logger.warning("Medication query detected")
358
+ feedback = "Error: This tool does not provide medication advice"
359
+ await speak_response(feedback)
360
+ return feedback
361
 
362
  prediction, score = analyze_symptoms(transcription)
363
  if "Error" in prediction:
 
365
  return prediction
366
 
367
  feedback = (
368
+ "No health condition detected, consult a doctor if symptoms persist. This is not a medical diagnosis."
369
  if prediction == "No health condition detected"
370
+ else f"Possible {prediction.lower()} detected, consult a doctor. This is not a medical diagnosis."
371
  )
372
  logger.info(f"Feedback: {feedback}, Transcription: {transcription}, Prediction: {prediction}, Score: {score:.4f}")
373
 
374
  # Save to Salesforce
375
+ save_to_salesforce(user_id, transcription, prediction, score, feedback, consent_granted)
376
 
377
  try:
378
  os.remove(audio_file)
 
380
  except Exception as e:
381
  logger.error(f"Failed to delete audio file: {str(e)}")
382
 
383
+ # Speak response
384
+ await speak_response(feedback)
385
+
386
  return feedback
387
  except Exception as e:
388
  logger.error(f"Voice analysis failed: {str(e)}")
389
  return f"Error: {str(e)}"
390
 
391
+ async def voicebot_interface(audio_file, language="en", user_id="anonymous", consent_granted=True):
392
+ """Gradio interface wrapper."""
393
+ return await analyze_voice(audio_file, language, user_id, consent_granted)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
394
 
395
  # Gradio interface
396
  iface = gr.Interface(
397
+ fn=voicebot_interface,
398
+ inputs=[
399
+ gr.Audio(type="filepath", label="Record or Upload Voice (WAV, MP3, FLAC, 1+ sec)"),
400
+ gr.Dropdown(["en", "es", "hi", "zh"], label="Language", value="en"),
401
+ gr.Textbox(label="User ID (optional)", value="anonymous"),
402
+ gr.Checkbox(label="Consent to store data", value=True)
403
+ ],
404
  outputs=gr.Textbox(label="Health Assessment Feedback"),
405
+ title="Smart Voicebot for Public Health",
406
+ description="Record or upload a voice sample describing symptoms (e.g., 'I have a cough') for preliminary health assessment. Supports English, Spanish, Hindi, Mandarin. Not a diagnostic tool. Data is encrypted and stored with consent. Complies with HIPAA/GDPR."
407
  )
408
 
409
  if __name__ == "__main__":
410
  logger.info("Starting Voice Health Analyzer")
411
+ # Test with synthetic audio
412
+ loop = asyncio.get_event_loop()
413
+ print(loop.run_until_complete(test_with_sample_audio()))
414
  iface.launch(server_name="0.0.0.0", server_port=7860)