Fluxy / app.py
mroccuper's picture
Update app.py
2911850 verified
import gradio as gr
import google.generativeai as genai
import os
import json
import re
import time
import random
# CSS for styling the app
css = """
body {
font-family: 'Poppins', -apple-system, BlinkMacSystemFont, sans-serif;
#background-color: #fafafa;
}
.container {
max-width: 900px;
margin: auto;
}
.title {
text-align: center;
margin-bottom: 0.5em;
color: #333;
font-weight: 800;
}
.subtitle {
text-align: center;
margin-bottom: 2em;
color: #555;
font-style: italic;
}
.output-box {
padding: 20px;
margin-top: 15px;
font-family: 'Courier New', monospace;
white-space: pre-wrap;
}
footer {
text-align: center;
margin-top: 2em;
padding: 15px;
font-size: 0.8em;
color: #888;
border-top: 1px solid #eee;
}
.api-key-container {
padding: 20px;
margin-bottom: 25px;
}
.prompt-container {
margin-bottom: 20px;
border-left: 4px solid #e94560;
padding-left: 15px;
}
.gr-button-primary {
background-color: #e94560 !important;
border: none !important;
}
.gr-button-primary:hover {
background-color: #d63d56 !important;
}
.copy-btn {
margin-top: 10px;
text-align: right;
}
.examples-container {
margin-top: 15px;
padding: 10px;
border-radius: 8px;
background-color: #f0f0f0;
}
.tab-content {
padding: 20px;
background-color: #fff;
}
.logo {
text-align: center;
margin: 20px auto;
}
.logo img {
max-width: 80px;
height: auto;
}
.tabs-container {
margin-top: 20px;
}
.style-options {
margin-top: 15px;
}
.market-box {
margin-top: 15px;
padding: 15px;
border: 1px solid #ddd;
border-radius: 8px;
background-color: #f9f9f9;
}
.keyword-list {
margin-top: 5px;
font-family: 'Courier New', monospace;
}
"""
# MODIFIED FUNCTION: Generate structured POD marketing content
def generate_pod_structured_output(api_key, quote, num_design_prompts, ui_selected_style_name="None (Let AI decide)"):
genai.configure(api_key=api_key)
model = genai.GenerativeModel(
model_name="gemini-1.5-pro",
generation_config={
"temperature": 0.85, # Slightly higher for more variety in more prompts
"top_p": 0.95,
"top_k": 40,
"max_output_tokens": 3072, # Increased slightly for potentially more prompts
}
)
style_for_header_tag = "AUTO"
style_guidance_for_llm = "AI chooses the best-fitting style automatically"
if ui_selected_style_name and ui_selected_style_name != "None (Let AI decide)":
style_for_header_tag = ui_selected_style_name
style_guidance_for_llm = f"The user has selected a preferred style: '{ui_selected_style_name}'. You MUST apply this style to all relevant prompts in Section 4."
else:
style_guidance_for_llm = "The user has left the style choice empty. AI should choose the best-fitting style automatically for Section 4."
# The 'num_design_prompts' now dictates the number of ideas in Section 4
system_prompt = f"""You are a smart Print-on-Demand (POD) marketing assistant.
Your task is to analyze the quote "{quote}" and return FOUR structured sections that help with marketing, audience targeting, and design execution.
The system has an OPTIONAL STYLES section.
{style_guidance_for_llm}
---
Return only the following four sections:
πŸ“ˆ 1. AMAZON SEARCH KEYWORDS
Generate a comma-separated list of 15-25 SEO-rich keywords relevant to the quote "{quote}".
- Mix of broad and niche-specific terms
- Focus on what real buyers would search for on Amazon
Example: dog lover shirt, funny pet shirt, dog humor tee, running with dog
---
πŸ“… 2. HOLIDAYS / EVENTS / NICHES
Identify all relevant holidays, occasions, events, and niche categories the quote "{quote}" fits into. (Provide at least 3-5 items)
- Be specific and include both evergreen and seasonal contexts
Example: Dog Lovers, Pet Adoption Month, National Dog Day, Funny Workout Shirts
---
🎯 3. TARGET AUDIENCE
Describe the ideal customer this quote/design ("{quote}") would appeal to.
- Include: age range, gender, interests, lifestyle traits, tone preference
Example: Adults 25–45, pet lovers, dog moms, people with sarcastic or playful humor, casual wearers
---
🎨 4. PROMPTS ({num_design_prompts} Ideas) β€” STYLE: [{style_for_header_tag}]
Generate exactly {num_design_prompts} creative design prompts for the quote: "{quote}", based on:
- Tone and meaning of the quote
- The style determined above (either user-selected '{style_for_header_tag if style_for_header_tag != "AUTO" else "AI Choice"}' or AI-recommended if AUTO)
- Each prompt should include layout, typography style, design elements, and color ideas.
If a style was provided by the user, apply it consistently.
If no style was provided (AUTO), analyze the quote and recommend and apply a fitting visual style.
Example (If User Selected Style = Silhouette and num_design_prompts = 1):
"1. Bold, stacked text in athletic font for THE PROVIDED QUOTE. A silhouette of a dog pulling a leash with a person chasing behind. High contrast black-and-white design optimized for light shirts."
Example (If Style = AUTO, AI chose Retro, and num_design_prompts = 1):
"1. Retro block lettering in faded orange and teal for THE PROVIDED QUOTE. Text arches above a cartoon dog flexing with sunglasses. Distressed texture for a vintage look."
---
⚠️ Output Format Guidelines:
- Use headers exactly as shown (including the emojis, numbering, and the dynamic prompt count in Section 4 header).
- For Section 4, number each design prompt idea starting from 1.
- Do NOT restate the provided quote "{quote}" unless it's within the design prompt itself where it's part of the design concept.
- Do NOT explain your reasoning outside the requested sections.
- Keep your output clean and implementation-ready, directly providing the content under each header.
- Ensure each section is clearly delineated by the headers and newlines.
"""
print("--- SYSTEM PROMPT BEING SENT TO API (First 500 chars) ---")
print(system_prompt[:500] + "...")
print("--- END OF SYSTEM PROMPT PREVIEW ---")
try:
print(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] Attempting to generate {num_design_prompts} prompts for quote: '{quote}' with style: '{ui_selected_style_name}'")
response = model.generate_content([system_prompt])
print(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] Received response from API.")
return response.text, None
except Exception as e:
error_message = f"Error during API call: {str(e)}"
print(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] {error_message}")
if hasattr(e, 'message'):
error_message = f"API Error: {e.message}"
return None, error_message
# MODIFIED PARSING FUNCTION for the structured output
def parse_structured_output(text_output):
data = {
"keywords_header": "πŸ“ˆ 1. AMAZON SEARCH KEYWORDS",
"keywords_content": "Not found or parsing error.",
"holidays_header": "πŸ“… 2. HOLIDAYS / EVENTS / NICHES",
"holidays_content": "Not found or parsing error.",
"audience_header": "🎯 3. TARGET AUDIENCE",
"audience_content": "Not found or parsing error.",
"prompts_header": "🎨 4. PROMPTS β€” STYLE: N/A", # Default, will be replaced
"prompts_content": "Not found or parsing error."
}
kw_match = re.search(r"(πŸ“ˆ 1\. AMAZON SEARCH KEYWORDS)\s*\n(.*?)(?=\n\nπŸ“… 2\. HOLIDAYS / EVENTS / NICHES|\Z)", text_output, re.DOTALL)
if kw_match:
data["keywords_header"] = kw_match.group(1).strip()
data["keywords_content"] = kw_match.group(2).strip()
hol_match = re.search(r"(πŸ“… 2\. HOLIDAYS / EVENTS / NICHES)\s*\n(.*?)(?=\n\n🎯 3\. TARGET AUDIENCE|\Z)", text_output, re.DOTALL)
if hol_match:
data["holidays_header"] = hol_match.group(1).strip()
data["holidays_content"] = hol_match.group(2).strip()
# Lookahead for prompts section needs to be dynamic or very general
aud_match = re.search(r"(🎯 3\. TARGET AUDIENCE)\s*\n(.*?)(?=\n\n🎨 4\. PROMPTS \(\d+ Ideas?\) β€” STYLE:|\Z)", text_output, re.DOTALL)
if aud_match:
data["audience_header"] = aud_match.group(1).strip()
data["audience_content"] = aud_match.group(2).strip()
# Regex for prompts header is now more general to capture the number of ideas
# It matches "🎨 4. PROMPTS (N Ideas) β€” STYLE: [STYLE]"
prompt_header_pattern_match = re.search(r"(🎨 4\. PROMPTS \(\d+\s*Ideas?\) β€” STYLE:.*?)\s*\n(.*?)\Z", text_output, re.DOTALL)
if prompt_header_pattern_match:
data["prompts_header"] = prompt_header_pattern_match.group(1).strip()
data["prompts_content"] = prompt_header_pattern_match.group(2).strip()
else:
# Fallback if the "(N Ideas)" part is missing for some reason
prompt_fallback_match = re.search(r"(🎨 4\. PROMPTS β€” STYLE:.*?)\s*\n(.*?)\Z", text_output, re.DOTALL)
if prompt_fallback_match:
data["prompts_header"] = prompt_fallback_match.group(1).strip()
data["prompts_content"] = prompt_fallback_match.group(2).strip()
print("Warning: Parsed prompts using fallback regex. Header might not exactly match '(N Ideas)'.")
return data
# Function to analyze quote sentiment and suggest styles
def analyze_quote(api_key, quote, progress=gr.Progress()):
if not api_key: return "Please enter your Gemini API key for analysis."
if not quote: return "Please enter a quote to analyze."
genai.configure(api_key=api_key)
model = genai.GenerativeModel(
model_name="gemini-1.5-pro",
generation_config={"temperature": 0.7, "top_p": 0.95, "max_output_tokens": 1000}
)
system_prompt = f"""Analyze the following quote: "{quote}"
Provide a brief analysis of:
1. The overall mood and sentiment of the quote
2. The most appropriate typography styles for this quote
3. Suggested color palettes that would complement the meaning
4. Best type of decorative elements to include
Format your response as a concise paragraph for each point."""
try:
response = model.generate_content([system_prompt])
return response.text
except Exception as e:
return f"Error analyzing quote: {str(e)}"
# MODIFIED Function to handle form submission
def process_quote(api_key, quote, num_prompts_from_slider, ui_style_preference_str, progress=gr.Progress()):
if not api_key: return "Please enter your Gemini API key.", "API key is required."
if not quote: return "Please enter a quote.", "A quote is required."
# Ensure num_prompts_from_slider is an int for the system prompt
try:
num_design_prompts = int(num_prompts_from_slider)
except ValueError:
return "Invalid number of prompts selected.", "Error with prompt count."
progress(0, desc="Initializing...")
time.sleep(0.1)
progress(0.2, desc=f"Generating {num_design_prompts} design prompts & marketing content...")
# Pass num_design_prompts to the generation function
raw_llm_output, error = generate_pod_structured_output(api_key, quote, num_design_prompts, ui_style_preference_str)
if error:
return f"Error from API: {error}", f"Marketing insights could not be retrieved. Details: {error}"
if not raw_llm_output:
return "Received no content from API.", "Received no content for marketing insights from API."
progress(0.7, desc="Parsing API output...")
parsed_data = parse_structured_output(raw_llm_output)
market_info_text = (
f"{parsed_data['keywords_header']}\n{parsed_data['keywords_content']}\n\n"
f"{parsed_data['holidays_header']}\n{parsed_data['holidays_content']}\n\n"
f"{parsed_data['audience_header']}\n{parsed_data['audience_content']}"
)
prompts_text = f"{parsed_data['prompts_header']}\n{parsed_data['prompts_content']}"
if "Not found or parsing error." in prompts_text and "Not found or parsing error." in market_info_text:
error_message = "Could not fully parse the API output. This might indicate an unexpected response format from the AI."
raw_output_preview = raw_llm_output[:1000] + "..." if len(raw_llm_output) > 1000 else raw_llm_output
prompts_text = error_message + "\n\nRaw API Output (preview):\n" + raw_output_preview
market_info_text = "See main output area for details on parsing failure."
progress(1.0, desc="Done!")
return prompts_text, market_info_text
example_quotes = [
"good vibes only", "dream big, sparkle more", "best mom ever",
"adventure awaits", "but first, coffee", "fueled by caffeine and chaos",
"stay wild moon child"
]
def create_demo():
with gr.Blocks(css=css, theme=gr.themes.Soft()) as demo:
gr.Markdown("""
<div class="title"><h1>✨ POD Marketing & Prompt Generator ✨</h1></div>
<div class="subtitle">Generate Amazon keywords, target audiences, niches, and design prompts for your quotes using Gemini 1.5 Pro</div>
""")
with gr.Tabs() as tabs:
with gr.TabItem("Generate Content"):
with gr.Group(elem_classes="api-key-container"):
api_key_input = gr.Textbox(label="Gemini API Key", placeholder="Enter your Gemini API key here...", type="password", info="Your API key is not stored and only used for this session")
quote_input = gr.Textbox(label="Your Quote/Text", placeholder="Enter the quote you want to analyze...", info="Example: \"good vibes only\"", lines=2)
with gr.Group(elem_classes="style-options"):
gr.Markdown("### Style Preferences for Design Prompts (Section 4)")
with gr.Row():
# num_prompts_slider now directly controls the number of design ideas
num_prompts_slider = gr.Slider(
minimum=5, maximum=15, value=7, step=1, # Default to 7
label="Number of Design Ideas for Section 4",
info="Select how many design prompts to generate for Section 4."
)
style_preference_radio = gr.Radio(
["None (Let AI decide)", "Retro 70s", "Modern Minimal", "Hand-drawn", "Feminine/Girly", "Bold Experimental", "Silhouette", "Cartoon", "Vintage"],
label="Preferred Style for Design Prompts", value="None (Let AI decide)", info="Optional: Choose a preferred style for Section 4 design prompts."
)
submit_btn = gr.Button("Generate Marketing Content & Prompts", variant="primary", size="lg")
gr.Examples(examples=example_quotes, inputs=quote_input, label="Example Quotes")
prompts_output_textbox = gr.Textbox(label="🎨 Design Prompts (Section 4)", placeholder="Creative design prompts will appear here...", lines=15, elem_classes=["output-box"], show_copy_button=True)
marketing_insights_textbox = gr.Textbox(label="πŸ“ˆπŸ“…πŸŽ― Marketing Insights (Sections 1-3)", placeholder="Amazon Keywords, Holidays/Niches, and Target Audience will appear here...", lines=15, elem_classes=["output-box", "market-box"], show_copy_button=True)
with gr.TabItem("Quote Analysis (Alternative)"):
with gr.Group(elem_classes="api-key-container"):
analysis_api_key = gr.Textbox(label="Gemini API Key", placeholder="Enter your Gemini API key here...", type="password", info="Your API key is not stored.")
analysis_quote_input = gr.Textbox(label="Your Quote/Text", placeholder="Enter a quote to analyze its mood, style, colors...", lines=2)
analyze_btn = gr.Button("Analyze Quote", variant="primary")
analysis_output_textbox = gr.Textbox(label="Quote Analysis Details", placeholder="Detailed analysis of mood, style, colors, elements will appear here...", lines=10, elem_classes=["output-box"])
with gr.TabItem("Help"):
gr.Markdown("""
### How to Use the POD Marketing & Prompt Generator
1. **Get a Gemini API Key**: Visit [Google AI Studio](https://makersuite.google.com/) and generate an API key.
2. **Enter Your API Key**: In the "Generate Content" or "Quote Analysis" tab.
3. **Enter Your Quote**: The text you want to base your T-shirt design and marketing on.
4. **Number of Design Ideas**: Use the slider to choose how many design prompts (5-15) you want for Section 4.
5. **Choose Style (Optional)**: Select a preferred visual style for the design prompts in Section 4. If "None" is selected, the AI will choose.
6. **Generate Content**: Click "Generate Marketing Content & Prompts".
### Output Sections (Generate Content Tab):
* **πŸ“ˆ 1. AMAZON SEARCH KEYWORDS**
* **πŸ“… 2. HOLIDAYS / EVENTS / NICHES**
* **🎯 3. TARGET AUDIENCE**
* **🎨 4. PROMPTS ([Number Selected] Ideas) β€” STYLE: [AUTO or SELECTED STYLE]**
Sections 1, 2, and 3 appear in "Marketing Insights". Section 4 appears in "Design Prompts".
### Quote Analysis Tab
Offers analysis on: mood, general typography styles, color palettes, decorative elements.
""")
submit_btn.click(
fn=process_quote_with_style_mapping,
inputs=[api_key_input, quote_input, num_prompts_slider, style_preference_radio],
outputs=[prompts_output_textbox, marketing_insights_textbox]
)
analyze_btn.click(fn=analyze_quote, inputs=[analysis_api_key, analysis_quote_input], outputs=analysis_output_textbox)
gr.HTML("""
<script>
function syncApiKeys() {
const apiKeyInputs = document.querySelectorAll('input[type="password"]');
if (apiKeyInputs.length < 2) return;
const mainApiKeyInput = apiKeyInputs[0];
const otherApiKeyInputs = Array.from(apiKeyInputs).slice(1);
mainApiKeyInput.addEventListener('input', function(e) {
otherApiKeyInputs.forEach(otherElem => {
if (otherElem.value !== e.target.value) {
otherElem.value = e.target.value;
otherElem.dispatchEvent(new Event('input', { bubbles: true }));
}
});
});
otherApiKeyInputs.forEach(otherInput => {
otherInput.addEventListener('input', function(e) {
if (mainApiKeyInput.value !== e.target.value) {
mainApiKeyInput.value = e.target.value;
mainApiKeyInput.dispatchEvent(new Event('input', { bubbles: true }));
}
otherApiKeyInputs.forEach(syncTarget => {
if (syncTarget !== e.target && syncTarget.value !== e.target.value) {
syncTarget.value = e.target.value;
syncTarget.dispatchEvent(new Event('input', { bubbles: true }));
}
});
});
});
}
let attempts = 0;
function trySync() {
if (document.querySelectorAll('input[type="password"]').length > 1) {
syncApiKeys();
} else if (attempts < 10) {
attempts++;
setTimeout(trySync, 500);
}
}
if (typeof NProgress !== 'undefined') {
const intervalId = setInterval(() => {
if (!NProgress.isStarted()) {
clearInterval(intervalId);
trySync();
}
}, 100);
} else {
document.addEventListener('DOMContentLoaded', () => {
setTimeout(trySync, 1000);
});
}
</script>
""", visible=False)
gr.Markdown("<footer>POD Marketing & Prompt Generator | Powered by Gemini AI</footer>")
return demo
def process_quote_with_style_mapping(api_key, quote, num_prompts, ui_style_preference_str, progress=gr.Progress()):
return process_quote(api_key, quote, num_prompts, ui_style_preference_str, progress)
if __name__ == "__main__":
demo = create_demo()
demo.launch()