File size: 9,102 Bytes
06b949d
c2bc8dc
 
06b949d
c2bc8dc
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
06b949d
c2bc8dc
06b949d
 
 
 
 
 
c2bc8dc
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
06b949d
c2bc8dc
06b949d
 
 
 
 
 
 
 
 
 
c2bc8dc
 
06b949d
c2bc8dc
06b949d
c2bc8dc
06b949d
 
 
 
c2bc8dc
06b949d
 
 
c2bc8dc
06b949d
c2bc8dc
06b949d
 
 
 
 
c2bc8dc
06b949d
 
 
 
 
 
 
 
 
 
 
 
 
c2bc8dc
06b949d
 
 
 
 
 
 
 
 
c2bc8dc
06b949d
 
 
 
 
 
 
 
 
c2bc8dc
06b949d
 
 
 
 
 
 
 
 
 
 
c2bc8dc
06b949d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
c2bc8dc
06b949d
c2bc8dc
 
06b949d
 
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
import gradio as gr
import openai
import json
from typing import Dict, Any, Tuple

# Load BLUF tags from JSON
def load_bluf_tags():
    bluf_tags = [
        {
            "prefix": "ACTION:",
            "example": "ACTION: Submit Timesheets by Friday",
            "description": "Recipient must take an action."
        },
        {
            "prefix": "SIGN:",
            "example": "SIGN: Approval Needed on Contract Addendum",
            "description": "Recipient needs to sign a document."
        },
        {
            "prefix": "INFO:",
            "example": "INFO: New Parking Policy Effective October 1",
            "description": "Informational only; no action required."
        },
        {
            "prefix": "DECISION:",
            "example": "DECISION: Choose Office Supply Vendor by Friday",
            "description": "Recipient must make a decision."
        },
        {
            "prefix": "REQUEST:",
            "example": "REQUEST: Vacation Days Approval for Oct 5–12",
            "description": "Sender is asking for permission or approval."
        },
        {
            "prefix": "COORD:",
            "example": "COORD: Schedule Product Launch Strategy Meeting",
            "description": "Coordination with or by the recipient is needed."
        }
    ]
    return bluf_tags

def get_system_prompt():
    return """1. **Input**:
   The user will provide the **draft body text** of an email.

2. **Output**:
   You must respond **only in JSON**. The output should always be a JSON array with a single object containing four fields:

   * `"subject"` β†’ A concise subject line for the email. It must:
     * Begin with the most appropriate **BLUF tag** from the list below.
     * Follow with a clear subject summary.

   * `"bluf_tag"` β†’ The BLUF tag used (e.g., `"ACTION:"`, `"INFO:"`).

   * `"bluf_summary"` β†’ A two-sentence summary of the email, written in plain language.

   * `"email"` β†’ The rewritten email body. It must contain:
     1. `Bottom Line Up Front` on its own line.
     2. An empty line.
     3. `Purpose: {BLUF tag}` on its own line.
     4. One empty line.
     5. The **BLUF summary**.
     6. One empty line.
     7. The **original email text** (verbatim).

3. **BLUF Tags** (choose the most fitting):

```json
[
  {
    "prefix": "ACTION:",
    "example": "ACTION: Submit Timesheets by Friday",
    "description": "Recipient must take an action."
  },
  {
    "prefix": "SIGN:",
    "example": "SIGN: Approval Needed on Contract Addendum",
    "description": "Recipient needs to sign a document."
  },
  {
    "prefix": "INFO:",
    "example": "INFO: New Parking Policy Effective October 1",
    "description": "Informational only; no action required."
  },
  {
    "prefix": "DECISION:",
    "example": "DECISION: Choose Office Supply Vendor by Friday",
    "description": "Recipient must make a decision."
  },
  {
    "prefix": "REQUEST:",
    "example": "REQUEST: Vacation Days Approval for Oct 5–12",
    "description": "Sender is asking for permission or approval."
  },
  {
    "prefix": "COORD:",
    "example": "COORD: Schedule Product Launch Strategy Meeting",
    "description": "Coordination with or by the recipient is needed."
  }
]
```

4. **Constraints**:
   * Respond **only in JSON**.
   * Never include extra commentary, markdown, or explanations outside the JSON.
   * Always return an **array** with one object."""

def format_email_with_openai(email_text: str, api_key: str) -> Tuple[str, str, str, str]:
    """Format email using OpenAI API"""
    if not api_key:
        return "❌ Error: Please provide your OpenAI API key", "", "", ""
    
    if not email_text.strip():
        return "❌ Error: Please provide email text to format", "", "", ""
    
    client = openai.OpenAI(api_key=api_key)
    
    try:
        response = client.chat.completions.create(
            model="gpt-4",
            messages=[
                {"role": "system", "content": get_system_prompt()},
                {"role": "user", "content": email_text}
            ],
            temperature=0.3,
            max_tokens=1000
        )
        
        result = response.choices[0].message.content.strip()
        
        # Parse JSON response
        parsed_result = json.loads(result)
        
        if isinstance(parsed_result, list) and len(parsed_result) > 0:
            data = parsed_result[0]
        else:
            data = parsed_result
        
        subject = data.get('subject', '')
        email_body = data.get('email', '')
        bluf_tag = data.get('bluf_tag', '')
        bluf_summary = data.get('bluf_summary', '')
        
        status = "βœ… Email formatted successfully!"
        
        return status, subject, email_body, f"**Tag:** {bluf_tag}\n\n{bluf_summary}"
            
    except json.JSONDecodeError as e:
        return f"❌ Error: Failed to parse AI response as JSON: {e}", "", "", ""
    except Exception as e:
        return f"❌ Error calling OpenAI API: {e}", "", "", ""

def create_bluf_reference():
    """Create BLUF tags reference text"""
    bluf_tags = load_bluf_tags()
    reference_text = "## πŸ“š BLUF Tags Reference\n\n"
    
    for tag in bluf_tags:
        reference_text += f"**{tag['prefix']}** - {tag['description']}\n"
        reference_text += f"*Example: {tag['example']}*\n\n"
    
    reference_text += "\n---\n\n**About BLUF:** Bottom Line Up Front (BLUF) is a communication technique that puts the most important information at the beginning of a message. It helps recipients quickly understand the purpose and required actions."
    
    return reference_text

# Create Gradio interface
def create_interface():
    """Create the main Gradio interface"""
    
    with gr.Blocks(title="πŸ“§ BLUF Email Formatter", theme=gr.themes.Soft()) as demo:
        gr.Markdown(
            """
            # πŸ“§ BLUF Email Formatter
            Transform your emails into clear, actionable communication using the **Bottom Line Up Front** methodology.
            
            **Instructions:**
            1. Enter your OpenAI API key
            2. Paste your draft email text
            3. Click "Format Email"
            4. Copy the formatted subject line and email body
            """
        )
        
        with gr.Row():
            with gr.Column(scale=1):
                gr.Markdown("## πŸ”‘ Configuration")
                api_key = gr.Textbox(
                    label="OpenAI API Key",
                    type="password",
                    placeholder="Enter your OpenAI API key...",
                    info="Your API key is not stored and only used for this session"
                )
                
                gr.Markdown("## πŸ“ Input")
                email_input = gr.Textbox(
                    label="Draft Email Text",
                    placeholder="Hi team, just reminding everyone that timesheets for this week are due on Friday. Please make sure to submit them by then so payroll can be processed.",
                    lines=8,
                    max_lines=15
                )
                
                format_btn = gr.Button("πŸš€ Format Email", variant="primary", size="lg")
            
            with gr.Column(scale=1):
                gr.Markdown("## ✨ Formatted Output")
                
                status_output = gr.Markdown(label="Status")
                
                with gr.Group():
                    gr.Markdown("### πŸ“¬ Subject Line")
                    subject_output = gr.Textbox(
                        label="Formatted Subject",
                        interactive=False,
                        show_copy_button=True
                    )
                
                with gr.Group():
                    gr.Markdown("### πŸ“§ Formatted Email")
                    email_output = gr.Textbox(
                        label="Formatted Email Body",
                        lines=8,
                        max_lines=15,
                        interactive=False,
                        show_copy_button=True
                    )
                
                with gr.Group():
                    gr.Markdown("### πŸ’‘ BLUF Summary")
                    summary_output = gr.Markdown()
        
        # BLUF Reference in an accordion
        with gr.Accordion("πŸ“š BLUF Tags Reference", open=False):
            gr.Markdown(create_bluf_reference())
        
        # Set up the format button click event
        format_btn.click(
            fn=format_email_with_openai,
            inputs=[email_input, api_key],
            outputs=[status_output, subject_output, email_output, summary_output]
        )
        
        gr.Markdown(
            """
            ---
            <div style='text-align: center; color: #666;'>
            <p>Built with ❀️ using Gradio β€’ Learn more about <a href='https://en.wikipedia.org/wiki/BLUF_(communication)' target='_blank'>BLUF Communication</a></p>
            </div>
            """
        )
    
    return demo

if __name__ == "__main__":
    demo = create_interface()
    demo.launch()