Update app.py
Browse files
app.py
CHANGED
|
@@ -235,30 +235,32 @@ def analyze_symptoms(text):
|
|
| 235 |
elif isinstance(result, (tuple, list)):
|
| 236 |
# Flatten nested tuples/lists
|
| 237 |
flattened = []
|
| 238 |
-
def flatten(item):
|
|
|
|
|
|
|
|
|
|
| 239 |
if isinstance(item, (tuple, list)):
|
| 240 |
for subitem in item:
|
| 241 |
-
flatten(subitem)
|
| 242 |
-
|
| 243 |
flattened.append(item)
|
|
|
|
|
|
|
| 244 |
flatten(result)
|
| 245 |
-
|
| 246 |
-
if not result:
|
| 247 |
logger.warning("Flattened model output is empty")
|
| 248 |
-
elif isinstance(
|
| 249 |
-
|
| 250 |
-
|
| 251 |
-
|
| 252 |
-
|
| 253 |
-
|
| 254 |
-
|
| 255 |
-
score = result[0]["score"]
|
| 256 |
elif isinstance(result, dict):
|
| 257 |
-
logger.debug("Model returned single dictionary
|
| 258 |
-
result
|
| 259 |
-
|
| 260 |
-
|
| 261 |
-
score = result[0]["score"]
|
| 262 |
else:
|
| 263 |
logger.warning(f"Missing label or score in dictionary: {result}")
|
| 264 |
|
|
@@ -390,97 +392,6 @@ async def analyze_voice(audio_file, language="en", user_id="anonymous", consent_
|
|
| 390 |
feedback = (
|
| 391 |
"No health condition detected, consult a doctor if symptoms persist. This is not a medical diagnosis."
|
| 392 |
if prediction == "No health condition detected"
|
| 393 |
-
else f"Possible {prediction.lower()} detected, consult a doctor. This is not a medical diagnosis."
|
| 394 |
)
|
| 395 |
-
logger.info(f"Feedback: {
|
| 396 |
-
|
| 397 |
-
# Save to Salesforce
|
| 398 |
-
save_to_salesforce(user_id, transcription, prediction, score, feedback, consent_granted)
|
| 399 |
-
|
| 400 |
-
try:
|
| 401 |
-
os.remove(audio_file)
|
| 402 |
-
logger.debug(f"Deleted audio file: {audio_file}")
|
| 403 |
-
except Exception as e:
|
| 404 |
-
logger.error(f"Failed to delete audio file: {str(e)}")
|
| 405 |
-
|
| 406 |
-
# Speak response
|
| 407 |
-
await speak_response(feedback)
|
| 408 |
-
|
| 409 |
-
return feedback
|
| 410 |
-
except Exception as e:
|
| 411 |
-
logger.error(f"Voice analysis failed: {str(e)}")
|
| 412 |
-
return f"Error: {str(e)}"
|
| 413 |
-
|
| 414 |
-
async def test_with_sample_audio(language="en", user_id="anonymous", consent_granted=True):
|
| 415 |
-
"""Test with synthetic audio."""
|
| 416 |
-
temp_dir = os.path.join(tempfile.gettempdir(), "audio_samples")
|
| 417 |
-
if not ensure_writable_dir(temp_dir):
|
| 418 |
-
fallback_dir = os.path.join(os.getcwd(), "temp_audio_samples")
|
| 419 |
-
if not ensure_writable_dir(fallback_dir):
|
| 420 |
-
logger.error(f"Temp directories {temp_dir} and {fallback_dir} not writable")
|
| 421 |
-
return f"Error: Temp directories not writable"
|
| 422 |
-
temp_dir = fallback_dir
|
| 423 |
-
|
| 424 |
-
sample_audio_path = os.path.join(temp_dir, "dummy_test.wav")
|
| 425 |
-
logger.info(f"Generating synthetic audio at: {sample_audio_path}")
|
| 426 |
-
sr = 16000
|
| 427 |
-
t = np.linspace(0, 2, 2 * sr)
|
| 428 |
-
freq_mod = 440 + 10 * np.sin(2 * np.pi * 0.5 * t)
|
| 429 |
-
amplitude_mod = 0.5 + 0.1 * np.sin(2 * np.pi * 0.3 * t)
|
| 430 |
-
noise = 0.01 * np.random.normal(0, 1, len(t))
|
| 431 |
-
dummy_audio = amplitude_mod * np.sin(2 * np.pi * freq_mod * t) + noise
|
| 432 |
-
try:
|
| 433 |
-
soundfile.write(dummy_audio, sr, sample_audio_path)
|
| 434 |
-
logger.info(f"Generated synthetic audio: {sample_audio_path}")
|
| 435 |
-
except Exception as e:
|
| 436 |
-
logger.error(f"Failed to write synthetic audio: {str(e)}")
|
| 437 |
-
return f"Error: Failed to generate synthetic audio: {str(e)}"
|
| 438 |
-
|
| 439 |
-
if not os.path.exists(sample_audio_path):
|
| 440 |
-
logger.error(f"Synthetic audio not created: {sample_audio_path}")
|
| 441 |
-
return f"Error: Synthetic audio not created: {sample_audio_path}"
|
| 442 |
-
|
| 443 |
-
mock_transcription = "I have a cough and sore throat"
|
| 444 |
-
logger.info(f"Mock transcription: {mock_transcription}")
|
| 445 |
-
prediction, score = analyze_symptoms(mock_transcription)
|
| 446 |
-
feedback = (
|
| 447 |
-
"No health condition detected, consult a doctor if symptoms persist. This is not a medical diagnosis."
|
| 448 |
-
if prediction == "No health condition detected"
|
| 449 |
-
else f"Possible {prediction.lower()} detected, consult a doctor. This is not a medical diagnosis."
|
| 450 |
-
)
|
| 451 |
-
logger.info(f"Test feedback: {feedback}, Prediction: {prediction}, Score: {score:.4f}")
|
| 452 |
-
|
| 453 |
-
# Save to Salesforce
|
| 454 |
-
save_to_salesforce(user_id, mock_transcription, prediction, score, feedback, consent_granted)
|
| 455 |
-
|
| 456 |
-
try:
|
| 457 |
-
os.remove(sample_audio_path)
|
| 458 |
-
logger.debug(f"Deleted test audio: {sample_audio_path}")
|
| 459 |
-
except Exception:
|
| 460 |
-
pass
|
| 461 |
-
return feedback
|
| 462 |
-
|
| 463 |
-
async def voicebot_interface(audio_file, language="en", user_id="anonymous", consent_granted=True):
|
| 464 |
-
"""Gradio interface wrapper."""
|
| 465 |
-
return await analyze_voice(audio_file, language, user_id, consent_granted)
|
| 466 |
-
|
| 467 |
-
# Gradio interface
|
| 468 |
-
iface = gr.Interface(
|
| 469 |
-
fn=voicebot_interface,
|
| 470 |
-
inputs=[
|
| 471 |
-
gr.Audio(type="filepath", label="Record or Upload Voice (WAV, MP3, FLAC, 1+ sec)"),
|
| 472 |
-
gr.Dropdown(["en", "es", "hi", "zh"], label="Language", value="en"),
|
| 473 |
-
gr.Textbox(label="User ID (optional)", value="anonymous"),
|
| 474 |
-
gr.Checkbox(label="Consent to store data", value=True)
|
| 475 |
-
],
|
| 476 |
-
outputs=gr.Textbox(label="Health Assessment Feedback"),
|
| 477 |
-
title="Smart Voicebot for Public Health",
|
| 478 |
-
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."
|
| 479 |
-
)
|
| 480 |
-
|
| 481 |
-
if __name__ == "__main__":
|
| 482 |
-
logger.info("Starting Voice Health Analyzer")
|
| 483 |
-
# Test with synthetic audio
|
| 484 |
-
loop = asyncio.get_event_loop()
|
| 485 |
-
print(loop.run_until_complete(test_with_sample_audio()))
|
| 486 |
-
iface.launch(server_name="0.0.0.0", server_port=7860)
|
|
|
|
| 235 |
elif isinstance(result, (tuple, list)):
|
| 236 |
# Flatten nested tuples/lists
|
| 237 |
flattened = []
|
| 238 |
+
def flatten(item, depth=0, max_depth=10):
|
| 239 |
+
if depth > max_depth:
|
| 240 |
+
logger.warning(f"Max recursion depth exceeded: {item}")
|
| 241 |
+
return
|
| 242 |
if isinstance(item, (tuple, list)):
|
| 243 |
for subitem in item:
|
| 244 |
+
flatten(subitem, depth + 1, max_depth)
|
| 245 |
+
elif isinstance(item, dict):
|
| 246 |
flattened.append(item)
|
| 247 |
+
else:
|
| 248 |
+
logger.warning(f"Skipping non-dict item: {item}")
|
| 249 |
flatten(result)
|
| 250 |
+
if not flattened:
|
|
|
|
| 251 |
logger.warning("Flattened model output is empty")
|
| 252 |
+
elif not all(isinstance(item, dict) for item in flattened):
|
| 253 |
+
logger.warning(f"Non-dictionary items in flattened result: {flattened}")
|
| 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 |
+
prediction = flattened[0]["label"]
|
| 258 |
+
score = flattened[0]["score"]
|
|
|
|
| 259 |
elif isinstance(result, dict):
|
| 260 |
+
logger.debug("Model returned single dictionary")
|
| 261 |
+
if "label" in result and "score" in result:
|
| 262 |
+
prediction = result["label"]
|
| 263 |
+
score = result["score"]
|
|
|
|
| 264 |
else:
|
| 265 |
logger.warning(f"Missing label or score in dictionary: {result}")
|
| 266 |
|
|
|
|
| 392 |
feedback = (
|
| 393 |
"No health condition detected, consult a doctor if symptoms persist. This is not a medical diagnosis."
|
| 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: {fat the BRD requirements, and ensures accurate feedback reflecting the voice input. Let me know how it performs, and share any logs or errors if issues persist!
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|