Update app.py
Browse files
app.py
CHANGED
|
@@ -254,8 +254,17 @@ def analyze_symptoms(text):
|
|
| 254 |
elif not all("label" in item and "score" in item for item in flattened):
|
| 255 |
logger.warning(f"Missing label or score in flattened result: {flattened}")
|
| 256 |
else:
|
| 257 |
-
|
| 258 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 259 |
elif isinstance(result, dict):
|
| 260 |
logger.debug("Model returned single dictionary")
|
| 261 |
if "label" in result and "score" in result:
|
|
@@ -394,4 +403,95 @@ async def analyze_voice(audio_file, language="en", user_id="anonymous", consent_
|
|
| 394 |
if prediction == "No health condition detected"
|
| 395 |
else f"Possible {prediction.lower()} detected based on symptoms like '{transcription.lower()}', consult a doctor. This is not a medical diagnosis."
|
| 396 |
)
|
| 397 |
-
logger.info(f"Feedback: {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 254 |
elif not all("label" in item and "score" in item for item in flattened):
|
| 255 |
logger.warning(f"Missing label or score in flattened result: {flattened}")
|
| 256 |
else:
|
| 257 |
+
# Sort by score (descending) and take the highest
|
| 258 |
+
valid_items = [
|
| 259 |
+
item for item in flattened
|
| 260 |
+
if isinstance(item, dict) and "label" in item and "score" in item
|
| 261 |
+
and isinstance(item["label"], str)
|
| 262 |
+
and isinstance(item["score"], (int, float)) and 0 <= item["score"] <= 1
|
| 263 |
+
]
|
| 264 |
+
if valid_items:
|
| 265 |
+
sorted_items = sorted(valid_items, key=lambda x: x["score"], reverse=True)
|
| 266 |
+
prediction = sorted_items[0]["label"]
|
| 267 |
+
score = sorted_items[0]["score"]
|
| 268 |
elif isinstance(result, dict):
|
| 269 |
logger.debug("Model returned single dictionary")
|
| 270 |
if "label" in result and "score" in result:
|
|
|
|
| 403 |
if prediction == "No health condition detected"
|
| 404 |
else f"Possible {prediction.lower()} detected based on symptoms like '{transcription.lower()}', consult a doctor. This is not a medical diagnosis."
|
| 405 |
)
|
| 406 |
+
logger.info(f"Feedback: {feedback}, Transcription: {transcription}, Prediction: {prediction}, Score: {score:.4f}")
|
| 407 |
+
|
| 408 |
+
# Save to Salesforce
|
| 409 |
+
save_to_salesforce(user_id, transcription, prediction, score, feedback, consent_granted)
|
| 410 |
+
|
| 411 |
+
try:
|
| 412 |
+
os.remove(audio_file)
|
| 413 |
+
logger.debug(f"Deleted audio file: {audio_file}")
|
| 414 |
+
except Exception as e:
|
| 415 |
+
logger.error(f"Failed to delete audio file: {str(e)}")
|
| 416 |
+
|
| 417 |
+
# Speak response
|
| 418 |
+
await speak_response(feedback)
|
| 419 |
+
|
| 420 |
+
return feedback
|
| 421 |
+
except Exception as e:
|
| 422 |
+
logger.error(f"Voice analysis failed: {str(e)}")
|
| 423 |
+
return f"Error: {str(e)}"
|
| 424 |
+
|
| 425 |
+
async def test_with_sample_audio(language="en", user_id="anonymous", consent_granted=True):
|
| 426 |
+
"""Test with synthetic audio."""
|
| 427 |
+
temp_dir = os.path.join(tempfile.gettempdir(), "audio_samples")
|
| 428 |
+
if not ensure_writable_dir(temp_dir):
|
| 429 |
+
fallback_dir = os.path.join(os.getcwd(), "temp_audio_samples")
|
| 430 |
+
if not ensure_writable_dir(fallback_dir):
|
| 431 |
+
logger.error(f"Temp directories {temp_dir} and {fallback_dir} not writable")
|
| 432 |
+
return f"Error: Temp directories not writable"
|
| 433 |
+
temp_dir = fallback_dir
|
| 434 |
+
|
| 435 |
+
sample_audio_path = os.path.join(temp_dir, "dummy_test.wav")
|
| 436 |
+
logger.info(f"Generating synthetic audio at: {sample_audio_path}")
|
| 437 |
+
sr = 16000
|
| 438 |
+
t = np.linspace(0, 2, 2 * sr)
|
| 439 |
+
freq_mod = 440 + 10 * np.sin(2 * np.pi * 0.5 * t)
|
| 440 |
+
amplitude_mod = 0.5 + 0.1 * np.sin(2 * np.pi * 0.3 * t)
|
| 441 |
+
noise = 0.01 * np.random.normal(0, 1, len(t))
|
| 442 |
+
dummy_audio = amplitude_mod * np.sin(2 * np.pi * freq_mod * t) + noise
|
| 443 |
+
try:
|
| 444 |
+
soundfile.write(dummy_audio, sr, sample_audio_path)
|
| 445 |
+
logger.info(f"Generated synthetic audio: {sample_audio_path}")
|
| 446 |
+
except Exception as e:
|
| 447 |
+
logger.error(f"Failed to write synthetic audio: {str(e)}")
|
| 448 |
+
return f"Error: Failed to generate synthetic audio: {str(e)}"
|
| 449 |
+
|
| 450 |
+
if not os.path.exists(sample_audio_path):
|
| 451 |
+
logger.error(f"Synthetic audio not created: {sample_audio_path}")
|
| 452 |
+
return f"Error: Synthetic audio not created: {sample_audio_path}"
|
| 453 |
+
|
| 454 |
+
mock_transcription = "I have a cough and sore throat"
|
| 455 |
+
logger.info(f"Mock transcription: {mock_transcription}")
|
| 456 |
+
prediction, score = analyze_symptoms(mock_transcription)
|
| 457 |
+
feedback = (
|
| 458 |
+
"No health condition detected, consult a doctor if symptoms persist. This is not a medical diagnosis."
|
| 459 |
+
if prediction == "No health condition detected"
|
| 460 |
+
else f"Possible {prediction.lower()} detected based on symptoms like '{mock_transcription.lower()}', consult a doctor. This is not a medical diagnosis."
|
| 461 |
+
)
|
| 462 |
+
logger.info(f"Test feedback: {feedback}, Prediction: {prediction}, Score: {score:.4f}")
|
| 463 |
+
|
| 464 |
+
# Save to Salesforce
|
| 465 |
+
save_to_salesforce(user_id, mock_transcription, prediction, score, feedback, consent_granted)
|
| 466 |
+
|
| 467 |
+
try:
|
| 468 |
+
os.remove(sample_audio_path)
|
| 469 |
+
logger.debug(f"Deleted test audio: {sample_audio_path}")
|
| 470 |
+
except Exception:
|
| 471 |
+
pass
|
| 472 |
+
return feedback
|
| 473 |
+
|
| 474 |
+
async def voicebot_interface(audio_file, language="en", user_id="anonymous", consent_granted=True):
|
| 475 |
+
"""Gradio interface wrapper."""
|
| 476 |
+
return await analyze_voice(audio_file, language, user_id, consent_granted)
|
| 477 |
+
|
| 478 |
+
# Gradio interface
|
| 479 |
+
iface = gr.Interface(
|
| 480 |
+
fn=voicebot_interface,
|
| 481 |
+
inputs=[
|
| 482 |
+
gr.Audio(type="filepath", label="Record or Upload Voice (WAV, MP3, FLAC, 1+ sec)"),
|
| 483 |
+
gr.Dropdown(["en", "es", "hi", "zh"], label="Language", value="en"),
|
| 484 |
+
gr.Textbox(label="User ID (optional)", value="anonymous"),
|
| 485 |
+
gr.Checkbox(label="Consent to store data", value=True)
|
| 486 |
+
],
|
| 487 |
+
outputs=gr.Textbox(label="Health Assessment Feedback"),
|
| 488 |
+
title="Smart Voicebot for Public Health",
|
| 489 |
+
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."
|
| 490 |
+
)
|
| 491 |
+
|
| 492 |
+
if __name__ == "__main__":
|
| 493 |
+
logger.info("Starting Voice Health Analyzer")
|
| 494 |
+
# Test with synthetic audio
|
| 495 |
+
loop = asyncio.get_event_loop()
|
| 496 |
+
print(loop.run_until_complete(test_with_sample_audio()))
|
| 497 |
+
iface.launch(server_name="0.0.0.0", server_port=7860)
|