diff --git "a/src/interface/simplified_gradio_app.py" "b/src/interface/simplified_gradio_app.py"
--- "a/src/interface/simplified_gradio_app.py"
+++ "b/src/interface/simplified_gradio_app.py"
@@ -22,21 +22,19 @@ from dotenv import load_dotenv
load_dotenv()
import gradio as gr
-import uuid
-from datetime import datetime
-from typing import Dict, Any, Optional, List
+
+# Import modularized components
+from src.interface.session_manager import SimplifiedSessionData
+from src.interface import stats_handlers
+from src.interface import chat_handlers
+from src.interface import prompt_handlers
+from src.interface import verification_handlers
+from src.interface import profile_handlers
+from src.interface import model_handlers
from src.core.simplified_medical_app import SimplifiedMedicalApp
from src.core.spiritual_state import SpiritualState
-from src.interface.verification_ui import VerificationUIComponents
-from src.interface.chaplain_feedback_ui import ChaplainFeedbackUIComponents
from src.interface.enhanced_verification_interface import create_enhanced_verification_tab
-from src.core.test_datasets import TestDatasetManager
-from src.core.verification_models import VerificationSession, VerificationRecord, TestMessage
-from src.core.verification_store import JSONVerificationStore
-from src.core.verification_csv_exporter import VerificationCSVExporter
-from src.core.chaplain_models import ClassificationFlowResult, DistressIndicator, FollowUpQuestion
-from src.core.error_pattern_analyzer import ErrorPatternAnalyzer
from src.interface.help_content import HELP_CONTENT
try:
@@ -58,36 +56,7 @@ except ImportError:
return FEATURE_FLAGS.get(feature_name, False)
-class SimplifiedSessionData:
- """Container for user session data."""
-
- def __init__(self, session_id: str = None):
- self.session_id = session_id or str(uuid.uuid4())
- self.app_instance = SimplifiedMedicalApp()
- self.created_at = datetime.now().isoformat()
- self.last_activity = datetime.now().isoformat()
-
- # Set default patient info from profile
- self.app_instance.set_patient_info(name="Serhii", phone="(555) 123-4567")
-
- # Update clinical_background to match default profile
- from src.core.core_classes import ClinicalBackground
- self.app_instance.clinical_background = ClinicalBackground(
- patient_name="Serhii",
- age=52,
- conditions=["Atrial fibrillation", "Deep vein thrombosis", "Obesity", "Hypertension"],
- primary_goal="Weight reduction and cardiovascular fitness improvement",
- exercise_preferences=["Swimming", "Walking", "Light cardio"],
- exercise_limitations=["Anticoagulation therapy", "Post-thrombotic recovery"]
- )
-
- # Update conversation logger patient name
- if hasattr(self.app_instance, 'conversation_logger'):
- self.app_instance.conversation_logger.patient_name = "Serhii"
-
- def update_activity(self):
- """Update last activity timestamp."""
- self.last_activity = datetime.now().isoformat()
+
def create_simplified_interface():
@@ -607,1478 +576,8 @@ def create_simplified_interface():
# Event handlers
- def handle_message(message: str, history, session: SimplifiedSessionData):
- """Handle user message."""
- if session is None:
- session = SimplifiedSessionData()
-
- session.update_activity()
-
- # Apply per-session model overrides (if configured in Model Settings)
- custom_models = getattr(session, 'custom_models', None)
- if custom_models:
- session.app_instance.set_model_overrides(custom_models)
- else:
- session.app_instance.set_model_overrides({})
-
- # Apply per-session prompt overrides (if configured in Edit Prompts)
- custom_prompts = getattr(session, 'custom_prompts', None)
- if custom_prompts:
- session.app_instance.set_prompt_overrides(custom_prompts)
- else:
- session.app_instance.set_prompt_overrides({})
- new_history, status = session.app_instance.process_message(message, history)
-
- # Get updated conversation stats
- stats = get_conversation_stats(session)
-
- # Check for provider summary (RED flag case)
- provider_summary_text = ""
- spiritual_care_msg = ""
- show_provider_panel = False
- last_summary = session.app_instance.get_last_provider_summary()
-
- # Debug logging
- print(f"DEBUG: last_summary exists: {last_summary is not None}")
- if last_summary:
- print(f"DEBUG: summary patient: {last_summary.patient_name}")
- print(f"DEBUG: summary indicators: {last_summary.indicators}")
- provider_summary_text = session.app_instance.provider_summary_generator.format_for_display(last_summary)
-
- # Generate LLM-based spiritual care message
- try:
- spiritual_care_msg = session.app_instance.generate_spiritual_care_message(
- language="English",
- session_id=session.session_id
- )
- if not spiritual_care_msg:
- spiritual_care_msg = ""
- print(f"DEBUG: spiritual care message generated: {len(spiritual_care_msg)} chars")
- except Exception as e:
- print(f"DEBUG: Error generating spiritual care message: {e}")
- spiritual_care_msg = ""
-
- show_provider_panel = True
- print(f"DEBUG: formatted summary length: {len(provider_summary_text)}")
- print(f"DEBUG: show_provider_panel: {show_provider_panel}")
- else:
- print("DEBUG: No provider summary found")
-
- # Debug: print what we're returning
- print(f"DEBUG RETURN: show_panel={show_provider_panel}, text_len={len(provider_summary_text)}")
- if provider_summary_text:
- print(f"DEBUG RETURN: first 100 chars: {provider_summary_text[:100]}")
-
- # Format as HTML for display
- if provider_summary_text:
- import html
- escaped_text = html.escape(provider_summary_text)
- html_content = f"
{escaped_text}"
- else:
- html_content = "No summary available
"
-
- # Use gr.update for both visibility and value
- if not provider_summary_text:
- provider_summary_text = ""
- html_content = ""
-
- # Generate status message for provider summary
- if show_provider_panel and provider_summary_text:
- status_msg = f"""**š“ Provider Summary Generated**
-
-**Patient:** {session.app_instance.patient_info.get('name', 'Test Patient')}
-**Classification:** RED FLAG
-**Indicators:** {len(session.app_instance.get_last_provider_summary().indicators) if session.app_instance.get_last_provider_summary() else 0} distress indicators detected
-**Summary Length:** {len(provider_summary_text)} characters
-
-Use the **Download Summary** button below to access the complete provider summary for the spiritual care team."""
- else:
- status_msg = "No provider summary available"
-
- return (
- new_history,
- status,
- session,
- "",
- stats,
- gr.update(visible=show_provider_panel), # provider_summary_content visibility
- status_msg, # provider_summary_status content
- gr.update(value=html_content, visible=True) if show_provider_panel else gr.update(visible=False), # provider_summary_display content
- spiritual_care_msg # spiritual_care_message content
- )
-
- def handle_clear(session: SimplifiedSessionData):
- """
- Handle clear chat button.
-
- Resets entire session including:
- - Chat history
- - Spiritual monitoring state
- - Provider summary panel (hides and clears content)
- - Conversation statistics
- """
- if session is None:
- session = SimplifiedSessionData()
-
- session.update_activity()
- new_history, status = session.app_instance.reset_session()
-
- # Hide and clear provider summary panel
- return (
- new_history, # Clear chat history
- status, # Reset status
- session, # Updated session
- gr.update(visible=False), # Hide provider_summary_content group
- "No provider summary available", # Clear provider_summary_status
- "", # Clear provider_summary_display HTML
- "" # Clear spiritual_care_message
- )
-
- def get_status(session: SimplifiedSessionData):
- """Get current status and stats."""
- if session is None:
- return "ā Session not initialized", "No stats", gr.update(visible=False), "No provider summary available", "", ""
-
- session.update_activity()
- status_info = session.app_instance._get_status_info()
-
- # Get stats
- stats_text = get_conversation_stats(session)
-
- # Check for provider summary
- last_summary = session.app_instance.get_last_provider_summary()
- show_provider_panel = last_summary is not None
-
- provider_summary_text = ""
- spiritual_care_msg = ""
-
- if last_summary:
- provider_summary_text = session.app_instance.provider_summary_generator.format_for_display(last_summary)
-
- # Generate spiritual care message
- try:
- spiritual_care_msg = session.app_instance.generate_spiritual_care_message(
- language="English",
- session_id=session.session_id
- )
- if not spiritual_care_msg:
- spiritual_care_msg = ""
- except Exception as e:
- print(f"Error generating spiritual care message in get_status: {e}")
- spiritual_care_msg = ""
-
- if provider_summary_text:
- import html
- escaped_text = html.escape(provider_summary_text)
- html_content = f"{escaped_text}"
-
- status_msg = f"""**š“ Provider Summary Generated**
-
-**Patient:** {session.app_instance.patient_info.get('name', 'Test Patient')}
-**Classification:** RED FLAG
-**Indicators:** {len(last_summary.indicators)} distress indicators detected
-**Summary Length:** {len(provider_summary_text)} characters
-
-Use the **Download Summary** button below to access the complete provider summary for the spiritual care team."""
- else:
- html_content = ""
- status_msg = "No provider summary available"
-
- return (
- status_info,
- stats_text,
- gr.update(visible=show_provider_panel),
- status_msg,
- html_content,
- spiritual_care_msg
- )
-
- def send_example(example_text: str, history, session: SimplifiedSessionData):
- """Send example message."""
- return handle_message(example_text, history, session)
-
- def get_conversation_stats(session: SimplifiedSessionData):
- """Get conversation statistics."""
- if session is None or not hasattr(session.app_instance, 'conversation_logger'):
- return "No conversation yet"
-
- try:
- summary = session.app_instance.get_conversation_summary()
- if not summary or summary.get('total_exchanges', 0) == 0:
- return "No conversation yet"
-
- stats_text = f"""**š Conversation Statistics**
-
-**Messages:** {summary['total_exchanges']} exchanges
-**Duration:** {summary['session_duration_minutes']} minutes
+ # Handlers moved to modular modules
-**Classifications:**
-š¢ Green: {summary['classification_counts']['green']} ({summary['classification_percentages']['green']}%)
-š” Yellow: {summary['classification_counts']['yellow']} ({summary['classification_percentages']['yellow']}%)
-š“ Red: {summary['classification_counts']['red']} ({summary['classification_percentages']['red']}%)
-
-**Average Confidence:** {summary['average_confidence']}
-
-**Top Indicators:**"""
-
- for indicator, count in list(summary['top_indicators'].items())[:3]:
- stats_text += f"\n⢠{indicator}: {count}"
-
- return stats_text
-
- except Exception as e:
- return f"Error getting stats: {str(e)}"
-
- def download_conversation_json(session: SimplifiedSessionData):
- """Download conversation as JSON."""
- if session is None or not hasattr(session.app_instance, 'conversation_logger'):
- return None
-
- try:
- log_path = session.app_instance.get_conversation_log_path()
- return log_path
- except Exception as e:
- print(f"Error downloading JSON: {e}")
- return None
-
- def download_conversation_csv(session: SimplifiedSessionData):
- """Download conversation as CSV."""
- if session is None or not hasattr(session.app_instance, 'conversation_logger'):
- return None
-
- try:
- csv_path = session.app_instance.export_conversation_csv()
- return csv_path
- except Exception as e:
- print(f"Error downloading CSV: {e}")
- return None
-
- def open_verification_window(session: SimplifiedSessionData):
- """Open verification window for current conversation."""
- if session is None or not hasattr(session.app_instance, 'conversation_logger'):
- return """
- ā No conversation to verify
- Start a conversation first
-
"""
-
- try:
- # Check if conversation has any entries
- if not session.app_instance.conversation_logger.entries:
- return """
- ā ļø No conversation exchanges to verify
- Send some messages in the chat first
-
"""
-
- print(f"š Opening verification for {len(session.app_instance.conversation_logger.entries)} exchanges...")
-
- # Create verification session
- from src.core.conversation_verification import ConversationVerificationManager
- import json
- import os
- from datetime import datetime
-
- manager = ConversationVerificationManager()
- verification_session = manager.create_verification_session(
- session.app_instance.conversation_logger,
- "Medical Professional"
- )
-
- print(f"ā
Created verification session: {verification_session.session_id}")
-
- # HF Spaces / Gradio limitation:
- # Launching a *second* Gradio server from inside a running Gradio app is unreliable
- # and is currently causing the Button._id error in Spaces.
- # Instead, export the verification session to a JSON file that the user can download.
-
- export_dir = os.path.join(os.getcwd(), "verification_sessions")
- os.makedirs(export_dir, exist_ok=True)
-
- export_filename = f"verification_session_{datetime.utcnow().strftime('%Y%m%d_%H%M%S')}_{verification_session.session_id}.json"
- export_path = os.path.join(export_dir, export_filename)
-
- # Serialize to JSON in a resilient way (dataclasses / pydantic / plain python).
- def _to_dict(obj):
- if hasattr(obj, "model_dump"):
- return obj.model_dump()
- if hasattr(obj, "dict") and callable(getattr(obj, "dict")):
- return obj.dict()
- if hasattr(obj, "__dict__"):
- return obj.__dict__
- return str(obj)
-
- payload = {
- "session_id": verification_session.session_id,
- "patient_name": verification_session.patient_name,
- "verifier_name": verification_session.verifier_name,
- "start_time": verification_session.start_time.isoformat() if hasattr(verification_session, "start_time") else None,
- "verification_records": [
- {
- # Conversation verification records use `exchange_id`.
- # Keep a `record_id` alias for backward compatibility with older exports.
- "exchange_id": getattr(r, "exchange_id", None),
- "record_id": getattr(r, "exchange_id", None),
- "timestamp": r.timestamp.isoformat() if hasattr(r, "timestamp") else None,
- "user_message": r.user_message,
- "assistant_response": r.assistant_response,
- "original_classification": r.original_classification,
- "original_confidence": r.original_confidence,
- "original_indicators": r.original_indicators,
- "original_reasoning": r.original_reasoning,
- "is_correct": r.is_correct,
- "correct_classification": r.correct_classification,
- "correction_reason": r.correction_reason,
- "verifier_notes": r.verifier_notes,
- }
- for r in verification_session.verification_records
- ],
- }
-
- with open(export_path, "w", encoding="utf-8") as f:
- json.dump(payload, f, ensure_ascii=False, indent=2, default=_to_dict)
-
- print(f"ā
Verification session exported: {export_path}")
-
- return f"""
- ā
Verification session exported
- Exchanges: {len(verification_session.verification_records)}
- Download JSON from the app's files panel (or add a dedicated download button).
-
"""
-
- except Exception as e:
- print(f"ā Error opening verification: {str(e)}")
- import traceback
- traceback.print_exc()
-
- return f"""
- ā Error opening verification
- {str(e)}
-
"""
-
- # Prompt editing handlers
- def format_prompt_with_html(prompt_text: str) -> str:
- """Format prompt with HTML tags for better visualization."""
- import re
- import html
-
- # Escape HTML first to prevent injection
- formatted = html.escape(prompt_text)
-
- # Highlight XML-like opening tags
- formatted = re.sub(
- r'<([a-z_]+)(?:\s+[^>]*)?>',
- r'<\1>',
- formatted,
- flags=re.IGNORECASE
- )
-
- # Highlight XML-like closing tags
- formatted = re.sub(
- r'</([a-z_]+)>',
- r'</\1>',
- formatted,
- flags=re.IGNORECASE
- )
-
- # Highlight XML attributes
- formatted = re.sub(
- r'([a-z_]+)="([^"]+)"',
- r'\1="\2"',
- formatted,
- flags=re.IGNORECASE
- )
-
- # Highlight bullet points
- formatted = re.sub(
- r'^([-ā¢]\s+.+)$',
- r'\1
',
- formatted,
- flags=re.MULTILINE
- )
-
- # Highlight numbered lists
- formatted = re.sub(
- r'^(\d+\.\s+.+)$',
- r'\1
',
- formatted,
- flags=re.MULTILINE
- )
-
- # Highlight important keywords
- keywords = [
- 'IMPORTANT', 'CRITICAL', 'REQUIRED', 'MUST', 'SHALL',
- 'WARNING', 'NOTE', 'TASK', 'GOAL', 'OUTPUT', 'ANY'
- ]
- for keyword in keywords:
- formatted = re.sub(
- f'\\b({keyword})\\b',
- r'\1',
- formatted,
- flags=re.IGNORECASE
- )
-
- # Highlight JSON/code blocks
- formatted = re.sub(
- r'(\{[^}]+\})',
- r'\1',
- formatted
- )
-
- # Convert newlines to
for proper display
- formatted = formatted.replace('\n', '
')
-
- # Wrap in container with monospace font for code-like appearance
- formatted = f'{formatted}
'
-
- return formatted
-
- def _prompt_name_to_agent(prompt_name: str) -> str:
- """Map UI prompt selection to internal agent/prompt key."""
- mapping = {
- "š Spiritual Monitor (Classifier)": "SpiritualDistressAnalyzer",
- "š” Soft Spiritual Triage": "SoftSpiritualTriage",
- "š Triage Response Evaluator": "TriageResponseEvaluator",
- "š„ Medical Assistant": "MedicalAssistant",
- "𩺠Soft Medical Triage": "SoftMedicalTriage",
- }
- return mapping.get(prompt_name, prompt_name)
-
- def load_prompt(prompt_name: str, session: Optional[SimplifiedSessionData] = None):
- """Load selected prompt for editing using enhanced prompt editor."""
- try:
- from src.interface.enhanced_prompt_editor import EnhancedPromptEditor
-
- # Initialize enhanced editor
- editor = EnhancedPromptEditor()
-
- # Get session ID
- session_id = getattr(session, 'session_id', 'default_session') if session else 'default_session'
-
- # Use enhanced editor to load prompt
- prompt_content, info_html, status_html = editor.load_prompt_for_editing(prompt_name, session_id)
-
- return prompt_content, info_html, status_html
-
- except Exception as e:
- # Fallback to old system if enhanced editor fails
- logger.warning(f"Enhanced prompt editor failed, using fallback: {e}")
-
- from src.core.spiritual_monitor import SYSTEM_PROMPT_SPIRITUAL_MONITOR
- from src.core.soft_triage_manager import (
- SYSTEM_PROMPT_TRIAGE_QUESTION,
- SYSTEM_PROMPT_TRIAGE_EVALUATE
- )
- from src.config.prompts import (
- SYSTEM_PROMPT_MEDICAL_ASSISTANT,
- SYSTEM_PROMPT_SOFT_MEDICAL_TRIAGE
- )
-
- prompts = {
- "š Spiritual Monitor (Classifier)": SYSTEM_PROMPT_SPIRITUAL_MONITOR,
- "š” Soft Spiritual Triage": SYSTEM_PROMPT_TRIAGE_QUESTION,
- "š Triage Response Evaluator": SYSTEM_PROMPT_TRIAGE_EVALUATE,
- "š„ Medical Assistant": SYSTEM_PROMPT_MEDICAL_ASSISTANT,
- "𩺠Soft Medical Triage": SYSTEM_PROMPT_SOFT_MEDICAL_TRIAGE
- }
-
- prompt_text = prompts.get(prompt_name, "")
-
- info = f"""**Loaded:** {prompt_name}
-**Length:** {len(prompt_text)} characters
-**Status:** Fallback mode (enhanced editor unavailable)"""
-
- status = """
-
ā ļø Fallback Mode
-
Using basic prompt editor. Enhanced features unavailable.
-
"""
-
- return prompt_text, info, status
-
- def apply_prompt_changes(prompt_name: str, prompt_text: str, session: SimplifiedSessionData):
- """Apply custom prompt changes using enhanced prompt editor."""
- try:
- from src.interface.enhanced_prompt_editor import EnhancedPromptEditor
-
- if session is None:
- session = SimplifiedSessionData()
-
- # Initialize enhanced editor
- editor = EnhancedPromptEditor()
-
- # Get session ID
- session_id = getattr(session, 'session_id', 'default_session')
-
- # Use enhanced editor to apply changes
- status_html, success = editor.apply_prompt_changes(prompt_name, prompt_text, session_id)
-
- if success:
- # Also store in session for backward compatibility
- if not hasattr(session, 'custom_prompts'):
- session.custom_prompts = {}
-
- agent_key = _prompt_name_to_agent(prompt_name)
- session.custom_prompts[agent_key] = prompt_text
-
- # Apply to session app instance if available
- if hasattr(session, 'app_instance') and hasattr(session.app_instance, 'set_prompt_overrides'):
- session.app_instance.set_prompt_overrides(session.custom_prompts)
-
- return status_html, session
-
- except Exception as e:
- # Fallback to old system
- logger.warning(f"Enhanced prompt editor failed, using fallback: {e}")
-
- if session is None:
- session = SimplifiedSessionData()
-
- if not prompt_text.strip():
- error_html = """
-
ā Error
-
Prompt cannot be empty
-
"""
- return error_html, session
-
- # Store custom prompt in session (session-scoped)
- if not hasattr(session, 'custom_prompts'):
- session.custom_prompts = {}
-
- agent_key = _prompt_name_to_agent(prompt_name)
- session.custom_prompts[agent_key] = prompt_text
-
- status = f"""
-
ā ļø Fallback Mode - Changes Applied
-
Prompt: {prompt_name}
-
Length: {len(prompt_text)} characters
-
Enhanced features unavailable, using basic session storage.
-
"""
-
- return status, session
-
- def reset_prompt(prompt_name: str, session: SimplifiedSessionData):
- """Reset prompt to default using enhanced prompt editor."""
- try:
- from src.interface.enhanced_prompt_editor import EnhancedPromptEditor
-
- if session is None:
- session = SimplifiedSessionData()
-
- # Initialize enhanced editor
- editor = EnhancedPromptEditor()
-
- # Get session ID
- session_id = getattr(session, 'session_id', 'default_session')
-
- # Use enhanced editor to reset prompt
- prompt_content, info_html, status_html = editor.reset_prompt_to_default(prompt_name, session_id)
-
- # Also remove from session for backward compatibility
- agent_key = _prompt_name_to_agent(prompt_name)
- if hasattr(session, 'custom_prompts') and agent_key in session.custom_prompts:
- del session.custom_prompts[agent_key]
-
- # Apply to session app instance if available
- if hasattr(session, 'app_instance') and hasattr(session.app_instance, 'set_prompt_overrides'):
- session.app_instance.set_prompt_overrides(getattr(session, 'custom_prompts', {}))
-
- return prompt_content, info_html, status_html, session
-
- except Exception as e:
- # Fallback to old system
- logger.warning(f"Enhanced prompt editor failed, using fallback: {e}")
-
- if session is None:
- session = SimplifiedSessionData()
-
- # Remove from custom prompts
- agent_key = _prompt_name_to_agent(prompt_name)
- if hasattr(session, 'custom_prompts') and agent_key in session.custom_prompts:
- del session.custom_prompts[agent_key]
-
- # Reload default
- prompt_text, info, status = load_prompt(prompt_name, session)
-
- reset_status = """
-
š Fallback Mode - Reset Complete
-
Prompt restored using basic system. Enhanced features unavailable.
-
"""
-
- return prompt_text, info, reset_status, session
-
- def promote_prompt_to_file(prompt_name: str, session: SimplifiedSessionData):
- """Promote session prompt override to permanent file."""
- try:
- from src.interface.enhanced_prompt_editor import EnhancedPromptEditor
-
- if session is None:
- return """
-
ā Error
-
No session data available
-
""", session
-
- # Initialize enhanced editor
- editor = EnhancedPromptEditor()
-
- # Get session ID
- session_id = getattr(session, 'session_id', 'default_session')
-
- # Use enhanced editor to promote prompt
- status_html, success = editor.promote_session_to_file(prompt_name, session_id)
-
- return status_html, session
-
- except Exception as e:
- logger.warning(f"Enhanced prompt editor failed: {e}")
- return f"""
-
ā Error
-
Failed to promote prompt: {str(e)}
-
""", session
-
- def validate_prompt_syntax(prompt_text: str):
- """Validate prompt syntax and structure."""
- try:
- from src.interface.enhanced_prompt_editor import EnhancedPromptEditor
-
- # Initialize enhanced editor
- editor = EnhancedPromptEditor()
-
- # Use enhanced editor to validate prompt
- validation_html, is_valid = editor.validate_prompt_syntax(prompt_text)
-
- return validation_html
-
- except Exception as e:
- logger.warning(f"Enhanced prompt editor failed: {e}")
- return f"""
-
ā Validation Error
-
Failed to validate prompt: {str(e)}
-
"""
-
- # Verification mode handlers
- def load_verification_dataset(dataset_name: str, store: JSONVerificationStore):
- """Load a verification dataset."""
- try:
- # Find dataset ID from name
- datasets = TestDatasetManager.get_dataset_list()
- dataset_id = None
- for d in datasets:
- if d['name'] in dataset_name:
- dataset_id = d['dataset_id']
- break
-
- if not dataset_id:
- return (
- None, # verification_session
- "ā Dataset not found", # dataset_info
- "", "", "", "", # message_text, decision_badge, confidence, indicators
- "", # progress_display
- "ā Dataset not found", # error_message
- 0, # current_message_index
- None, # current_dataset_id
- [], # message_queue
- [], # verification_records
- )
-
- # Load dataset
- dataset = TestDatasetManager.load_dataset(dataset_id)
-
- # Create new verification session
- new_session = VerificationSession(
- session_id=str(uuid.uuid4()),
- verifier_name="Medical Professional",
- dataset_id=dataset_id,
- dataset_name=dataset.name,
- total_messages=dataset.message_count,
- message_queue=[m.message_id for m in dataset.messages],
- )
-
- # Save session
- store.save_session(new_session)
-
- # Get first message
- if dataset.messages:
- first_message = dataset.messages[0]
- message_text, decision_badge, confidence, indicators = VerificationUIComponents.render_message_review(
- first_message,
- first_message.pre_classified_label,
- 0.85, # Default confidence
- ["Distress indicator 1", "Distress indicator 2"]
- )
-
- progress = VerificationUIComponents.update_progress_display(0, dataset.message_count)
-
- dataset_info_text = f"**{dataset.name}**\n\n{dataset.description}\n\nš {dataset.message_count} messages to review"
-
- return (
- new_session, # verification_session
- dataset_info_text, # dataset_info
- message_text, # message_text
- decision_badge, # decision_badge
- confidence, # confidence
- indicators, # indicators
- progress, # progress_display
- "", # error_message (empty = no error)
- 0, # current_message_index
- dataset_id, # current_dataset_id
- [m.message_id for m in dataset.messages], # message_queue
- [], # verification_records
- )
- else:
- return (
- None, # verification_session
- "ā Dataset is empty", # dataset_info
- "", "", "", "", # message_text, decision_badge, confidence, indicators
- "", # progress_display
- "ā Dataset is empty", # error_message
- 0, # current_message_index
- dataset_id, # current_dataset_id
- [], # message_queue
- [], # verification_records
- )
-
- except Exception as e:
- return (
- None, # verification_session
- f"ā Error loading dataset: {str(e)}", # dataset_info
- "", "", "", "", # message_text, decision_badge, confidence, indicators
- "", # progress_display
- f"ā Error: {str(e)}", # error_message
- 0, # current_message_index
- None, # current_dataset_id
- [], # message_queue
- [], # verification_records
- )
-
- def handle_correct_feedback(session: VerificationSession, current_idx: int, dataset_id: str, message_queue: List[str], records: List[dict], store: JSONVerificationStore):
- """Handle correct feedback."""
- try:
- if not session or current_idx >= len(message_queue):
- return (
- session,
- "ā Error: Invalid session state",
- "", "", "", "",
- "",
- "ā Correct: 0",
- "ā Incorrect: 0",
- "š Accuracy: 0%",
- current_idx,
- records,
- )
-
- # Get current message
- dataset = TestDatasetManager.load_dataset(dataset_id)
- current_message_id = message_queue[current_idx]
- current_message = next((m for m in dataset.messages if m.message_id == current_message_id), None)
-
- if not current_message:
- return (
- session,
- "ā Error: Message not found",
- "", "", "", "",
- "",
- "ā Correct: 0",
- "ā Incorrect: 0",
- "š Accuracy: 0%",
- current_idx,
- records,
- )
-
- # Create verification record
- record = VerificationRecord(
- message_id=current_message.message_id,
- original_message=current_message.text,
- classifier_decision=current_message.pre_classified_label,
- classifier_confidence=0.85,
- classifier_indicators=["Distress indicator 1", "Distress indicator 2"],
- ground_truth_label=current_message.pre_classified_label,
- verifier_notes="",
- is_correct=True,
- )
-
- # Add to session
- session.verifications.append(record)
- session.verified_count += 1
- session.correct_count += 1
-
- # Save session
- store.save_session(session)
-
- # Move to next message
- next_idx = current_idx + 1
-
- if next_idx >= len(message_queue):
- # Session complete
- session.is_complete = True
- session.completed_at = datetime.now()
- store.save_session(session)
-
- correct_str, incorrect_str, accuracy_str = VerificationUIComponents.update_statistics_display(
- session.correct_count,
- session.incorrect_count
- )
-
- return (
- session,
- "ā
Verification complete!",
- "", "", "", "",
- "",
- correct_str,
- incorrect_str,
- accuracy_str,
- next_idx,
- [r.to_dict() for r in session.verifications],
- )
- else:
- # Load next message
- next_message = next((m for m in dataset.messages if m.message_id == message_queue[next_idx]), None)
- if next_message:
- message_text, decision_badge, confidence, indicators = VerificationUIComponents.render_message_review(
- next_message,
- next_message.pre_classified_label,
- 0.85,
- ["Distress indicator 1", "Distress indicator 2"]
- )
-
- progress = VerificationUIComponents.update_progress_display(next_idx, len(message_queue))
- correct_str, incorrect_str, accuracy_str = VerificationUIComponents.update_statistics_display(
- session.correct_count,
- session.incorrect_count
- )
-
- return (
- session,
- "",
- message_text,
- decision_badge,
- confidence,
- indicators,
- progress,
- correct_str,
- incorrect_str,
- accuracy_str,
- next_idx,
- [r.to_dict() for r in session.verifications],
- )
-
- return (
- session,
- "ā Error processing feedback",
- "", "", "", "",
- "",
- "ā Correct: 0",
- "ā Incorrect: 0",
- "š Accuracy: 0%",
- current_idx,
- records,
- )
-
- except Exception as e:
- return (
- session,
- f"ā Error: {str(e)}",
- "", "", "", "",
- "",
- "ā Correct: 0",
- "ā Incorrect: 0",
- "š Accuracy: 0%",
- current_idx,
- records,
- )
-
- def handle_incorrect_feedback(session: VerificationSession, current_idx: int, dataset_id: str, message_queue: List[str], records: List[dict]):
- """Show correction selector."""
- return "ā Please select the correct classification below"
-
- def handle_submit_correction(session: VerificationSession, current_idx: int, dataset_id: str, message_queue: List[str], records: List[dict], correction: str, notes: str, store: JSONVerificationStore):
- """Handle correction submission."""
- try:
- if not correction:
- return (
- "ā Please select a correction before submitting",
- session,
- current_idx,
- dataset_id,
- message_queue,
- records,
- "", "", "", "",
- "",
- "ā Correct: 0",
- "ā Incorrect: 0",
- "š Accuracy: 0%",
- "",
- "",
- )
-
- # Get current message
- dataset = TestDatasetManager.load_dataset(dataset_id)
- current_message_id = message_queue[current_idx]
- current_message = next((m for m in dataset.messages if m.message_id == current_message_id), None)
-
- if not current_message:
- return (
- "ā Error: Message not found",
- session,
- current_idx,
- dataset_id,
- message_queue,
- records,
- "", "", "", "",
- "",
- "ā Correct: 0",
- "ā Incorrect: 0",
- "š Accuracy: 0%",
- "",
- "",
- )
-
- # Create verification record
- record = VerificationRecord(
- message_id=current_message.message_id,
- original_message=current_message.text,
- classifier_decision=current_message.pre_classified_label,
- classifier_confidence=0.85,
- classifier_indicators=["Distress indicator 1", "Distress indicator 2"],
- ground_truth_label=correction,
- verifier_notes=notes,
- is_correct=current_message.pre_classified_label == correction,
- )
-
- # Add to session
- session.verifications.append(record)
- session.verified_count += 1
- if record.is_correct:
- session.correct_count += 1
- else:
- session.incorrect_count += 1
-
- # Save session
- store.save_session(session)
-
- # Move to next message
- next_idx = current_idx + 1
-
- if next_idx >= len(message_queue):
- # Session complete
- session.is_complete = True
- session.completed_at = datetime.now()
- store.save_session(session)
-
- correct_str, incorrect_str, accuracy_str = VerificationUIComponents.update_statistics_display(
- session.correct_count,
- session.incorrect_count
- )
-
- summary = VerificationUIComponents.render_summary_card(session, session.verifications)
-
- return (
- "ā
Verification complete!",
- session,
- next_idx,
- dataset_id,
- message_queue,
- [r.to_dict() for r in session.verifications],
- "", "", "", "",
- "",
- correct_str,
- incorrect_str,
- accuracy_str,
- "",
- summary,
- )
- else:
- # Load next message
- next_message = next((m for m in dataset.messages if m.message_id == message_queue[next_idx]), None)
- if next_message:
- message_text, decision_badge, confidence, indicators = VerificationUIComponents.render_message_review(
- next_message,
- next_message.pre_classified_label,
- 0.85,
- ["Distress indicator 1", "Distress indicator 2"]
- )
-
- progress = VerificationUIComponents.update_progress_display(next_idx, len(message_queue))
- correct_str, incorrect_str, accuracy_str = VerificationUIComponents.update_statistics_display(
- session.correct_count,
- session.incorrect_count
- )
-
- return (
- "",
- session,
- next_idx,
- dataset_id,
- message_queue,
- [r.to_dict() for r in session.verifications],
- message_text,
- decision_badge,
- confidence,
- indicators,
- progress,
- correct_str,
- incorrect_str,
- accuracy_str,
- "",
- "",
- )
-
- return (
- "ā Error processing correction",
- session,
- current_idx,
- dataset_id,
- message_queue,
- records,
- "", "", "", "",
- "",
- "ā Correct: 0",
- "ā Incorrect: 0",
- "š Accuracy: 0%",
- "",
- "",
- )
-
- except Exception as e:
- return (
- f"ā Error: {str(e)}",
- session,
- current_idx,
- dataset_id,
- message_queue,
- records,
- "", "", "", "",
- "",
- "ā Correct: 0",
- "ā Incorrect: 0",
- "š Accuracy: 0%",
- "",
- "",
- )
-
- def handle_download_csv(session: VerificationSession, store: JSONVerificationStore):
- """Handle CSV download - returns file path for DownloadButton."""
- try:
- if not session or session.verified_count == 0:
- return None
-
- csv_content = VerificationCSVExporter.generate_csv_content(session)
- filename = VerificationCSVExporter.generate_csv_filename()
-
- import os
- import tempfile
-
- # Use temp directory for Hugging Face compatibility
- temp_dir = tempfile.gettempdir()
- file_path = os.path.join(temp_dir, filename)
-
- with open(file_path, 'w', encoding='utf-8') as f:
- f.write(csv_content)
-
- return file_path
-
- except Exception as e:
- import traceback
- print(f"CSV Export Error: {traceback.format_exc()}")
- return None
-
- # Bind verification events (only if Standard Verification UI is enabled).
- # The Standard Verification tab is currently hidden, so keep bindings disabled
- # to avoid referencing undefined components.
- if False and is_feature_enabled("standard_verification_enabled"):
- load_dataset_btn.click(
- load_verification_dataset,
- inputs=[dataset_selector, verification_store],
- outputs=[
- verification_session,
- dataset_info,
- message_text,
- decision_badge,
- confidence,
- indicators,
- progress_display,
- error_message,
- current_message_index,
- current_dataset_id,
- message_queue,
- verification_records,
- ]
- ).then(
- lambda: gr.Row(visible=True), # Show message_review_section
- outputs=[message_review_section]
- )
-
- correct_btn.click(
- handle_correct_feedback,
- inputs=[verification_session, current_message_index, current_dataset_id, message_queue, verification_records, verification_store],
- outputs=[
- verification_session,
- error_message,
- message_text,
- decision_badge,
- confidence,
- indicators,
- progress_display,
- correct_count_display,
- incorrect_count_display,
- accuracy_display,
- current_message_index,
- verification_records,
- ]
- )
-
- incorrect_btn.click(
- handle_incorrect_feedback,
- inputs=[verification_session, current_message_index, current_dataset_id, message_queue, verification_records],
- outputs=[error_message]
- ).then(
- lambda: (gr.Row(visible=True), gr.Row(visible=True)),
- outputs=[correction_section, submit_correction_row]
- )
-
- submit_correction_btn.click(
- handle_submit_correction,
- inputs=[verification_session, current_message_index, current_dataset_id, message_queue, verification_records, correction_selector, notes_field, verification_store],
- outputs=[
- error_message,
- verification_session,
- current_message_index,
- current_dataset_id,
- message_queue,
- verification_records,
- message_text,
- decision_badge,
- confidence,
- indicators,
- progress_display,
- correct_count_display,
- incorrect_count_display,
- accuracy_display,
- breakdown_display,
- results_summary,
- ]
- ).then(
- lambda: (gr.Row(visible=False), gr.Row(visible=False)),
- outputs=[correction_section, submit_correction_row]
- )
-
- cancel_correction_btn.click(
- lambda: "",
- outputs=[error_message]
- )
-
- download_csv_btn.click(
- handle_download_csv,
- inputs=[verification_session, verification_store],
- outputs=[csv_download, error_message]
- )
-
- # Navigation buttons handlers
- def handle_next_message(session: VerificationSession, current_idx: int, dataset_id: str, message_queue: List[str], records: List[dict]):
- """Move to next message."""
- if not session or current_idx >= len(message_queue) - 1:
- return (
- session,
- "ā No more messages",
- "", "", "", "",
- "",
- "ā Correct: 0",
- "ā Incorrect: 0",
- "š Accuracy: 0%",
- current_idx,
- records,
- )
-
- next_idx = current_idx + 1
- dataset = TestDatasetManager.load_dataset(dataset_id)
- next_message = next((m for m in dataset.messages if m.message_id == message_queue[next_idx]), None)
-
- if next_message:
- message_text, decision_badge, confidence, indicators = VerificationUIComponents.render_message_review(
- next_message,
- next_message.pre_classified_label,
- 0.85,
- ["Distress indicator 1", "Distress indicator 2"]
- )
-
- progress = VerificationUIComponents.update_progress_display(next_idx, len(message_queue))
- correct_str, incorrect_str, accuracy_str = VerificationUIComponents.update_statistics_display(
- session.correct_count,
- session.incorrect_count
- )
-
- return (
- session,
- "",
- message_text,
- decision_badge,
- confidence,
- indicators,
- progress,
- correct_str,
- incorrect_str,
- accuracy_str,
- next_idx,
- records,
- )
-
- return (
- session,
- "ā Error loading next message",
- "", "", "", "",
- "",
- "ā Correct: 0",
- "ā Incorrect: 0",
- "š Accuracy: 0%",
- current_idx,
- records,
- )
-
- def handle_previous_message(session: VerificationSession, current_idx: int, dataset_id: str, message_queue: List[str], records: List[dict]):
- """Move to previous message."""
- if not session or current_idx <= 0:
- return (
- session,
- "ā No previous messages",
- "", "", "", "",
- "",
- "ā Correct: 0",
- "ā Incorrect: 0",
- "š Accuracy: 0%",
- current_idx,
- records,
- )
-
- prev_idx = current_idx - 1
- dataset = TestDatasetManager.load_dataset(dataset_id)
- prev_message = next((m for m in dataset.messages if m.message_id == message_queue[prev_idx]), None)
-
- if prev_message:
- message_text, decision_badge, confidence, indicators = VerificationUIComponents.render_message_review(
- prev_message,
- prev_message.pre_classified_label,
- 0.85,
- ["Distress indicator 1", "Distress indicator 2"]
- )
-
- progress = VerificationUIComponents.update_progress_display(prev_idx, len(message_queue))
- correct_str, incorrect_str, accuracy_str = VerificationUIComponents.update_statistics_display(
- session.correct_count,
- session.incorrect_count
- )
-
- return (
- session,
- "",
- message_text,
- decision_badge,
- confidence,
- indicators,
- progress,
- correct_str,
- incorrect_str,
- accuracy_str,
- prev_idx,
- records,
- )
-
- return (
- session,
- "ā Error loading previous message",
- "", "", "", "",
- "",
- "ā Correct: 0",
- "ā Incorrect: 0",
- "š Accuracy: 0%",
- current_idx,
- records,
- )
-
- def handle_skip_message(session: VerificationSession, current_idx: int, dataset_id: str, message_queue: List[str], records: List[dict]):
- """Skip current message and move to next."""
- return handle_next_message(session, current_idx, dataset_id, message_queue, records)
-
- # Bind navigation buttons
- next_btn.click(
- handle_next_message,
- inputs=[verification_session, current_message_index, current_dataset_id, message_queue, verification_records],
- outputs=[
- verification_session,
- error_message,
- message_text,
- decision_badge,
- confidence,
- indicators,
- progress_display,
- correct_count_display,
- incorrect_count_display,
- accuracy_display,
- current_message_index,
- verification_records,
- ]
- )
-
- prev_btn.click(
- handle_previous_message,
- inputs=[verification_session, current_message_index, current_dataset_id, message_queue, verification_records],
- outputs=[
- verification_session,
- error_message,
- message_text,
- decision_badge,
- confidence,
- indicators,
- progress_display,
- correct_count_display,
- incorrect_count_display,
- accuracy_display,
- current_message_index,
- verification_records,
- ]
- )
-
- skip_btn.click(
- handle_skip_message,
- inputs=[verification_session, current_message_index, current_dataset_id, message_queue, verification_records],
- outputs=[
- verification_session,
- error_message,
- message_text,
- decision_badge,
- confidence,
- indicators,
- progress_display,
- correct_count_display,
- incorrect_count_display,
- accuracy_display,
- current_message_index,
- verification_records,
- ]
- )
-
- # Save results button - DownloadButton triggers download directly
- save_results_btn.click(
- handle_download_csv,
- inputs=[verification_session, verification_store],
- outputs=[save_results_btn]
- )
-
- # Clear session button
- def handle_clear_session():
- """Clear current verification session."""
- return (
- None, # verification_session
- "ā
Session cleared", # error_message
- "", "", "", "", # message components
- "", # progress
- "ā Correct: 0", # correct count
- "ā Incorrect: 0", # incorrect count
- "š Accuracy: 0%", # accuracy
- 0, # current index
- [], # records
- )
-
- clear_session_btn.click(
- handle_clear_session,
- outputs=[
- verification_session,
- error_message,
- message_text,
- decision_badge,
- confidence,
- indicators,
- progress_display,
- correct_count_display,
- incorrect_count_display,
- accuracy_display,
- current_message_index,
- verification_records,
- ]
- )
-
- # Chaplain Feedback Event Handlers
- def show_chaplain_feedback_section():
- """Show chaplain feedback section after message review."""
- return gr.Row(visible=True)
-
- def handle_submit_feedback(
- classification_correct: bool,
- classification_subcategory: Optional[str],
- correct_classification: Optional[str],
- question_issues: List[str],
- question_comments: str,
- referral_issues: List[str],
- referral_comments: str,
- indicator_issues: str,
- indicator_comments: str,
- general_notes: str,
- session: VerificationSession,
- current_idx: int,
- message_queue: List[str],
- ):
- """Handle chaplain feedback submission."""
- try:
- if not session or current_idx >= len(message_queue):
- return "ā Error: Invalid session state", session, current_idx
-
- # Create tagging record
- from src.core.chaplain_models import TaggingRecord
- import uuid
-
- current_message_id = message_queue[current_idx]
-
- tagging_record = TaggingRecord(
- record_id=str(uuid.uuid4()),
- message_id=current_message_id,
- is_classification_correct=classification_correct,
- classification_subcategory=classification_subcategory,
- correct_classification=correct_classification,
- question_issues=question_issues or [],
- question_comments=question_comments,
- referral_issues=referral_issues or [],
- referral_comments=referral_comments,
- indicator_issues=[i.strip() for i in indicator_issues.split(",") if i.strip()],
- indicator_comments=indicator_comments,
- general_notes=general_notes,
- )
-
- # Store tagging record in session (would need to extend VerificationSession)
- # For now, just confirm submission
- success_msg = f"ā
Feedback submitted for message {current_idx + 1}"
-
- return success_msg, session, current_idx
-
- except Exception as e:
- return f"ā Error: {str(e)}", session, current_idx
-
- def display_classification_flow(flow_result: Optional[ClassificationFlowResult]):
- """Display classification flow result."""
- if not flow_result:
- return "", "", "", ""
-
- badge, explanation, content, indicators = ChaplainFeedbackUIComponents.render_classification_flow(flow_result)
- return badge, explanation, content, indicators
-
- # Bind chaplain feedback events
- submit_feedback_btn.click(
- handle_submit_feedback,
- inputs=[
- is_correct, # is_correct radio
- subcategory, # subcategory dropdown
- correct_classification, # correct_classification radio
- question_issues, # question_issues checkbox
- question_comments, # question_comments textbox
- referral_issues, # referral_issues checkbox
- referral_comments, # referral_comments textbox
- indicator_issues, # indicator_issues textbox
- indicator_comments, # indicator_comments textbox
- general_notes,
- verification_session,
- current_message_index,
- message_queue,
- ],
- outputs=[error_message, verification_session, current_message_index]
- ).then(
- lambda: gr.Row(visible=False),
- outputs=[chaplain_feedback_section]
- )
# Bind events
demo.load(
@@ -2088,549 +587,110 @@ Use the **Download Summary** button below to access the complete provider summar
# Send message
send_btn.click(
- handle_message,
+ chat_handlers.handle_message,
inputs=[msg, chatbot, session_data],
outputs=[chatbot, status_box, session_data, msg, conversation_stats, provider_summary_content, provider_summary_status, provider_summary_display, spiritual_care_message]
)
msg.submit(
- handle_message,
+ chat_handlers.handle_message,
inputs=[msg, chatbot, session_data],
outputs=[chatbot, status_box, session_data, msg, conversation_stats, provider_summary_content, provider_summary_status, provider_summary_display, spiritual_care_message]
)
# Clear chat
clear_btn.click(
- handle_clear,
+ chat_handlers.handle_clear,
inputs=[session_data],
outputs=[chatbot, status_box, session_data, provider_summary_content, provider_summary_status, provider_summary_display, spiritual_care_message]
)
# Refresh status
refresh_btn.click(
- get_status,
+ stats_handlers.get_status,
inputs=[session_data],
outputs=[status_box, conversation_stats, provider_summary_content, provider_summary_status, provider_summary_display, spiritual_care_message]
)
# Example buttons
- def send_example_with_stats(example_text: str, history, session: SimplifiedSessionData):
- """Send example message and return stats."""
- return handle_message(example_text, history, session)
-
example_medical.click(
- lambda h, s: send_example_with_stats("I am fine", h, s),
+ lambda h, s: chat_handlers.send_example_with_stats("I am fine", h, s),
inputs=[chatbot, session_data],
outputs=[chatbot, status_box, session_data, msg, conversation_stats, provider_summary_content, provider_summary_status, provider_summary_display, spiritual_care_message]
)
example_wellness.click(
- lambda h, s: send_example_with_stats("I'm feeling stressed and overwhelmed lately", h, s),
+ lambda h, s: chat_handlers.send_example_with_stats("I'm feeling stressed and overwhelmed lately", h, s),
inputs=[chatbot, session_data],
outputs=[chatbot, status_box, session_data, msg, conversation_stats, provider_summary_content, provider_summary_status, provider_summary_display, spiritual_care_message]
)
example_help.click(
- lambda h, s: send_example_with_stats("I am currently experiencing an emotional crisis", h, s),
+ lambda h, s: chat_handlers.send_example_with_stats("I am currently experiencing an emotional crisis", h, s),
inputs=[chatbot, session_data],
outputs=[chatbot, status_box, session_data, msg, conversation_stats, provider_summary_content, provider_summary_status, provider_summary_display, spiritual_care_message]
)
# Conversation logging buttons
download_json_btn.click(
- download_conversation_json,
+ stats_handlers.download_conversation_json,
inputs=[session_data],
outputs=[download_json_btn]
)
download_csv_btn.click(
- download_conversation_csv,
+ stats_handlers.download_conversation_csv,
inputs=[session_data],
outputs=[download_csv_btn]
)
# Provider Summary panel handlers
- def download_provider_summary(session: SimplifiedSessionData):
- """Download provider summary as text file."""
- if session is None:
- return None
-
- last_summary = session.app_instance.get_last_provider_summary()
- if not last_summary:
- return None
-
- try:
- import tempfile
- import os
- from datetime import datetime
-
- # Create temp file with summary
- timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
- filename = f"provider_summary_{timestamp}.txt"
- filepath = os.path.join(tempfile.gettempdir(), filename)
-
- summary_text = session.app_instance.provider_summary_generator.format_for_display(last_summary)
- with open(filepath, 'w', encoding='utf-8') as f:
- f.write(summary_text)
-
- return filepath
- except Exception as e:
- print(f"Error downloading provider summary: {e}")
- return None
-
- def clear_provider_summary():
- """Clear provider summary panel."""
- return gr.update(visible=False), "No provider summary available", "", ""
-
- def regenerate_spiritual_care_message(
- session: SimplifiedSessionData,
- include_conversation: bool = True,
- include_situation: bool = False,
- include_indicators: bool = False,
- include_profile: bool = False
- ):
- """Regenerate LLM-based spiritual care message."""
- if session is None:
- return ""
-
- try:
- message = session.app_instance.generate_spiritual_care_message(
- language="English",
- session_id=session.session_id,
- include_conversation_context=include_conversation,
- include_situation_analysis=include_situation,
- include_distress_indicators=include_indicators,
- include_patient_profile=include_profile
- )
- return message if message else "No provider summary available to generate message from."
- except Exception as e:
- print(f"Error regenerating spiritual care message: {e}")
- return f"Error generating message: {str(e)}"
-
- def download_spiritual_care_message(session: SimplifiedSessionData):
- """Download spiritual care message as text file."""
- if session is None:
- return None
-
- try:
- import tempfile
- import os
- from datetime import datetime
-
- message = session.app_instance.generate_spiritual_care_message(
- language="English",
- session_id=session.session_id
- )
- if not message:
- return None
-
- # Create temp file with message
- timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
- filename = f"spiritual_care_message_{timestamp}.txt"
- filepath = os.path.join(tempfile.gettempdir(), filename)
-
- # Add header
- patient_name = session.app_instance.patient_info.get('name', 'Patient')
- header = f"SPIRITUAL CARE TEAM MESSAGE\n"
- header += f"Generated: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n"
- header += f"Patient: {patient_name}\n"
- header += f"Classification: RED FLAG - Spiritual Distress\n"
- header += "=" * 60 + "\n\n"
-
- with open(filepath, 'w', encoding='utf-8') as f:
- f.write(header)
- f.write(message)
-
- return filepath
- except Exception as e:
- print(f"Error downloading spiritual care message: {e}")
- return None
-
download_summary_btn.click(
- download_provider_summary,
+ stats_handlers.download_provider_summary,
inputs=[session_data],
outputs=[download_summary_btn]
)
clear_summary_btn.click(
- clear_provider_summary,
+ stats_handlers.clear_provider_summary,
inputs=[],
outputs=[provider_summary_content, provider_summary_status, provider_summary_display, spiritual_care_message]
)
# Spiritual care message handlers
generate_message_btn.click(
- regenerate_spiritual_care_message,
+ stats_handlers.regenerate_spiritual_care_message,
inputs=[session_data, include_conversation_context, include_situation_analysis, include_distress_indicators, include_patient_profile],
outputs=[spiritual_care_message]
)
download_message_btn.click(
- download_spiritual_care_message,
+ stats_handlers.download_spiritual_care_message,
inputs=[session_data],
outputs=[download_message_btn]
)
- # Download helper (used by embedded Conversation Verification tab)
- def _download_latest_verification_json(session: SimplifiedSessionData):
- """Return the most recently exported verification session JSON path (if present)."""
- # open_verification_window exports into ./verification_sessions
- import glob
- import os
-
- export_dir = os.path.join(os.getcwd(), "verification_sessions")
- if not os.path.isdir(export_dir):
- return None
-
- candidates = sorted(
- glob.glob(os.path.join(export_dir, "verification_session_*.json")),
- key=lambda p: os.path.getmtime(p),
- reverse=True,
- )
- return candidates[0] if candidates else None
-
-
-
- # Embedded conversation verification (tab)
- def _render_conv_exchange(records: list, index: int):
- if not records:
- return "", "", ""
- index = max(0, min(index, len(records) - 1))
- r = records[index]
- # Reuse renderer from conversation_verification_ui to keep style consistent
- from src.core.conversation_verification import VerificationRecord
- from src.interface.conversation_verification_ui import VerificationInterface
- from src.core.conversation_verification import ConversationVerificationManager
-
- vi = VerificationInterface(ConversationVerificationManager())
- # If we already have dicts, build a lightweight VerificationRecord
- if isinstance(r, dict):
- rec = VerificationRecord(
- exchange_id=r.get("exchange_id") or r.get("record_id", ""),
- exchange_number=r.get("exchange_number", 0),
- user_message=r.get("user_message", ""),
- assistant_response=r.get("assistant_response", ""),
- original_classification=r.get("original_classification", ""),
- original_confidence=r.get("original_confidence", 0.0),
- original_indicators=r.get("original_indicators", []),
- original_reasoning=r.get("original_reasoning", ""),
- timestamp=r.get("timestamp"),
- is_correct=r.get("is_correct"),
- correct_classification=r.get("correct_classification"),
- correction_reason=r.get("correction_reason"),
- verifier_notes=r.get("verifier_notes"),
- )
- else:
- rec = r
- html = vi._render_exchange_review(rec)
- # status badge
- cur_is_correct = (r.get("is_correct") if isinstance(r, dict) else getattr(r, "is_correct", None))
- if cur_is_correct is True:
- badge = "ā
"
- elif cur_is_correct is False:
- badge = "ā"
- else:
- badge = "ā³"
- pos = f"### {badge} Exchange {index + 1} of {len(records)}"
-
- # richer stats
- reviewed = 0
- correct = 0
- incorrect = 0
- incorrect_with_comment = 0
- corrections = {} # Track classification corrections
-
- for x in records:
- v = (x.get("is_correct") if isinstance(x, dict) else getattr(x, "is_correct", None))
- if v is None:
- continue
- reviewed += 1
- if v is True:
- correct += 1
- else:
- incorrect += 1
- note = (x.get("verifier_notes") if isinstance(x, dict) else getattr(x, "verifier_notes", None))
- if note and str(note).strip():
- incorrect_with_comment += 1
-
- # Track classification corrections
- original_class = (x.get("original_classification") if isinstance(x, dict) else getattr(x, "original_classification", ""))
- correct_class = (x.get("correct_classification") if isinstance(x, dict) else getattr(x, "correct_classification", None))
- if original_class and correct_class:
- correction_key = f"{original_class}ā{correct_class}"
- corrections[correction_key] = corrections.get(correction_key, 0) + 1
-
- stats_parts = [
- f"Reviewed: {reviewed}/{len(records)}
",
- f"ā
Correct: {correct}
",
- f"ā Incorrect: {incorrect}
",
- f"š Incorrect w/ comment: {incorrect_with_comment}
"
- ]
-
- # Add correction breakdown if any corrections exist
- if corrections:
- correction_text = ", ".join([f"{k}: {v}" for k, v in corrections.items()])
- stats_parts.append(f"š Corrections: {correction_text}
")
-
- stats = (
- ""
- + "".join(stats_parts) +
- "
"
- )
- return html, pos, stats
-
- def _comment_ui_state(records: list, idx: int):
- """Return (row_update, note_value) based on current record state."""
- if not records:
- return gr.update(visible=False), ""
- idx = max(0, min(idx, len(records) - 1))
- r = records[idx]
- is_incorrect = (r.get("is_correct") is False) if isinstance(r, dict) else (getattr(r, "is_correct", None) is False)
- if not is_incorrect:
- return gr.update(visible=False), ""
- note = (r.get("verifier_notes") or "") if isinstance(r, dict) else (getattr(r, "verifier_notes", "") or "")
- return gr.update(visible=True), str(note)
-
- def _export_conv_records_to_json(meta: dict, records: list):
- """Write reviewed conversation verification results to a JSON file and return its path."""
- import json
- import os
- from datetime import datetime
-
- export_dir = os.path.join(os.getcwd(), "verification_sessions")
- os.makedirs(export_dir, exist_ok=True)
-
- session_id = (meta or {}).get("session_id") or "conversation_verification"
- export_filename = f"conversation_verification_reviewed_{datetime.utcnow().strftime('%Y%m%d_%H%M%S')}_{session_id}.json"
- export_path = os.path.join(export_dir, export_filename)
-
- payload = {
- **(meta or {}),
- "verification_records": records or [],
- }
-
- with open(export_path, "w", encoding="utf-8") as f:
- json.dump(payload, f, ensure_ascii=False, indent=2, default=str)
- return export_path
-
- def _export_conv_records_to_csv(meta: dict, records: list):
- """Write reviewed conversation verification results to a CSV file and return its path."""
- import csv
- import os
- from datetime import datetime
-
- export_dir = os.path.join(os.getcwd(), "verification_exports")
- os.makedirs(export_dir, exist_ok=True)
-
- session_id = (meta or {}).get("session_id") or "conversation_verification"
- export_filename = f"conversation_verification_reviewed_{datetime.utcnow().strftime('%Y%m%d_%H%M%S')}_{session_id}.csv"
- export_path = os.path.join(export_dir, export_filename)
-
- fieldnames = [
- "session_id",
- "patient_name",
- "patient_phone",
- "verifier_name",
- "start_time",
- "exchange_number",
- "exchange_id",
- "original_classification",
- "original_confidence",
- "is_correct",
- "correct_classification",
- "verifier_notes",
- "user_message",
- "assistant_response",
- "provider_summary",
- ]
-
- with open(export_path, "w", encoding="utf-8", newline="") as f:
- w = csv.DictWriter(f, fieldnames=fieldnames)
- w.writeheader()
- for r in records or []:
- # Include provider_summary only for RED cases
- provider_summary = ""
- if r.get("original_classification", "").upper() == "RED":
- provider_summary = r.get("provider_summary") or ""
-
- row = {
- "session_id": (meta or {}).get("session_id"),
- "patient_name": (meta or {}).get("patient_name"),
- "patient_phone": (meta or {}).get("patient_phone") or "",
- "verifier_name": (meta or {}).get("verifier_name"),
- "start_time": (meta or {}).get("start_time"),
- "exchange_number": r.get("exchange_number"),
- "exchange_id": r.get("exchange_id") or r.get("record_id"),
- "original_classification": r.get("original_classification"),
- "original_confidence": r.get("original_confidence"),
- "is_correct": r.get("is_correct"),
- "correct_classification": r.get("correct_classification") or "",
- "verifier_notes": r.get("verifier_notes") or "",
- "user_message": r.get("user_message"),
- "assistant_response": r.get("assistant_response"),
- "provider_summary": provider_summary,
- }
- w.writerow(row)
- return export_path
-
- def _generate_conv_verification(session: SimplifiedSessionData):
- if session is None or not hasattr(session.app_instance, "conversation_logger"):
- return None, [], 0, "ā No session/conversation found", "", ""
- if not session.app_instance.conversation_logger.entries:
- return None, [], 0, "ā ļø No exchanges to verify yet", "", ""
-
- from src.core.conversation_verification import ConversationVerificationManager
- manager = ConversationVerificationManager()
- vs = manager.create_verification_session(session.app_instance.conversation_logger, "Medical Professional")
-
- # Get patient phone from app if available
- patient_phone = ""
- if hasattr(session.app_instance, 'patient_info'):
- patient_phone = session.app_instance.patient_info.get("phone") or ""
-
- meta = {
- "session_id": vs.session_id,
- "patient_name": vs.patient_name,
- "patient_phone": patient_phone,
- "verifier_name": vs.verifier_name,
- "start_time": vs.start_time.isoformat() if hasattr(vs, "start_time") else None,
- }
-
- # Get provider summary if available (for RED cases)
- provider_summary_text = ""
- if hasattr(session.app_instance, 'get_last_provider_summary'):
- summary = session.app_instance.get_last_provider_summary()
- if summary and hasattr(session.app_instance, 'provider_summary_generator'):
- provider_summary_text = session.app_instance.provider_summary_generator.format_for_export(summary)
-
- records_as_dicts = [
- {
- "exchange_id": r.exchange_id,
- "exchange_number": r.exchange_number,
- "record_id": r.exchange_id,
- "timestamp": r.timestamp,
- "user_message": r.user_message,
- "assistant_response": r.assistant_response,
- "original_classification": r.original_classification,
- "original_confidence": r.original_confidence,
- "original_indicators": r.original_indicators,
- "original_reasoning": r.original_reasoning,
- "is_correct": r.is_correct,
- "correct_classification": r.correct_classification,
- "correction_reason": r.correction_reason,
- "verifier_notes": r.verifier_notes,
- "provider_summary": provider_summary_text if r.original_classification.upper() == "RED" else "",
- }
- for r in vs.verification_records
- ]
- html, pos, stats = _render_conv_exchange(records_as_dicts, 0)
- return meta, records_as_dicts, 0, f"ā
Generated session `{vs.session_id}`", html, pos, stats
-
- def _mark_conv_correct(records: list, idx: int):
- if not records:
- return records, idx, "", "", "", gr.update(visible=False), "", ""
- idx = max(0, min(idx, len(records) - 1))
- if isinstance(records[idx], dict):
- records[idx]["is_correct"] = True
- # clear comment and correct_classification when marked correct (avoid stale data)
- records[idx]["verifier_notes"] = ""
- records[idx]["correct_classification"] = None
- html, pos, stats = _render_conv_exchange(records, idx)
- row_upd, note_val = _comment_ui_state(records, idx)
- return records, idx, "ā
Marked correct", html, pos, stats, row_upd, note_val, ""
-
- def _mark_conv_incorrect(records: list, idx: int):
- if not records:
- return records, idx, "", "", "", gr.update(visible=False), "", ""
- idx = max(0, min(idx, len(records) - 1))
- if isinstance(records[idx], dict):
- records[idx]["is_correct"] = False
- html, pos, stats = _render_conv_exchange(records, idx)
- row_upd, note_val = _comment_ui_state(records, idx)
- # Get existing correct_classification if any
- existing_classification = ""
- if isinstance(records[idx], dict):
- correct_class = records[idx].get("correct_classification")
- if correct_class:
- # Map back to display text
- reverse_map = {
- "GREEN": "š¢ Should be GREEN - No distress",
- "YELLOW": "š” Should be YELLOW - Needs clarification",
- "RED": "š“ Should be RED - Spiritual distress"
- }
- existing_classification = reverse_map.get(correct_class, "")
- return records, idx, "ā Marked incorrect", html, pos, stats, row_upd, note_val, existing_classification
-
- def _show_incorrect_comment_ui(records: list, idx: int):
- """Mark incorrect and open the comment row, pre-filling any existing note."""
- records, idx, status, html, pos, stats, _row, note, existing_classification = _mark_conv_incorrect(records, idx)
- return records, idx, status, html, pos, stats, gr.update(visible=True), note, existing_classification
-
- def _save_incorrect_comment(records: list, idx: int, note: str, correct_classification: str):
- if not records:
- return records, idx, "", "", "", "", gr.update(visible=False), "", ""
- idx = max(0, min(idx, len(records) - 1))
- if isinstance(records[idx], dict):
- records[idx]["verifier_notes"] = (note or "").strip()
- # Map display text to classification code
- classification_map = {
- "š¢ Should be GREEN - No distress": "GREEN",
- "š” Should be YELLOW - Needs clarification": "YELLOW",
- "š“ Should be RED - Spiritual distress": "RED"
- }
- if correct_classification and correct_classification in classification_map:
- records[idx]["correct_classification"] = classification_map[correct_classification]
- html, pos, stats = _render_conv_exchange(records, idx)
- row_upd, note_val = _comment_ui_state(records, idx)
- # keep row visible after save (since still incorrect)
- return records, idx, "š¾ Comment saved", html, pos, stats, row_upd, note_val, ""
-
- def _download_reviewed_json(meta: dict, records: list):
- return _export_conv_records_to_json(meta, records)
-
- def _download_reviewed_csv(meta: dict, records: list):
- return _export_conv_records_to_csv(meta, records)
-
- def _nav_conv(records: list, idx: int, delta: int):
- if not records:
- return idx, "", "", "", gr.update(visible=False), "", ""
- idx = max(0, min(idx + delta, len(records) - 1))
- html, pos, stats = _render_conv_exchange(records, idx)
- row_upd, note_val = _comment_ui_state(records, idx)
- # Get existing correct_classification if any
- existing_classification = ""
- if isinstance(records[idx], dict):
- correct_class = records[idx].get("correct_classification")
- if correct_class:
- # Map back to display text
- reverse_map = {
- "GREEN": "š¢ Should be GREEN - No distress",
- "YELLOW": "š” Should be YELLOW - Needs clarification",
- "RED": "š“ Should be RED - Spiritual distress"
- }
- existing_classification = reverse_map.get(correct_class, "")
- return idx, html, pos, stats, row_upd, note_val, existing_classification
-
+ # Conversation Verification events
generate_conv_verification_btn.click(
- _generate_conv_verification,
+ verification_handlers._generate_conv_verification,
inputs=[session_data],
outputs=[conv_verify_state, conv_verify_records, conv_verify_index, conv_verify_status, conv_verify_exchange, conv_position, conv_stats]
)
conv_verify_download_btn.click(
- _download_reviewed_json,
+ verification_handlers._download_reviewed_json,
inputs=[conv_verify_state, conv_verify_records],
outputs=[conv_verify_download_btn]
)
conv_verify_download_csv_btn.click(
- _download_reviewed_csv,
+ verification_handlers._download_reviewed_csv,
inputs=[conv_verify_state, conv_verify_records],
outputs=[conv_verify_download_csv_btn]
)
conv_correct_btn.click(
- _mark_conv_correct,
+ verification_handlers._mark_conv_correct,
inputs=[conv_verify_records, conv_verify_index],
outputs=[
conv_verify_records,
@@ -2646,7 +706,7 @@ Use the **Download Summary** button below to access the complete provider summar
)
conv_incorrect_btn.click(
- _show_incorrect_comment_ui,
+ verification_handlers._show_incorrect_comment_ui,
inputs=[conv_verify_records, conv_verify_index],
outputs=[
conv_verify_records,
@@ -2662,7 +722,7 @@ Use the **Download Summary** button below to access the complete provider summar
)
conv_save_comment_btn.click(
- _save_incorrect_comment,
+ verification_handlers._save_incorrect_comment,
inputs=[conv_verify_records, conv_verify_index, conv_incorrect_comment, conv_correct_classification],
outputs=[
conv_verify_records,
@@ -2678,413 +738,84 @@ Use the **Download Summary** button below to access the complete provider summar
)
conv_prev_btn.click(
- lambda records, idx: _nav_conv(records, idx, -1),
+ lambda records, idx: verification_handlers._nav_conv(records, idx, -1),
inputs=[conv_verify_records, conv_verify_index],
outputs=[conv_verify_index, conv_verify_exchange, conv_position, conv_stats, conv_incorrect_comment_row, conv_incorrect_comment, conv_correct_classification]
)
conv_next_btn.click(
- lambda records, idx: _nav_conv(records, idx, 1),
+ lambda records, idx: verification_handlers._nav_conv(records, idx, 1),
inputs=[conv_verify_records, conv_verify_index],
outputs=[conv_verify_index, conv_verify_exchange, conv_position, conv_stats, conv_incorrect_comment_row, conv_incorrect_comment, conv_correct_classification]
)
+
# Prompt editing events
load_prompt_btn.click(
- load_prompt,
+ prompt_handlers.load_prompt,
inputs=[prompt_selector, session_data],
outputs=[prompt_editor, prompt_info_display, prompt_status]
)
apply_prompt_btn.click(
- apply_prompt_changes,
+ prompt_handlers.apply_prompt_changes,
inputs=[prompt_selector, prompt_editor, session_data],
outputs=[prompt_status, session_data]
)
reset_prompt_btn.click(
- reset_prompt,
+ prompt_handlers.reset_prompt,
inputs=[prompt_selector, session_data],
outputs=[prompt_editor, prompt_info_display, prompt_status, session_data]
)
promote_prompt_btn.click(
- promote_prompt_to_file,
+ prompt_handlers.promote_prompt_to_file,
inputs=[prompt_selector, session_data],
outputs=[prompt_status, session_data]
)
validate_prompt_btn.click(
- validate_prompt_syntax,
+ prompt_handlers.validate_prompt_syntax,
inputs=[prompt_editor],
outputs=[prompt_status]
)
# Auto-load prompt when selector changes
prompt_selector.change(
- load_prompt,
+ prompt_handlers.load_prompt,
inputs=[prompt_selector, session_data],
outputs=[prompt_editor, prompt_info_display, prompt_status]
)
- # Model selection handlers
- def apply_model_settings(spiritual_model: str, soft_spiritual_triage_model: str, triage_evaluate_model: str, medical_model: str, soft_triage_model: str, spiritual_care_message_model: str, session: SimplifiedSessionData):
- """Apply custom model settings."""
- if session is None:
- session = SimplifiedSessionData()
-
- # Store model settings in session
- if not hasattr(session, 'custom_models'):
- session.custom_models = {}
-
- session.custom_models = {
- 'SpiritualDistressAnalyzer': spiritual_model,
- 'SoftSpiritualTriage': soft_spiritual_triage_model,
- 'TriageResponseEvaluator': triage_evaluate_model,
- 'MedicalAssistant': medical_model,
- 'SoftMedicalTriage': soft_triage_model,
- 'SpiritualCareMessage': spiritual_care_message_model
- }
-
- status = f"""
-
ā
Model Settings Applied
-
-
š Spiritual Monitor: {spiritual_model}
-
š” Soft Spiritual Triage: {soft_spiritual_triage_model}
-
š Triage Response Evaluator: {triage_evaluate_model}
-
š„ Medical Assistant: {medical_model}
-
𩺠Soft Medical Triage: {soft_triage_model}
-
š¬ Spiritual Care Message: {spiritual_care_message_model}
-
-
-ā ļø Note: Model changes apply to this session only.
-
-
"""
-
- return status, session
-
- def reset_model_settings(session: SimplifiedSessionData):
- """Reset models to defaults."""
- if session is None:
- session = SimplifiedSessionData()
-
- # Clear custom models
- if hasattr(session, 'custom_models'):
- session.custom_models = {}
-
- status = """
-
š Models Reset to Defaults
-
-
š Spiritual Monitor: gemini-2.5-flash
-
š” Soft Spiritual Triage: claude-sonnet-4-5-20250929
-
š Triage Response Evaluator: gemini-2.5-flash
-
š„ Medical Assistant: claude-sonnet-4-5-20250929
-
𩺠Soft Medical Triage: claude-sonnet-4-5-20250929
-
š¬ Spiritual Care Message: claude-sonnet-4-5-20250929
-
-
Default models are now active.
-
"""
-
- return status, session
-
# Bind model selection events
apply_models_btn.click(
- apply_model_settings,
+ model_handlers.apply_model_settings,
inputs=[spiritual_model, soft_spiritual_triage_model, triage_evaluate_model, medical_model, soft_triage_model, spiritual_care_message_model, session_data],
outputs=[model_status, session_data]
)
reset_models_btn.click(
- reset_model_settings,
+ model_handlers.reset_model_settings,
inputs=[session_data],
outputs=[model_status, session_data]
)
- # Patient profile handlers
- def load_profile(profile_name: str, session: SimplifiedSessionData):
- """Load predefined patient profile and apply it to the session."""
- profiles = {
- "š¤ Default Profile (Serhii)": {
- "name": "Serhii",
- "phone": "(555) 123-4567",
- "age": 52,
- "conditions": "Atrial fibrillation, Deep vein thrombosis, Obesity, Hypertension",
- "goal": "Weight reduction and cardiovascular fitness improvement",
- "exercise": "Swimming, Walking, Light cardio",
- "limitations": "Anticoagulation therapy, Post-thrombotic recovery"
- },
- "š¢ GREEN - Healthy Coping": {
- "name": "James",
- "phone": "(555) 234-5678",
- "age": 40,
- "conditions": "No chronic conditions, Excellent health",
- "goal": "Maintain fitness and wellness",
- "exercise": "Running, Gym, Sports",
- "limitations": "None"
- },
- "š” YELLOW - Mild Distress": {
- "name": "Lisa",
- "phone": "(555) 345-6789",
- "age": 45,
- "conditions": "Hypertension, Mild anxiety, Sleep issues",
- "goal": "Manage stress and improve sleep quality",
- "exercise": "Yoga, Walking, Meditation",
- "limitations": "Stress-related fatigue, Occasional insomnia"
- },
- "š” YELLOW - Grief & Loss": {
- "name": "Michael",
- "phone": "(555) 456-7890",
- "age": 58,
- "conditions": "Recent loss of spouse, Mild depression",
- "goal": "Process grief and rebuild routine",
- "exercise": "Gentle walking, Support groups",
- "limitations": "Low motivation, Emotional exhaustion"
- },
- "š” YELLOW - Existential Questions": {
- "name": "Patricia",
- "phone": "(555) 567-8901",
- "age": 62,
- "conditions": "Chronic pain, Questioning life purpose",
- "goal": "Find meaning and manage chronic pain",
- "exercise": "Tai Chi, Meditation, Gentle stretching",
- "limitations": "Chronic pain, Existential concerns"
- },
- "š” YELLOW - Spiritual Disconnection": {
- "name": "David",
- "phone": "(555) 678-9012",
- "age": 55,
- "conditions": "Loss of faith, Isolation from community",
- "goal": "Reconnect with spiritual community",
- "exercise": "Walking, Community activities",
- "limitations": "Spiritual disconnection, Social isolation"
- },
- "š“ RED - Crisis (Suicidal Risk)": {
- "name": "Thomas",
- "phone": "(555) 789-0123",
- "age": 35,
- "conditions": "Severe depression, Suicidal ideation",
- "goal": "Immediate crisis intervention and support",
- "exercise": "None - Crisis support priority",
- "limitations": "CRISIS - Suicidal thoughts, Immediate referral needed"
- },
- "š“ RED - Severe Hopelessness": {
- "name": "Jennifer",
- "phone": "(555) 890-1234",
- "age": 48,
- "conditions": "Major depression, Complete hopelessness",
- "goal": "Crisis stabilization and professional support",
- "exercise": "None - Medical intervention priority",
- "limitations": "CRISIS - Severe hopelessness, Unable to function"
- },
- "š“ RED - Spiritual Crisis": {
- "name": "Christopher",
- "phone": "(555) 901-2345",
- "age": 52,
- "conditions": "Moral injury, Spiritual crisis, Anger at God",
- "goal": "Spiritual crisis intervention and healing",
- "exercise": "None - Spiritual support priority",
- "limitations": "CRISIS - Spiritual crisis, Rage, Existential despair"
- },
- "š« Cardiac Patient (Rehabilitation)": {
- "name": "John",
- "phone": "(555) 012-3456",
- "age": 65,
- "conditions": "Coronary artery disease, Hypertension, Hyperlipidemia",
- "goal": "Cardiac rehabilitation and risk factor management",
- "exercise": "Supervised walking, Cardiac rehab program",
- "limitations": "Recent MI, Limited exertion tolerance"
- },
- "𩸠Diabetic Patient (Management)": {
- "name": "Maria",
- "phone": "(555) 111-2222",
- "age": 58,
- "conditions": "Type 2 Diabetes, Obesity, Hypertension",
- "goal": "Blood sugar control and weight management",
- "exercise": "Moderate walking, Resistance training",
- "limitations": "Neuropathy, Retinopathy risk"
- },
- "š„ Post-Surgery (Recovery)": {
- "name": "Alex",
- "phone": "(555) 222-3333",
- "age": 45,
- "conditions": "Post-surgical recovery, Pain management",
- "goal": "Safe return to normal activities",
- "exercise": "Gentle mobility, Gradual progression",
- "limitations": "Surgical site healing, Limited ROM"
- },
- "š§ Mental Health (Anxiety/Depression)": {
- "name": "Emma",
- "phone": "(555) 333-4444",
- "age": 35,
- "conditions": "Depression, Anxiety, Sedentary lifestyle",
- "goal": "Mood improvement through activity",
- "exercise": "Yoga, Walking, Group activities",
- "limitations": "Low motivation, Energy fluctuations"
- },
- "š“ Elderly Patient (Chronic Care)": {
- "name": "Robert",
- "phone": "(555) 444-5555",
- "age": 78,
- "conditions": "Arthritis, Osteoporosis, Hypertension",
- "goal": "Maintain independence and mobility",
- "exercise": "Tai Chi, Water aerobics, Balance training",
- "limitations": "Fall risk, Joint pain, Medication interactions"
- },
- "š Athletic Patient (Injury/Training)": {
- "name": "Sarah",
- "phone": "(555) 555-6666",
- "age": 32,
- "conditions": "Mild hypertension, Overtraining syndrome",
- "goal": "Optimize performance and prevent injury",
- "exercise": "Running, Strength training, Cross-training",
- "limitations": "Overuse injuries, Recovery needs"
- }
- }
-
- profile = profiles.get(profile_name, profiles["š¤ Default Profile (Serhii)"])
-
- # Automatically apply the profile to the session
- if session and hasattr(session.app_instance, 'set_patient_info'):
- session.app_instance.set_patient_info(
- name=profile['name'],
- phone=profile.get('phone', '')
- )
-
- # Update clinical_background for medical context
- from src.core.core_classes import ClinicalBackground
- session.app_instance.clinical_background = ClinicalBackground(
- patient_name=profile['name'],
- age=profile['age'],
- conditions=profile['conditions'].split(',') if isinstance(profile['conditions'], str) else profile['conditions'],
- primary_goal=profile['goal'],
- exercise_preferences=profile['exercise'].split(',') if isinstance(profile['exercise'], str) else profile['exercise'],
- exercise_limitations=profile['limitations'].split(',') if isinstance(profile['limitations'], str) else profile['limitations']
- )
-
- # Update conversation logger patient name
- if hasattr(session.app_instance, 'conversation_logger'):
- session.app_instance.conversation_logger.patient_name = profile['name']
-
- print(f"DEBUG: Auto-applied profile - Name: {profile['name']}, Phone: {profile.get('phone', '')}")
- print(f"DEBUG: Clinical background updated - Age: {profile['age']}, Conditions: {profile['conditions']}")
-
- status = f"""
-
ā
Profile Loaded & Applied
-
Patient: {profile['name']}, {profile['age']} years old
-
Phone: {profile.get('phone', 'Not provided')}
-
Profile: {profile_name}
-
Status: Profile has been automatically applied to this session
-
ā Ready to use in conversations and reports
-
"""
-
- return (
- profile['name'],
- profile.get('phone', ''),
- profile['age'],
- profile['conditions'],
- profile['goal'],
- profile['exercise'],
- profile['limitations'],
- status
- )
-
- def save_profile(name: str, phone: str, age: float, conditions: str, goal: str, exercise: str, limitations: str, session: SimplifiedSessionData):
- """Save current profile settings and update app patient info."""
- if not name.strip():
- return """
-
ā Error
-
Patient name cannot be empty
-
"""
-
- # Update session's app instance patient info for provider summaries
- if session and hasattr(session.app_instance, 'set_patient_info'):
- session.app_instance.set_patient_info(name=name.strip(), phone=phone.strip() if phone else None)
-
- # Also update clinical_background for medical context
- from src.core.core_classes import ClinicalBackground
- session.app_instance.clinical_background = ClinicalBackground(
- patient_name=name.strip(),
- age=int(age) if age else None,
- conditions=conditions.strip().split(',') if conditions.strip() else [],
- primary_goal=goal.strip(),
- exercise_preferences=exercise.strip().split(',') if exercise.strip() else [],
- exercise_limitations=limitations.strip().split(',') if limitations.strip() else []
- )
-
- # Update conversation logger patient name
- if hasattr(session.app_instance, 'conversation_logger'):
- session.app_instance.conversation_logger.patient_name = name.strip()
-
- print(f"DEBUG: Updated patient info - Name: {name.strip()}, Phone: {phone.strip() if phone else None}")
- print(f"DEBUG: Updated clinical_background - Age: {int(age) if age else None}, Conditions: {conditions}")
-
- status = f"""
-
š¾ Profile Saved
-
Patient: {name}, {int(age)} years old
-
Phone: {phone if phone else 'Not provided'}
-
Conditions: {conditions}
-
Primary Goal: {goal}
-
Profile settings have been updated for this session.
-
"""
-
- return status
-
- def reset_profile(session: SimplifiedSessionData):
- """Reset profile to default."""
- # Reset session's app instance patient info
- if session and hasattr(session.app_instance, 'set_patient_info'):
- session.app_instance.set_patient_info(name="Serhii", phone="(555) 123-4567")
-
- # Also reset clinical_background
- from src.core.core_classes import ClinicalBackground
- session.app_instance.clinical_background = ClinicalBackground(
- patient_name="Serhii",
- age=52,
- conditions=["Atrial fibrillation", "Deep vein thrombosis", "Obesity", "Hypertension"],
- primary_goal="Weight reduction and cardiovascular fitness improvement",
- exercise_preferences=["Swimming", "Walking", "Light cardio"],
- exercise_limitations=["Anticoagulation therapy", "Post-thrombotic recovery"]
- )
-
- # Update conversation logger patient name
- if hasattr(session.app_instance, 'conversation_logger'):
- session.app_instance.conversation_logger.patient_name = "Serhii"
-
- print(f"DEBUG: Reset patient info to default")
-
- status = """
-
š Profile Reset
-
Patient: Serhii, 52 years old
-
Phone: (555) 123-4567
-
Default profile has been restored.
-
"""
-
- return (
- "Serhii",
- "(555) 123-4567",
- 52,
- "Atrial fibrillation, Deep vein thrombosis, Obesity, Hypertension",
- "Weight reduction and cardiovascular fitness improvement",
- "Swimming, Walking, Light cardio",
- "Anticoagulation therapy, Post-thrombotic recovery",
- status
- )
-
# Bind profile events
load_profile_btn.click(
- load_profile,
+ profile_handlers.load_profile,
inputs=[profile_selector, session_data],
outputs=[patient_name, patient_phone, patient_age, conditions, primary_goal, exercise_prefs, exercise_limits, profile_status]
)
save_profile_btn.click(
- save_profile,
+ profile_handlers.save_profile,
inputs=[patient_name, patient_phone, patient_age, conditions, primary_goal, exercise_prefs, exercise_limits, session_data],
outputs=[profile_save_status]
)
reset_profile_btn.click(
- reset_profile,
+ profile_handlers.reset_profile,
inputs=[session_data],
outputs=[patient_name, patient_phone, patient_age, conditions, primary_goal, exercise_prefs, exercise_limits, profile_save_status]
)