import gradio as gr import google.generativeai as genai import re import json def count_characters(text): """Count characters in text.""" return len(text) if text else 0 def format_output(title, brand_name, bullet1, bullet2, suggested_keywords=None): """Format the output with character counts.""" output = f"Title ({count_characters(title)}/60 characters):\n{title}\n\n" output += f"Brand Name ({count_characters(brand_name)}/50 characters):\n{brand_name}\n\n" output += f"Bullet Point 1 ({count_characters(bullet1)}/256 characters):\n{bullet1}\n\n" output += f"Bullet Point 2 ({count_characters(bullet2)}/256 characters):\n{bullet2}" if suggested_keywords: output += f"\n\nSuggested Additional Keywords:\n{suggested_keywords}" return output def generate_prompt(quote, niche, target, keywords): """Generate the prompt for Gemini API.""" combined_prompt = f"""You are an Amazon Merch on Demand SEO expert specializing in creating optimized t-shirt and apparel listings. MY INPUT IS ABOUT: A {niche} t-shirt with the design/quote: "{quote}" for {target}. YOU MUST ONLY create an Amazon apparel listing about that EXACT input - no substitutions or different themes allowed. Generate a listing that includes: 1. Title (exactly 60 characters): Must include "{niche}" and reference the design/quote "{quote}" and target audience "{target}" 2. Brand Name (34-50 characters): Create a fitting brand name for this specific {niche} apparel for {target} 3. Bullet Point 1 (240-256 characters): Highlight key features using ALL CAPS for the first 2-3 words. Focus ONLY on the design, quote, and niche theme. 4. Bullet Point 2 (240-256 characters): Highlight additional features using ALL CAPS for the first 2-3 words. Focus ONLY on the design, quote, and niche theme. IMPORTANT RULES FOR BULLET POINTS: - Bullet points must be between 240-256 characters - DO NOT include generic phrases like "PREMIUM QUALITY" or references to material quality - DO NOT include phrases like "This comfortable and stylish tee is made with high-quality materials for a soft feel and long-lasting wear" - Focus ONLY on the specific design, niche, and quote provided - Every sentence must directly relate to the quote, niche theme, and target audience - Do not include any content that strays from the specific theme provided The listing should be specifically for t-shirts, hoodies, or sweaters for the Amazon Merch on Demand program. The listing MUST be about: {niche} + {quote} + for {target}. Do not generate content about other holidays, quotes, or audiences. Use these specific keywords in your listing: {keywords} Respond ONLY with a JSON object in this format: {{ "title": "The title with exactly 60 characters", "brand_name": "Brand name between 34-50 characters", "bullet_point_1": "First bullet point between 240-256 characters that focuses on the design and theme", "bullet_point_2": "Second bullet point between 240-256 characters that focuses on the design and theme", "suggested_keywords": "5 additional keywords separated by commas" }} REMINDER: Make sure to count the characters carefully. Title should be EXACTLY 60 characters. Bullet points should be between 240-256 characters.""" return combined_prompt def generate_multiple_variations_prompt(quote, niche, target, keywords): """Generate the prompt for multiple variations of title and brand name.""" combined_prompt = f"""You are an Amazon Merch on Demand SEO expert specializing in creating optimized t-shirt and apparel listings. MY INPUT IS ABOUT: A {niche} t-shirt with the design/quote: "{quote}" for {target}. YOU MUST ONLY create variations about that EXACT input - no substitutions or different themes allowed. Generate 3 different variations for the Title and Brand Name based on the provided information. All variations MUST be about {niche} + "{quote}" + for {target} audience. The titles MUST include: - The specific holiday/event: {niche} - Reference to the quote/design: "{quote}" - The target audience: {target} Focus only on t-shirts, sweaters, and hoodies for the Amazon Merch on Demand program. All titles must be exactly 60 characters and brand names between 34-50 characters. Respond ONLY with a JSON object in this format: {{ "title_variations": [ "Title variation 1 - exactly 60 characters, count carefully", "Title variation 2 - exactly 60 characters, count carefully", "Title variation 3 - exactly 60 characters, count carefully" ], "brand_name_variations": [ "Brand name variation 1 (34-50 characters)", "Brand name variation 2 (34-50 characters)", "Brand name variation 3 (34-50 characters)" ] }} REMINDER: Make sure each title is EXACTLY 60 characters. Count carefully!""" return combined_prompt def generate_amazon_listing(api_key, quote, niche, target, keywords): """Generate Amazon listing using Gemini API.""" # Input validation if not api_key: return "Error: Please enter a valid Gemini API key" if not quote or not niche or not target: return "Error: Please fill in all required fields (Quote, Holiday/Event, and Target Audience)" try: # Configure the Gemini API with the provided key genai.configure(api_key=api_key) # Create model with optimized settings model = genai.GenerativeModel( 'gemini-1.5-pro', generation_config={ "temperature": 0.3, "top_p": 0.8, "max_output_tokens": 1024, # Reduced for faster response } ) # Generate the main listing prompt = generate_prompt(quote, niche, target, keywords) try: # First try to get just the main listing for faster response response = model.generate_content(prompt) # Extract JSON from the response response_text = response.text match = re.search(r'{.*}', response_text, re.DOTALL) if not match: return "Error: Could not extract JSON from Gemini API response. Please try again." json_str = match.group(0) try: result = json.loads(json_str) except json.JSONDecodeError: return "Error parsing JSON response from Gemini API. Please try again." # Validate that the output actually matches the input criteria title = result.get("title", "") if not (quote.lower() in title.lower() or niche.lower() in title.lower() or any(t.lower() in title.lower() for t in target.lower().split(','))): return f"Error: Generated title doesn't match the requested theme: '{quote}', '{niche}', or '{target}'. Please try again." # Validate bullet point lengths bullet1 = result.get("bullet_point_1", "") bullet2 = result.get("bullet_point_2", "") #if len(bullet1) < 240 or len(bullet1) > 256: #return f"Error: Bullet point 1 length ({len(bullet1)}) is not between 240-256 characters. Please try again." #if len(bullet2) < 240 or len(bullet2) > 256: #return f"Error: Bullet point 2 length ({len(bullet2)}) is not between 240-256 characters. Please try again." # Check for generic content in bullet points generic_phrases = ["premium quality", "high-quality materials", "soft feel", "long-lasting wear", "comfortable and stylish", "perfect gift", "great gift"] for phrase in generic_phrases: if phrase in bullet1.lower() or phrase in bullet2.lower(): return f"Error: Generated bullet points contain generic phrase '{phrase}'. Please try again." # Format main output first - so we have something to show quickly main_output = format_output( result.get("title", "Error generating title"), result.get("brand_name", "Error generating brand name"), result.get("bullet_point_1", "Error generating bullet point 1"), result.get("bullet_point_2", "Error generating bullet point 2"), result.get("suggested_keywords", "Error generating suggested keywords") ) # Now try to get variations in a separate call try: variations_prompt = generate_multiple_variations_prompt(quote, niche, target, keywords) response_var = model.generate_content( variations_prompt, generation_config={ "temperature": 0.4, "top_p": 0.8, "max_output_tokens": 1024 } ) # Extract JSON from the variations response response_var_text = response_var.text match_var = re.search(r'{.*}', response_var_text, re.DOTALL) if match_var: json_str_var = match_var.group(0) try: variations = json.loads(json_str_var) # Format variations output variations_output = "\n\nADDITIONAL VARIATIONS:\n\n" variations_output += "Title Variations:\n" for i, var in enumerate(variations.get("title_variations", []), 1): variations_output += f"{i}. {var} ({count_characters(var)}/60 characters)\n" variations_output += "\nBrand Name Variations:\n" for i, var in enumerate(variations.get("brand_name_variations", []), 1): variations_output += f"{i}. {var} ({count_characters(var)}/50 characters)\n" # Combine main output with variations return main_output + variations_output except json.JSONDecodeError: # Return just the main output if we can't parse variations return main_output + "\n\n(Could not generate variations)" else: # Return just the main output if we can't extract JSON for variations return main_output + "\n\n(Could not generate variations)" except Exception as var_error: # Return just the main output if variations fail return main_output + f"\n\n(Could not generate variations: {str(var_error)})" except genai.types.generation_types.BlockedPromptException as e: return f"Error: The prompt was blocked by Gemini API safety filters. Please modify your input and try again." except Exception as e: return f"Error generating main listing: {str(e)}" except Exception as e: return f"Error: {str(e)}" # Create the Gradio interface def create_interface(): with gr.Blocks(title="Amazon Merch on Demand Listing Generator") as app: gr.Markdown("# Amazon Merch on Demand Listing Generator") gr.Markdown("Generate SEO-optimized t-shirt and apparel listings for Amazon Merch on Demand using Gemini 1.5 Pro AI.") with gr.Row(): with gr.Column(): api_key = gr.Textbox(label="Gemini API Key", placeholder="Enter your Gemini API key", type="password") quote = gr.Textbox(label="Quote/Design/Idea", placeholder="Enter the quote or design idea", value="") niche = gr.Textbox(label="Holiday/Event", placeholder="Enter the holiday or event (e.g., St Patrick's Day)", value="") target = gr.Textbox(label="Target Audience", placeholder="Teacher, Mom, Dad, etc.", value="") keywords = gr.Textbox(label="Target Keywords", placeholder="Enter keywords separated by commas", lines=5, value="") submit_btn = gr.Button("Generate Amazon Listing", variant="primary") with gr.Column(): status = gr.Textbox(label="Status", value="Ready to generate listing", interactive=False) output = gr.Textbox(label="Generated Amazon Listing", lines=25) def on_submit(api_key, quote, niche, target, keywords): # Update status first yield "Generating listing... Please wait.", output.value # Generate the listing result = generate_amazon_listing(api_key, quote, niche, target, keywords) # Update status with completion message if "Error" in result: yield "Error occurred. See details below.", result else: yield "Listing generated successfully!", result submit_btn.click( fn=on_submit, inputs=[api_key, quote, niche, target, keywords], outputs=[status, output], show_progress="minimal" ) gr.Markdown("## Example Input") gr.Markdown(''' ``` Quote/Design: Rainbow with a quote "Lucky To Be A Teacher" Holiday: St Patricks Day Target: Teacher, Teacher Mom Keywords: lucky, teacher, rainbow, st, patricks, day, t-shirt, patrick's, outfit, design, leopard, cheetah, print, shamrock, clover, perfect, men, women, teachers, celebrate, saint, patrick, special, unique, makes, great, gifts, idea, substitute, love, irish, culture, pattys, holiday, teach, shamrocks, cute, design, awesome, show, students ``` ''') gr.Markdown(""" ## Troubleshooting Tips - If you experience timeouts, try using shorter, more specific inputs - Make sure your Gemini API key is valid and has sufficient quota - The app will prioritize showing the main listing first, then try to generate variations """) return app # Create and launch the app app = create_interface() # For deployment on Hugging Face Spaces if __name__ == "__main__": app.launch()