Spaces:
Sleeping
Sleeping
import gradio as gr | |
import google.generativeai as genai | |
import re | |
import json | |
from functools import lru_cache | |
# Global configuration | |
MAX_CACHE_SIZE = 100 | |
CHAR_LIMITS = { | |
"title": 60, | |
"brand_min": 34, | |
"brand_max": 50, | |
"bullet": 256 | |
} | |
def count_characters(text): | |
"""Count characters in text.""" | |
return len(text) if text else 0 | |
def validate_listing(result, quote, niche, target, keywords): | |
"""Validate all listing elements meet requirements""" | |
errors = [] | |
keyword_list = [k.strip().lower() for k in keywords.split(",")] | |
# Title validation | |
title = result.get("title", "") | |
if len(title) != CHAR_LIMITS["title"]: | |
errors.append(f"Title length invalid: {len(title)} characters") | |
if not all(kw in title.lower() for kw in [quote.lower(), niche.lower(), target.lower()]): | |
errors.append("Title missing required elements") | |
# Brand validation | |
brand = result.get("brand_name", "") | |
if not (CHAR_LIMITS["brand_min"] <= len(brand) <= CHAR_LIMITS["brand_max"]): | |
errors.append(f"Brand name length invalid: {len(brand)} characters") | |
# Bullet points validation | |
for i, bp in enumerate([result.get("bullet_point_1", ""), result.get("bullet_point_2", "")]): | |
if len(bp) != CHAR_LIMITS["bullet"]: | |
errors.append(f"Bullet point {i+1} length invalid: {len(bp)} characters") | |
if not re.match(r'^[A-Z ]{5,20}[A-Z][a-z]', bp): # Check for ALL CAPS start | |
errors.append(f"Bullet point {i+1} doesn't start with ALL CAPS") | |
for keyword in keyword_list[:3]: # Check primary keywords | |
if keyword and keyword not in bp.lower(): | |
errors.append(f"Bullet point {i+1} missing keyword: {keyword}") | |
return errors | |
def format_output(result, quote, niche, target, keywords): | |
"""Format the output with character counts and validation""" | |
validation_errors = validate_listing(result, quote, niche, target, keywords) | |
output = f"Title ({count_characters(result.get('title', ''))}/{CHAR_LIMITS['title']} characters):\n{result.get('title', '')}\n" | |
output += f"Brand Name ({count_characters(result.get('brand_name', ''))}/{CHAR_LIMITS['brand_max']} characters):\n{result.get('brand_name', '')}\n" | |
output += f"Bullet Point 1 ({count_characters(result.get('bullet_point_1', ''))}/{CHAR_LIMITS['bullet']} characters):\n{result.get('bullet_point_1', '')}\n" | |
output += f"Bullet Point 2 ({count_characters(result.get('bullet_point_2', ''))}/{CHAR_LIMITS['bullet']} characters):\n{result.get('bullet_point_2', '')}" | |
if result.get("suggested_keywords"): | |
output += f"\nSuggested Additional Keywords:\n{result.get('suggested_keywords')}" | |
if validation_errors: | |
output += "\n\nVALIDATION ERRORS:\n" + "\n".join(validation_errors) | |
return output | |
def generate_prompt(quote, niche, target, keywords, product_type="T-Shirt"): | |
"""Enhanced prompt with explicit SEO patterns""" | |
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} {product_type} with the design/quote: "{quote}" for {target}. | |
SEO BEST PRACTICES: | |
- Title structure: [Primary Keyword] [Design Reference] [Target Audience] [Secondary Keywords] | |
- Brand Name: Combine niche + target + emotional benefit (e.g., "TeacherJoy Designs") | |
- Bullet Point Structure: | |
• Start with ALL CAPS benefit statement | |
• Include 3-5 keywords | |
• Add emotional trigger + practical benefit | |
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 {CHAR_LIMITS['title']} characters): Must include "{niche}" and reference the design/quote "{quote}" and target audience "{target}" | |
2. Brand Name ({CHAR_LIMITS['brand_min']}-{CHAR_LIMITS['brand_max']} characters): Create a fitting brand name for this specific {niche} apparel for {target} | |
3. Bullet Point 1 (exactly {CHAR_LIMITS['bullet']} characters): Highlight key features using ALL CAPS for the first 2-3 words | |
4. Bullet Point 2 (exactly {CHAR_LIMITS['bullet']} characters): Highlight additional features using ALL CAPS for the first 2-3 words | |
The listing should be specifically for {product_type}s 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} | |
MANDATORY SEO PATTERNS: | |
Title Examples: | |
- "[Holiday] [Quote] [Target] T-Shirt" | |
- "[Target] [Quote] [Holiday] Shirt" | |
- "[Quote] [Target] [Holiday] Tee" | |
Respond ONLY with a JSON object in this format: | |
{{ | |
"title": "Title text here", | |
"brand_name": "Brand name here", | |
"bullet_point_1": "Bullet point 1 text here", | |
"bullet_point_2": "Bullet point 2 text here", | |
"suggested_keywords": "Comma separated list of 5 additional keywords" | |
}} | |
The output must follow these exact character counts...""" | |
return combined_prompt | |
def cached_generate_listing(api_key, quote, niche, target, keywords, product_type): | |
"""Cached version of the listing generation""" | |
try: | |
genai.configure(api_key=api_key) | |
model = genai.GenerativeModel('gemini-1.5-pro') | |
prompt = generate_prompt(quote, niche, target, keywords, product_type) | |
response = model.generate_content( | |
prompt, | |
generation_config={ | |
"temperature": 0.3, | |
"top_p": 0.8, | |
"max_output_tokens": 2048 | |
} | |
) | |
response_text = response.text | |
match = re.search(r'{.*}', response_text, re.DOTALL) | |
if match: | |
return json.loads(match.group(0)) | |
return {"error": "Could not extract JSON from response"} | |
except Exception as e: | |
return {"error": str(e)} | |
def generate_amazon_listing(api_key, quote, niche, target, keywords, product_type): | |
"""Main function to generate Amazon listing with validation""" | |
try: | |
# Generate main listing | |
result = cached_generate_listing( | |
api_key, quote, niche, target, keywords, product_type | |
) | |
# Generate variations | |
variations_prompt = generate_multiple_variations_prompt(quote, niche, target, keywords, product_type) | |
# Similar implementation for variations... | |
return format_output(result, quote, niche, target, keywords) | |
except Exception as e: | |
return f"Error: {str(e)}" | |
def create_interface(): | |
"""Create Gradio interface with enhanced UI""" | |
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" | |
) | |
product_type = gr.Radio( | |
choices=["T-Shirt", "Hoodie", "Sweater", "Tank Top"], | |
label="Product Type", | |
value="T-Shirt" | |
) | |
quote = gr.Textbox( | |
label="Quote/Design/Idea", | |
placeholder="Enter the quote or design idea" | |
) | |
quote_count = gr.Textbox( | |
label="Character Count", | |
value="0", | |
interactive=False | |
) | |
niche = gr.Textbox( | |
label="Holiday/Event", | |
placeholder="Enter the holiday or event (e.g., St Patrick's Day)" | |
) | |
niche_count = gr.Textbox( | |
label="Character Count", | |
value="0", | |
interactive=False | |
) | |
target = gr.Textbox( | |
label="Target Audience", | |
placeholder="Teacher, Mom, Dad, etc." | |
) | |
target_count = gr.Textbox( | |
label="Character Count", | |
value="0", | |
interactive=False | |
) | |
keywords = gr.Textbox( | |
label="Target Keywords", | |
placeholder="Enter keywords separated by commas", | |
lines=5 | |
) | |
submit_btn = gr.Button("Generate Amazon Listing", variant="primary") | |
with gr.Column(): | |
output = gr.Textbox( | |
label="Generated Amazon Listing", | |
lines=25 | |
) | |
validation_status = gr.Textbox( | |
label="Validation Status", | |
value="Pending", | |
interactive=False | |
) | |
# Event listeners for live character counting | |
quote.change(fn=lambda x: str(count_characters(x)), inputs=[quote], outputs=[quote_count]) | |
niche.change(fn=lambda x: str(count_characters(x)), inputs=[niche], outputs=[niche_count]) | |
target.change(fn=lambda x: str(count_characters(x)), inputs=[target], outputs=[target_count]) | |
submit_btn.click( | |
fn=generate_amazon_listing, | |
inputs=[api_key, quote, niche, target, keywords, product_type], | |
outputs=[output, validation_status] | |
) | |
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 | |
Product Type: T-Shirt | |
''') | |
return app | |
# Create and launch the app | |
app = create_interface() | |
if __name__ == "__main__": | |
app.launch() |