ZENLLC commited on
Commit
3964fee
·
verified ·
1 Parent(s): f55ced2

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +189 -92
app.py CHANGED
@@ -2,60 +2,111 @@ import os
2
  import gradio as gr
3
  import openai
4
 
5
- # --------------- PRESETS ---------------
6
 
7
  PRESETS = {
8
  "Executive Summary": {
9
- "system": (
10
- "You are an executive briefing assistant. "
11
- "Turn complex text into a concise 3–5 bullet executive summary "
12
- "with clear insights and recommendations."
13
  ),
14
- "user": "Paste a long report, transcript, or document here and I'll summarize it.",
15
  },
16
  "Polished Email Reply": {
17
- "system": (
18
  "You are a professional communications assistant. "
19
- "Write clear, concise, and friendly emails suitable for executives. "
20
- "Preserve tone and key details."
21
  ),
22
- "user": "Paste the email you received and a few notes on how you want to respond.",
23
  },
24
  "Meeting Notes → Action Plan": {
25
- "system": (
26
- "You convert messy meeting notes into a clear action-oriented summary. "
27
- "Always include: 1) Key decisions, 2) Action items with owners and timelines, "
28
- "3) Risks or next steps."
29
  ),
30
- "user": "Paste your rough meeting notes or bullet points and I'll structure them.",
31
  },
32
  "Idea Generator / Brainstorm": {
33
- "system": (
34
- "You are a creative strategist. Generate structured ideas with titles, "
35
- "descriptions, and next steps."
36
  ),
37
- "user": "Describe what you want to build, improve, or launch. I'll propose ideas.",
38
  },
39
  }
40
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
41
 
42
  # --------------- CORE CHAT LOGIC ---------------
43
 
44
- def run_completion(api_key, model, system_prompt, user_prompt, history):
45
  """
46
  Core call to the OpenAI Chat Completions API.
47
 
48
- IMPORTANT:
49
- - Does NOT raise errors for missing API key; returns a friendly message instead.
50
- - Does NOT send temperature or max_tokens/max_completion_tokens, to avoid
51
- model-specific 400 errors like you saw with GPT-5.
52
  """
53
 
54
  history = history or []
55
 
56
  if not api_key:
57
- # No exception; just return a message in the output area
58
- msg = "❌ No API key found. Paste your key in the API Key field and click **Save Key** first."
59
  history.append(("System", msg))
60
  return history, msg
61
 
@@ -64,17 +115,32 @@ def run_completion(api_key, model, system_prompt, user_prompt, history):
64
  history.append(("System", msg))
65
  return history, msg
66
 
67
- # Configure OpenAI
68
  openai.api_key = api_key.strip()
69
 
70
- # Build messages for the chat completion
71
- messages = []
 
 
 
 
 
 
 
72
  if system_prompt.strip():
73
- messages.append({"role": "system", "content": system_prompt.strip()})
74
- messages.append({"role": "user", "content": user_prompt.strip()})
 
 
 
 
 
 
 
 
 
75
 
76
  try:
77
- # Minimal, model-agnostic call: no temperature / max_* params
78
  response = openai.chat.completions.create(
79
  model=model.strip(),
80
  messages=messages,
@@ -82,7 +148,6 @@ def run_completion(api_key, model, system_prompt, user_prompt, history):
82
  answer = (response.choices[0].message.content or "").strip()
83
  if not answer:
84
  answer = "⚠️ The model returned an empty response. Try again with more context."
85
-
86
  except Exception as e:
87
  answer = f"❌ Error while calling the model:\n\n`{e}`"
88
 
@@ -95,11 +160,6 @@ def run_completion(api_key, model, system_prompt, user_prompt, history):
95
  # --------------- HELPERS ---------------
96
 
97
  def save_api_key(raw_key: str):
98
- """
99
- Save the API key for this session.
100
-
101
- We still allow non 'sk-' prefixes so people can drop in OpenRouter or other provider keys.
102
- """
103
  cleaned = (raw_key or "").strip()
104
  if not cleaned:
105
  raise gr.Error("🔐 Please paste a valid API key before saving.")
@@ -107,84 +167,102 @@ def save_api_key(raw_key: str):
107
  if not cleaned.startswith("sk-"):
108
  gr.Info("Key saved, but it doesn’t start with 'sk-'. Double-check your provider format.")
109
 
110
- # Return key for State + clear textbox with a new placeholder
111
  return cleaned, gr.update(value="", placeholder="API key saved ✔")
112
 
113
 
114
  def load_preset(preset_name: str):
115
- """
116
- Load system + user text for a selected preset.
117
- """
118
  if not preset_name or preset_name not in PRESETS:
119
  return gr.update(), gr.update()
120
  preset = PRESETS[preset_name]
121
- return preset["system"], preset["user"]
122
 
123
 
124
  # --------------- UI ---------------
125
 
126
  CUSTOM_CSS = """
127
  #zen-root {
128
- background: radial-gradient(circle at top left, #111827, #020617 65%);
129
- color: #f3f4f6;
130
  font-family: 'Inter', system-ui, -apple-system, BlinkMacSystemFont, sans-serif;
131
  min-height: 100vh;
 
132
  }
133
  .zen-card {
134
  border-radius: 20px;
135
- background: rgba(15, 23, 42, 0.82);
136
- border: 1px solid rgba(148, 163, 184, 0.35);
137
- backdrop-filter: blur(18px);
138
- box-shadow: 0 24px 60px rgba(0, 0, 0, 0.55);
139
- padding: 1.8rem;
 
 
 
 
 
140
  }
141
  .zen-pill {
142
  border-radius: 9999px;
143
- padding: 0.28rem 0.9rem;
144
- border: 1px solid rgba(129, 140, 248, 0.65);
145
  color: #a5b4fc;
146
  font-size: 0.75rem;
147
  text-transform: uppercase;
148
- letter-spacing: 0.08em;
149
  }
150
  .zen-title {
151
  font-size: 1.9rem;
152
  font-weight: 700;
153
- letter-spacing: -0.025em;
154
  }
155
  .zen-subtitle {
156
  font-size: 0.9rem;
157
  color: #9ca3af;
158
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
159
  """
160
 
 
161
  with gr.Blocks(
162
  css=CUSTOM_CSS,
163
  elem_id="zen-root",
164
  fill_height=True,
165
- title="ZEN Promptboard — GPT-5",
166
  ) as demo:
167
-
168
  # Header
169
  gr.Markdown(
170
  """
171
- <div class="zen-card">
172
- <div class="zen-pill">ZEN VANGUARD • GPT-5 CONSOLE</div>
173
  <div class="zen-title" style="margin-top: 0.8rem;">Professional AI Promptboard</div>
174
  <div class="zen-subtitle" style="margin-top: 0.5rem;">
175
- Transform unstructured content into polished, ready-to-send executive artifacts.
 
176
  </div>
177
  </div>
178
- """
179
  )
180
 
181
  api_key_state = gr.State("")
182
 
183
  with gr.Row(equal_height=True):
184
- # LEFT COLUMN: API + model + presets
185
  with gr.Column(scale=1):
186
- with gr.Group():
187
- gr.Markdown("### 🔐 1. Connect")
188
 
189
  api_key_input = gr.Textbox(
190
  label="API Key",
@@ -193,48 +271,62 @@ with gr.Blocks(
193
  )
194
 
195
  with gr.Row():
196
- save_btn = gr.Button("Save Key", variant="primary")
197
  key_status = gr.Markdown("Key not saved yet.")
198
 
199
  model_name = gr.Textbox(
200
  label="Model ID",
201
  value="gpt-5",
202
- info="Default: gpt-5. You can switch to gpt-4o, gpt-4.1-mini, etc. if needed.",
203
  )
204
 
205
- with gr.Group():
206
- gr.Markdown("### 2. Presets")
207
 
208
  preset_radio = gr.Radio(
209
- label="Quick workflows",
210
  choices=list(PRESETS.keys()),
211
  interactive=True,
212
  )
213
 
214
- # RIGHT COLUMN: prompts + output
215
- with gr.Column(scale=2):
216
- system_box = gr.Textbox(
217
- label="System / Role Instructions",
218
- placeholder="How should the AI behave? (e.g., 'You are my executive assistant...')",
219
- lines=4,
220
- )
221
 
222
- user_box = gr.Textbox(
223
- label="Your Prompt or Notes",
224
- placeholder="Paste content, meeting notes, or a draft you want rewritten...",
225
- lines=8,
226
- )
227
 
228
- run_btn = gr.Button("Run Prompt", variant="primary")
 
 
 
 
229
 
230
- output_md = gr.Markdown("Output will appear here.")
231
 
232
- history_chat = gr.Chatbot(
233
- label="History",
234
- height=300,
235
- type="tuples", # explicit to avoid the deprecation warning
236
- show_copy_button=True,
237
- )
 
 
 
 
 
 
 
 
 
 
 
238
 
239
  # --------------- WIRES / EVENTS ---------------
240
 
@@ -255,19 +347,24 @@ with gr.Blocks(
255
  outputs=[system_box, user_box],
256
  )
257
 
258
- def _run(api_state, model, system, user, history):
259
- return run_completion(
260
  api_key=api_state,
261
  model=model,
 
262
  system_prompt=system,
263
  user_prompt=user,
264
  history=history,
265
  )
 
 
 
 
266
 
267
  run_btn.click(
268
  _run,
269
- inputs=[api_key_state, model_name, system_box, user_box, history_chat],
270
- outputs=[history_chat, output_md],
271
  )
272
 
273
  if __name__ == "__main__":
 
2
  import gradio as gr
3
  import openai
4
 
5
+ # --------------- PRESETS & FORMATS ---------------
6
 
7
  PRESETS = {
8
  "Executive Summary": {
9
+ "system_role": (
10
+ "You are an executive briefing assistant for senior leaders. "
11
+ "You distill complex material into clear, non-fluffy summaries that support decisions."
 
12
  ),
13
+ "user_hint": "Paste a long report, transcript, or document here and I'll summarize it.",
14
  },
15
  "Polished Email Reply": {
16
+ "system_role": (
17
  "You are a professional communications assistant. "
18
+ "You write clear, concise, and respectful business emails suitable for executives."
 
19
  ),
20
+ "user_hint": "Paste the email you received and notes on how you want to respond.",
21
  },
22
  "Meeting Notes → Action Plan": {
23
+ "system_role": (
24
+ "You transform messy meeting notes into a crisp, action-oriented summary."
 
 
25
  ),
26
+ "user_hint": "Paste rough notes or bullet points from a meeting, even if they are messy.",
27
  },
28
  "Idea Generator / Brainstorm": {
29
+ "system_role": (
30
+ "You are a creative strategist. You generate structured ideas with next steps."
 
31
  ),
32
+ "user_hint": "Describe what you want to build, improve, or launch. I'll propose ideas.",
33
  },
34
  }
35
 
36
+ # How each preset should format its output in Markdown
37
+ PRESET_FORMAT_SPECS = {
38
+ "Executive Summary": """
39
+ Output format spec (use GitHub-flavored Markdown):
40
+
41
+ - Start with: `## Executive Summary`
42
+ - Then add `### Context` with 2–4 sentences.
43
+ - Add `### Key Points` as a bullet list (•) with the most important insights.
44
+ - Add `### Recommendations` as a numbered list (1., 2., 3.) with concrete actions.
45
+ - Use **bold** for critical terms and *italics* for nuance where helpful.
46
+ """,
47
+ "Polished Email Reply": """
48
+ Output format spec (use GitHub-flavored Markdown):
49
+
50
+ - Start with: `## Email Draft`
51
+ - Then include:
52
+ - `**Subject:** ...`
53
+ - A realistic greeting line (e.g., `Hi Alex,`)
54
+ - Body in short paragraphs (2–4 sentences each).
55
+ - Optional bullet points if listing items.
56
+ - A professional closing (e.g., `Best,` or `Kind regards,`) and a placeholder name.
57
+ - Do NOT include meta-comments about the email. Only output the email content.
58
+ """,
59
+ "Meeting Notes → Action Plan": """
60
+ Output format spec (use GitHub-flavored Markdown):
61
+
62
+ - Start with: `## Meeting Summary`
63
+ - Then sections:
64
+ - `### Context` – 2–3 sentences on what the meeting was about.
65
+ - `### Key Decisions` – bullet list.
66
+ - `### Action Items` – numbered list, each item formatted like:
67
+ `1. **Owner:** Name (or Generic Owner) — *Due:* date or timeframe — Task description`
68
+ - `### Risks & Open Questions` – bullets with risks, blockers, or unknowns.
69
+ """,
70
+ "Idea Generator / Brainstorm": """
71
+ Output format spec (use GitHub-flavored Markdown):
72
+
73
+ - Start with: `## Idea Set`
74
+ - For each idea:
75
+ - `### Idea N — Short Title`
76
+ - Bullet list:
77
+ - `**Overview:** ...`
78
+ - `**Why it matters:** ...`
79
+ - `**Next steps (1–3):** ...`
80
+ - Aim for 3–5 strong ideas rather than many weak ones.
81
+ """,
82
+ }
83
+
84
+ BASE_SYSTEM = """
85
+ You are the ZEN Promptboard Pro assistant.
86
+
87
+ Rules:
88
+ - Always respond in GitHub-flavored Markdown.
89
+ - Follow any provided "Output format spec" exactly, especially headings and bullet structure.
90
+ - Make the output immediately usable and ready to send or paste into a document.
91
+ - Never explain what you are doing; do NOT include meta-instructions in the final output.
92
+ """
93
+
94
 
95
  # --------------- CORE CHAT LOGIC ---------------
96
 
97
+ def run_completion(api_key, model, preset_name, system_prompt, user_prompt, history):
98
  """
99
  Core call to the OpenAI Chat Completions API.
100
 
101
+ - Does NOT throw on missing API key; returns a friendly message instead.
102
+ - Uses model-agnostic parameters (no temperature / max_tokens) to avoid 400s.
103
+ - Injects preset-specific formatting rules so output is richly structured.
 
104
  """
105
 
106
  history = history or []
107
 
108
  if not api_key:
109
+ msg = "❌ No API key found. Paste your key and click **Save Key** first."
 
110
  history.append(("System", msg))
111
  return history, msg
112
 
 
115
  history.append(("System", msg))
116
  return history, msg
117
 
 
118
  openai.api_key = api_key.strip()
119
 
120
+ # Build system message combining base, preset role, and preset formatting
121
+ system_parts = [BASE_SYSTEM]
122
+
123
+ if preset_name in PRESETS:
124
+ system_parts.append(f"Preset role description:\n{PRESETS[preset_name]['system_role']}")
125
+ fmt = PRESET_FORMAT_SPECS.get(preset_name, "")
126
+ if fmt:
127
+ system_parts.append(fmt)
128
+
129
  if system_prompt.strip():
130
+ system_parts.append(
131
+ "Additional system instructions from the user (apply after all above rules):\n"
132
+ + system_prompt.strip()
133
+ )
134
+
135
+ final_system = "\n\n".join(system_parts)
136
+
137
+ messages = [
138
+ {"role": "system", "content": final_system},
139
+ {"role": "user", "content": user_prompt.strip()},
140
+ ]
141
 
142
  try:
143
+ # Minimal API call: no temperature / max_* params to avoid model quirks
144
  response = openai.chat.completions.create(
145
  model=model.strip(),
146
  messages=messages,
 
148
  answer = (response.choices[0].message.content or "").strip()
149
  if not answer:
150
  answer = "⚠️ The model returned an empty response. Try again with more context."
 
151
  except Exception as e:
152
  answer = f"❌ Error while calling the model:\n\n`{e}`"
153
 
 
160
  # --------------- HELPERS ---------------
161
 
162
  def save_api_key(raw_key: str):
 
 
 
 
 
163
  cleaned = (raw_key or "").strip()
164
  if not cleaned:
165
  raise gr.Error("🔐 Please paste a valid API key before saving.")
 
167
  if not cleaned.startswith("sk-"):
168
  gr.Info("Key saved, but it doesn’t start with 'sk-'. Double-check your provider format.")
169
 
 
170
  return cleaned, gr.update(value="", placeholder="API key saved ✔")
171
 
172
 
173
  def load_preset(preset_name: str):
 
 
 
174
  if not preset_name or preset_name not in PRESETS:
175
  return gr.update(), gr.update()
176
  preset = PRESETS[preset_name]
177
+ return preset["system_role"], preset["user_hint"]
178
 
179
 
180
  # --------------- UI ---------------
181
 
182
  CUSTOM_CSS = """
183
  #zen-root {
184
+ background: radial-gradient(circle at top left, #020617 0, #020617 40%, #000000 100%);
185
+ color: #f9fafb;
186
  font-family: 'Inter', system-ui, -apple-system, BlinkMacSystemFont, sans-serif;
187
  min-height: 100vh;
188
+ padding-bottom: 2rem;
189
  }
190
  .zen-card {
191
  border-radius: 20px;
192
+ background: linear-gradient(135deg, rgba(15,23,42,0.95), rgba(15,23,42,0.7));
193
+ border: 1px solid rgba(148,163,184,0.45);
194
+ backdrop-filter: blur(22px);
195
+ box-shadow:
196
+ 0 24px 60px rgba(15,23,42,0.95),
197
+ 0 0 0 1px rgba(15,23,42,0.85);
198
+ }
199
+ .zen-header {
200
+ padding: 1.8rem 2rem;
201
+ margin-bottom: 1.5rem;
202
  }
203
  .zen-pill {
204
  border-radius: 9999px;
205
+ padding: 0.25rem 0.9rem;
206
+ border: 1px solid rgba(129,140,248,0.7);
207
  color: #a5b4fc;
208
  font-size: 0.75rem;
209
  text-transform: uppercase;
210
+ letter-spacing: 0.12em;
211
  }
212
  .zen-title {
213
  font-size: 1.9rem;
214
  font-weight: 700;
215
+ letter-spacing: -0.03em;
216
  }
217
  .zen-subtitle {
218
  font-size: 0.9rem;
219
  color: #9ca3af;
220
  }
221
+ #zen-output-card {
222
+ padding: 1.3rem 1.6rem;
223
+ }
224
+ #zen-output-card .markdown-body h2 {
225
+ margin-top: 0.2rem;
226
+ }
227
+ #zen-output-card .markdown-body h3 {
228
+ margin-top: 0.9rem;
229
+ }
230
+ #zen-meta-card {
231
+ padding: 0.75rem 1rem;
232
+ font-size: 0.85rem;
233
+ color: #e5e7eb;
234
+ border-bottom: 1px solid rgba(148,163,184,0.35);
235
+ }
236
  """
237
 
238
+
239
  with gr.Blocks(
240
  css=CUSTOM_CSS,
241
  elem_id="zen-root",
242
  fill_height=True,
243
+ title="ZEN Promptboard Pro — GPT-5",
244
  ) as demo:
 
245
  # Header
246
  gr.Markdown(
247
  """
248
+ <div class="zen-card zen-header">
249
+ <div class="zen-pill">ZEN VANGUARD • GPT-5 WORKSTATION</div>
250
  <div class="zen-title" style="margin-top: 0.8rem;">Professional AI Promptboard</div>
251
  <div class="zen-subtitle" style="margin-top: 0.5rem;">
252
+ Transform unstructured content into structured, ready-to-use executive artifacts
253
+ summaries, action plans, emails, and idea sets.
254
  </div>
255
  </div>
256
+ """,
257
  )
258
 
259
  api_key_state = gr.State("")
260
 
261
  with gr.Row(equal_height=True):
262
+ # LEFT: Controls
263
  with gr.Column(scale=1):
264
+ with gr.Group(elem_classes=["zen-card"], elem_id="zen-left-card"):
265
+ gr.Markdown("#### 1. Connect to your model")
266
 
267
  api_key_input = gr.Textbox(
268
  label="API Key",
 
271
  )
272
 
273
  with gr.Row():
274
+ save_btn = gr.Button("Save Key", variant="primary", scale=1)
275
  key_status = gr.Markdown("Key not saved yet.")
276
 
277
  model_name = gr.Textbox(
278
  label="Model ID",
279
  value="gpt-5",
280
+ info="Default: gpt-5. You can switch to gpt-4o, gpt-4.1-mini, etc.",
281
  )
282
 
283
+ gr.Markdown("---")
284
+ gr.Markdown("#### 2. Choose a workflow")
285
 
286
  preset_radio = gr.Radio(
287
+ label="Quick presets",
288
  choices=list(PRESETS.keys()),
289
  interactive=True,
290
  )
291
 
292
+ with gr.Group(elem_classes=["zen-card"]):
293
+ gr.Markdown("#### 3. System behavior (optional)")
294
+ system_box = gr.Textbox(
295
+ label="Additional system / role instructions",
296
+ placeholder="Optional: refine how the AI behaves (e.g., 'Write at director level, avoid buzzwords.')",
297
+ lines=4,
298
+ )
299
 
300
+ # RIGHT: Workspace
301
+ with gr.Column(scale=2):
302
+ with gr.Group(elem_classes=["zen-card"]):
303
+ gr.Markdown("#### 4. Your content")
 
304
 
305
+ user_box = gr.Textbox(
306
+ label="Prompt / Notes / Source Content",
307
+ placeholder="Paste meeting notes, drafts, transcripts, or describe what you want.",
308
+ lines=9,
309
+ )
310
 
311
+ run_btn = gr.Button("Run Prompt", variant="primary")
312
 
313
+ with gr.Group(elem_classes=["zen-card"], elem_id="zen-output-wrapper"):
314
+ meta_md = gr.Markdown(
315
+ "Ready. Choose a preset and run your first prompt.",
316
+ elem_id="zen-meta-card",
317
+ )
318
+ with gr.Tab("Formatted Output"):
319
+ output_md = gr.Markdown(
320
+ "Output will appear here.",
321
+ elem_id="zen-output-card",
322
+ )
323
+ with gr.Tab("History"):
324
+ history_chat = gr.Chatbot(
325
+ label="Conversation History",
326
+ height=320,
327
+ type="tuples", # explicit, no deprecation warning
328
+ show_copy_button=True,
329
+ )
330
 
331
  # --------------- WIRES / EVENTS ---------------
332
 
 
347
  outputs=[system_box, user_box],
348
  )
349
 
350
+ def _run(api_state, model, preset_name, system, user, history):
351
+ history, answer = run_completion(
352
  api_key=api_state,
353
  model=model,
354
+ preset_name=preset_name,
355
  system_prompt=system,
356
  user_prompt=user,
357
  history=history,
358
  )
359
+ # Meta card text: which preset + which model
360
+ preset_label = preset_name or "Custom"
361
+ meta_text = f"**Preset:** {preset_label} &nbsp;·&nbsp; **Model:** `{model.strip() or 'gpt-5'}`"
362
+ return meta_text, history, answer
363
 
364
  run_btn.click(
365
  _run,
366
+ inputs=[api_key_state, model_name, preset_radio, system_box, user_box, history_chat],
367
+ outputs=[meta_md, history_chat, output_md],
368
  )
369
 
370
  if __name__ == "__main__":