mroccuper commited on
Commit
15ee1ac
·
verified ·
1 Parent(s): f6213e6

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +171 -162
app.py CHANGED
@@ -2,94 +2,49 @@ import gradio as gr
2
  import google.generativeai as genai
3
  import re
4
  import json
5
- from functools import lru_cache
6
-
7
- # Global configuration
8
- MAX_CACHE_SIZE = 100
9
- CHAR_LIMITS = {
10
- "title": 60,
11
- "brand_min": 34,
12
- "brand_max": 50,
13
- "bullet": 256
14
- }
15
 
16
  def count_characters(text):
17
  """Count characters in text."""
18
  return len(text) if text else 0
19
 
20
- def validate_listing(result, quote, niche, target, keywords):
21
- """Validate all listing elements meet requirements"""
22
- errors = []
23
- keyword_list = [k.strip().lower() for k in keywords.split(",")]
24
-
25
- # Title validation
26
- title = result.get("title", "")
27
- if len(title) != CHAR_LIMITS["title"]:
28
- errors.append(f"Title length invalid: {len(title)} characters")
29
- if not all(kw in title.lower() for kw in [quote.lower(), niche.lower(), target.lower()]):
30
- errors.append("Title missing required elements")
31
-
32
- # Brand validation
33
- brand = result.get("brand_name", "")
34
- if not (CHAR_LIMITS["brand_min"] <= len(brand) <= CHAR_LIMITS["brand_max"]):
35
- errors.append(f"Brand name length invalid: {len(brand)} characters")
36
-
37
- # Bullet points validation
38
- for i, bp in enumerate([result.get("bullet_point_1", ""), result.get("bullet_point_2", "")]):
39
- if len(bp) != CHAR_LIMITS["bullet"]:
40
- errors.append(f"Bullet point {i+1} length invalid: {len(bp)} characters")
41
- if not re.match(r'^[A-Z ]{5,20}[A-Z][a-z]', bp): # Check for ALL CAPS start
42
- errors.append(f"Bullet point {i+1} doesn't start with ALL CAPS")
43
- for keyword in keyword_list[:3]: # Check primary keywords
44
- if keyword and keyword not in bp.lower():
45
- errors.append(f"Bullet point {i+1} missing keyword: {keyword}")
46
-
47
- return errors
48
-
49
- def format_output(result, quote, niche, target, keywords):
50
- """Format the output with character counts and validation"""
51
- validation_errors = validate_listing(result, quote, niche, target, keywords)
52
-
53
- output = f"Title ({count_characters(result.get('title', ''))}/{CHAR_LIMITS['title']} characters):\n{result.get('title', '')}\n"
54
- output += f"Brand Name ({count_characters(result.get('brand_name', ''))}/{CHAR_LIMITS['brand_max']} characters):\n{result.get('brand_name', '')}\n"
55
- output += f"Bullet Point 1 ({count_characters(result.get('bullet_point_1', ''))}/{CHAR_LIMITS['bullet']} characters):\n{result.get('bullet_point_1', '')}\n"
56
- output += f"Bullet Point 2 ({count_characters(result.get('bullet_point_2', ''))}/{CHAR_LIMITS['bullet']} characters):\n{result.get('bullet_point_2', '')}"
57
-
58
- if result.get("suggested_keywords"):
59
- output += f"\nSuggested Additional Keywords:\n{result.get('suggested_keywords')}"
60
 
61
- if validation_errors:
62
- output += "\n\nVALIDATION ERRORS:\n" + "\n".join(validation_errors)
63
 
64
  return output
65
 
66
- def generate_prompt(quote, niche, target, keywords, product_type="T-Shirt"):
67
- """Enhanced prompt with explicit SEO patterns"""
68
  combined_prompt = f"""You are an Amazon Merch on Demand SEO expert specializing in creating optimized t-shirt and apparel listings.
69
- MY INPUT IS ABOUT: A {niche} {product_type} with the design/quote: "{quote}" for {target}.
70
- SEO BEST PRACTICES:
71
- - Title structure: [Primary Keyword] [Design Reference] [Target Audience] [Secondary Keywords]
72
- - Brand Name: Combine niche + target + emotional benefit (e.g., "TeacherJoy Designs")
73
- - Bullet Point Structure:
74
- • Start with ALL CAPS benefit statement
75
- • Include 3-5 keywords
76
- • Add emotional trigger + practical benefit
77
 
 
78
  YOU MUST ONLY create an Amazon apparel listing about that EXACT input - no substitutions or different themes allowed.
 
79
  Generate a listing that includes:
80
- 1. Title (exactly {CHAR_LIMITS['title']} characters): Must include "{niche}" and reference the design/quote "{quote}" and target audience "{target}"
81
- 2. Brand Name ({CHAR_LIMITS['brand_min']}-{CHAR_LIMITS['brand_max']} characters): Create a fitting brand name for this specific {niche} apparel for {target}
82
- 3. Bullet Point 1 (exactly {CHAR_LIMITS['bullet']} characters): Highlight key features using ALL CAPS for the first 2-3 words
83
- 4. Bullet Point 2 (exactly {CHAR_LIMITS['bullet']} characters): Highlight additional features using ALL CAPS for the first 2-3 words
84
- The listing should be specifically for {product_type}s for the Amazon Merch on Demand program.
 
 
 
 
 
 
 
 
 
 
85
  The listing MUST be about: {niche} + {quote} + for {target}. Do not generate content about other holidays, quotes, or audiences.
86
- Use these specific keywords in your listing: {keywords}
87
 
88
- MANDATORY SEO PATTERNS:
89
- Title Examples:
90
- - "[Holiday] [Quote] [Target] T-Shirt"
91
- - "[Target] [Quote] [Holiday] Shirt"
92
- - "[Quote] [Target] [Holiday] Tee"
93
 
94
  Respond ONLY with a JSON object in this format:
95
  {{
@@ -99,144 +54,198 @@ Respond ONLY with a JSON object in this format:
99
  "bullet_point_2": "Bullet point 2 text here",
100
  "suggested_keywords": "Comma separated list of 5 additional keywords"
101
  }}
102
- The output must follow these exact character counts..."""
 
 
 
 
 
 
103
  return combined_prompt
104
 
105
- @lru_cache(maxsize=MAX_CACHE_SIZE)
106
- def cached_generate_listing(api_key, quote, niche, target, keywords, product_type):
107
- """Cached version of the listing generation"""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
108
  try:
 
109
  genai.configure(api_key=api_key)
110
  model = genai.GenerativeModel('gemini-1.5-pro')
111
 
112
- prompt = generate_prompt(quote, niche, target, keywords, product_type)
 
113
 
114
  response = model.generate_content(
115
  prompt,
116
  generation_config={
117
- "temperature": 0.3,
118
  "top_p": 0.8,
119
  "max_output_tokens": 2048
120
  }
121
  )
122
 
 
123
  response_text = response.text
124
  match = re.search(r'{.*}', response_text, re.DOTALL)
125
  if match:
126
- return json.loads(match.group(0))
127
- return {"error": "Could not extract JSON from response"}
128
- except Exception as e:
129
- return {"error": str(e)}
130
-
131
- def generate_amazon_listing(api_key, quote, niche, target, keywords, product_type):
132
- """Main function to generate Amazon listing with validation"""
133
- try:
134
- # Generate main listing
135
- result = cached_generate_listing(
136
- api_key, quote, niche, target, keywords, product_type
137
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
138
 
139
  # Generate variations
140
- variations_prompt = generate_multiple_variations_prompt(quote, niche, target, keywords, product_type)
141
- # Similar implementation for variations...
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
142
 
143
- return format_output(result, quote, niche, target, keywords)
144
  except Exception as e:
145
  return f"Error: {str(e)}"
146
 
 
147
  def create_interface():
148
- """Create Gradio interface with enhanced UI"""
149
  with gr.Blocks(title="Amazon Merch on Demand Listing Generator") as app:
150
  gr.Markdown("# Amazon Merch on Demand Listing Generator")
151
  gr.Markdown("Generate SEO-optimized t-shirt and apparel listings for Amazon Merch on Demand using Gemini 1.5 Pro AI.")
152
 
153
  with gr.Row():
154
  with gr.Column():
155
- api_key = gr.Textbox(
156
- label="Gemini API Key",
157
- placeholder="Enter your Gemini API key",
158
- type="password"
159
- )
160
-
161
- product_type = gr.Radio(
162
- choices=["T-Shirt", "Hoodie", "Sweater", "Tank Top"],
163
- label="Product Type",
164
- value="T-Shirt"
165
- )
166
-
167
- quote = gr.Textbox(
168
- label="Quote/Design/Idea",
169
- placeholder="Enter the quote or design idea"
170
- )
171
- quote_count = gr.Textbox(
172
- label="Character Count",
173
- value="0",
174
- interactive=False
175
- )
176
-
177
- niche = gr.Textbox(
178
- label="Holiday/Event",
179
- placeholder="Enter the holiday or event (e.g., St Patrick's Day)"
180
- )
181
- niche_count = gr.Textbox(
182
- label="Character Count",
183
- value="0",
184
- interactive=False
185
- )
186
-
187
- target = gr.Textbox(
188
- label="Target Audience",
189
- placeholder="Teacher, Mom, Dad, etc."
190
- )
191
- target_count = gr.Textbox(
192
- label="Character Count",
193
- value="0",
194
- interactive=False
195
- )
196
-
197
- keywords = gr.Textbox(
198
- label="Target Keywords",
199
- placeholder="Enter keywords separated by commas",
200
- lines=5
201
- )
202
-
203
  submit_btn = gr.Button("Generate Amazon Listing", variant="primary")
204
 
205
  with gr.Column():
206
- output = gr.Textbox(
207
- label="Generated Amazon Listing",
208
- lines=25
209
- )
210
- validation_status = gr.Textbox(
211
- label="Validation Status",
212
- value="Pending",
213
- interactive=False
214
- )
215
-
216
- # Event listeners for live character counting
217
- quote.change(fn=lambda x: str(count_characters(x)), inputs=[quote], outputs=[quote_count])
218
- niche.change(fn=lambda x: str(count_characters(x)), inputs=[niche], outputs=[niche_count])
219
- target.change(fn=lambda x: str(count_characters(x)), inputs=[target], outputs=[target_count])
220
 
221
  submit_btn.click(
222
  fn=generate_amazon_listing,
223
- inputs=[api_key, quote, niche, target, keywords, product_type],
224
- outputs=[output, validation_status]
225
  )
226
 
227
  gr.Markdown("## Example Input")
228
  gr.Markdown('''
 
229
  Quote/Design: Rainbow with a quote "Lucky To Be A Teacher"
230
  Holiday: St Patricks Day
231
  Target: Teacher, Teacher Mom
232
  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
233
- Product Type: T-Shirt
234
- ''')
235
-
236
  return app
237
 
238
  # Create and launch the app
239
  app = create_interface()
240
 
 
241
  if __name__ == "__main__":
242
  app.launch()
 
2
  import google.generativeai as genai
3
  import re
4
  import json
 
 
 
 
 
 
 
 
 
 
5
 
6
  def count_characters(text):
7
  """Count characters in text."""
8
  return len(text) if text else 0
9
 
10
+ def format_output(title, brand_name, bullet1, bullet2, suggested_keywords=None):
11
+ """Format the output with character counts."""
12
+ output = f"Title ({count_characters(title)}/60 characters):\n{title}\n\n"
13
+ output += f"Brand Name ({count_characters(brand_name)}/50 characters):\n{brand_name}\n\n"
14
+ output += f"Bullet Point 1 ({count_characters(bullet1)}/256 characters):\n{bullet1}\n\n"
15
+ output += f"Bullet Point 2 ({count_characters(bullet2)}/256 characters):\n{bullet2}"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
16
 
17
+ if suggested_keywords:
18
+ output += f"\n\nSuggested Additional Keywords:\n{suggested_keywords}"
19
 
20
  return output
21
 
22
+ def generate_prompt(quote, niche, target, keywords):
23
+ """Generate the prompt for Gemini API."""
24
  combined_prompt = f"""You are an Amazon Merch on Demand SEO expert specializing in creating optimized t-shirt and apparel listings.
 
 
 
 
 
 
 
 
25
 
26
+ MY INPUT IS ABOUT: A {niche} t-shirt with the design/quote: "{quote}" for {target}.
27
  YOU MUST ONLY create an Amazon apparel listing about that EXACT input - no substitutions or different themes allowed.
28
+
29
  Generate a listing that includes:
30
+
31
+ 1. Title (exactly 60 characters): Must include "{niche}" and reference the design/quote "{quote}" and target audience "{target}"
32
+ 2. Brand Name (34-50 characters): Create a fitting brand name for this specific {niche} apparel for {target}
33
+ 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.
34
+ 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.
35
+
36
+ IMPORTANT RULES FOR BULLET POINTS:
37
+ - Bullet points must be between 240-256 characters
38
+ - DO NOT include generic phrases like "PREMIUM QUALITY" or references to material quality
39
+ - DO NOT include phrases like "This comfortable and stylish tee is made with high-quality materials for a soft feel and long-lasting wear"
40
+ - Focus ONLY on the specific design, niche, and quote provided
41
+ - Every sentence must directly relate to the quote, niche theme, and target audience
42
+ - Do not include any content that strays from the specific theme provided
43
+
44
+ The listing should be specifically for t-shirts, hoodies, or sweaters for the Amazon Merch on Demand program.
45
  The listing MUST be about: {niche} + {quote} + for {target}. Do not generate content about other holidays, quotes, or audiences.
 
46
 
47
+ Use these specific keywords in your listing: {keywords}
 
 
 
 
48
 
49
  Respond ONLY with a JSON object in this format:
50
  {{
 
54
  "bullet_point_2": "Bullet point 2 text here",
55
  "suggested_keywords": "Comma separated list of 5 additional keywords"
56
  }}
57
+
58
+ The output must follow these exact character counts:
59
+ - Title: Exactly 60 characters
60
+ - Brand Name: Between 34-50 characters
61
+ - Bullet Point 1: Between 240-256 characters
62
+ - Bullet Point 2: Between 240-256 characters"""
63
+
64
  return combined_prompt
65
 
66
+ def generate_multiple_variations_prompt(quote, niche, target, keywords):
67
+ """Generate the prompt for multiple variations of title and brand name."""
68
+ combined_prompt = f"""You are an Amazon Merch on Demand SEO expert specializing in creating optimized t-shirt and apparel listings.
69
+
70
+ MY INPUT IS ABOUT: A {niche} t-shirt with the design/quote: "{quote}" for {target}.
71
+ YOU MUST ONLY create variations about that EXACT input - no substitutions or different themes allowed.
72
+
73
+ Generate 3 different variations for the Title and Brand Name based on the provided information.
74
+ All variations MUST be about {niche} + "{quote}" + for {target} audience.
75
+
76
+ The titles MUST include:
77
+ - The specific holiday/event: {niche}
78
+ - Reference to the quote/design: "{quote}"
79
+ - The target audience: {target}
80
+
81
+ Focus only on t-shirts, sweaters, and hoodies for the Amazon Merch on Demand program.
82
+ All titles must be exactly 60 characters and brand names between 34-50 characters.
83
+
84
+ Respond ONLY with a JSON object in this format:
85
+ {{
86
+ "title_variations": [
87
+ "Title variation 1 text here (exactly 60 characters)",
88
+ "Title variation 2 text here (exactly 60 characters)",
89
+ "Title variation 3 text here (exactly 60 characters)"
90
+ ],
91
+ "brand_name_variations": [
92
+ "Brand name variation 1 here (34-50 characters)",
93
+ "Brand name variation 2 here (34-50 characters)",
94
+ "Brand name variation 3 here (34-50 characters)"
95
+ ]
96
+ }}"""
97
+
98
+ return combined_prompt
99
+
100
+ def generate_amazon_listing(api_key, quote, niche, target, keywords):
101
+ """Generate Amazon listing using Gemini API."""
102
  try:
103
+ # Configure the Gemini API with the provided key
104
  genai.configure(api_key=api_key)
105
  model = genai.GenerativeModel('gemini-1.5-pro')
106
 
107
+ # Generate the main listing
108
+ prompt = generate_prompt(quote, niche, target, keywords)
109
 
110
  response = model.generate_content(
111
  prompt,
112
  generation_config={
113
+ "temperature": 0.3, # Lower temperature for more focused results
114
  "top_p": 0.8,
115
  "max_output_tokens": 2048
116
  }
117
  )
118
 
119
+ # Extract JSON from the response
120
  response_text = response.text
121
  match = re.search(r'{.*}', response_text, re.DOTALL)
122
  if match:
123
+ json_str = match.group(0)
124
+ try:
125
+ result = json.loads(json_str)
126
+
127
+ # Validate that the output actually matches the input criteria
128
+ title = result.get("title", "")
129
+ if not (quote.lower() in title.lower() or
130
+ niche.lower() in title.lower() or
131
+ any(t.lower() in title.lower() for t in target.lower().split(','))):
132
+ return f"Error: Generated title doesn't match the requested theme: '{quote}', '{niche}', or '{target}'. Please try again."
133
+
134
+ # Validate bullet point lengths
135
+ bullet1 = result.get("bullet_point_1", "")
136
+ bullet2 = result.get("bullet_point_2", "")
137
+
138
+ if len(bullet1) < 240 or len(bullet1) > 256:
139
+ return f"Error: Bullet point 1 length ({len(bullet1)}) is not between 240-256 characters. Please try again."
140
+
141
+ if len(bullet2) < 240 or len(bullet2) > 256:
142
+ return f"Error: Bullet point 2 length ({len(bullet2)}) is not between 240-256 characters. Please try again."
143
+
144
+ # Check for generic content in bullet points
145
+ generic_phrases = ["premium quality", "high-quality materials", "soft feel", "long-lasting wear",
146
+ "comfortable and stylish", "perfect gift", "great gift"]
147
+
148
+ for phrase in generic_phrases:
149
+ if phrase in bullet1.lower() or phrase in bullet2.lower():
150
+ return f"Error: Generated bullet points contain generic phrase '{phrase}'. Please try again."
151
+
152
+ except json.JSONDecodeError:
153
+ return "Error parsing JSON response from Gemini API"
154
+ else:
155
+ return "Could not extract JSON from Gemini API response"
156
 
157
  # Generate variations
158
+ variations_prompt = generate_multiple_variations_prompt(quote, niche, target, keywords)
159
+ response_var = model.generate_content(
160
+ variations_prompt,
161
+ generation_config={
162
+ "temperature": 0.4, # Lower temperature for more focused results
163
+ "top_p": 0.8,
164
+ "max_output_tokens": 2048
165
+ }
166
+ )
167
+
168
+ # Extract JSON from the variations response
169
+ response_var_text = response_var.text
170
+ match_var = re.search(r'{.*}', response_var_text, re.DOTALL)
171
+ if match_var:
172
+ json_str_var = match_var.group(0)
173
+ try:
174
+ variations = json.loads(json_str_var)
175
+ except json.JSONDecodeError:
176
+ variations = {
177
+ "title_variations": ["Error generating variations"],
178
+ "brand_name_variations": ["Error generating variations"]
179
+ }
180
+ else:
181
+ variations = {
182
+ "title_variations": ["Error generating variations"],
183
+ "brand_name_variations": ["Error generating variations"]
184
+ }
185
+
186
+ # Format main output
187
+ main_output = format_output(
188
+ result.get("title", "Error generating title"),
189
+ result.get("brand_name", "Error generating brand name"),
190
+ result.get("bullet_point_1", "Error generating bullet point 1"),
191
+ result.get("bullet_point_2", "Error generating bullet point 2"),
192
+ result.get("suggested_keywords", "Error generating suggested keywords")
193
+ )
194
+
195
+ # Format variations output
196
+ variations_output = "ADDITIONAL VARIATIONS:\n\n"
197
+ variations_output += "Title Variations:\n"
198
+ for i, var in enumerate(variations.get("title_variations", []), 1):
199
+ variations_output += f"{i}. {var} ({count_characters(var)}/60 characters)\n"
200
+
201
+ variations_output += "\nBrand Name Variations:\n"
202
+ for i, var in enumerate(variations.get("brand_name_variations", []), 1):
203
+ variations_output += f"{i}. {var} ({count_characters(var)}/50 characters)\n"
204
+
205
+ return main_output + "\n\n" + variations_output
206
 
 
207
  except Exception as e:
208
  return f"Error: {str(e)}"
209
 
210
+ # Create the Gradio interface
211
  def create_interface():
 
212
  with gr.Blocks(title="Amazon Merch on Demand Listing Generator") as app:
213
  gr.Markdown("# Amazon Merch on Demand Listing Generator")
214
  gr.Markdown("Generate SEO-optimized t-shirt and apparel listings for Amazon Merch on Demand using Gemini 1.5 Pro AI.")
215
 
216
  with gr.Row():
217
  with gr.Column():
218
+ api_key = gr.Textbox(label="Gemini API Key", placeholder="Enter your Gemini API key", type="password")
219
+ quote = gr.Textbox(label="Quote/Design/Idea", placeholder="Enter the quote or design idea")
220
+ niche = gr.Textbox(label="Holiday/Event", placeholder="Enter the holiday or event (e.g., St Patrick's Day)")
221
+ target = gr.Textbox(label="Target Audience", placeholder="Teacher, Mom, Dad, etc.")
222
+ keywords = gr.Textbox(label="Target Keywords", placeholder="Enter keywords separated by commas", lines=5)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
223
  submit_btn = gr.Button("Generate Amazon Listing", variant="primary")
224
 
225
  with gr.Column():
226
+ output = gr.Textbox(label="Generated Amazon Listing", lines=25)
 
 
 
 
 
 
 
 
 
 
 
 
 
227
 
228
  submit_btn.click(
229
  fn=generate_amazon_listing,
230
+ inputs=[api_key, quote, niche, target, keywords],
231
+ outputs=output
232
  )
233
 
234
  gr.Markdown("## Example Input")
235
  gr.Markdown('''
236
+ ```
237
  Quote/Design: Rainbow with a quote "Lucky To Be A Teacher"
238
  Holiday: St Patricks Day
239
  Target: Teacher, Teacher Mom
240
  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
241
+ ```
242
+ ''')
243
+
244
  return app
245
 
246
  # Create and launch the app
247
  app = create_interface()
248
 
249
+ # For deployment on Hugging Face Spaces
250
  if __name__ == "__main__":
251
  app.launch()