import os import gradio as gr from email_processor import ( load_examples, setup_clients, process_email, AVAILABLE_MODELS, load_email_guidelines ) # Initialize global variables PASSWORD_PROTECTION_ENABLED = False PASSWORD = os.getenv("PASSWD") EMAIL_EXAMPLES = load_examples() CLIENTS = setup_clients() DEFAULT_GUIDELINES = load_email_guidelines() def process_email_wrapper( preceding_conversation, drafted_user_reply, session_password, system_message, model_choice, max_tokens, temperature, top_p, custom_guidelines, identity ): """ Wrapper function to call the email processor with all necessary parameters. This separates the Gradio interface from the core processing logic. When PASSWORD_PROTECTION_ENABLED is False, we bypass password validation entirely. """ if not PASSWORD_PROTECTION_ENABLED: # If password protection is disabled, use a dummy password that will always match dummy_password = "dummy_password" return process_email( preceding_conversation, drafted_user_reply, dummy_password, # Use dummy password for session_password system_message, model_choice, max_tokens, temperature, top_p, dummy_password, # Use same dummy password for PASSWORD CLIENTS, custom_guidelines, identity ) else: # Normal flow with password protection return process_email( preceding_conversation, drafted_user_reply, session_password, system_message, model_choice, max_tokens, temperature, top_p, PASSWORD, CLIENTS, custom_guidelines, identity ) def check_password(input_password): """ Validates the password and, if valid, shows the interface. If password protection is disabled, always grants access. Returns updates to UI components based on password validation. """ if not PASSWORD_PROTECTION_ENABLED or input_password == PASSWORD: return gr.update(visible=False), gr.update(visible=True), input_password, gr.update(visible=False) else: return gr.update(value="", interactive=True), gr.update(visible=False), "", gr.update(value="Invalid password. Please try again.", visible=True) def reset_form(): """Reset all input fields.""" return "", "" def reset_guidelines(): """Reset guidelines to default from file.""" return DEFAULT_GUIDELINES # Define load functions for each example def load_example_0(): return EMAIL_EXAMPLES[0]["preceding_conversation"], EMAIL_EXAMPLES[0]["drafted_user_reply"] def load_example_1(): return EMAIL_EXAMPLES[1]["preceding_conversation"], EMAIL_EXAMPLES[1]["drafted_user_reply"] def load_example_2(): return EMAIL_EXAMPLES[2]["preceding_conversation"], EMAIL_EXAMPLES[2]["drafted_user_reply"] def load_example_3(): return EMAIL_EXAMPLES[3]["preceding_conversation"], EMAIL_EXAMPLES[3]["drafted_user_reply"] def load_example_4(): return EMAIL_EXAMPLES[4]["preceding_conversation"], EMAIL_EXAMPLES[4]["drafted_user_reply"] # Build the Gradio interface with gr.Blocks(title="Email Assistant") as demo: # Session password state session_password = gr.State("") # Password interface - initially hidden if password protection is disabled with gr.Column(visible=PASSWORD_PROTECTION_ENABLED) as password_interface: gr.Markdown("# Email Assistant") gr.Markdown("Please enter the password to access the application.") password_input = gr.Textbox( type="password", label="Enter Password", interactive=True ) submit_button = gr.Button("Submit") error_message = gr.Textbox( label="Error", visible=False, interactive=False ) # Main application interface - initially visible if password protection is disabled with gr.Column(visible=not PASSWORD_PROTECTION_ENABLED) as app_interface: gr.Markdown("# Email Assistant") gr.Markdown("This tool helps you improve your email responses using AI.") # Create empty containers for our layout examples_section = gr.Column() # Container for examples main_inputs = gr.Column() # Container for textboxes buttons_section = gr.Row() # Container for action buttons outputs_section = gr.Column() # Container for outputs advanced_section = gr.Column() # Container for advanced options # Define input textboxes first (for variable references), but don't render yet with gr.Column(visible=True) as temp_container: preceding_conversation = gr.Textbox( label="Email Thread Context", placeholder="Paste the complete email thread that you want to reply to...", lines=15, render=False # Don't render immediately ) drafted_user_reply = gr.Textbox( label="Your Draft Reply", placeholder="Type your draft reply here. The assistant will help improve it.", lines=8, render=False # Don't render immediately ) # Define the advanced options for later use (but don't render them yet) with gr.Accordion("Advanced Options", open=False, render=False) as advanced_options: with gr.Row(): with gr.Column(scale=2): system_prompt = gr.Textbox( value="You are a helpful assistant specialized in email communication. Help the user craft a professional, clear, and effective email response.", label="System Instructions", lines=2 ) with gr.Column(scale=1): model_choice = gr.Dropdown( choices=list(AVAILABLE_MODELS.keys()), value=list(AVAILABLE_MODELS.keys())[0], label="Select Model" ) with gr.Row(): with gr.Column(scale=1): max_tokens = gr.Slider( minimum=1, maximum=4096, value=1024, step=100, label="Max Tokens" ) with gr.Column(scale=1): temperature = gr.Slider( minimum=0.1, maximum=1.0, value=0.7, step=0.1, label="Temperature" ) with gr.Column(scale=1): top_p = gr.Slider( minimum=0.1, maximum=1.0, value=0.95, step=0.05, label="Top-p" ) with gr.Row(): identity = gr.Textbox( value="", label="Reply Identity (optional)", placeholder="Enter the perspective this email is being written from (e.g., 'Sales Manager', 'Customer Support')", lines=1 ) with gr.Row(): with gr.Column(): email_guidelines = gr.Textbox( value=DEFAULT_GUIDELINES, label="Email Formatting Guidelines", lines=10, placeholder="Enter custom email formatting guidelines here..." ) with gr.Row(): reset_guidelines_button = gr.Button("Reset Guidelines to Default") # Define output components (but don't render yet) assistant_response = gr.Textbox( label="Improved Email Response", lines=10, render=False ) suggestions = gr.Textbox( label="Suggestions", lines=5, render=False ) # Now populate each section in the desired order # 1. Examples section first (at the top) with examples_section: gr.Markdown("### Click to Load Example Email Threads and Draft Replies") with gr.Row(): for i in range(min(5, len(EMAIL_EXAMPLES))): gr.Button( EMAIL_EXAMPLES[i]["title"], size="sm", variant="secondary", ).click( fn=globals()[f"load_example_{i}"], inputs=[], outputs=[preceding_conversation, drafted_user_reply] ) # 2. Main input textboxes next with main_inputs: # Render the textboxes here preceding_conversation.render() drafted_user_reply.render() # 3. Action buttons with buttons_section: clear_button = gr.Button("Clear") process_button = gr.Button("Process Email", variant="primary") # 4. Output section with outputs_section: # Render the output components here assistant_response.render() suggestions.render() # 5. Advanced options last (at the bottom) with advanced_section: # Render the advanced options accordion here advanced_options.render() # Set up the processing function process_button.click( fn=process_email_wrapper, inputs=[ preceding_conversation, drafted_user_reply, session_password, system_prompt, model_choice, max_tokens, temperature, top_p, email_guidelines, identity ], outputs=[assistant_response, suggestions] ) # Set up the clear function clear_button.click( fn=reset_form, inputs=[], outputs=[preceding_conversation, drafted_user_reply] ) # Set up the reset guidelines function reset_guidelines_button.click( fn=reset_guidelines, inputs=[], outputs=[email_guidelines] ) # Password validation submit_button.click( fn=check_password, inputs=password_input, outputs=[password_interface, app_interface, session_password, error_message] ) if __name__ == "__main__": demo.launch()