Spaces:
Sleeping
Sleeping
| """ | |
| Feedback UI integration for structured error category selection. | |
| Integrates with the existing verification interface to provide structured feedback capture. | |
| """ | |
| import gradio as gr | |
| from typing import Dict, List, Optional, Tuple, Any | |
| from datetime import datetime | |
| from config.prompt_management.feedback_system import FeedbackSystem | |
| from config.prompt_management.data_models import ( | |
| ErrorType, ErrorSubcategory, QuestionIssueType, ReferralProblemType, ScenarioType | |
| ) | |
| class FeedbackUIIntegration: | |
| """ | |
| UI integration for structured feedback capture. | |
| Provides Gradio components for: | |
| - Structured error category selection | |
| - Predefined subcategories from documentation | |
| - Pattern analysis display for reviewers | |
| - Integration with existing verification interface | |
| """ | |
| def __init__(self, feedback_system: Optional[FeedbackSystem] = None): | |
| """ | |
| Initialize the feedback UI integration. | |
| Args: | |
| feedback_system: Optional feedback system instance. If None, creates default. | |
| """ | |
| self.feedback_system = feedback_system or FeedbackSystem() | |
| # Define UI options based on data models | |
| self.error_type_options = [ | |
| ("Wrong Classification", "wrong_classification"), | |
| ("Severity Misjudgment", "severity_misjudgment"), | |
| ("Missed Indicators", "missed_indicators"), | |
| ("False Positive", "false_positive"), | |
| ("Context Misunderstanding", "context_misunderstanding"), | |
| ("Language Interpretation", "language_interpretation") | |
| ] | |
| self.subcategory_mapping = { | |
| "wrong_classification": [ | |
| ("GREEN → YELLOW", "green_to_yellow"), | |
| ("GREEN → RED", "green_to_red"), | |
| ("YELLOW → GREEN", "yellow_to_green"), | |
| ("YELLOW → RED", "yellow_to_red"), | |
| ("RED → GREEN", "red_to_green"), | |
| ("RED → YELLOW", "red_to_yellow") | |
| ], | |
| "severity_misjudgment": [ | |
| ("Underestimated Distress", "underestimated_distress"), | |
| ("Overestimated Distress", "overestimated_distress") | |
| ], | |
| "missed_indicators": [ | |
| ("Emotional Indicators", "emotional_indicators"), | |
| ("Spiritual Indicators", "spiritual_indicators"), | |
| ("Social Indicators", "social_indicators") | |
| ], | |
| "false_positive": [ | |
| ("Misinterpreted Statement", "misinterpreted_statement"), | |
| ("Cultural Misunderstanding", "cultural_misunderstanding") | |
| ], | |
| "context_misunderstanding": [ | |
| ("Ignored History", "ignored_history"), | |
| ("Missed Defensive Response", "missed_defensive_response") | |
| ], | |
| "language_interpretation": [ | |
| ("Literal Interpretation", "literal_interpretation"), | |
| ("Missed Subtext", "missed_subtext") | |
| ] | |
| } | |
| self.question_issue_options = [ | |
| ("Inappropriate Question", "inappropriate_question"), | |
| ("Insensitive Language", "insensitive_language"), | |
| ("Wrong Scenario Targeting", "wrong_scenario_targeting"), | |
| ("Unclear Question", "unclear_question"), | |
| ("Leading Question", "leading_question") | |
| ] | |
| self.referral_problem_options = [ | |
| ("Incomplete Summary", "incomplete_summary"), | |
| ("Missing Contact Info", "missing_contact_info"), | |
| ("Incorrect Urgency", "incorrect_urgency"), | |
| ("Poor Context Description", "poor_context_description") | |
| ] | |
| self.scenario_options = [ | |
| ("Loss of Interest", "loss_of_interest"), | |
| ("Loss of Loved One", "loss_of_loved_one"), | |
| ("No Support", "no_support"), | |
| ("Vague Stress", "vague_stress"), | |
| ("Sleep Issues", "sleep_issues"), | |
| ("Spiritual Practice Change", "spiritual_practice_change") | |
| ] | |
| def create_classification_error_interface(self) -> gr.Group: | |
| """ | |
| Create UI components for recording classification errors. | |
| Returns: | |
| gr.Group: Gradio group containing classification error interface | |
| """ | |
| with gr.Group() as classification_group: | |
| gr.Markdown("### Classification Error Feedback") | |
| with gr.Row(): | |
| error_type = gr.Dropdown( | |
| choices=[label for label, _ in self.error_type_options], | |
| label="Error Type", | |
| info="Select the type of classification error" | |
| ) | |
| subcategory = gr.Dropdown( | |
| choices=[], | |
| label="Subcategory", | |
| info="Specific subcategory (updates based on error type)" | |
| ) | |
| with gr.Row(): | |
| expected_category = gr.Dropdown( | |
| choices=["GREEN", "YELLOW", "RED"], | |
| label="Expected Category", | |
| info="What the classification should have been" | |
| ) | |
| actual_category = gr.Dropdown( | |
| choices=["GREEN", "YELLOW", "RED"], | |
| label="Actual Category", | |
| info="What the system classified it as" | |
| ) | |
| message_content = gr.Textbox( | |
| label="Patient Message", | |
| placeholder="Enter the patient message that was misclassified...", | |
| lines=3, | |
| info="The original patient message" | |
| ) | |
| reviewer_comments = gr.Textbox( | |
| label="Reviewer Comments", | |
| placeholder="Explain why this is an error and what should have happened...", | |
| lines=3, | |
| info="Detailed explanation of the error" | |
| ) | |
| confidence_level = gr.Slider( | |
| minimum=0.0, | |
| maximum=1.0, | |
| value=0.8, | |
| step=0.1, | |
| label="Confidence Level", | |
| info="How confident are you in this feedback?" | |
| ) | |
| submit_error = gr.Button("Record Classification Error", variant="primary") | |
| error_result = gr.Textbox(label="Result", interactive=False) | |
| # Update subcategory options when error type changes | |
| def update_subcategories(error_type_label): | |
| if not error_type_label: | |
| return gr.Dropdown(choices=[]) | |
| # Find the error type value | |
| error_type_value = None | |
| for label, value in self.error_type_options: | |
| if label == error_type_label: | |
| error_type_value = value | |
| break | |
| if error_type_value and error_type_value in self.subcategory_mapping: | |
| choices = [label for label, _ in self.subcategory_mapping[error_type_value]] | |
| return gr.Dropdown(choices=choices) | |
| else: | |
| return gr.Dropdown(choices=[]) | |
| error_type.change( | |
| fn=update_subcategories, | |
| inputs=[error_type], | |
| outputs=[subcategory] | |
| ) | |
| # Handle error submission | |
| def submit_classification_error(error_type_label, subcategory_label, expected, actual, | |
| message, comments, confidence): | |
| try: | |
| # Convert labels to values | |
| error_type_value = None | |
| for label, value in self.error_type_options: | |
| if label == error_type_label: | |
| error_type_value = value | |
| break | |
| if not error_type_value: | |
| return "Error: Invalid error type selected" | |
| subcategory_value = None | |
| if error_type_value in self.subcategory_mapping: | |
| for label, value in self.subcategory_mapping[error_type_value]: | |
| if label == subcategory_label: | |
| subcategory_value = value | |
| break | |
| if not subcategory_value: | |
| return "Error: Invalid subcategory selected" | |
| # Validate required fields | |
| if not all([expected, actual, message, comments]): | |
| return "Error: All fields are required" | |
| # Record the error | |
| error_id = self.feedback_system.record_classification_error( | |
| error_type=ErrorType(error_type_value), | |
| subcategory=ErrorSubcategory(subcategory_value), | |
| expected_category=expected, | |
| actual_category=actual, | |
| message_content=message, | |
| reviewer_comments=comments, | |
| confidence_level=confidence, | |
| session_id=f"ui_session_{datetime.now().strftime('%Y%m%d_%H%M%S')}", | |
| additional_context={"source": "ui_interface"} | |
| ) | |
| return f"✓ Classification error recorded successfully (ID: {error_id[:8]}...)" | |
| except Exception as e: | |
| return f"Error recording classification error: {str(e)}" | |
| submit_error.click( | |
| fn=submit_classification_error, | |
| inputs=[error_type, subcategory, expected_category, actual_category, | |
| message_content, reviewer_comments, confidence_level], | |
| outputs=[error_result] | |
| ) | |
| return classification_group | |
| def create_question_issue_interface(self) -> gr.Group: | |
| """ | |
| Create UI components for recording question issues. | |
| Returns: | |
| gr.Group: Gradio group containing question issue interface | |
| """ | |
| with gr.Group() as question_group: | |
| gr.Markdown("### Question Issue Feedback") | |
| with gr.Row(): | |
| issue_type = gr.Dropdown( | |
| choices=[label for label, _ in self.question_issue_options], | |
| label="Issue Type", | |
| info="Type of issue with the generated question" | |
| ) | |
| scenario_type = gr.Dropdown( | |
| choices=[label for label, _ in self.scenario_options], | |
| label="Scenario Type", | |
| info="The scenario the question was targeting" | |
| ) | |
| question_content = gr.Textbox( | |
| label="Problematic Question", | |
| placeholder="Enter the question that has issues...", | |
| lines=2, | |
| info="The generated question that needs improvement" | |
| ) | |
| reviewer_comments = gr.Textbox( | |
| label="Issue Description", | |
| placeholder="Explain what's wrong with this question...", | |
| lines=3, | |
| info="Detailed explanation of the issue" | |
| ) | |
| with gr.Row(): | |
| severity = gr.Dropdown( | |
| choices=["low", "medium", "high"], | |
| label="Severity", | |
| value="medium", | |
| info="How severe is this issue?" | |
| ) | |
| suggested_improvement = gr.Textbox( | |
| label="Suggested Improvement (Optional)", | |
| placeholder="Suggest a better question...", | |
| lines=2, | |
| info="Optional suggestion for how to improve the question" | |
| ) | |
| submit_question = gr.Button("Record Question Issue", variant="primary") | |
| question_result = gr.Textbox(label="Result", interactive=False) | |
| # Handle question issue submission | |
| def submit_question_issue(issue_type_label, scenario_label, question, comments, | |
| severity_val, improvement): | |
| try: | |
| # Convert labels to values | |
| issue_type_value = None | |
| for label, value in self.question_issue_options: | |
| if label == issue_type_label: | |
| issue_type_value = value | |
| break | |
| scenario_value = None | |
| for label, value in self.scenario_options: | |
| if label == scenario_label: | |
| scenario_value = value | |
| break | |
| if not all([issue_type_value, scenario_value, question, comments, severity_val]): | |
| return "Error: All required fields must be filled" | |
| # Record the issue | |
| issue_id = self.feedback_system.record_question_issue( | |
| issue_type=QuestionIssueType(issue_type_value), | |
| question_content=question, | |
| scenario_type=ScenarioType(scenario_value), | |
| reviewer_comments=comments, | |
| severity=severity_val, | |
| session_id=f"ui_session_{datetime.now().strftime('%Y%m%d_%H%M%S')}", | |
| suggested_improvement=improvement if improvement else None | |
| ) | |
| return f"✓ Question issue recorded successfully (ID: {issue_id[:8]}...)" | |
| except Exception as e: | |
| return f"Error recording question issue: {str(e)}" | |
| submit_question.click( | |
| fn=submit_question_issue, | |
| inputs=[issue_type, scenario_type, question_content, reviewer_comments, | |
| severity, suggested_improvement], | |
| outputs=[question_result] | |
| ) | |
| return question_group | |
| def create_pattern_analysis_display(self) -> gr.Group: | |
| """ | |
| Create UI components for displaying error pattern analysis. | |
| Returns: | |
| gr.Group: Gradio group containing pattern analysis display | |
| """ | |
| with gr.Group() as pattern_group: | |
| gr.Markdown("### Error Pattern Analysis") | |
| refresh_patterns = gr.Button("Refresh Pattern Analysis", variant="secondary") | |
| pattern_display = gr.Markdown( | |
| value="Click 'Refresh Pattern Analysis' to see current error patterns and improvement suggestions.", | |
| label="Pattern Analysis Results" | |
| ) | |
| # Handle pattern analysis refresh | |
| def refresh_pattern_analysis(): | |
| try: | |
| # Get feedback summary | |
| summary = self.feedback_system.get_feedback_summary() | |
| # Analyze patterns | |
| patterns = self.feedback_system.analyze_error_patterns(min_frequency=2) | |
| # Format results | |
| result = "## Current Feedback Summary\n\n" | |
| result += f"- **Total Errors:** {summary['total_errors']}\n" | |
| result += f"- **Total Question Issues:** {summary['total_question_issues']}\n" | |
| result += f"- **Total Referral Problems:** {summary['total_referral_problems']}\n" | |
| result += f"- **Average Confidence:** {summary['average_confidence']:.2f}\n" | |
| result += f"- **Recent Errors:** {summary['recent_errors']}\n\n" | |
| if patterns: | |
| result += "## Identified Error Patterns\n\n" | |
| for i, pattern in enumerate(patterns[:5], 1): # Top 5 patterns | |
| result += f"### {i}. {pattern.pattern_type.replace('_', ' ').title()}\n" | |
| result += f"- **Frequency:** {pattern.frequency}\n" | |
| result += f"- **Description:** {pattern.description}\n" | |
| result += f"- **Confidence:** {pattern.confidence_score:.2f}\n" | |
| result += "- **Suggested Improvements:**\n" | |
| for suggestion in pattern.suggested_improvements[:3]: # Top 3 suggestions | |
| result += f" - {suggestion}\n" | |
| result += "\n" | |
| else: | |
| result += "## No Significant Patterns Detected\n\n" | |
| result += "Not enough data to identify patterns (minimum 2 occurrences required).\n\n" | |
| # Add top improvement suggestions | |
| if summary['improvement_suggestions']: | |
| result += "## Top Improvement Suggestions\n\n" | |
| for i, suggestion in enumerate(summary['improvement_suggestions'][:5], 1): | |
| result += f"{i}. {suggestion}\n" | |
| return result | |
| except Exception as e: | |
| return f"Error analyzing patterns: {str(e)}" | |
| refresh_patterns.click( | |
| fn=refresh_pattern_analysis, | |
| outputs=[pattern_display] | |
| ) | |
| return pattern_group | |
| def create_complete_feedback_interface(self) -> gr.Tabs: | |
| """ | |
| Create the complete feedback interface with all components. | |
| Returns: | |
| gr.Tabs: Complete feedback interface with multiple tabs | |
| """ | |
| with gr.Tabs() as feedback_tabs: | |
| with gr.Tab("Classification Errors"): | |
| self.create_classification_error_interface() | |
| with gr.Tab("Question Issues"): | |
| self.create_question_issue_interface() | |
| with gr.Tab("Pattern Analysis"): | |
| self.create_pattern_analysis_display() | |
| return feedback_tabs | |
| def create_feedback_ui_demo(): | |
| """ | |
| Create a demo of the feedback UI integration. | |
| Returns: | |
| gr.Blocks: Gradio interface for testing feedback UI | |
| """ | |
| feedback_ui = FeedbackUIIntegration() | |
| with gr.Blocks(title="Structured Feedback System Demo") as demo: | |
| gr.Markdown("# Structured Feedback System") | |
| gr.Markdown("This interface allows reviewers to provide structured feedback on AI classifications, questions, and referrals.") | |
| feedback_ui.create_complete_feedback_interface() | |
| gr.Markdown("---") | |
| gr.Markdown("**Note:** This is a demonstration of the structured feedback capture system. In production, this would be integrated with the main verification interface.") | |
| return demo | |
| if __name__ == "__main__": | |
| # Run the demo | |
| demo = create_feedback_ui_demo() | |
| demo.launch(share=False, server_name="127.0.0.1", server_port=7861) |