ProximileAdmin commited on
Commit
f35057b
·
verified ·
1 Parent(s): cdec834

Upload 5 files

Browse files
Files changed (5) hide show
  1. app.py +258 -0
  2. email_examples.json +27 -0
  3. email_guidelines.txt +58 -0
  4. email_processor.py +204 -0
  5. requirements.txt +4 -0
app.py ADDED
@@ -0,0 +1,258 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import gradio as gr
3
+ from email_processor import (
4
+ load_examples,
5
+ setup_clients,
6
+ process_email,
7
+ AVAILABLE_MODELS,
8
+ load_email_guidelines
9
+ )
10
+
11
+ # Initialize global variables
12
+ PASSWORD = os.getenv("PASSWD")
13
+ EMAIL_EXAMPLES = load_examples()
14
+ CLIENTS = setup_clients()
15
+ DEFAULT_GUIDELINES = load_email_guidelines()
16
+
17
+ def process_email_wrapper(
18
+ preceding_conversation,
19
+ drafted_user_reply,
20
+ session_password,
21
+ system_message,
22
+ model_choice,
23
+ max_tokens,
24
+ temperature,
25
+ top_p,
26
+ custom_guidelines
27
+ ):
28
+ """
29
+ Wrapper function to call the email processor with all necessary parameters.
30
+ This separates the Gradio interface from the core processing logic.
31
+ """
32
+ return process_email(
33
+ preceding_conversation,
34
+ drafted_user_reply,
35
+ session_password,
36
+ system_message,
37
+ model_choice,
38
+ max_tokens,
39
+ temperature,
40
+ top_p,
41
+ PASSWORD,
42
+ CLIENTS,
43
+ custom_guidelines
44
+ )
45
+
46
+ def check_password(input_password):
47
+ """
48
+ Validates the password and, if valid, shows the interface.
49
+
50
+ Returns updates to UI components based on password validation.
51
+ """
52
+ if input_password == PASSWORD:
53
+ return gr.update(visible=False), gr.update(visible=True), input_password, gr.update(visible=False)
54
+ else:
55
+ return gr.update(value="", interactive=True), gr.update(visible=False), "", gr.update(value="Invalid password. Please try again.", visible=True)
56
+
57
+ def reset_form():
58
+ """Reset all input fields."""
59
+ return "", ""
60
+
61
+ def reset_guidelines():
62
+ """Reset guidelines to default from file."""
63
+ return DEFAULT_GUIDELINES
64
+
65
+ # Define load functions for each example
66
+ def load_example_0():
67
+ return EMAIL_EXAMPLES[0]["preceding_conversation"], EMAIL_EXAMPLES[0]["drafted_user_reply"]
68
+
69
+ def load_example_1():
70
+ return EMAIL_EXAMPLES[1]["preceding_conversation"], EMAIL_EXAMPLES[1]["drafted_user_reply"]
71
+
72
+ def load_example_2():
73
+ return EMAIL_EXAMPLES[2]["preceding_conversation"], EMAIL_EXAMPLES[2]["drafted_user_reply"]
74
+
75
+ def load_example_3():
76
+ return EMAIL_EXAMPLES[3]["preceding_conversation"], EMAIL_EXAMPLES[3]["drafted_user_reply"]
77
+
78
+ def load_example_4():
79
+ return EMAIL_EXAMPLES[4]["preceding_conversation"], EMAIL_EXAMPLES[4]["drafted_user_reply"]
80
+
81
+ # Build the Gradio interface
82
+ with gr.Blocks(title="Email Assistant") as demo:
83
+ # Session password state
84
+ session_password = gr.State("")
85
+
86
+ # Password interface
87
+ with gr.Column() as password_interface:
88
+ gr.Markdown("# Email Assistant")
89
+ gr.Markdown("Please enter the password to access the application.")
90
+ password_input = gr.Textbox(
91
+ type="password", label="Enter Password", interactive=True
92
+ )
93
+ submit_button = gr.Button("Submit")
94
+ error_message = gr.Textbox(
95
+ label="Error", visible=False, interactive=False
96
+ )
97
+
98
+ # Main application interface (hidden initially)
99
+ with gr.Column(visible=False) as app_interface:
100
+ gr.Markdown("# Email Assistant")
101
+ gr.Markdown("This tool helps you improve your email responses using AI.")
102
+
103
+ # Create empty containers for our layout
104
+ examples_section = gr.Column() # Container for examples
105
+ main_inputs = gr.Column() # Container for textboxes
106
+ buttons_section = gr.Row() # Container for action buttons
107
+ outputs_section = gr.Column() # Container for outputs
108
+ advanced_section = gr.Column() # Container for advanced options
109
+
110
+ # Define input textboxes first (for variable references), but don't render yet
111
+ with gr.Column(visible=True) as temp_container:
112
+ preceding_conversation = gr.Textbox(
113
+ label="Email Thread Context",
114
+ placeholder="Paste the complete email thread that you want to reply to...",
115
+ lines=15,
116
+ render=False # Don't render immediately
117
+ )
118
+
119
+ drafted_user_reply = gr.Textbox(
120
+ label="Your Draft Reply",
121
+ placeholder="Type your draft reply here. The assistant will help improve it.",
122
+ lines=8,
123
+ render=False # Don't render immediately
124
+ )
125
+
126
+ # Define the advanced options for later use (but don't render them yet)
127
+ with gr.Accordion("Advanced Options", open=False, render=False) as advanced_options:
128
+ with gr.Row():
129
+ with gr.Column(scale=2):
130
+ system_prompt = gr.Textbox(
131
+ value="You are a helpful assistant specialized in email communication. Help the user craft a professional, clear, and effective email response.",
132
+ label="System Instructions",
133
+ lines=2
134
+ )
135
+ with gr.Column(scale=1):
136
+ model_choice = gr.Dropdown(
137
+ choices=list(AVAILABLE_MODELS.keys()),
138
+ value=list(AVAILABLE_MODELS.keys())[0],
139
+ label="Select Model"
140
+ )
141
+
142
+ with gr.Row():
143
+ with gr.Column(scale=1):
144
+ max_tokens = gr.Slider(
145
+ minimum=1, maximum=4096, value=1024, step=100, label="Max Tokens"
146
+ )
147
+ with gr.Column(scale=1):
148
+ temperature = gr.Slider(
149
+ minimum=0.1, maximum=1.0, value=0.7, step=0.1, label="Temperature"
150
+ )
151
+ with gr.Column(scale=1):
152
+ top_p = gr.Slider(
153
+ minimum=0.1, maximum=1.0, value=0.95, step=0.05, label="Top-p"
154
+ )
155
+
156
+ with gr.Row():
157
+ with gr.Column():
158
+ email_guidelines = gr.Textbox(
159
+ value=DEFAULT_GUIDELINES,
160
+ label="Email Formatting Guidelines",
161
+ lines=10,
162
+ placeholder="Enter custom email formatting guidelines here..."
163
+ )
164
+ with gr.Row():
165
+ reset_guidelines_button = gr.Button("Reset Guidelines to Default")
166
+
167
+ # Define output components (but don't render yet)
168
+ assistant_response = gr.Textbox(
169
+ label="Improved Email Response",
170
+ lines=10,
171
+ render=False
172
+ )
173
+
174
+ suggestions = gr.Textbox(
175
+ label="Suggestions",
176
+ lines=5,
177
+ render=False
178
+ )
179
+
180
+ # Now populate each section in the desired order
181
+
182
+ # 1. Examples section first (at the top)
183
+ with examples_section:
184
+ gr.Markdown("### Click to Load Example Email Threads and Draft Replies")
185
+ with gr.Row():
186
+ for i in range(min(5, len(EMAIL_EXAMPLES))):
187
+ gr.Button(
188
+ EMAIL_EXAMPLES[i]["title"],
189
+ size="sm",
190
+ variant="secondary",
191
+ ).click(
192
+ fn=globals()[f"load_example_{i}"],
193
+ inputs=[],
194
+ outputs=[preceding_conversation, drafted_user_reply]
195
+ )
196
+
197
+ # 2. Main input textboxes next
198
+ with main_inputs:
199
+ # Render the textboxes here
200
+ preceding_conversation.render()
201
+ drafted_user_reply.render()
202
+
203
+ # 3. Action buttons
204
+ with buttons_section:
205
+ clear_button = gr.Button("Clear")
206
+ process_button = gr.Button("Process Email", variant="primary")
207
+
208
+ # 4. Output section
209
+ with outputs_section:
210
+ # Render the output components here
211
+ assistant_response.render()
212
+ suggestions.render()
213
+
214
+ # 5. Advanced options last (at the bottom)
215
+ with advanced_section:
216
+ # Render the advanced options accordion here
217
+ advanced_options.render()
218
+
219
+ # Set up the processing function
220
+ process_button.click(
221
+ fn=process_email_wrapper,
222
+ inputs=[
223
+ preceding_conversation,
224
+ drafted_user_reply,
225
+ session_password,
226
+ system_prompt,
227
+ model_choice,
228
+ max_tokens,
229
+ temperature,
230
+ top_p,
231
+ email_guidelines
232
+ ],
233
+ outputs=[assistant_response, suggestions]
234
+ )
235
+
236
+ # Set up the clear function
237
+ clear_button.click(
238
+ fn=reset_form,
239
+ inputs=[],
240
+ outputs=[preceding_conversation, drafted_user_reply]
241
+ )
242
+
243
+ # Set up the reset guidelines function
244
+ reset_guidelines_button.click(
245
+ fn=reset_guidelines,
246
+ inputs=[],
247
+ outputs=[email_guidelines]
248
+ )
249
+
250
+ # Password validation
251
+ submit_button.click(
252
+ fn=check_password,
253
+ inputs=password_input,
254
+ outputs=[password_interface, app_interface, session_password, error_message]
255
+ )
256
+
257
+ if __name__ == "__main__":
258
+ demo.launch()
email_examples.json ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [
2
+ {
3
+ "title": "Project Timeline Negotiation",
4
+ "preceding_conversation": "From: sarah.johnson@company.com\nTo: michael.roberts@client.org\nSubject: Re: Project Timeline Concerns\n\nMichael,\n\nI've reviewed the timeline concerns you raised in our meeting yesterday. While I understand your desire to accelerate delivery, the current schedule already represents our best-case scenario given resource constraints.\n\nMoving up deliverables as you suggested would require us to:\n1. Reallocate team members from other active projects\n2. Skip certain QA testing phases\n3. Potentially compromise on some of the feature refinements you previously indicated were priorities\n\nCould we schedule a call tomorrow to discuss specific milestones that are most critical for your team? Perhaps we can find a middle ground that addresses your most pressing needs while maintaining overall quality.\n\nBest regards,\nSarah Johnson\nProject Manager\n\n---\n\nFrom: michael.roberts@client.org\nTo: sarah.johnson@company.com\nSubject: Re: Re: Project Timeline Concerns\n\nSarah,\n\nThanks for your prompt response. I appreciate you taking our concerns seriously.\n\nWhile I understand your constraints, our leadership team is facing significant pressure from stakeholders regarding the Q3 launch. Even a two-week acceleration on the core functionality would make a substantial difference for us.\n\nI'm available for a call tomorrow between 2-4pm EST. Could we also invite Raj from your technical team? He mentioned some potential optimizations during our last review that might help thread this needle.\n\nLooking forward to finding a solution,\nMichael\n\n---\n\nFrom: sarah.johnson@company.com\nTo: michael.roberts@client.org\nSubject: Re: Re: Re: Project Timeline Concerns\n\nMichael,\n\nI've scheduled us for 2:30pm EST tomorrow and included Raj in the invitation. He's prepared to discuss those potential optimizations, though I should note they would involve some trade-offs we should evaluate together.\n\nBefore our call, it would be helpful if you could prioritize the specific features needed for your Q3 launch versus those that could potentially shift to a secondary release. This would give us concrete areas to focus our acceleration efforts.\n\nI've attached a spreadsheet template you can use to indicate these priorities.\n\nBest regards,\nSarah",
5
+ "drafted_user_reply": "Need to thank Sarah for setting up the meeting and tell her I've completed the spreadsheet with our priorities. Should mention that our CEO wants to join part of the call for 15 minutes at the beginning. Also need to ask if there's anything else I should prepare before the meeting tomorrow. Should keep it friendly but emphasize how important this timeline adjustment is for us."
6
+ },
7
+ {
8
+ "title": "Customer Service Complaint Escalation",
9
+ "preceding_conversation": "From: jason.nguyen@personal.net\nTo: support@onlinestore.com\nSubject: Order #39572 - Damaged Item Received\n\nTo Whom It May Concern,\n\nI received my order (#39572) yesterday, and unfortunately, the ceramic vase arrived broken into several pieces. The packaging appeared intact, so the damage likely occurred before shipping.\n\nI've attached photos of the damaged item and packaging. I would like to request a replacement or refund as soon as possible, as this was intended as a gift.\n\nThank you for your assistance,\nJason Nguyen\n\n---\n\nFrom: support@onlinestore.com\nTo: jason.nguyen@personal.net\nSubject: Re: Order #39572 - Damaged Item Received\n\nDear Jason,\n\nThank you for bringing this to our attention. I'm sorry to hear about the damaged vase from your order #39572.\n\nI've reviewed the photos you provided, and I've initiated a replacement order for you. The new vase should ship within 2-3 business days. You don't need to return the damaged item.\n\nPlease let me know if you have any other questions or concerns.\n\nBest regards,\nEmma Patterson\nCustomer Support Representative\nOnlineStore.com\n\n---\n\nFrom: jason.nguyen@personal.net\nTo: support@onlinestore.com\nSubject: Re: Re: Order #39572 - Damaged Item Received\n\nDear Emma,\n\nThank you for your quick response and for arranging the replacement. I appreciate your handling of this issue.\n\nHowever, I'm concerned about the 2-3 day shipping timeframe. As mentioned, this is a gift for an event this Saturday (just 4 days away). Is there any possibility of expediting the shipping to ensure it arrives by Friday at the latest?\n\nI'd be willing to pay a reasonable expedited shipping fee if necessary.\n\nThank you for your understanding,\nJason",
10
+ "drafted_user_reply": "Need to be apologetic and helpful. Should offer complimentary expedited shipping since this was our mistake. Can mention that I've upgraded his shipping to overnight delivery at no cost, and the replacement will arrive by Thursday. Maybe throw in a small discount code for future purchase to make up for the inconvenience. Ask him to confirm if Thursday delivery will work for his event on Saturday."
11
+ },
12
+ {
13
+ "title": "Job Application Follow-Up",
14
+ "preceding_conversation": "From: alex.rivera@personal.net\nTo: hiring@techfirm.com\nSubject: Application for Senior Developer Position\n\nDear Hiring Manager,\n\nI'm writing to apply for the Senior Developer position advertised on your careers page. With over eight years of experience in full-stack development and a specialization in cloud architecture, I believe I'd be a strong addition to your team.\n\nMy experience at InnovateTech involved leading a team of five developers to rebuild their customer portal, resulting in a 40% improvement in user engagement and a significant reduction in support tickets.\n\nI've attached my resume and portfolio for your review. I'm particularly excited about TechFirm's work in the healthcare sector, as my previous project with MediConnect aligns well with your current initiatives.\n\nI look forward to the opportunity to discuss how my skills and experience could benefit TechFirm.\n\nBest regards,\nAlex Rivera\n\n---\n\nFrom: hiring@techfirm.com\nTo: alex.rivera@personal.net\nSubject: Re: Application for Senior Developer Position\n\nDear Alex,\n\nThank you for your application for the Senior Developer position at TechFirm. Your experience, particularly your work at InnovateTech and MediConnect, is impressive.\n\nI'm pleased to inform you that we would like to arrange an initial interview. This would be a 45-minute video call with our Technical Director, Priya Sharma, and myself.\n\nCould you please provide your availability for next week? We're generally flexible between 9am-5pm EST, Monday through Thursday.\n\nIf you have any questions about the interview process or position before then, please don't hesitate to ask.\n\nRegards,\nMarcus Chen\nTalent Acquisition Specialist\nTechFirm Inc.\n\n---\n\nFrom: alex.rivera@personal.net\nTo: hiring@techfirm.com\nSubject: Re: Re: Application for Senior Developer Position\n\nDear Marcus,\n\nThank you for your prompt response. I'm delighted to be considered for an interview for the Senior Developer position.\n\nI'm available next week at the following times (EST):\n- Monday: 9am-12pm\n- Tuesday: 1pm-5pm\n- Wednesday: 9am-11am, 3pm-5pm\n- Thursday: 10am-5pm\n\nWould it be possible to learn more about the format of the interview? I'd appreciate knowing if I should prepare for specific technical questions or if there will be a coding exercise to complete.\n\nI'm looking forward to meeting with you and Priya.\n\nBest regards,\nAlex Rivera",
15
+ "drafted_user_reply": "Need to confirm the interview for Wednesday at 10am EST. Should include the Zoom link and calendar invitation. Should explain that the first 30 minutes will be behavioral and experience questions, followed by 15 minutes of technical discussion about system design principles (no coding exercise during the interview). Should mention that Priya is especially interested in discussing my MediConnect experience since it relates to their current project. Need to ask candidate to complete a brief coding assessment via our online platform after the interview if we decide to move forward."
16
+ },
17
+ {
18
+ "title": "Vendor Negotiation",
19
+ "preceding_conversation": "From: procurement@manufacturerinc.com\nTo: sales@supplierco.com\nSubject: Bulk Order Quote Request - Industrial Pumps\n\nDear SupplierCo Sales Team,\n\nManufacturerInc is planning to upgrade the equipment in our Dallas and Phoenix facilities over the next quarter. We're interested in obtaining a quote for:\n\n- 12 Model X500 Industrial Pumps\n- 8 Model S350 Compact Pumps\n- Necessary installation kits and 3-year maintenance package\n\nBased on our long-standing relationship and previous bulk orders, we anticipate a competitive price point. Could you please provide a detailed quote including unit costs, bulk discount, estimated delivery timeframe, and warranty information?\n\nWe're evaluating several suppliers and hope to make a decision within the next three weeks.\n\nRegards,\nLinda Torres\nProcurement Manager\nManufacturerInc\n\n---\n\nFrom: sales@supplierco.com\nTo: procurement@manufacturerinc.com\nSubject: Re: Bulk Order Quote Request - Industrial Pumps\n\nDear Linda,\n\nThank you for considering SupplierCo for your equipment upgrade project. I've prepared a quote for the items you requested:\n\n- 12 Model X500 Industrial Pumps: $4,200 each ($50,400 total)\n- 8 Model S350 Compact Pumps: $2,800 each ($22,400 total)\n- Installation kits: $450 per pump ($9,000 total)\n- 3-year maintenance package: $15,000\n\nSubtotal: $96,800\nBulk order discount (10%): -$9,680\nTotal: $87,120\n\nEstimated delivery timeframe: 6-8 weeks from order confirmation\nWarranty: 5-year manufacturer warranty on all pumps\n\nPlease note that our current lead time is longer than usual due to ongoing supply chain challenges. If this timeline works for your project schedule, we can guarantee these prices for 30 days.\n\nI'm happy to discuss any questions or concerns.\n\nBest regards,\nDavid Park\nSenior Account Manager\nSupplierCo\n\n---\n\nFrom: procurement@manufacturerinc.com\nTo: sales@supplierco.com\nSubject: Re: Re: Bulk Order Quote Request - Industrial Pumps\n\nDavid,\n\nThank you for the detailed quote. While your pricing for the pumps themselves is competitive, we have some concerns:\n\n1. The 6-8 week delivery timeframe exceeds our project timeline requirements. Our Phoenix facility needs to be operational with the new equipment in 5 weeks.\n\n2. One of your competitors is offering a 15% bulk discount for an order of similar size.\n\n3. The installation kit pricing seems higher than what we've paid previously.\n\nWould it be possible to:\na) Expedite delivery for at least the Phoenix portion of the order (5 X500 and 3 S350 pumps)?\nb) Review the bulk discount percentage?\nc) Provide a breakdown of the installation kit components to help us understand the price increase?\n\nWe value our relationship with SupplierCo and would prefer to continue working with your team if we can address these points.\n\nRegards,\nLinda",
20
+ "drafted_user_reply": "Need to be accommodating but still protect our margins. Can offer expedited shipping for the Phoenix portion at no extra cost (we can absorb that). Should increase the bulk discount to 12% but not the full 15% they're asking for. Need to explain that installation kit prices have increased due to component costs but will include a detailed breakdown showing this. Should emphasize our superior warranty and service history with them compared to competitors. Maybe mention that if they commit within 10 days, we can throw in free operator training that normally costs $2,500."
21
+ },
22
+ {
23
+ "title": "Conference Speaking Invitation",
24
+ "preceding_conversation": "From: events@techconference.org\nTo: dr.miller@university.edu\nSubject: Invitation to Speak at TechInnovate 2025\n\nDear Dr. Miller,\n\nOn behalf of the TechInnovate Conference committee, I would like to invite you to be a keynote speaker at our upcoming event, scheduled for May 15-17, 2025, in Boston.\n\nYour recent paper on quantum computing applications in drug discovery has generated significant interest in the scientific community, and we believe our attendees would benefit greatly from hearing about your research directly from you.\n\nThe keynote session would be 45 minutes, followed by a 15-minute Q&A. We can offer:\n- Full conference pass\n- Round-trip business class airfare\n- Three nights' accommodation at the conference hotel\n- Honorarium of $3,000\n\nPlease let me know if you would be interested in this opportunity. We're finalizing our speaker lineup by the end of next month, so we hope to hear from you soon.\n\nBest regards,\nSophia Chen\nProgram Chair\nTechInnovate Conference\n\n---\n\nFrom: dr.miller@university.edu\nTo: events@techconference.org\nSubject: Re: Invitation to Speak at TechInnovate 2025\n\nDear Sophia,\n\nThank you for the invitation to speak at TechInnovate 2025. I'm honored to be considered for a keynote presentation.\n\nIn principle, I am interested in participating. However, I have a few questions before confirming:\n\n1. Would there be an opportunity to also participate in a panel discussion on emerging technologies? I believe this could provide additional value to attendees.\n\n2. My research team includes two post-doctoral fellows who contributed significantly to the work. Would it be possible to include them as co-presenters for a portion of the talk?\n\n3. Are speakers expected to submit a paper for the conference proceedings, or would the presentation materials be sufficient?\n\nI look forward to your response.\n\nBest regards,\nDr. Taylor Miller\nQuantum Computing Research Lab\nUniversity Science Department\n\n---\n\nFrom: events@techconference.org\nTo: dr.miller@university.edu\nSubject: Re: Re: Invitation to Speak at TechInnovate 2025\n\nDear Dr. Miller,\n\nThank you for your prompt response and for considering our invitation.\n\nRegarding your questions:\n\n1. We would be delighted to have you participate in our \"Future of Computing\" panel discussion on May 16th. This would be in addition to your keynote address.\n\n2. While our keynote slots are typically reserved for a single presenter, we can certainly accommodate your post-doctoral fellows as co-presenters. Perhaps they could present for 15 minutes of your 45-minute slot, with you handling the introduction, main concepts, and conclusion.\n\n3. A full paper isn't required for keynote speakers. We would only need your presentation slides one week before the event and permission to share them with attendees afterward.\n\nAdditionally, all speakers and co-presenters will receive full conference passes, though the travel and accommodation allowance is for the primary speaker only.\n\nPlease let me know if these arrangements would work for you.\n\nBest regards,\nSophia Chen\nProgram Chair\nTechInnovate Conference",
25
+ "drafted_user_reply": "Need to officially accept the invitation now. Should thank Sophia for accommodating my requests about the panel and including my post-docs. Need to confirm my understanding of the dates (keynote on the 15th and panel on the 16th). Should ask about technical requirements for the presentation (projector resolution, audio needs, etc.) and whether there will be a rehearsal opportunity before the keynote. Should mention that I'll need a vegetarian meal option if food is provided. Ask about the deadline for submitting a bio and headshot for promotional materials."
26
+ }
27
+ ]
email_guidelines.txt ADDED
@@ -0,0 +1,58 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ 1. Alignment with Brand Identity
2
+
3
+ Ensure all communications reflect the organization's brand identity through consistent use of typography, color schemes, and design elements.​
4
+ Typography
5
+
6
+ Typeface: Utilize the organization's designated typeface across all communications to maintain brand consistency.​
7
+ Font Size: Maintain a minimum font size of 12pt to ensure readability across various devices.​
8
+
9
+ Color Scheme
10
+
11
+ Primary Colors: Adhere to the organization's primary color palette for headings and hyperlinks.​
12
+ Secondary Colors: Use approved secondary colors for body text and backgrounds to maintain a professional aesthetic.​
13
+
14
+ Graphics and Logos
15
+
16
+ Logo Placement: Position the organization's logo in the upper-left corner of communications, following the established grid system for visual consistency.​
17
+ Imagery: Ensure all images align with the organization's photography guidelines and are approved by the brand governance team before use.​
18
+
19
+ 2. Structural Standards for Communication Composition
20
+
21
+ Structure communications to prioritize clarity and actionability, facilitating easy comprehension and response.​
22
+ Subject Lines
23
+
24
+ Conciseness: Limit subject lines to 50–60 characters to prevent truncation on mobile devices.​
25
+
26
+ Specificity: Incorporate keywords such as "Action Required," "Deadline," or "Update" to indicate urgency.​
27
+ Example: "Q3 KPI Review – Action Required by March 15"​
28
+
29
+ Body Content
30
+
31
+ Greetings: Use formal salutations like "Dear [First Name] [Last Name]" to maintain professionalism.​
32
+ Paragraph Structure: Keep paragraphs to 2–3 sentences, separated by blank lines to enhance readability.​
33
+ Call-to-Action (CTA): Highlight CTAs in bold or the organization's primary color, embedding hyperlinks with descriptive anchor text.​
34
+
35
+ Signatures
36
+
37
+ Adopt a uniform signature format for all communications, including:​
38
+
39
+ [Full Name]
40
+ [Job Title] | [Department] | [Organization Name]
41
+ Phone: [+1 (XXX) XXX-XXXX] | Email: [alias@organization.com]
42
+ *“Organization's Tagline or Motto.”*
43
+
44
+ Exclude social media icons and promotional links in internal communications; reserve them for external marketing materials.​
45
+ 3. Accessibility and Inclusivity Protocols
46
+
47
+ Ensure communications are accessible and inclusive, adhering to established accessibility standards.​
48
+
49
+ Alt Text: Include descriptive alt text for all images to accommodate screen readers.​
50
+ Inclusive Language: Use gender-neutral terms (e.g., "they/them") and avoid culturally specific idioms to ensure inclusivity.​
51
+
52
+ 4. Technical Specifications
53
+
54
+ Adhere to technical standards to ensure compatibility and optimal rendering across various platforms.​
55
+
56
+ HTML vs. Plain Text: Prefer HTML for newsletters but provide plain-text alternatives for compatibility with older clients.​
57
+ Responsive Design: Use single-column layouts (500–700px width) and fluid tables to ensure consistency across email clients and devices.​
58
+ CSS Inlining: Inline all CSS styles to prevent stripping by email clients like Gmail.​
email_processor.py ADDED
@@ -0,0 +1,204 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import json
3
+ from openai import OpenAI
4
+ from markdown import markdown
5
+ from bs4 import BeautifulSoup
6
+
7
+ # Define available models
8
+ AVAILABLE_MODELS = {
9
+ "DeepSeek V3 (Hyperbolic.xyz)": {
10
+ "model_name": "deepseek-ai/DeepSeek-V3",
11
+ "type": "hyperbolic"
12
+ },
13
+ "Llama3.3-70b-Instruct (Hyperbolic.xyz)": {
14
+ "model_name": "meta-llama/Llama-3.3-70B-Instruct",
15
+ "type": "hyperbolic"
16
+ },
17
+ #"DeepSeek V3 (HuggingFace.co)": {
18
+ # "model_name": "deepseek-ai/DeepSeek-V3",
19
+ # "type": "huggingface"
20
+ #},
21
+ }
22
+
23
+ # Load email examples from JSON file
24
+ def load_examples():
25
+ """
26
+ Load example emails from a JSON file.
27
+ Returns a list of example dictionaries or a default empty example if loading fails.
28
+ """
29
+ try:
30
+ with open("email_examples.json", "r") as f:
31
+ return json.load(f)
32
+ except Exception as e:
33
+ print(f"Error loading examples: {e}")
34
+ # Return default empty examples if file can't be loaded
35
+ return [
36
+ {"title": "No examples found", "preceding_conversation": "", "drafted_user_reply": ""}
37
+ ]
38
+
39
+ # Set up the clients for API access
40
+ def setup_clients():
41
+ """
42
+ Initialize and configure API clients for different model providers.
43
+ Returns a dictionary of configured clients.
44
+ """
45
+ clients = {
46
+ "hyperbolic": {"key": os.getenv('HYPERBOLIC_XYZ_KEY'), "endpoint": "https://api.hyperbolic.xyz/v1"},
47
+ #"huggingface": {"key": os.getenv('HF_KEY'), "endpoint": "https://huggingface.co/api/inference-proxy/together"},
48
+ }
49
+
50
+ for client_type in clients:
51
+ clients[client_type]["client"] = OpenAI(
52
+ base_url=clients[client_type]["endpoint"],
53
+ api_key=clients[client_type]["key"]
54
+ )
55
+
56
+ return clients
57
+
58
+ def markdown_to_text(markdown_string):
59
+ # Convert Markdown to HTML
60
+ html = markdown(markdown_string)
61
+ # Parse HTML and extract text
62
+ soup = BeautifulSoup(html, features="html.parser")
63
+ return soup.get_text()
64
+
65
+ def load_email_guidelines():
66
+ """
67
+ Load email formatting guidelines from a text file.
68
+ Returns the content as a string or a default message if loading fails.
69
+ """
70
+ try:
71
+ with open("email_guidelines.txt", "r") as f:
72
+ return f.read().strip()
73
+ except Exception as e:
74
+ print(f"Error loading email guidelines: {e}")
75
+ return "No guidelines available."
76
+
77
+ def create_email_format_prompt(email_guidelines):
78
+ """
79
+ Create the prompt template that will be sent to the language model.
80
+ Incorporates the provided email guidelines and placeholders for user input.
81
+
82
+ Args:
83
+ email_guidelines: The guidelines text to include in the prompt
84
+ """
85
+ return """<EMAIL_GUIDELINES>
86
+ """ + email_guidelines + """
87
+ </EMAIL_GUIDELINES>
88
+ =========================
89
+
90
+ Your Task:
91
+ 1. Review the user's draft email.
92
+ 2. Apply the above style, formatting, and general content guidelines to the email.
93
+ 3. Output a reformatted email ready for use.
94
+
95
+ The following email thread precedes the user's draft email:
96
+ <PREVIOUS_EMAILS>
97
+ {preceding_conversation}
98
+ </PREVIOUS_EMAILS>
99
+
100
+ The rough draft of the user's email is as follows:
101
+ <USER_EMAIL_DRAFT>
102
+ {drafted_user_reply}
103
+ </USER_EMAIL_DRAFT>
104
+
105
+ Your task is to rewrite the user's email draft to align with the provided guidelines.
106
+ Begin your reply with the exact string EMAIL_STARTS_HERE followed by the revised email.
107
+ If you have additional suggestions, include them only AFTER the revised email draft, separated by the string SUGGESTIONS_START_HERE.
108
+ Do not assume any new information or context beyond what is provided in the user's immediate draft email. Do not add any new URLs, attachments, phone numbers, or other content beyond what is provided in the user's immediate draft email. Do not add a subject line. Begin the email body immediately after EMAIL_STARTS_HERE. Include the user's typical signature at the end of the email, if they have one. Do not use info (even placeholders) that is not provided in the user's immediate draft email or in the preceding email thread (even if the guidelines call for it). Do not use markdown formatting. Assume your redrafted reply message might be sent by the user after only a cursory review."""
109
+
110
+ def process_email(
111
+ preceding_conversation,
112
+ drafted_user_reply,
113
+ session_password,
114
+ system_message,
115
+ model_choice,
116
+ max_tokens,
117
+ temperature,
118
+ top_p,
119
+ password,
120
+ clients,
121
+ custom_guidelines=None
122
+ ):
123
+ """
124
+ Process the email and return a formatted response and suggestions.
125
+
126
+ Args:
127
+ preceding_conversation: The email thread context
128
+ drafted_user_reply: The user's draft email reply
129
+ session_password: The current session password
130
+ system_message: System instructions for the AI model
131
+ model_choice: The selected AI model
132
+ max_tokens: Maximum tokens for the response
133
+ temperature: Temperature setting for response generation
134
+ top_p: Top-p setting for response generation
135
+ password: The actual password to verify against
136
+ clients: Dictionary of configured API clients
137
+ custom_guidelines: Optional custom guidelines to use instead of default
138
+
139
+ Returns:
140
+ Tuple of (formatted_email, suggestions)
141
+ """
142
+ # Re-check the session password
143
+ if session_password != password:
144
+ return "Error: Invalid session password. Please refresh the page and enter the correct password.", ""
145
+
146
+ if model_choice not in AVAILABLE_MODELS:
147
+ return "Error: Invalid model selection.", ""
148
+
149
+ # Use custom guidelines if provided, otherwise load from file
150
+ if custom_guidelines is None or custom_guidelines.strip() == "":
151
+ email_guidelines = load_email_guidelines()
152
+ else:
153
+ email_guidelines = custom_guidelines
154
+
155
+ # Get the email format prompt template
156
+ email_format_prompt = create_email_format_prompt(email_guidelines)
157
+
158
+ # Format the prompt using the template
159
+ formatted_prompt = email_format_prompt.format(
160
+ preceding_conversation=preceding_conversation,
161
+ drafted_user_reply=drafted_user_reply,
162
+ )
163
+
164
+ messages = [{"role": "system", "content": system_message}]
165
+ messages.append({"role": "user", "content": formatted_prompt})
166
+
167
+ selected_client = clients[AVAILABLE_MODELS[model_choice]["type"]]["client"]
168
+
169
+ try:
170
+ response = selected_client.chat.completions.create(
171
+ model=AVAILABLE_MODELS[model_choice]["model_name"],
172
+ messages=messages,
173
+ max_tokens=max_tokens,
174
+ temperature=temperature,
175
+ top_p=top_p,
176
+ )
177
+
178
+ # Split the response based on EMAIL_STARTS_HERE and SUGGESTIONS_START_HERE
179
+ full_response = response.choices[0].message.content
180
+
181
+ #remove markdown formatting
182
+ full_response = markdown_to_text(full_response)
183
+
184
+ # First check if EMAIL_STARTS_HERE is in the response
185
+ if "EMAIL_STARTS_HERE" in full_response:
186
+ email_parts = full_response.split("EMAIL_STARTS_HERE", 1)
187
+ email_content = email_parts[1].strip()
188
+
189
+ # Then check if there are suggestions
190
+ if "SUGGESTIONS_START_HERE" in email_content:
191
+ parts = email_content.split("SUGGESTIONS_START_HERE", 1)
192
+ return parts[0].strip(), parts[1].strip()
193
+ else:
194
+ return email_content.strip(), ""
195
+ else:
196
+ # Fallback to original behavior if marker isn't found
197
+ if "SUGGESTIONS_START_HERE" in full_response:
198
+ parts = full_response.split("SUGGESTIONS_START_HERE", 1)
199
+ return parts[0].strip(), parts[1].strip()
200
+ else:
201
+ return full_response.strip(), ""
202
+
203
+ except Exception as e:
204
+ return f"Error: {str(e)}", ""
requirements.txt ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ openai
2
+ gradio
3
+ markdown
4
+ beautifulsoup4