| import os |
| import gradio as gr |
| import openai |
|
|
| |
|
|
| PRESETS = { |
| "Executive Summary": { |
| "system_role": ( |
| "You are an executive briefing assistant for senior leaders. " |
| "You distill complex material into clear, non-fluffy summaries that support decisions." |
| ), |
| "user_hint": "Paste a long report, transcript, or document here and I'll summarize it.", |
| }, |
| "Polished Email Reply": { |
| "system_role": ( |
| "You are a professional communications assistant. " |
| "You write clear, concise, and respectful business emails suitable for executives." |
| ), |
| "user_hint": "Paste the email you received and notes on how you want to respond.", |
| }, |
| "Meeting Notes β Action Plan": { |
| "system_role": ( |
| "You transform messy meeting notes into a crisp, action-oriented summary." |
| ), |
| "user_hint": "Paste rough notes or bullet points from a meeting, even if they are messy.", |
| }, |
| "Idea Generator / Brainstorm": { |
| "system_role": ( |
| "You are a creative strategist. You generate structured ideas with next steps." |
| ), |
| "user_hint": "Describe what you want to build, improve, or launch. I'll propose ideas.", |
| }, |
| } |
|
|
| |
| PRESET_FORMAT_SPECS = { |
| "Executive Summary": """ |
| Output format spec (use GitHub-flavored Markdown): |
| |
| - Start with: `## Executive Summary` |
| - Then add `### Context` with 2β4 sentences. |
| - Add `### Key Points` as a bullet list (β’) with the most important insights. |
| - Add `### Recommendations` as a numbered list (1., 2., 3.) with concrete actions. |
| - Use **bold** for critical terms and *italics* for nuance where helpful. |
| """, |
| "Polished Email Reply": """ |
| Output format spec (use GitHub-flavored Markdown): |
| |
| - Start with: `## Email Draft` |
| - Then include: |
| - `**Subject:** ...` |
| - A realistic greeting line (e.g., `Hi Alex,`) |
| - Body in short paragraphs (2β4 sentences each). |
| - Optional bullet points if listing items. |
| - A professional closing (e.g., `Best,` or `Kind regards,`) and a placeholder name. |
| - Do NOT include meta-comments about the email. Only output the email content. |
| """, |
| "Meeting Notes β Action Plan": """ |
| Output format spec (use GitHub-flavored Markdown): |
| |
| - Start with: `## Meeting Summary` |
| - Then sections: |
| - `### Context` β 2β3 sentences on what the meeting was about. |
| - `### Key Decisions` β bullet list. |
| - `### Action Items` β numbered list, each item formatted like: |
| `1. **Owner:** Name (or Generic Owner) β *Due:* date or timeframe β Task description` |
| - `### Risks & Open Questions` β bullets with risks, blockers, or unknowns. |
| """, |
| "Idea Generator / Brainstorm": """ |
| Output format spec (use GitHub-flavored Markdown): |
| |
| - Start with: `## Idea Set` |
| - For each idea: |
| - `### Idea N β Short Title` |
| - Bullet list: |
| - `**Overview:** ...` |
| - `**Why it matters:** ...` |
| - `**Next steps (1β3):** ...` |
| - Aim for 3β5 strong ideas rather than many weak ones. |
| """, |
| } |
|
|
| BASE_SYSTEM = """ |
| You are the ZEN Promptboard Pro assistant. |
| |
| Rules: |
| - Always respond in GitHub-flavored Markdown. |
| - Follow any provided "Output format spec" exactly, especially headings and bullet structure. |
| - Make the output immediately usable and ready to send or paste into a document. |
| - Never explain what you are doing; do NOT include meta-instructions in the final output. |
| """ |
|
|
|
|
| |
|
|
| def run_completion(api_key, model, preset_name, system_prompt, user_prompt, history): |
| """ |
| Core call to the OpenAI Chat Completions API. |
| |
| - Does NOT throw on missing API key; returns a friendly message instead. |
| - Uses model-agnostic parameters (no temperature / max_tokens) to avoid 400s. |
| - Injects preset-specific formatting rules so output is richly structured. |
| """ |
|
|
| history = history or [] |
|
|
| if not api_key: |
| msg = "β No API key found. Paste your key and click **Save Key** first." |
| history.append(("System", msg)) |
| return history, msg |
|
|
| if not user_prompt.strip(): |
| msg = "β οΈ Please enter a prompt or paste some content before running." |
| history.append(("System", msg)) |
| return history, msg |
|
|
| openai.api_key = api_key.strip() |
|
|
| |
| system_parts = [BASE_SYSTEM] |
|
|
| if preset_name in PRESETS: |
| system_parts.append(f"Preset role description:\n{PRESETS[preset_name]['system_role']}") |
| fmt = PRESET_FORMAT_SPECS.get(preset_name, "") |
| if fmt: |
| system_parts.append(fmt) |
|
|
| if system_prompt.strip(): |
| system_parts.append( |
| "Additional system instructions from the user (apply after all above rules):\n" |
| + system_prompt.strip() |
| ) |
|
|
| final_system = "\n\n".join(system_parts) |
|
|
| messages = [ |
| {"role": "system", "content": final_system}, |
| {"role": "user", "content": user_prompt.strip()}, |
| ] |
|
|
| try: |
| |
| response = openai.chat.completions.create( |
| model=model.strip(), |
| messages=messages, |
| ) |
| answer = (response.choices[0].message.content or "").strip() |
| if not answer: |
| answer = "β οΈ The model returned an empty response. Try again with more context." |
| except Exception as e: |
| answer = f"β Error while calling the model:\n\n`{e}`" |
|
|
| history.append(("You", user_prompt)) |
| history.append(("Assistant", answer)) |
|
|
| return history, answer |
|
|
|
|
| |
|
|
| def save_api_key(raw_key: str): |
| cleaned = (raw_key or "").strip() |
| if not cleaned: |
| raise gr.Error("π Please paste a valid API key before saving.") |
|
|
| if not cleaned.startswith("sk-"): |
| gr.Info("Key saved, but it doesnβt start with 'sk-'. Double-check your provider format.") |
|
|
| return cleaned, gr.update(value="", placeholder="API key saved β") |
|
|
|
|
| def load_preset(preset_name: str): |
| if not preset_name or preset_name not in PRESETS: |
| return gr.update(), gr.update() |
| preset = PRESETS[preset_name] |
| return preset["system_role"], preset["user_hint"] |
|
|
|
|
| |
|
|
| CUSTOM_CSS = """ |
| #zen-root { |
| background: radial-gradient(circle at top left, #020617 0, #020617 40%, #000000 100%); |
| color: #f9fafb; |
| font-family: 'Inter', system-ui, -apple-system, BlinkMacSystemFont, sans-serif; |
| min-height: 100vh; |
| padding-bottom: 2rem; |
| } |
| .zen-card { |
| border-radius: 20px; |
| background: linear-gradient(135deg, rgba(15,23,42,0.95), rgba(15,23,42,0.7)); |
| border: 1px solid rgba(148,163,184,0.45); |
| backdrop-filter: blur(22px); |
| box-shadow: |
| 0 24px 60px rgba(15,23,42,0.95), |
| 0 0 0 1px rgba(15,23,42,0.85); |
| } |
| .zen-header { |
| padding: 1.8rem 2rem; |
| margin-bottom: 1.5rem; |
| } |
| .zen-pill { |
| border-radius: 9999px; |
| padding: 0.25rem 0.9rem; |
| border: 1px solid rgba(129,140,248,0.7); |
| color: #a5b4fc; |
| font-size: 0.75rem; |
| text-transform: uppercase; |
| letter-spacing: 0.12em; |
| } |
| .zen-title { |
| font-size: 1.9rem; |
| font-weight: 700; |
| letter-spacing: -0.03em; |
| } |
| .zen-subtitle { |
| font-size: 0.9rem; |
| color: #9ca3af; |
| } |
| #zen-output-card { |
| padding: 1.3rem 1.6rem; |
| } |
| #zen-output-card .markdown-body h2 { |
| margin-top: 0.2rem; |
| } |
| #zen-output-card .markdown-body h3 { |
| margin-top: 0.9rem; |
| } |
| #zen-meta-card { |
| padding: 0.75rem 1rem; |
| font-size: 0.85rem; |
| color: #e5e7eb; |
| border-bottom: 1px solid rgba(148,163,184,0.35); |
| } |
| """ |
|
|
|
|
| with gr.Blocks( |
| css=CUSTOM_CSS, |
| elem_id="zen-root", |
| fill_height=True, |
| title="ZEN Promptboard Pro β GPT-5", |
| ) as demo: |
| |
| gr.Markdown( |
| """ |
| <div class="zen-card zen-header"> |
| <div class="zen-pill">ZEN VANGUARD β’ GPT-5 WORKSTATION</div> |
| <div class="zen-title" style="margin-top: 0.8rem;">Professional AI Promptboard</div> |
| <div class="zen-subtitle" style="margin-top: 0.5rem;"> |
| Transform unstructured content into structured, ready-to-use executive artifacts β |
| summaries, action plans, emails, and idea sets. |
| </div> |
| </div> |
| """, |
| ) |
|
|
| api_key_state = gr.State("") |
|
|
| with gr.Row(equal_height=True): |
| |
| with gr.Column(scale=1): |
| with gr.Group(elem_classes=["zen-card"], elem_id="zen-left-card"): |
| gr.Markdown("#### 1. Connect to your model") |
|
|
| api_key_input = gr.Textbox( |
| label="API Key", |
| placeholder="Paste your GPT-style key here (not stored on server)", |
| type="password", |
| ) |
|
|
| with gr.Row(): |
| save_btn = gr.Button("Save Key", variant="primary", scale=1) |
| key_status = gr.Markdown("Key not saved yet.") |
|
|
| model_name = gr.Textbox( |
| label="Model ID", |
| value="gpt-5", |
| info="Default: gpt-5. You can switch to gpt-4o, gpt-4.1-mini, etc.", |
| ) |
|
|
| gr.Markdown("---") |
| gr.Markdown("#### 2. Choose a workflow") |
|
|
| preset_radio = gr.Radio( |
| label="Quick presets", |
| choices=list(PRESETS.keys()), |
| interactive=True, |
| ) |
|
|
| with gr.Group(elem_classes=["zen-card"]): |
| gr.Markdown("#### 3. System behavior (optional)") |
| system_box = gr.Textbox( |
| label="Additional system / role instructions", |
| placeholder="Optional: refine how the AI behaves (e.g., 'Write at director level, avoid buzzwords.')", |
| lines=4, |
| ) |
|
|
| |
| with gr.Column(scale=2): |
| with gr.Group(elem_classes=["zen-card"]): |
| gr.Markdown("#### 4. Your content") |
|
|
| user_box = gr.Textbox( |
| label="Prompt / Notes / Source Content", |
| placeholder="Paste meeting notes, drafts, transcripts, or describe what you want.", |
| lines=9, |
| ) |
|
|
| run_btn = gr.Button("Run Prompt", variant="primary") |
|
|
| with gr.Group(elem_classes=["zen-card"], elem_id="zen-output-wrapper"): |
| meta_md = gr.Markdown( |
| "Ready. Choose a preset and run your first prompt.", |
| elem_id="zen-meta-card", |
| ) |
| with gr.Tab("Formatted Output"): |
| output_md = gr.Markdown( |
| "Output will appear here.", |
| elem_id="zen-output-card", |
| ) |
| with gr.Tab("History"): |
| history_chat = gr.Chatbot( |
| label="Conversation History", |
| height=320, |
| type="tuples", |
| show_copy_button=True, |
| ) |
|
|
| |
|
|
| def _save_and_ack(key: str): |
| stored_key, placeholder_update = save_api_key(key) |
| status_msg = "β
API key stored for this session." |
| return stored_key, status_msg, placeholder_update |
|
|
| save_btn.click( |
| _save_and_ack, |
| inputs=[api_key_input], |
| outputs=[api_key_state, key_status, api_key_input], |
| ) |
|
|
| preset_radio.change( |
| fn=load_preset, |
| inputs=[preset_radio], |
| outputs=[system_box, user_box], |
| ) |
|
|
| def _run(api_state, model, preset_name, system, user, history): |
| history, answer = run_completion( |
| api_key=api_state, |
| model=model, |
| preset_name=preset_name, |
| system_prompt=system, |
| user_prompt=user, |
| history=history, |
| ) |
| |
| preset_label = preset_name or "Custom" |
| meta_text = f"**Preset:** {preset_label} Β· **Model:** `{model.strip() or 'gpt-5'}`" |
| return meta_text, history, answer |
|
|
| run_btn.click( |
| _run, |
| inputs=[api_key_state, model_name, preset_radio, system_box, user_box, history_chat], |
| outputs=[meta_md, history_chat, output_md], |
| ) |
|
|
| if __name__ == "__main__": |
| demo.launch() |
|
|