Spaces:
Runtime error
Runtime error
| """ | |
| Mad Libs AI Prompt Generator | |
| An interactive step-by-step wizard where an AI co-pilot suggests | |
| creative ideas to spark imagination. Fill in the blanks bit by bit! | |
| """ | |
| import gradio as gr | |
| import random | |
| import json | |
| from typing import List, Dict | |
| # ═══════════════════════════════════════════════════════════════════════════════ | |
| # CREATIVE SUGGESTION DATABASE — The AI Co-Pilot's imagination engine | |
| # ═══════════════════════════════════════════════════════════════════════════════ | |
| CREATIVE_BANK: Dict[str, List[str]] = { | |
| "setting": [ | |
| "a cyberpunk city where neon rain never stops", | |
| "an abandoned space station orbiting a dying star", | |
| "a tiny village that exists inside a giant tree", | |
| "a library where every book is a portal to another world", | |
| "a post-apocalyptic carnival frozen in time", | |
| "a submarine city deep beneath Europa's ice ocean", | |
| "a desert where the sand sings memories aloud", | |
| "a Victorian mansion that rearranges its rooms each night", | |
| "a floating marketplace suspended between two cliffs", | |
| "a world where gravity works sideways after sunset", | |
| "a garden where forgotten ideas grow as strange flowers", | |
| "a train that travels through dreams instead of towns", | |
| ], | |
| "character": [ | |
| "a retired superhero who runs a coffee shop", | |
| "a sentient AI that writes poetry in binary", | |
| "a ghost who doesn't realize they're dead", | |
| "a time-traveling librarian from the 23rd century", | |
| "a cat who secretly runs an underground information network", | |
| "a former villain trying to make amends one small act at a time", | |
| "a child who can hear the thoughts of buildings", | |
| "a robot learning what it means to be afraid", | |
| "a bard whose songs literally reshape reality", | |
| "a detective who solves crimes by tasting emotions", | |
| "a character who ages backwards and knows the future", | |
| "a shapeshifter stuck in the wrong form for a decade", | |
| ], | |
| "mood": [ | |
| "nostalgic yet unsettling, like déjà vu that won't fade", | |
| "wildly optimistic despite overwhelming darkness", | |
| "bittersweet with a hint of cosmic wonder", | |
| "tense and electric, like the moment before lightning strikes", | |
| "dreamy and surreal with sudden moments of sharp clarity", | |
| "warm and cozy but with something watching from the shadows", | |
| "chaotic and joyful, like a symphony played by fireworks", | |
| "melancholic yet strangely comforting", | |
| "adrenaline-fueled wonder mixed with childlike awe", | |
| "quietly profound, the feeling of reading a secret message", | |
| "playfully sinister with unexpected heart", | |
| "ethereal and transcendent, beyond ordinary emotion", | |
| ], | |
| "action": [ | |
| "discovers a door in their home that leads to their own childhood", | |
| "accidentally swaps bodies with their worst enemy for 24 hours", | |
| "finds a message in a bottle from their future self", | |
| "invents a machine that translates animal thoughts into music", | |
| "challenges Death to a game of their own creation", | |
| "discovers they've been fictional all along and meets their author", | |
| "trades one memory for one wish at a mysterious pawn shop", | |
| "befriends the monster under their bed who turns out to be lonely", | |
| "tries to return a library book that's 500 years overdue", | |
| "learns the city they live in is actually a giant spaceship waking up", | |
| "finds a camera that shows photos of tomorrow", | |
| "becomes the first human to understand whale songs completely", | |
| ], | |
| "twist": [ | |
| "the villain was trying to save everyone all along", | |
| "the magic was actually advanced forgotten technology", | |
| "the protagonist has been dead since chapter one", | |
| "the world is a simulation, but the real world is worse", | |
| "the sidekick was the main character's future self in disguise", | |
| "the quest object was inside them the entire time", | |
| "everyone else is lying, and only the 'crazy' person tells the truth", | |
| "time is running backward, and only the reader realizes it", | |
| "the narrator is unreliable and hiding a terrible secret", | |
| "the love interest is actually a manifestation of the protagonist's guilt", | |
| "the fantasy kingdom is a metaphor for a mental health journey", | |
| "the happy ending requires an impossible sacrifice no one foresaw", | |
| ], | |
| "style": [ | |
| "in the style of a noir detective story with poetic flourishes", | |
| "written as a series of unsent letters and found objects", | |
| "like a fairy tale told by someone who never believed in them", | |
| "as a dialogue-only screenplay with stage directions that lie", | |
| "in second person, putting YOU directly in the story", | |
| "as fragmented journal entries from multiple unreliable perspectives", | |
| "like an ancient myth being retold by a sarcastic modern narrator", | |
| "as a choose-your-own-adventure where every choice leads to wonder", | |
| "in the rhythm and structure of a villanelle poem but as prose", | |
| "as an IKEA instruction manual that slowly reveals a tragedy", | |
| "like a nature documentary narrating human behavior with alien detachment", | |
| "as a recipe that gradually becomes a love story", | |
| ], | |
| "object": [ | |
| "a pocket watch that ticks only when lies are told nearby", | |
| "a pair of sunglasses that show people's true desires", | |
| "an umbrella that makes it rain only where you point it", | |
| "a compass that points to what you need, not where you want", | |
| "a mirror that reflects who you will be in ten years", | |
| "a pen that writes stories that come true for 24 hours", | |
| "a music box that plays the soundtrack of your best day", | |
| "a key that opens any door but locks one memory away forever", | |
| "a pair of shoes that walk you to where you're needed most", | |
| "a candle that burns with the color of your current emotion", | |
| "a photograph frame that shows the last moment of its previous owner", | |
| "a coin that always lands on what would make the best story", | |
| ], | |
| } | |
| WIZARD_STEPS = [ | |
| { | |
| "id": "setting", | |
| "title": "🌍 Where does your story live?", | |
| "label": "Setting / World", | |
| "description": "Paint the canvas. Is it a crumbling castle? A neon future? A world made of cake?", | |
| "placeholder": "Describe your setting...", | |
| "emoji": "🌍", | |
| }, | |
| { | |
| "id": "character", | |
| "title": "🧙♂️ Who is your hero (or villain)?", | |
| "label": "Main Character", | |
| "description": "Every tale needs a heart. What makes them weird, wonderful, or wonderfully flawed?", | |
| "placeholder": "Describe your character...", | |
| "emoji": "🧙♂️", | |
| }, | |
| { | |
| "id": "object", | |
| "title": "🔮 What mysterious object appears?", | |
| "label": "Magical Object / MacGuffin", | |
| "description": "The plot needs a spark. A cursed coin? A singing toaster? A door to yesterday?", | |
| "placeholder": "Describe your mysterious object...", | |
| "emoji": "🔮", | |
| }, | |
| { | |
| "id": "action", | |
| "title": "⚡ What wild thing happens?", | |
| "label": "Inciting Incident / Action", | |
| "description": "Shake things up! What impossible, hilarious, or terrifying event kicks it all off?", | |
| "placeholder": "Describe the key event...", | |
| "emoji": "⚡", | |
| }, | |
| { | |
| "id": "mood", | |
| "title": "🎭 What should it FEEL like?", | |
| "label": "Mood / Atmosphere", | |
| "description": "Set the emotional temperature. Creepy-cozy? Wildly hopeful? Melancholy magic?", | |
| "placeholder": "Describe the mood...", | |
| "emoji": "🎭", | |
| }, | |
| { | |
| "id": "twist", | |
| "title": "🌀 What jaw-dropping twist awaits?", | |
| "label": "Plot Twist / Surprise", | |
| "description": "Drop the mic. What revelation flips everything upside down?", | |
| "placeholder": "Describe your twist...", | |
| "emoji": "🌀", | |
| }, | |
| { | |
| "id": "style", | |
| "title": "✍️ How should it be TOLD?", | |
| "label": "Writing Style / Format", | |
| "description": "Form follows function. A noir monologue? A recipe that becomes a tragedy? Found letters?", | |
| "placeholder": "Describe the style or format...", | |
| "emoji": "✍️", | |
| }, | |
| ] | |
| def get_suggestions(category: str, count: int = 5) -> List[str]: | |
| pool = CREATIVE_BANK.get(category, []) | |
| if not pool: | |
| return [] | |
| shuffled = random.sample(pool, min(count, len(pool))) | |
| return shuffled | |
| def format_suggestions(suggestions: List[str], category: str) -> str: | |
| lines = [f"### 💡 AI Co-Pilot Suggestions for **{category}**"] | |
| for i, s in enumerate(suggestions, 1): | |
| lines.append(f"**{i}.** {s}") | |
| return "\n\n".join(lines) | |
| def init_state(): | |
| return {"step": 0, "answers": {}, "history": []} | |
| def next_step(state): | |
| state = json.loads(state) | |
| step_idx = state["step"] | |
| if step_idx < len(WIZARD_STEPS): | |
| step_idx += 1 | |
| state["step"] = step_idx | |
| return json.dumps(state) | |
| def prev_step(state): | |
| state = json.loads(state) | |
| step_idx = state["step"] | |
| if step_idx > 0: | |
| step_idx -= 1 | |
| state["step"] = step_idx | |
| return json.dumps(state) | |
| def save_answer(state, answer: str): | |
| state = json.loads(state) | |
| step_idx = state["step"] | |
| if step_idx < len(WIZARD_STEPS): | |
| cat = WIZARD_STEPS[step_idx]["id"] | |
| state["answers"][cat] = answer | |
| return json.dumps(state) | |
| def render_wizard(state_json: str, current_input: str = ""): | |
| state = json.loads(state_json) | |
| step_idx = state["step"] | |
| answers = state.get("answers", {}) | |
| sidebar_lines = ["## 📋 Your Story So Far", ""] | |
| for i, step_def in enumerate(WIZARD_STEPS): | |
| emoji = step_def["emoji"] | |
| label = step_def["label"] | |
| ans = answers.get(step_def["id"], "") | |
| if i < step_idx: | |
| sidebar_lines.append(f"{emoji} **{label}** ✓") | |
| sidebar_lines.append(f"\n> {ans[:60]}{'...' if len(ans) > 60 else ''}\n") | |
| elif i == step_idx: | |
| sidebar_lines.append(f"{emoji} **{label}** ← 🖊️ *you are here*") | |
| sidebar_lines.append("\n> _filling in..._\n") | |
| else: | |
| sidebar_lines.append(f"{emoji} *{label}* ○") | |
| sidebar_lines.append("") | |
| sidebar = "\n".join(sidebar_lines) | |
| if step_idx >= len(WIZARD_STEPS): | |
| return sidebar, render_final_prompt(answers), gr.update(visible=False), gr.update(visible=False), gr.update(visible=False), gr.update(visible=False), gr.update(visible=False) | |
| step = WIZARD_STEPS[step_idx] | |
| cat = step["id"] | |
| suggestions = get_suggestions(cat, 6) | |
| existing = answers.get(cat, "") | |
| header = f"## {step['emoji']} Step {step_idx + 1} of {len(WIZARD_STEPS)}: {step['title']}" | |
| desc = step["description"] | |
| sugg_md = format_suggestions(suggestions, step["label"]) | |
| show_prev = step_idx > 0 | |
| show_next = True | |
| show_generate = step_idx == len(WIZARD_STEPS) - 1 | |
| textbox_val = existing if existing else (current_input if current_input else "") | |
| progress_pct = int((step_idx / len(WIZARD_STEPS)) * 100) | |
| progress_bar = f"### Progress: {progress_pct}%\n" + "█" * (progress_pct // 5) + "░" * (20 - progress_pct // 5) | |
| main_content = f"{progress_bar}\n\n{header}\n\n{desc}\n\n---\n\n{sugg_md}" | |
| return ( | |
| sidebar, | |
| main_content, | |
| gr.update(value=textbox_val, visible=True), | |
| gr.update(visible=show_prev), | |
| gr.update(visible=show_next and not show_generate), | |
| gr.update(visible=show_generate), | |
| gr.update(visible=False), | |
| ) | |
| def render_final_prompt(answers: dict) -> str: | |
| setting = answers.get("setting", "[mysterious place]") | |
| character = answers.get("character", "[unlikely hero]") | |
| obj = answers.get("object", "[strange artifact]") | |
| action = answers.get("action", "[something impossible happens]") | |
| mood = answers.get("mood", "[indescribable feeling]") | |
| twist = answers.get("twist", "[revelation that changes everything]") | |
| style = answers.get("style", "[unique narrative voice]") | |
| return f"""# ✨ YOUR MAD LIBS AI PROMPT ✨ | |
| --- | |
| ## 📜 The Prompt | |
| Write a story **{style}** set in **{setting}**. | |
| The protagonist is **{character}**, who **{action}** after discovering **{obj}**. | |
| The tale should feel **{mood}**, and must end with the stunning revelation that **{twist}**. | |
| --- | |
| ## 🎨 The Blanks You Filled | |
| | Blank | Your Answer | | |
| |-------|-------------| | |
| | 🌍 Setting | {setting} | | |
| | 🧙♂️ Character | {character} | | |
| | 🔮 Object | {obj} | | |
| | ⚡ Action | {action} | | |
| | 🎭 Mood | {mood} | | |
| | 🌀 Twist | {twist} | | |
| | ✍️ Style | {style} | | |
| --- | |
| ## 🚀 Ready to Use! | |
| Copy the prompt above into your favorite AI (ChatGPT, Claude, Gemini, or an image generator like Midjourney/Stable Diffusion for visual storytelling) and watch your mad-libbed creation come to life! | |
| *Want another spin? Hit **Start Over** and let the AI co-pilot suggest completely new wild ideas.* | |
| """ | |
| def handle_next(state_json, current_text): | |
| state_json = save_answer(state_json, current_text) | |
| state_json = next_step(state_json) | |
| return render_wizard(state_json) | |
| def handle_prev(state_json, current_text): | |
| state_json = save_answer(state_json, current_text) | |
| state_json = prev_step(state_json) | |
| return render_wizard(state_json) | |
| def handle_generate(state_json, current_text): | |
| state_json = save_answer(state_json, current_text) | |
| state = json.loads(state_json) | |
| state["step"] = len(WIZARD_STEPS) | |
| state_json = json.dumps(state) | |
| sidebar, main, tb, prev, nxt, gen, done = render_wizard(state_json) | |
| return sidebar, main, gr.update(visible=False), gr.update(visible=False), gr.update(visible=False), gr.update(visible=False), gr.update(visible=True, value="🔄 Start Over") | |
| def handle_start_over(): | |
| return render_wizard(json.dumps(init_state())) | |
| def handle_randomize_all(): | |
| state = init_state() | |
| state["answers"] = { | |
| step["id"]: random.choice(CREATIVE_BANK.get(step["id"], ["something magical"])) | |
| for step in WIZARD_STEPS | |
| } | |
| state["step"] = len(WIZARD_STEPS) | |
| return render_wizard(json.dumps(state)) | |
| with gr.Blocks( | |
| title="🎭 Mad Libs AI Prompt Generator", | |
| css=""" | |
| .gradio-container {max-width: 1100px !important;} | |
| .suggestion-btn { margin: 4px 0; text-align: left !important; } | |
| .step-active { font-weight: bold; color: #6366f1; } | |
| .step-done { color: #22c55e; } | |
| .step-todo { color: #9ca3af; } | |
| """, | |
| theme=gr.themes.Soft(primary_hue="indigo", secondary_hue="violet"), | |
| ) as demo: | |
| gr.Markdown(""" | |
| # 🎭 Mad Libs AI Prompt Generator | |
| ## *Your AI Co-Pilot for Wild, Weird, Wonderful Ideas* | |
| Fill in the blanks **bit by bit** like a classic Mad Libs game — but with an AI assistant | |
| whispering wildly creative suggestions at every step. Spark your imagination, then unleash | |
| the final prompt on any AI (ChatGPT, Claude, Midjourney, Stable Diffusion, you name it)! | |
| """) | |
| state = gr.State(json.dumps(init_state())) | |
| with gr.Row(): | |
| with gr.Column(scale=1, min_width=250): | |
| sidebar_md = gr.Markdown("## 📋 Your Story So Far\n\n*Start filling blanks to build your prompt!*") | |
| randomize_btn = gr.Button("🎲 I'm Feeling Lucky! (Randomize All)", variant="secondary", size="sm") | |
| gr.Markdown("---") | |
| gr.Markdown("💡 **Pro tip:** Click any suggestion to instantly fill it in, or write your own wild idea!") | |
| with gr.Column(scale=3): | |
| main_md = gr.Markdown("### Click **Next** to begin your creative journey!") | |
| with gr.Row(): | |
| with gr.Column(scale=3): | |
| text_input = gr.Textbox( | |
| label="✏️ Your Answer for This Blank", | |
| placeholder="Type your own idea, or pick from suggestions below...", | |
| lines=2, | |
| show_copy_button=True, | |
| ) | |
| with gr.Column(scale=1): | |
| gr.Markdown("### 🎰 Quick Pick") | |
| sugg_btns = [] | |
| for i in range(6): | |
| btn = gr.Button(f"Suggestion {i+1}", size="sm", variant="secondary") | |
| sugg_btns.append(btn) | |
| with gr.Row(): | |
| prev_btn = gr.Button("⬅️ Previous", variant="secondary") | |
| next_btn = gr.Button("Next ➡️", variant="primary") | |
| generate_btn = gr.Button("✨ Generate Final Prompt!", variant="primary", visible=False) | |
| restart_btn = gr.Button("🔄 Start Over", variant="secondary", visible=False) | |
| def _fill_suggestion(index: int): | |
| def fn(state_json): | |
| state = json.loads(state_json) | |
| step_idx = state["step"] | |
| if step_idx >= len(WIZARD_STEPS): | |
| return "" | |
| cat = WIZARD_STEPS[step_idx]["id"] | |
| suggestions = get_suggestions(cat, 6) | |
| if index < len(suggestions): | |
| return suggestions[index] | |
| return "" | |
| return fn | |
| for i, btn in enumerate(sugg_btns): | |
| btn.click(fn=_fill_suggestion(i), inputs=[state], outputs=[text_input]) | |
| next_btn.click( | |
| fn=lambda s, t: handle_next(s, t), | |
| inputs=[state, text_input], | |
| outputs=[sidebar_md, main_md, text_input, prev_btn, next_btn, generate_btn, restart_btn], | |
| ).then(fn=lambda s: s, inputs=[state], outputs=[state]) | |
| prev_btn.click( | |
| fn=lambda s, t: handle_prev(s, t), | |
| inputs=[state, text_input], | |
| outputs=[sidebar_md, main_md, text_input, prev_btn, next_btn, generate_btn, restart_btn], | |
| ).then(fn=lambda s: s, inputs=[state], outputs=[state]) | |
| generate_btn.click( | |
| fn=lambda s, t: handle_generate(s, t), | |
| inputs=[state, text_input], | |
| outputs=[sidebar_md, main_md, text_input, prev_btn, next_btn, generate_btn, restart_btn], | |
| ).then(fn=lambda s: s, inputs=[state], outputs=[state]) | |
| restart_btn.click( | |
| fn=handle_start_over, | |
| inputs=[], | |
| outputs=[sidebar_md, main_md, text_input, prev_btn, next_btn, generate_btn, restart_btn], | |
| ).then(fn=lambda: json.dumps(init_state()), inputs=[], outputs=[state]) | |
| randomize_btn.click( | |
| fn=handle_randomize_all, | |
| inputs=[], | |
| outputs=[sidebar_md, main_md, text_input, prev_btn, next_btn, generate_btn, restart_btn], | |
| ).then(fn=lambda s: s, inputs=[state], outputs=[state]) | |
| demo.load( | |
| fn=lambda s: render_wizard(s), | |
| inputs=[state], | |
| outputs=[sidebar_md, main_md, text_input, prev_btn, next_btn, generate_btn, restart_btn], | |
| ) | |
| if __name__ == "__main__": | |
| demo.launch() | |