File size: 10,936 Bytes
f35057b
 
 
 
 
 
 
 
 
 
 
e98ba5c
f35057b
 
 
 
 
 
 
 
 
 
 
 
 
 
b5d96ec
 
f35057b
 
 
 
94f9077
 
f35057b
94f9077
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
f35057b
 
 
 
e98ba5c
f35057b
 
 
e98ba5c
f35057b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
e98ba5c
 
f35057b
 
 
 
 
 
 
 
 
 
e98ba5c
 
f35057b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
b5d96ec
 
 
 
 
 
 
 
f35057b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
b5d96ec
 
f35057b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
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()