Spaces:
Sleeping
Sleeping
| """ | |
| Enhanced Edit Prompts UI Integration | |
| This module provides enhanced UI integration for the Edit Prompts interface, | |
| integrating with the centralized PromptController system while maintaining | |
| existing UI functionality and adding new features. | |
| **Feature: prompt-optimization, Task 11.4: Enhance Edit Prompts UI integration** | |
| **Validates: Requirements 9.1, 9.4** | |
| """ | |
| import gradio as gr | |
| from typing import Dict, List, Optional, Tuple, Any | |
| from datetime import datetime | |
| import sys | |
| import os | |
| # Add src to path for imports | |
| sys.path.append('src') | |
| from config.prompt_management.prompt_controller import PromptController | |
| from config.prompt_management.data_models import PromptConfig | |
| class EnhancedPromptEditor: | |
| """Enhanced prompt editor with centralized prompt system integration.""" | |
| def __init__(self): | |
| self.controller = PromptController() | |
| self._agent_mapping = { | |
| "π Spiritual Monitor (Classifier)": "spiritual_monitor", | |
| "π‘ Soft Spiritual Triage": "triage_question", | |
| "π Triage Response Evaluator": "triage_evaluator", | |
| "π₯ Medical Assistant": "medical_assistant", | |
| "π©Ί Soft Medical Triage": "soft_medical_triage", | |
| "π¬ Spiritual Care Message": "spiritual_care_message" | |
| } | |
| self._reverse_mapping = {v: k for k, v in self._agent_mapping.items()} | |
| def get_available_prompts(self) -> List[str]: | |
| """Get list of available prompts for the dropdown.""" | |
| return list(self._agent_mapping.keys()) | |
| def load_prompt_for_editing(self, prompt_name: str, session_id: Optional[str] = None) -> Tuple[str, str, str]: | |
| """ | |
| Load a prompt for editing with enhanced information display. | |
| Args: | |
| prompt_name: Display name of the prompt | |
| session_id: Optional session ID for session-specific overrides | |
| Returns: | |
| Tuple of (prompt_content, info_html, status_html) | |
| """ | |
| try: | |
| agent_type = self._agent_mapping.get(prompt_name) | |
| if not agent_type: | |
| return "", self._generate_error_info("Unknown prompt type"), self._generate_error_status("Invalid prompt selection") | |
| # Get prompt configuration | |
| config = self.controller.get_prompt(agent_type, session_id=session_id) | |
| # Determine prompt source | |
| prompt_source = "Default Fallback" | |
| if config.session_override: | |
| prompt_source = f"Session Override ({session_id[:8]}...)" | |
| elif agent_type in ['spiritual_monitor', 'triage_question', 'triage_evaluator']: | |
| prompt_source = "Centralized File" | |
| # Generate enhanced info display | |
| info_html = self._generate_prompt_info( | |
| prompt_name=prompt_name, | |
| config=config, | |
| prompt_source=prompt_source, | |
| session_id=session_id | |
| ) | |
| # Generate status | |
| status_html = self._generate_load_status(prompt_name, prompt_source) | |
| return config.base_prompt, info_html, status_html | |
| except Exception as e: | |
| error_info = self._generate_error_info(f"Error loading prompt: {str(e)}") | |
| error_status = self._generate_error_status("Failed to load prompt") | |
| return "", error_info, error_status | |
| def apply_prompt_changes(self, prompt_name: str, prompt_content: str, session_id: str) -> Tuple[str, bool]: | |
| """ | |
| Apply prompt changes to the session. | |
| Args: | |
| prompt_name: Display name of the prompt | |
| prompt_content: New prompt content | |
| session_id: Session identifier | |
| Returns: | |
| Tuple of (status_html, success) | |
| """ | |
| try: | |
| if not prompt_content.strip(): | |
| return self._generate_error_status("Prompt content cannot be empty"), False | |
| agent_type = self._agent_mapping.get(prompt_name) | |
| if not agent_type: | |
| return self._generate_error_status("Invalid prompt type"), False | |
| # Set session override | |
| success = self.controller.set_session_override(agent_type, prompt_content, session_id) | |
| if success: | |
| status_html = self._generate_apply_success_status( | |
| prompt_name=prompt_name, | |
| content_length=len(prompt_content), | |
| session_id=session_id | |
| ) | |
| return status_html, True | |
| else: | |
| return self._generate_error_status("Failed to apply prompt changes"), False | |
| except Exception as e: | |
| return self._generate_error_status(f"Error applying changes: {str(e)}"), False | |
| def reset_prompt_to_default(self, prompt_name: str, session_id: str) -> Tuple[str, str, str]: | |
| """ | |
| Reset prompt to default (remove session override). | |
| Args: | |
| prompt_name: Display name of the prompt | |
| session_id: Session identifier | |
| Returns: | |
| Tuple of (prompt_content, info_html, status_html) | |
| """ | |
| try: | |
| agent_type = self._agent_mapping.get(prompt_name) | |
| if not agent_type: | |
| error_info = self._generate_error_info("Invalid prompt type") | |
| error_status = self._generate_error_status("Reset failed") | |
| return "", error_info, error_status | |
| # Clear session override for this agent | |
| if session_id in self.controller._session_overrides: | |
| if agent_type in self.controller._session_overrides[session_id]: | |
| del self.controller._session_overrides[session_id][agent_type] | |
| # Clear cache entry | |
| cache_key = f"{agent_type}_{session_id}" | |
| if cache_key in self.controller._prompt_cache: | |
| del self.controller._prompt_cache[cache_key] | |
| # Reload default prompt | |
| return self.load_prompt_for_editing(prompt_name, session_id) | |
| except Exception as e: | |
| error_info = self._generate_error_info(f"Error resetting prompt: {str(e)}") | |
| error_status = self._generate_error_status("Reset failed") | |
| return "", error_info, error_status | |
| def get_session_prompt_status(self, session_id: str) -> str: | |
| """ | |
| Get status of all session prompt overrides. | |
| Args: | |
| session_id: Session identifier | |
| Returns: | |
| HTML status display | |
| """ | |
| try: | |
| session_overrides = self.controller.get_session_overrides(session_id) | |
| if not session_overrides: | |
| return """ | |
| <div style="padding: 1em; background-color: #f9fafb; border-radius: 8px; border: 1px solid #e5e7eb;"> | |
| <h4 style="margin-top: 0; color: #6b7280;">π Session Status</h4> | |
| <p style="margin-bottom: 0; color: #6b7280;">No active prompt overrides in this session.</p> | |
| </div> | |
| """ | |
| override_list = [] | |
| for agent_type, content in session_overrides.items(): | |
| display_name = self._reverse_mapping.get(agent_type, agent_type) | |
| content_preview = content[:100] + "..." if len(content) > 100 else content | |
| override_list.append(f"<li><strong>{display_name}</strong>: {len(content)} chars</li>") | |
| return f""" | |
| <div style="padding: 1em; background-color: #ecfdf5; border-radius: 8px; border: 1px solid #10b981;"> | |
| <h4 style="margin-top: 0; color: #059669;">β Active Session Overrides</h4> | |
| <ul style="margin-bottom: 0; color: #065f46;"> | |
| {''.join(override_list)} | |
| </ul> | |
| </div> | |
| """ | |
| except Exception as e: | |
| return f""" | |
| <div style="padding: 1em; background-color: #fef2f2; border-radius: 8px; border: 1px solid #dc2626;"> | |
| <h4 style="margin-top: 0; color: #dc2626;">β Error</h4> | |
| <p style="margin-bottom: 0;">Failed to get session status: {str(e)}</p> | |
| </div> | |
| """ | |
| def promote_session_to_file(self, prompt_name: str, session_id: str) -> Tuple[str, bool]: | |
| """ | |
| Promote session override to permanent file. | |
| Args: | |
| prompt_name: Display name of the prompt | |
| session_id: Session identifier | |
| Returns: | |
| Tuple of (status_html, success) | |
| """ | |
| try: | |
| agent_type = self._agent_mapping.get(prompt_name) | |
| if not agent_type: | |
| return self._generate_error_status("Invalid prompt type"), False | |
| success = self.controller.promote_session_to_file(agent_type, session_id) | |
| if success: | |
| status_html = f""" | |
| <div style="padding: 1em; background-color: #ecfdf5; border-radius: 8px; border: 1px solid #10b981;"> | |
| <h4 style="margin-top: 0; color: #059669;">β Promoted to File</h4> | |
| <p><strong>Prompt:</strong> {prompt_name}</p> | |
| <p><strong>Action:</strong> Session override promoted to permanent file</p> | |
| <p style="margin-bottom: 0; color: #d97706;"> | |
| β οΈ <strong>Note:</strong> Original file backed up with timestamp. | |
| </p> | |
| </div> | |
| """ | |
| return status_html, True | |
| else: | |
| return self._generate_error_status("No session override to promote"), False | |
| except Exception as e: | |
| return self._generate_error_status(f"Error promoting to file: {str(e)}"), False | |
| def validate_prompt_syntax(self, prompt_content: str) -> Tuple[str, bool]: | |
| """ | |
| Validate prompt syntax and structure. | |
| Args: | |
| prompt_content: Prompt content to validate | |
| Returns: | |
| Tuple of (validation_html, is_valid) | |
| """ | |
| try: | |
| issues = [] | |
| warnings = [] | |
| # Basic validation checks | |
| if not prompt_content.strip(): | |
| issues.append("Prompt cannot be empty") | |
| if len(prompt_content) < 50: | |
| warnings.append("Prompt is very short (< 50 characters)") | |
| if len(prompt_content) > 10000: | |
| warnings.append("Prompt is very long (> 10,000 characters)") | |
| # Check for common structural elements | |
| if "<system_role>" not in prompt_content: | |
| warnings.append("Missing <system_role> section") | |
| if "<output_format>" not in prompt_content: | |
| warnings.append("Missing <output_format> section") | |
| # Check for placeholder usage | |
| placeholder_count = prompt_content.count("{{SHARED_") | |
| if placeholder_count > 0: | |
| warnings.append(f"Contains {placeholder_count} placeholder(s) - will be replaced with actual content") | |
| # Generate validation result | |
| if issues: | |
| validation_html = f""" | |
| <div style="padding: 0.8em; background-color: #fef2f2; border-radius: 6px; border: 1px solid #dc2626; max-height: 200px; overflow-y: auto;"> | |
| <h4 style="margin: 0 0 0.5em 0; color: #dc2626; font-size: 0.9em;">β Validation Errors</h4> | |
| <ul style="margin: 0; padding-left: 1.2em; color: #dc2626; font-size: 0.85em;"> | |
| {''.join(f'<li style="margin-bottom: 0.2em;">{issue}</li>' for issue in issues)} | |
| </ul> | |
| </div> | |
| """ | |
| return validation_html, False | |
| elif warnings: | |
| validation_html = f""" | |
| <div style="padding: 0.8em; background-color: #fffbeb; border-radius: 6px; border: 1px solid #f59e0b; max-height: 200px; overflow-y: auto;"> | |
| <h4 style="margin: 0 0 0.5em 0; color: #d97706; font-size: 0.9em;">β οΈ Validation Warnings</h4> | |
| <ul style="margin: 0; padding-left: 1.2em; color: #d97706; font-size: 0.85em;"> | |
| {''.join(f'<li style="margin-bottom: 0.2em;">{warning}</li>' for warning in warnings)} | |
| </ul> | |
| </div> | |
| """ | |
| return validation_html, True | |
| else: | |
| validation_html = """ | |
| <div style="padding: 0.8em; background-color: #ecfdf5; border-radius: 6px; border: 1px solid #10b981; max-height: 200px; overflow-y: auto;"> | |
| <h4 style="margin: 0 0 0.3em 0; color: #059669; font-size: 0.9em;">β Validation Passed</h4> | |
| <p style="margin: 0; color: #065f46; font-size: 0.85em;">Prompt structure looks good!</p> | |
| </div> | |
| """ | |
| return validation_html, True | |
| except Exception as e: | |
| error_html = f""" | |
| <div style="padding: 0.8em; background-color: #fef2f2; border-radius: 6px; border: 1px solid #dc2626;"> | |
| <h4 style="margin: 0 0 0.3em 0; color: #dc2626; font-size: 0.9em;">β Validation Error</h4> | |
| <p style="margin: 0; font-size: 0.85em;">Failed to validate: {str(e)}</p> | |
| </div> | |
| """ | |
| return error_html, False | |
| def _generate_prompt_info(self, prompt_name: str, config: PromptConfig, prompt_source: str, session_id: Optional[str]) -> str: | |
| """Generate enhanced prompt information display.""" | |
| # Calculate statistics | |
| content_length = len(config.base_prompt) | |
| line_count = len(config.base_prompt.split('\n')) | |
| word_count = len(config.base_prompt.split()) | |
| # Check for placeholders | |
| placeholder_count = config.base_prompt.count("{{SHARED_") | |
| # Generate shared components info | |
| components_info = f""" | |
| <p><strong>Shared Components:</strong></p> | |
| <ul style="margin-left: 1em;"> | |
| <li>Indicators: {len(config.shared_indicators)}</li> | |
| <li>Rules: {len(config.shared_rules)}</li> | |
| <li>Templates: {len(config.templates)}</li> | |
| </ul> | |
| """ | |
| # Generate session info | |
| session_info = "" | |
| if session_id: | |
| session_info = f""" | |
| <p><strong>Session:</strong> <code>{session_id[:12]}...</code></p> | |
| """ | |
| # Generate source indicator | |
| source_color = "#059669" if "Session Override" in prompt_source else "#3b82f6" | |
| source_icon = "π§" if "Session Override" in prompt_source else "π" | |
| return f""" | |
| <div style="font-family: system-ui; padding: 1em; background-color: #f9fafb; border-radius: 8px; border: 1px solid #e5e7eb;"> | |
| <h4 style="margin-top: 0; color: #374151;">π Prompt Information</h4> | |
| <p><strong>Name:</strong> {prompt_name}</p> | |
| <p><strong>Source:</strong> <span style="color: {source_color};">{source_icon} {prompt_source}</span></p> | |
| {session_info} | |
| <p><strong>Statistics:</strong></p> | |
| <ul style="margin-left: 1em;"> | |
| <li>Length: {content_length:,} characters</li> | |
| <li>Lines: {line_count:,}</li> | |
| <li>Words: {word_count:,}</li> | |
| <li>Placeholders: {placeholder_count}</li> | |
| </ul> | |
| {components_info} | |
| <p><strong>Last Updated:</strong> {config.last_updated.strftime('%Y-%m-%d %H:%M:%S')}</p> | |
| <p><strong>Version:</strong> {config.version}</p> | |
| </div> | |
| """ | |
| def _generate_load_status(self, prompt_name: str, prompt_source: str) -> str: | |
| """Generate load success status.""" | |
| return f""" | |
| <div style="padding: 1em; background-color: #ecfdf5; border-left: 4px solid #10b981; border-radius: 4px;"> | |
| <h4 style="color: #059669; margin-top: 0;">β Prompt Loaded</h4> | |
| <p><strong>Prompt:</strong> {prompt_name}</p> | |
| <p><strong>Source:</strong> {prompt_source}</p> | |
| <p style="margin-bottom: 0;">Ready to edit. Make your changes and click "Apply Changes".</p> | |
| </div> | |
| """ | |
| def _generate_apply_success_status(self, prompt_name: str, content_length: int, session_id: str) -> str: | |
| """Generate apply success status.""" | |
| return f""" | |
| <div style="padding: 1em; background-color: #ecfdf5; border-left: 4px solid #10b981; border-radius: 4px;"> | |
| <h4 style="color: #059669; margin-top: 0;">β Prompt Applied Successfully</h4> | |
| <p><strong>Prompt:</strong> {prompt_name}</p> | |
| <p><strong>Length:</strong> {content_length:,} characters</p> | |
| <p><strong>Session:</strong> <code>{session_id[:12]}...</code></p> | |
| <p style="color: #d97706; margin-bottom: 0;"> | |
| β οΈ <strong>Note:</strong> Changes are active for this session only. | |
| Use "Promote to File" to make permanent. | |
| </p> | |
| </div> | |
| """ | |
| def _generate_error_info(self, error_message: str) -> str: | |
| """Generate error information display.""" | |
| return f""" | |
| <div style="font-family: system-ui; padding: 1em; background-color: #fef2f2; border-radius: 8px; border: 1px solid #dc2626;"> | |
| <h4 style="margin-top: 0; color: #dc2626;">β Error</h4> | |
| <p style="margin-bottom: 0; color: #dc2626;">{error_message}</p> | |
| </div> | |
| """ | |
| def _generate_error_status(self, error_message: str) -> str: | |
| """Generate error status display.""" | |
| return f""" | |
| <div style="padding: 1em; background-color: #fef2f2; border-left: 4px solid #dc2626; border-radius: 4px;"> | |
| <h4 style="color: #dc2626; margin-top: 0;">β Error</h4> | |
| <p style="margin-bottom: 0;">{error_message}</p> | |
| </div> | |
| """ | |
| def create_enhanced_prompt_editor_ui() -> Tuple[Any, ...]: | |
| """ | |
| Create enhanced prompt editor UI components. | |
| Returns: | |
| Tuple of Gradio components for the enhanced prompt editor | |
| """ | |
| editor = EnhancedPromptEditor() | |
| with gr.TabItem("π§ Edit Prompts", id="edit_prompts"): | |
| gr.Markdown("## π§ Enhanced Prompt Editor") | |
| gr.Markdown("β οΈ **Note:** Changes apply only to your current session. Use 'Promote to File' to make permanent.") | |
| # Session status display | |
| with gr.Row(): | |
| session_status_display = gr.HTML(value="", visible=True) | |
| # Prompt selector and controls | |
| with gr.Row(): | |
| with gr.Column(scale=2): | |
| prompt_selector = gr.Dropdown( | |
| choices=editor.get_available_prompts(), | |
| value=editor.get_available_prompts()[0] if editor.get_available_prompts() else None, | |
| label="Select Prompt to Edit", | |
| interactive=True | |
| ) | |
| with gr.Column(scale=1): | |
| load_prompt_btn = gr.Button("π₯ Load Prompt", variant="secondary") | |
| validate_prompt_btn = gr.Button("π Validate", variant="secondary") | |
| # Main editor area | |
| with gr.Row(): | |
| with gr.Column(scale=3): | |
| # Prompt editor | |
| prompt_editor = gr.Code( | |
| label="System Prompt", | |
| value="", | |
| language="markdown", | |
| lines=25, | |
| interactive=True | |
| ) | |
| # Validation display | |
| validation_display = gr.HTML(value="", visible=True) | |
| # Action buttons | |
| with gr.Row(): | |
| apply_prompt_btn = gr.Button("β Apply Changes", variant="primary", scale=2) | |
| reset_prompt_btn = gr.Button("π Reset to Default", variant="secondary", scale=1) | |
| promote_prompt_btn = gr.Button("π€ Promote to File", variant="stop", scale=1) | |
| # Status display | |
| prompt_status = gr.HTML(value="", visible=True) | |
| with gr.Column(scale=1): | |
| # Enhanced info panel | |
| gr.Markdown("### π Prompt Information") | |
| prompt_info_display = gr.HTML( | |
| value=""" | |
| <div style="font-family: system-ui; padding: 1em; background-color: #f9fafb; border-radius: 8px;"> | |
| <p><strong>Select a prompt to edit</strong></p> | |
| <p>Enhanced features:</p> | |
| <ul style="margin-left: 1em;"> | |
| <li>π§ Session-level editing</li> | |
| <li>π Real-time validation</li> | |
| <li>π Easy reset/revert</li> | |
| <li>π€ Promote to permanent</li> | |
| <li>π Detailed statistics</li> | |
| </ul> | |
| </div> | |
| """, | |
| visible=True | |
| ) | |
| return ( | |
| prompt_selector, prompt_editor, prompt_info_display, prompt_status, | |
| validation_display, session_status_display, load_prompt_btn, | |
| apply_prompt_btn, reset_prompt_btn, promote_prompt_btn, validate_prompt_btn | |
| ) | |
| # Helper function for integration with existing UI | |
| def integrate_with_existing_ui(session_data_component): | |
| """ | |
| Integration helper for existing Gradio UI. | |
| Args: | |
| session_data_component: Existing session data Gradio component | |
| """ | |
| editor = EnhancedPromptEditor() | |
| def enhanced_load_prompt(prompt_name: str, session_data): | |
| """Enhanced load prompt handler.""" | |
| session_id = getattr(session_data, 'session_id', 'default_session') if session_data else 'default_session' | |
| return editor.load_prompt_for_editing(prompt_name, session_id) | |
| def enhanced_apply_prompt(prompt_name: str, prompt_content: str, session_data): | |
| """Enhanced apply prompt handler.""" | |
| session_id = getattr(session_data, 'session_id', 'default_session') if session_data else 'default_session' | |
| status_html, success = editor.apply_prompt_changes(prompt_name, prompt_content, session_id) | |
| return status_html, session_data | |
| def enhanced_reset_prompt(prompt_name: str, session_data): | |
| """Enhanced reset prompt handler.""" | |
| session_id = getattr(session_data, 'session_id', 'default_session') if session_data else 'default_session' | |
| prompt_content, info_html, status_html = editor.reset_prompt_to_default(prompt_name, session_id) | |
| return prompt_content, info_html, status_html, session_data | |
| def enhanced_validate_prompt(prompt_content: str): | |
| """Enhanced validate prompt handler.""" | |
| return editor.validate_prompt_syntax(prompt_content) | |
| def enhanced_session_status(session_data): | |
| """Enhanced session status handler.""" | |
| session_id = getattr(session_data, 'session_id', 'default_session') if session_data else 'default_session' | |
| return editor.get_session_prompt_status(session_id) | |
| def enhanced_promote_prompt(prompt_name: str, session_data): | |
| """Enhanced promote prompt handler.""" | |
| session_id = getattr(session_data, 'session_id', 'default_session') if session_data else 'default_session' | |
| status_html, success = editor.promote_session_to_file(prompt_name, session_id) | |
| return status_html, session_data | |
| return { | |
| 'load_prompt': enhanced_load_prompt, | |
| 'apply_prompt': enhanced_apply_prompt, | |
| 'reset_prompt': enhanced_reset_prompt, | |
| 'validate_prompt': enhanced_validate_prompt, | |
| 'session_status': enhanced_session_status, | |
| 'promote_prompt': enhanced_promote_prompt | |
| } |