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("""