"""
Module 1: Cross-Cultural Semantic Translator MVP
=================================================
A medical AI platform for translating cultural pain metaphors into structured medical ontologies.
"""
import gradio as gr
import json
import os
from typing import Dict, Tuple, Optional
# ============================================================================
# CONFIGURATION
# ============================================================================
OPENAI_API_KEY = os.getenv("OPENAI_API_KEY", "")
TRANSCRIPTION_MODE = "api"
OPENAI_MODEL = "gpt-5.2"
# ============================================================================
# SETUP
# ============================================================================
try:
from openai import OpenAI
client = OpenAI(api_key=OPENAI_API_KEY) if OPENAI_API_KEY else None
except ImportError:
print("ERROR: OpenAI library not installed.")
client = None
# ============================================================================
# SYSTEM PROMPT
# ============================================================================
MEDICAL_ANTHROPOLOGIST_PROMPT = """You are an expert Medical Anthropologist. Your goal is to translate cultural pain metaphors into structured medical ontologies. Do NOT act as a doctor making a final diagnosis. Analyze the patient's transcript and output a strict JSON object with these exact keys: 'literal_translation', 'metaphor_mapping', 'mcgill_pain_ontology', 'psychological_and_stoicism_flags', 'physician_action_note'. Make sure to include English and original language in metaphor_mapping for reference."""
# ============================================================================
# TRANSCRIPTION
# ============================================================================
def transcribe_audio(audio_path: Optional[str]) -> Tuple[str, str]:
if audio_path is None:
return "", "⚠️ No audio recorded."
if client is None:
return "", "❌ OpenAI client not initialized."
try:
with open(audio_path, "rb") as audio_file:
transcript = client.audio.transcriptions.create(
model="whisper-1",
file=audio_file,
response_format="text"
)
return transcript.strip(), "✓ Transcribed via OpenAI Whisper API"
except Exception as e:
return "", f"❌ Transcription error: {str(e)}"
# ============================================================================
# LLM ANALYSIS
# ============================================================================
def analyze_with_llm(transcription: str) -> Tuple[str, str]:
if not transcription or not client:
return "
''']
# Debug section
import json
raw_json = json.dumps(data, indent=2, ensure_ascii=False)
html_parts.append(f'''
🔍 Debug: Raw JSON
{raw_json}
''')
# 1. Literal Translation
if 'literal_translation' in data:
html_parts.append(f'''
📝 Patient's Description
"{data['literal_translation']}"
''')
# 2. Metaphor Mapping
if 'metaphor_mapping' in data:
metaphor = data['metaphor_mapping']
html_parts.append('''
🔗 Cultural Context
''')
def render_value(val, indent=0):
margin_left = indent * 20
if isinstance(val, dict):
items = []
for k, v in val.items():
k_readable = k.replace('_', ' ').title()
items.append(f'
{k_readable}:{render_value(v, indent+1)}
')
return ''.join(items)
elif isinstance(val, list):
if not val:
return '
None'
items_html = '
'
for item in val:
items_html += f'- {render_value(item, indent) if isinstance(item, (dict, list)) else str(item)}
'
items_html += '
'
return items_html
else:
return f'
{str(val)}'
html_parts.append(render_value(metaphor))
html_parts.append('
')
# 3. McGill Pain Ontology
if 'mcgill_pain_ontology' in data:
mcgill = data['mcgill_pain_ontology']
html_parts.append('''
🏥 McGill Pain Assessment
''')
field_icons = {
'location': '📍',
'temporal_pattern': '⏱️',
'intensity': '📊',
'quality_descriptors': '💭',
'associated_symptoms_to_query': '🔍',
'functional_impact_to_query': '🚶',
'pain_or_sensory_type': '🩺'
}
def render_mcgill(val, indent=1):
margin_left = indent * 20
if isinstance(val, dict):
items = []
for k, v in val.items():
k_readable = k.replace('_', ' ').title()
items.append(f'
{k_readable}:{render_mcgill(v, indent+1)}
')
return ''.join(items)
elif isinstance(val, list):
if not val:
return '
None specified'
return '
' + ', '.join(str(v) for v in val) + ''
else:
return f'
{str(val)}'
if isinstance(mcgill, list):
for item in mcgill:
if isinstance(item, dict):
for key, value in item.items():
key_readable = key.replace('_', ' ').title()
icon = field_icons.get(key, '•')
html_parts.append(f'
{icon} {key_readable}:{render_mcgill(value)}
')
else:
html_parts.append(f'
')
elif isinstance(mcgill, dict):
for key, value in mcgill.items():
key_readable = key.replace('_', ' ').title()
icon = field_icons.get(key, '•')
html_parts.append(f'
{icon} {key_readable}:{render_mcgill(value)}
')
else:
html_parts.append(f'
')
html_parts.append('
')
# 4. Psychological Flags
if 'psychological_and_stoicism_flags' in data:
psych = data['psychological_and_stoicism_flags']
html_parts.append('''
🧠 Psychological Assessment
''')
for key, value in psych.items():
key_readable = key.replace('_', ' ').title()
if isinstance(value, dict):
html_parts.append(f'
{key_readable}:
')
for sub_key, sub_value in value.items():
sub_key_readable = sub_key.replace('_', ' ').title()
html_parts.append(f'
• {sub_key_readable}: {sub_value}
')
else:
html_parts.append(f'
{key_readable}: {value}
')
html_parts.append('
')
# 5. Physician Action Note
if 'physician_action_note' in data:
html_parts.append(f'''
⚕️ Clinical Recommendations
{data['physician_action_note']}
''')
html_parts.append('
')
return ''.join(html_parts)
# ============================================================================
# MAIN PROCESSING
# ============================================================================
def process_patient_audio(audio) -> Tuple[str, str, str]:
try:
transcription, trans_status = transcribe_audio(audio)
if "Error" in trans_status or not transcription.strip():
return trans_status, transcription, "⚠️ Cannot analyze without transcription.
"
formatted_html, json_output = analyze_with_llm(transcription)
if "Error" in formatted_html:
return "❌ Analysis failed", transcription, formatted_html
return "✅ Analysis complete", transcription, formatted_html
except Exception as e:
import traceback
error_html = f"""
Analysis results will appear here...
')
gr.Markdown(f"""
---
**Configuration:** `API` mode | `{OPENAI_MODEL}`
**Deployed on:** [Hugging Face Spaces](https://huggingface.co/spaces/DIrtyCha/Module1demo)
""")
submit_btn.click(fn=process_patient_audio, inputs=[audio_input], outputs=[status_output, transcription_output, analysis_output])
return app
# ============================================================================
# MAIN
# ============================================================================
if __name__ == "__main__":
print("=" * 70)
print("🚀 Medical AI Semantic Translator MVP")
print("=" * 70)
if not OPENAI_API_KEY:
print("⚠️ WARNING: OPENAI_API_KEY not set!")
print(" Go to Settings → Repository Secrets")
else:
print("✅ OpenAI API key loaded")
print("=" * 70)
app = create_ui()
app.launch()