perry-alchemist / app.py
meemeealm's picture
Upload 7 files
633c7e9 verified
Raw
History Blame Contribute Delete
8.54 kB
import base64
import io
import os
import random
from pathlib import Path
import gradio as gr
import requests
from PIL import Image
from magic_theme import MAGIC_CSS, Magic
BASE_DIR = Path(__file__).resolve().parent
PERRI_IMAGE = (BASE_DIR / "perri.png").resolve()
if not PERRI_IMAGE.exists():
raise FileNotFoundError(f"Missing Perri image asset: {PERRI_IMAGE}")
# ==========================================
# 2. NETWORKING ENGINE BRIDGE
# ==========================================
MODAL_API_URL = os.environ.get(
"MODAL_ENDPOINT_URL",
"https://meemeealm--perri-comic-pipeline-generate-comic-api.modal.run",
)
def simulate_comic_generation(user_prompt, color_mode, theme, tone):
"""
Connects frontend configuration settings to the deployed Modal engine over HTTP.
Decodes returned string sequences into a single PIL image asset.
"""
payload = {
"prompt": user_prompt,
"color_mode": color_mode,
"theme": theme,
"tone": tone,
}
try:
response = requests.post(MODAL_API_URL, json=payload, timeout=150)
response.raise_for_status()
response_data = response.json()
if response_data.get("status") == "success":
panel_data = response_data.get("panel", {})
img_b64_str = panel_data.get("image_b64", "")
if img_b64_str:
img_b64_str = img_b64_str.strip()
if "," in img_b64_str and img_b64_str.startswith("data:"):
img_b64_str = img_b64_str.split(",", 1)[1]
img_bytes = base64.b64decode(img_b64_str)
pil_img = Image.open(io.BytesIO(img_bytes))
return [
gr.update(value=pil_img, visible=True),
gr.update(visible=True),
gr.update(value="### 🎉 Generation Complete! Your masterpiece is ready below."),
]
except Exception as e:
print(f"[Network Pipeline Exception Handled]: {e}")
return [
gr.update(value=None, visible=True),
gr.update(visible=False),
gr.update(value="#### ⚠️ Connection Timeout or Pipeline Failure. Please check your connection."),
]
SURPRISE_PROMPTS = [
"A young man asks for a love potion, but accidentally he becomes a cat.",
"A knight wants a potion to make his horse fly, but it only makes the horse glow in the dark.",
"An old woman wants to see the future, but the alchemist's crystal ball just shows tomorrow's weather.",
"A greedy merchant wants a bag to turn stones into gold, but it turns it into explosive popcorn instead.",
"A beautiful lady wants to meet the future spouse, so Perri tell her where to go",
]
def get_random_prompt():
return random.choice(SURPRISE_PROMPTS)
def reveal_workspace():
return gr.update(visible=False), gr.update(visible=True)
UNLOCK_WORKSPACE_JS = """
() => {
document.documentElement.classList.add('workspace-unlocked');
document.body.classList.add('workspace-unlocked');
}
"""
# ==========================================
# 3. INTERFACE COMPOSITION
# ==========================================
def build_greeter_card(perri_image_path):
with gr.Column(visible=True, elem_classes="greeter-shell") as greeter_shell:
with gr.Row(elem_classes="greeter-card"):
with gr.Column(scale=1, elem_classes="greeter-portrait"):
gr.Image(
value=str(perri_image_path),
show_label=False,
interactive=False,
)
with gr.Column(scale=2, elem_classes="greeter-copy"):
gr.Markdown(
"""
<h2>Welcome to the comic workshop ...</h2>
<p>I'm Perri, an alchemist. I am thoughtful, energetic and sometimes humorous. I will guide you through the comic forge.</p>
<p>Give me a story seed, and I'll help you turn it into a polished comic strip.</p>
"""
)
with gr.Row(elem_classes="greeter-actions"):
enter_btn = gr.Button("Enter the workshop", variant="primary")
return greeter_shell, enter_btn
with gr.Blocks() as demo:
greeter_shell, enter_btn = build_greeter_card(PERRI_IMAGE)
with gr.Column(visible=False, elem_classes="container") as workspace_shell:
gr.Markdown(
"""
# A day of Perri, The Alchemist
Let's create unique, retro comic strips featuring **Perri**.
"""
)
with gr.Accordion("Collapsible Guidebook & Prompting Tips", open=False):
gr.Markdown(
"""
### How to get the best comic strips:
* **Keep it narrative:** Describe a clear cause-and-effect situation, such as someone wanting X but/and the Alchemist/someone doing Y.
* **Character Focus:** The AI understands 'The Alchemist' as a wise, slightly chaotic lady with a messy workshop.
* **Tone Impact:** Choosing Philisophical vs Satire changes how the text model interprets the ending of your story.
### Example Prompts to Try:
1. `Perri wants a love potion for his crush, but it made him a colorful cat.`
2. `A warrior asks for an elixir of eternal strength before a tournament, and he became a dragon.`
3. `A thief begs for an invisibility potion to rob the royal vault, but he becomes a duck.`
4. `A beautiful lady wants to meet the future spouse, so Perri tells her where to go.`
"""
)
with gr.Row():
with gr.Column(scale=1):
gr.Markdown("### Comic Controls")
with gr.Group():
user_prompt = gr.Textbox(
label="What is the story outline?",
placeholder="e.g., Annie want to see their future fortunes...",
lines=3,
container=False,
)
with gr.Row():
surprise_btn = gr.Button(
"Pre-defined prompt",
elem_classes="filter-btn",
size="sm",
)
color_mode = gr.Radio(
choices=["Black and White", "Full Color"],
value="Black and White",
label="Visual Aesthetic",
)
theme = gr.Dropdown(
choices=["Mortality", "Potion", "Fortune-telling", "Wealth"],
value="Potion",
label="Story Theme",
)
tone = gr.Dropdown(
choices=["Humor", "Satire", "Dark", "Inspirational", "Caring", "Philosophical"],
value="Humor",
label="Story Narrative Tone",
)
generate_btn = gr.Button("Generate Comic Strip", variant="primary")
with gr.Column(scale=2):
gr.Markdown("### The Comic Canvas")
status_tracker = gr.Markdown("*Your masterpiece will appear below when ready.*")
p1 = gr.Image(label="Comic Panel",
interactive=False,
visible=True,
height=300,
width=400)
download_btn = gr.Button("Download Whole Image (.png)", variant="success", visible=False)
# ==========================================
# 4. GLOBAL EVENT CONTROLLER WIRE-UP
# ==========================================
enter_btn.click(
fn=None,
inputs=None,
outputs=None,
js=UNLOCK_WORKSPACE_JS,
)
enter_btn.click(
fn=reveal_workspace,
inputs=None,
outputs=[greeter_shell, workspace_shell],
)
surprise_btn.click(
fn=get_random_prompt,
inputs=None,
outputs=user_prompt,
)
generate_btn.click(
fn=simulate_comic_generation,
inputs=[user_prompt, color_mode, theme, tone],
outputs=[p1, download_btn, status_tracker],
)
# ==========================================
# 5. APPLICATION RUNTIME RUN
# ==========================================
if __name__ == "__main__":
demo.launch(theme=Magic(), css=MAGIC_CSS)