ZENLLC commited on
Commit
bea4b68
Β·
verified Β·
1 Parent(s): e1bbacc

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +187 -217
app.py CHANGED
@@ -1,252 +1,222 @@
1
  import os
2
- from typing import Tuple
3
-
4
  import gradio as gr
5
- from openai import OpenAI
6
-
7
- APP_TITLE = "ZEN Executive Briefing Desk"
8
- DEFAULT_MODEL = "gpt-4.1-mini" # learners can change this to gpt-5.x when available
9
-
10
- SYSTEM_PROMPT = """
11
- You are the ZEN Executive Briefing Copilot.
12
-
13
- You help busy professionals transform raw inputs (notes, transcripts, emails, web research,
14
- brain dumps) into crisp, actionable, and well-structured outputs.
15
-
16
- You ALWAYS:
17
- - Use clear, direct language.
18
- - Avoid jargon unless explicitly requested.
19
- - Highlight decisions, risks, and next steps.
20
- - Write in a tone that matches the selected tone parameter.
21
- - Match the requested format (summary, email, LinkedIn post, action plan, etc.).
22
-
23
- If the user input is chaotic, infer structure and clean it up.
24
- If the user provides bullet points, upgrade them into a polished professional artifact.
25
- """
26
-
27
- BRIEF_TYPES = {
28
- "Executive Summary": "Write a concise executive summary with key points and a short conclusion.",
29
- "Action Plan": "Produce a numbered action plan with owners (generic if not specified) and timelines.",
30
- "Decision Brief": "Write a decision brief: context, options, trade-offs, recommendation.",
31
- "Polished Email": "Write a professional email body only, no greeting name unless specified.",
32
- "LinkedIn Post": "Write a compelling LinkedIn post, optimized for engagement but not cringe.",
33
- "Meeting Minutes": "Produce structured meeting minutes: attendees, topics, decisions, action items.",
 
 
 
 
 
 
 
 
 
 
 
34
  }
35
 
36
- TONES = [
37
- "Neutral / Standard",
38
- "Confident",
39
- "Concise",
40
- "Visionary",
41
- "Friendly",
42
- ]
43
-
44
- LENGTH_OPTIONS = {
45
- "Very short": "Target 150–250 words.",
46
- "Short": "Target 250–400 words.",
47
- "Medium": "Target 400–650 words.",
48
- "Long": "Target 650–900 words.",
49
- }
50
 
 
 
 
51
 
52
- def get_client(effective_key: str) -> OpenAI:
53
- return OpenAI(api_key=effective_key)
54
 
 
 
 
 
 
55
 
56
- def save_api_key(user_key: str) -> Tuple[str, str]:
57
- """
58
- Save the API key into session state.
59
- NOTE: In Spaces, this is only in-memory per session, not persisted to disk.
60
- """
61
- user_key = (user_key or "").strip()
62
- if not user_key:
63
- return "⚠️ No API key provided. Please paste a valid key.", ""
64
- # Very light validation: pattern check, but don't over-assume.
65
- if not (user_key.startswith("sk-") and len(user_key) > 20):
66
- # Still accept it, just warn.
67
- return "⚠️ Key saved, but it doesn't look like a standard OpenAI key. Double-check if requests fail.", user_key
68
- return "βœ… API key saved for this session.", user_key
69
 
 
 
 
 
 
70
 
71
- def build_user_prompt(
72
- brief_type: str,
73
- tone: str,
74
- length_choice: str,
75
- title: str,
76
- context: str,
77
- extra_instructions: str,
78
- ) -> str:
79
- type_instruction = BRIEF_TYPES.get(brief_type, "")
80
- length_instruction = LENGTH_OPTIONS.get(length_choice, "")
81
- tone_instruction = f"Desired tone: {tone}."
82
 
83
- title_part = f"Working title or subject: {title.strip()}\n" if title.strip() else ""
84
 
85
- extra_part = f"\nAdditional instructions from user:\n{extra_instructions.strip()}\n" if extra_instructions.strip() else ""
 
 
 
 
 
 
86
 
87
- return f"""
88
- FORMAT REQUEST:
89
- - Brief type: {brief_type}
90
- - {type_instruction}
91
- - {tone_instruction}
92
- - {length_instruction}
93
 
94
- {title_part}
95
- Raw notes / source material:
96
- \"\"\"{context.strip()}\"\"\"
 
 
97
 
98
- {extra_part}
99
 
100
- Produce only the final formatted output. Do NOT include meta commentary about what you are doing.
101
- """
102
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
103
 
104
- def generate_brief(
105
- stored_key: str,
106
- inline_key: str,
107
- model: str,
108
- brief_type: str,
109
- tone: str,
110
- length_choice: str,
111
- title: str,
112
- context: str,
113
- extra_instructions: str,
114
- ):
115
- # Resolve effective key priority: stored key -> inline key -> env variable
116
- effective_key = (stored_key or "").strip() or (inline_key or "").strip() or os.getenv("OPENAI_API_KEY", "").strip()
117
-
118
- if not effective_key:
119
- return "❌ No API key available. Please paste your key and click 'Save API Key' first."
120
-
121
- if not context.strip():
122
- return "⚠️ Please paste some notes, text, or content to transform."
123
-
124
- user_prompt = build_user_prompt(
125
- brief_type=brief_type,
126
- tone=tone,
127
- length_choice=length_choice,
128
- title=title,
129
- context=context,
130
- extra_instructions=extra_instructions,
131
  )
132
 
133
- try:
134
- client = get_client(effective_key)
135
- response = client.chat.completions.create(
136
- model=model or DEFAULT_MODEL,
137
- messages=[
138
- {"role": "system", "content": SYSTEM_PROMPT},
139
- {"role": "user", "content": user_prompt},
140
- ],
141
- temperature=0.5,
142
- max_tokens=1200,
143
- )
144
- content = response.choices[0].message.content
145
- if not content or not content.strip():
146
- return "⚠️ Model returned an empty response. Try again or adjust your prompt."
147
- return content.strip()
148
- except Exception as e:
149
- return f"❌ Error while calling the model:\n\n`{e}`"
150
-
151
-
152
- def build_ui():
153
- with gr.Blocks(fill_height=True, theme=gr.themes.Soft(), title=APP_TITLE) as demo:
154
- gr.Markdown(
155
- f"# 🧠 {APP_TITLE}\n"
156
- "Turn messy notes, transcripts, and drafts into clean executive artifacts.\n"
157
- "Paste your content, pick a format, and let your model do the heavy lifting."
158
- )
159
-
160
- api_key_state = gr.State(value="")
161
 
162
- with gr.Row():
163
- with gr.Column(scale=1):
164
- gr.Markdown("### πŸ” API & Model")
165
-
166
- inline_key = gr.Textbox(
167
- label="OpenAI / GPT API Key (optional if environment variable is set)",
168
- placeholder="sk-...",
169
  type="password",
170
- lines=1,
171
- )
172
- save_button = gr.Button("πŸ’Ύ Save API Key for This Session", variant="secondary")
173
- save_status = gr.Markdown("")
174
-
175
- model = gr.Textbox(
176
- label="Model name",
177
- value=DEFAULT_MODEL,
178
- placeholder="e.g., gpt-4.1-mini or gpt-5.x when available",
179
- )
180
-
181
- brief_type = gr.Radio(
182
- label="Brief type",
183
- choices=list(BRIEF_TYPES.keys()),
184
- value="Executive Summary",
185
- )
186
-
187
- tone = gr.Radio(
188
- label="Tone",
189
- choices=TONES,
190
- value="Neutral / Standard",
191
- )
192
-
193
- length_choice = gr.Radio(
194
- label="Length",
195
- choices=list(LENGTH_OPTIONS.keys()),
196
- value="Short",
197
  )
198
-
199
- with gr.Column(scale=2):
200
- gr.Markdown("### πŸ“ Input")
201
-
202
- title = gr.Textbox(
203
- label="Title / Subject (optional)",
204
- placeholder="e.g., Q4 AI Initiative Brief, Client Proposal Follow-up, etc.",
 
205
  )
206
-
207
- context = gr.Textbox(
208
- label="Paste your notes, transcript, draft, or source content",
209
- placeholder="Drop in meeting notes, raw transcript, bullet points, or an ugly draft email...",
210
- lines=14,
211
  )
212
 
213
- extra_instructions = gr.Textbox(
214
- label="Extra instructions (optional)",
215
- placeholder="Anything special? e.g., 'Emphasize cost savings', 'Avoid technical terms', 'Target directors-level audience'.",
216
- lines=3,
 
 
217
  )
218
 
219
- generate_button = gr.Button("⚑ Generate Brief", variant="primary")
220
-
221
- gr.Markdown("### πŸ“„ Output")
222
- output = gr.Markdown("Your formatted brief will appear here.")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
223
 
224
- # Wiring
225
- save_button.click(
226
- fn=save_api_key,
227
- inputs=[inline_key],
228
- outputs=[save_status, api_key_state],
229
- )
230
 
231
- generate_button.click(
232
- fn=generate_brief,
233
- inputs=[
234
- api_key_state,
235
- inline_key,
236
- model,
237
- brief_type,
238
- tone,
239
- length_choice,
240
- title,
241
- context,
242
- extra_instructions,
243
- ],
244
- outputs=[output],
245
  )
 
246
 
247
- return demo
248
-
 
 
 
249
 
250
  if __name__ == "__main__":
251
- demo = build_ui()
252
  demo.launch()
 
1
  import os
 
 
2
  import gradio as gr
3
+ import openai
4
+
5
+ # --------------- PRESETS ---------------
6
+
7
+ PRESETS = {
8
+ "Executive Summary":
9
+ {
10
+ "system": (
11
+ "You are an executive briefing assistant. "
12
+ "Turn complex text into a concise 3–5 bullet executive summary "
13
+ "with clear insights and recommendations."
14
+ ),
15
+ "user": "Paste a long report, transcript, or document here and I'll summarize it."
16
+ },
17
+ "Polished Email Reply":
18
+ {
19
+ "system": (
20
+ "You are a professional communications assistant. "
21
+ "Write clear, concise, and friendly emails suitable for executives. "
22
+ "Preserve tone and key details."
23
+ ),
24
+ "user": "Paste the email you received and a few notes on how you want to respond."
25
+ },
26
+ "Meeting Notes β†’ Action Plan":
27
+ {
28
+ "system": (
29
+ "You convert messy meeting notes into a clear action-oriented summary. "
30
+ "Always include: 1) Key decisions, 2) Action items with owners and timelines, "
31
+ "3) Risks or next steps."
32
+ ),
33
+ "user": "Paste your rough meeting notes or bullet points and I'll structure them."
34
+ },
35
+ "Idea Generator / Brainstorm":
36
+ {
37
+ "system": (
38
+ "You are a creative strategist. Generate structured ideas with titles, "
39
+ "descriptions, and next steps."
40
+ ),
41
+ "user": "Describe what you want to build, improve, or launch. I'll propose ideas."
42
+ },
43
  }
44
 
45
+ # --------------- CORE CHAT LOGIC ---------------
 
 
 
 
 
 
 
 
 
 
 
 
 
46
 
47
+ def run_completion(api_key, model, system_prompt, user_prompt, temperature, history):
48
+ if not api_key:
49
+ raise gr.Error("πŸ”‘ Please save your API key first.")
50
 
51
+ if not user_prompt.strip():
52
+ raise gr.Error("✏️ Add a prompt or select a preset before running.")
53
 
54
+ openai.api_key = api_key
55
+ messages = []
56
+ if system_prompt.strip():
57
+ messages.append({"role": "system", "content": system_prompt.strip()})
58
+ messages.append({"role": "user", "content": user_prompt.strip()})
59
 
60
+ # GPT-5 models use max_completion_tokens instead of max_tokens
61
+ response = openai.chat.completions.create(
62
+ model=model,
63
+ messages=messages,
64
+ temperature=float(temperature),
65
+ max_completion_tokens=1200,
66
+ )
 
 
 
 
 
 
67
 
68
+ answer = response.choices[0].message.content.strip()
69
+ history = history or []
70
+ history.append(("You", user_prompt))
71
+ history.append(("Assistant", answer))
72
+ return history, answer
73
 
 
 
 
 
 
 
 
 
 
 
 
74
 
75
+ # --------------- HELPERS ---------------
76
 
77
+ def save_api_key(raw_key):
78
+ cleaned = raw_key.strip()
79
+ if not cleaned:
80
+ raise gr.Error("πŸ” Please paste a valid API key.")
81
+ if not cleaned.startswith("sk-"):
82
+ gr.Info("Key saved, but it doesn’t start with 'sk-'. Double-check your provider format.")
83
+ return cleaned, gr.update(value="", placeholder="API key saved βœ”")
84
 
 
 
 
 
 
 
85
 
86
+ def load_preset(preset_name):
87
+ if not preset_name or preset_name not in PRESETS:
88
+ return gr.update(), gr.update()
89
+ preset = PRESETS[preset_name]
90
+ return preset["system"], preset["user"]
91
 
 
92
 
93
+ # --------------- UI ---------------
 
94
 
95
+ CUSTOM_CSS = """
96
+ #zen-root {
97
+ background: radial-gradient(circle at top left, #111827, #020617 65%);
98
+ color: #f3f4f6;
99
+ font-family: 'Inter', system-ui, sans-serif;
100
+ min-height: 100vh;
101
+ }
102
+ .zen-card {
103
+ border-radius: 20px;
104
+ background: rgba(17, 24, 39, 0.6);
105
+ border: 1px solid rgba(255,255,255,0.08);
106
+ backdrop-filter: blur(16px);
107
+ box-shadow: 0 0 40px rgba(0,0,0,0.35);
108
+ padding: 1.75rem;
109
+ }
110
+ .zen-pill {
111
+ border-radius: 9999px;
112
+ padding: 0.3rem 0.9rem;
113
+ border: 1px solid rgba(255,255,255,0.15);
114
+ color: #a5b4fc;
115
+ font-size: 0.75rem;
116
+ text-transform: uppercase;
117
+ letter-spacing: 0.08em;
118
+ }
119
+ .zen-title {
120
+ font-size: 1.9rem;
121
+ font-weight: 700;
122
+ letter-spacing: -0.02em;
123
+ }
124
+ """
125
 
126
+ with gr.Blocks(css=CUSTOM_CSS, elem_id="zen-root", fill_height=True, title="ZEN Promptboard β€” GPT-5") as demo:
127
+ gr.Markdown(
128
+ """
129
+ <div class="zen-card">
130
+ <div class="zen-pill">ZEN VANGUARD β€’ GPT-5 CONSOLE</div>
131
+ <div class="zen-title" style="margin-top: 0.8rem;">Professional AI Promptboard</div>
132
+ <div style="margin-top: 0.5rem; color:#9ca3af;">
133
+ Transform unstructured content into polished professional outputs β€” with GPT-5 as your assistant.
134
+ </div>
135
+ </div>
136
+ """
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
137
  )
138
 
139
+ api_key_state = gr.State("")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
140
 
141
+ with gr.Row(equal_height=True):
142
+ with gr.Column(scale=1):
143
+ with gr.Group():
144
+ gr.Markdown("### πŸ” Connect")
145
+ api_key_input = gr.Textbox(
146
+ label="API Key",
147
+ placeholder="Paste your GPT-5 key here (not stored on server)",
148
  type="password",
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
149
  )
150
+ with gr.Row():
151
+ save_btn = gr.Button("Save Key", variant="primary")
152
+ key_status = gr.Markdown("Key not saved yet.")
153
+
154
+ model_name = gr.Textbox(
155
+ label="Model ID",
156
+ value="gpt-5",
157
+ info="Default: GPT-5. You may switch to gpt-4o or gpt-4.1-mini if needed.",
158
  )
159
+ temperature = gr.Slider(
160
+ 0.0, 1.2, 0.5, 0.05, label="Creativity"
 
 
 
161
  )
162
 
163
+ with gr.Group():
164
+ gr.Markdown("### ⚑ Presets")
165
+ preset_radio = gr.Radio(
166
+ label="Quick workflows",
167
+ choices=list(PRESETS.keys()),
168
+ interactive=True,
169
  )
170
 
171
+ with gr.Column(scale=2):
172
+ system_box = gr.Textbox(
173
+ label="System / Role Instructions",
174
+ placeholder="How should the AI behave?",
175
+ lines=4,
176
+ )
177
+ user_box = gr.Textbox(
178
+ label="Your Prompt or Notes",
179
+ placeholder="Paste text, meeting notes, or raw input...",
180
+ lines=8,
181
+ )
182
+ run_btn = gr.Button("Run Prompt", variant="primary")
183
+ output_md = gr.Markdown("Output will appear here.")
184
+ history_chat = gr.Chatbot(label="History", height=300)
185
+
186
+ # --- Wiring ---
187
+
188
+ def _save_and_ack(key):
189
+ stored_key, placeholder_update = save_api_key(key)
190
+ return stored_key, "βœ… API key stored for this session.", placeholder_update
191
+
192
+ save_btn.click(
193
+ _save_and_ack,
194
+ inputs=[api_key_input],
195
+ outputs=[api_key_state, key_status, api_key_input],
196
+ )
197
 
198
+ preset_radio.change(
199
+ fn=load_preset,
200
+ inputs=[preset_radio],
201
+ outputs=[system_box, user_box],
202
+ )
 
203
 
204
+ def _run(api_state, model, system, user, temp, history):
205
+ history, answer = run_completion(
206
+ api_key=api_state,
207
+ model=model,
208
+ system_prompt=system,
209
+ user_prompt=user,
210
+ temperature=temp,
211
+ history=history,
 
 
 
 
 
 
212
  )
213
+ return history, answer
214
 
215
+ run_btn.click(
216
+ _run,
217
+ inputs=[api_key_state, model_name, system_box, user_box, temperature, history_chat],
218
+ outputs=[history_chat, output_md],
219
+ )
220
 
221
  if __name__ == "__main__":
 
222
  demo.launch()