Update app.py
Browse files
app.py
CHANGED
@@ -1,17 +1,18 @@
|
|
1 |
"""
|
2 |
-
|
3 |
-
|
|
|
4 |
"""
|
5 |
|
6 |
import gradio as gr
|
7 |
import os
|
8 |
import logging
|
9 |
-
import asyncio
|
10 |
import json
|
11 |
-
|
|
|
|
|
12 |
from PIL import Image
|
13 |
import io
|
14 |
-
import base64
|
15 |
|
16 |
# Google Service Account Authentication Setup
|
17 |
def setup_google_credentials():
|
@@ -44,29 +45,391 @@ def setup_google_credentials():
|
|
44 |
# Setup Google credentials on startup
|
45 |
setup_google_credentials()
|
46 |
|
47 |
-
#
|
48 |
try:
|
49 |
-
|
50 |
-
|
|
|
51 |
except ImportError:
|
52 |
-
|
53 |
|
54 |
# Configure logging
|
55 |
logging.basicConfig(level=logging.INFO)
|
56 |
logger = logging.getLogger(__name__)
|
57 |
|
58 |
-
#
|
59 |
-
|
60 |
-
|
61 |
-
|
62 |
-
|
63 |
-
|
64 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
65 |
|
66 |
-
def
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
67 |
"""Process API response and return image and review text for Gradio display"""
|
68 |
try:
|
69 |
-
response_data =
|
70 |
|
71 |
if not response_data.get('success', False):
|
72 |
return None, f"β Generation failed: {response_data.get('error', 'Unknown error')}"
|
@@ -91,7 +454,6 @@ def process_generated_image_and_results(api_response):
|
|
91 |
quality_score = review_data.get('quality_score', 0)
|
92 |
passed = review_data.get('passed', False)
|
93 |
final_status = review_data.get('final_status', 'unknown')
|
94 |
-
iterations = review_data.get('iterations', 0)
|
95 |
recommendations = review_data.get('recommendations', [])
|
96 |
|
97 |
status_emoji = "π’" if passed else "π΄"
|
@@ -99,23 +461,19 @@ def process_generated_image_and_results(api_response):
|
|
99 |
# Extract metadata about generation method
|
100 |
metadata = response_data.get('metadata', {})
|
101 |
generation_method = metadata.get('generation_method', 'unknown')
|
102 |
-
real_ai = metadata.get('real_ai_generation', False)
|
103 |
|
104 |
generation_info = ""
|
105 |
-
if generation_method == "
|
106 |
-
generation_info = "π¨ **Generated with**: Imagen3
|
107 |
-
elif generation_method == "google-genai-sdk":
|
108 |
-
generation_info = "π¨ **Generated with**: Google Genai SDK (Real AI)\n"
|
109 |
elif generation_method == "placeholder":
|
110 |
generation_info = "π¨ **Generated with**: Placeholder (Fallback)\n"
|
111 |
|
112 |
-
review_text = f"""**π
|
113 |
|
114 |
{generation_info}
|
115 |
**Quality Score:** {quality_score:.2f}/1.0
|
116 |
**Status:** {status_emoji} {final_status.upper()}
|
117 |
-
**
|
118 |
-
**Architecture:** A2A Agent Communication
|
119 |
|
120 |
**π‘ Recommendations:**
|
121 |
"""
|
@@ -125,15 +483,7 @@ def process_generated_image_and_results(api_response):
|
|
125 |
review_text += f"{i}. {rec}\n"
|
126 |
else:
|
127 |
review_text += "β’ Image meets quality standards\n"
|
128 |
-
|
129 |
-
# Add workflow history
|
130 |
-
workflow_history = review_data.get('workflow_history', [])
|
131 |
-
if workflow_history and len(workflow_history) > 1:
|
132 |
-
review_text += "\n**π A2A Workflow History:**\n"
|
133 |
-
for item in workflow_history:
|
134 |
-
iteration = item.get('iteration', 'N/A')
|
135 |
-
score = item.get('review_score', 'N/A')
|
136 |
-
review_text += f"β’ Iteration {iteration}: Score {score:.2f}\n"
|
137 |
else:
|
138 |
review_text = "β οΈ Review data not available"
|
139 |
|
@@ -142,80 +492,21 @@ def process_generated_image_and_results(api_response):
|
|
142 |
except Exception as e:
|
143 |
return None, f"β Error processing results: {str(e)}"
|
144 |
|
145 |
-
def
|
146 |
-
"""
|
147 |
if not prompt.strip():
|
148 |
return None, "β οΈ Please enter a prompt to generate an image."
|
149 |
|
150 |
-
if not A2A_AVAILABLE or not orchestrator:
|
151 |
-
return None, "β A2A Orchestrator not available. Please check setup."
|
152 |
-
|
153 |
try:
|
154 |
-
|
155 |
-
|
156 |
-
|
157 |
-
result = asyncio.run(
|
158 |
-
orchestrator.generate_image_with_review(
|
159 |
-
prompt=prompt,
|
160 |
-
style=style,
|
161 |
-
max_retries=max_retries,
|
162 |
-
review_threshold=review_threshold,
|
163 |
-
review_guidelines=review_guidelines
|
164 |
-
)
|
165 |
-
)
|
166 |
-
|
167 |
-
if result["success"]:
|
168 |
-
image, review_text = process_generated_image_and_results(result)
|
169 |
-
success_message = f"β
A2A Image generation successful!\n\n{review_text}"
|
170 |
-
return image, success_message
|
171 |
-
else:
|
172 |
-
error_message = f"β A2A Generation failed: {result.get('error', 'Unknown error')}"
|
173 |
-
return None, error_message
|
174 |
-
|
175 |
except Exception as e:
|
176 |
-
error_message = f"β
|
177 |
logger.error(error_message)
|
178 |
return None, error_message
|
179 |
|
180 |
-
|
181 |
-
"""Check if A2A agents are available"""
|
182 |
-
if not orchestrator:
|
183 |
-
return {"agent1": False, "agent2": False, "orchestrator": False}
|
184 |
-
|
185 |
-
try:
|
186 |
-
health_status = await orchestrator.check_agents_health()
|
187 |
-
health_status["orchestrator"] = True
|
188 |
-
return health_status
|
189 |
-
except Exception as e:
|
190 |
-
logger.error(f"Error checking agent health: {e}")
|
191 |
-
return {"agent1": False, "agent2": False, "orchestrator": False}
|
192 |
-
|
193 |
-
def get_a2a_system_status():
|
194 |
-
"""Get A2A system status for display"""
|
195 |
-
if not A2A_AVAILABLE:
|
196 |
-
return "**π§ A2A System Status:**\n\nβ A2A not available\nβ οΈ Install google-a2a to enable A2A functionality"
|
197 |
-
|
198 |
-
try:
|
199 |
-
health_status = asyncio.run(check_a2a_agents())
|
200 |
-
|
201 |
-
status_text = "**π§ A2A System Status:**\n\n"
|
202 |
-
status_text += f"{'β
' if health_status.get('orchestrator', False) else 'β'} A2A Orchestrator\n"
|
203 |
-
status_text += f"{'β
' if health_status.get('agent1', False) else 'β'} Agent1 (Image Generator)\n"
|
204 |
-
status_text += f"{'β
' if health_status.get('agent2', False) else 'β'} Agent2 (Marketing Reviewer)\n"
|
205 |
-
|
206 |
-
all_healthy = all(health_status.values())
|
207 |
-
if all_healthy:
|
208 |
-
status_text += "\nπ All A2A agents are running!"
|
209 |
-
else:
|
210 |
-
status_text += "\nβ οΈ Some A2A agents are not responding."
|
211 |
-
status_text += "\nRun: `python a2a_servers.py` to start agents"
|
212 |
-
|
213 |
-
return status_text
|
214 |
-
|
215 |
-
except Exception as e:
|
216 |
-
return f"**π§ A2A System Status:**\n\nβ Error checking status: {str(e)}"
|
217 |
-
|
218 |
-
# Define suggested prompts (same as before)
|
219 |
SUGGESTED_PROMPTS = {
|
220 |
"Modern office team collaboration": ("A modern office space with diverse professionals collaborating around a sleek conference table, natural lighting, professional attire, English signage visible", "realistic"),
|
221 |
"Executive boardroom meeting": ("Professional executive boardroom with polished conference table, city skyline view, business documents, English presentations on screens", "realistic"),
|
@@ -226,12 +517,14 @@ SUGGESTED_PROMPTS = {
|
|
226 |
}
|
227 |
|
228 |
# Create Gradio interface
|
229 |
-
with gr.Blocks(title="
|
230 |
gr.Markdown("""
|
231 |
-
# π¨
|
232 |
-
### Professional AI image generation with
|
233 |
|
234 |
-
**
|
|
|
|
|
235 |
""")
|
236 |
|
237 |
with gr.Row():
|
@@ -255,45 +548,21 @@ with gr.Blocks(title="A2A Marketing Image Generator", theme=gr.themes.Soft()) as
|
|
255 |
|
256 |
review_guidelines = gr.Textbox(
|
257 |
label="π Marketing Review Guidelines (Optional)",
|
258 |
-
placeholder="e.g., All text must be in English only, focus on professional appearance, ensure brand colors are prominent
|
259 |
lines=3,
|
260 |
-
info="Provide specific marketing guidelines for
|
261 |
)
|
262 |
|
263 |
-
# Advanced settings
|
264 |
-
with gr.Accordion("π§ Advanced Settings", open=False):
|
265 |
-
max_retries = gr.Slider(
|
266 |
-
minimum=1,
|
267 |
-
maximum=5,
|
268 |
-
value=3,
|
269 |
-
step=1,
|
270 |
-
label="Max Retries",
|
271 |
-
info="Maximum number of retry attempts if generation fails"
|
272 |
-
)
|
273 |
-
|
274 |
-
review_threshold = gr.Slider(
|
275 |
-
minimum=0.0,
|
276 |
-
maximum=1.0,
|
277 |
-
value=0.8,
|
278 |
-
step=0.1,
|
279 |
-
label="Quality Threshold",
|
280 |
-
info="Minimum quality score required for auto-approval"
|
281 |
-
)
|
282 |
-
|
283 |
# Generate button
|
284 |
-
generate_btn = gr.Button("π Generate
|
285 |
|
286 |
# Status
|
287 |
-
gr.Markdown("π **Mode**:
|
288 |
-
|
289 |
-
# System status
|
290 |
-
with gr.Accordion("π A2A System Status", open=False):
|
291 |
-
status_display = gr.Markdown(get_a2a_system_status())
|
292 |
-
refresh_status_btn = gr.Button("π Refresh A2A Status", size="sm")
|
293 |
|
294 |
with gr.Column(scale=2):
|
295 |
# Results display
|
296 |
-
gr.Markdown("### πΌοΈ Generated Image &
|
297 |
|
298 |
image_output = gr.Image(
|
299 |
label="Generated Marketing Image",
|
@@ -303,8 +572,8 @@ with gr.Blocks(title="A2A Marketing Image Generator", theme=gr.themes.Soft()) as
|
|
303 |
)
|
304 |
|
305 |
review_output = gr.Markdown(
|
306 |
-
value="Click **Generate
|
307 |
-
label="
|
308 |
)
|
309 |
|
310 |
# Suggested prompts section
|
@@ -334,36 +603,25 @@ with gr.Blocks(title="A2A Marketing Image Generator", theme=gr.themes.Soft()) as
|
|
334 |
|
335 |
# Event handlers
|
336 |
generate_btn.click(
|
337 |
-
fn=
|
338 |
-
inputs=[prompt, style,
|
339 |
outputs=[image_output, review_output],
|
340 |
show_progress=True
|
341 |
)
|
342 |
|
343 |
-
refresh_status_btn.click(
|
344 |
-
fn=get_a2a_system_status,
|
345 |
-
outputs=status_display
|
346 |
-
)
|
347 |
-
|
348 |
# Footer
|
349 |
gr.Markdown("""
|
350 |
---
|
351 |
<div style='text-align: center; color: #666; font-size: 0.9rem;'>
|
352 |
-
<p>π¨
|
353 |
-
<p>
|
354 |
-
<p>
|
355 |
</div>
|
356 |
""")
|
357 |
|
358 |
if __name__ == "__main__":
|
359 |
-
logger.info("π Starting
|
360 |
-
logger.info(f"
|
361 |
-
|
362 |
-
if A2A_AVAILABLE:
|
363 |
-
logger.info("π‘ Using proper A2A agent-to-agent communication")
|
364 |
-
logger.info("π Architecture: Gradio β Orchestrator β Agent1 (MCP-Imagen3) β Agent2 (Reviewer)")
|
365 |
-
logger.info("π Make sure to start agents: python a2a_servers.py")
|
366 |
-
else:
|
367 |
-
logger.warning("π‘ A2A not available - install google-a2a for full functionality")
|
368 |
|
369 |
-
demo.launch()
|
|
|
1 |
"""
|
2 |
+
Marketing Image Generator with Gradio MCP Server
|
3 |
+
Professional AI image generation using Google Imagen3 with marketing review
|
4 |
+
Deployed on HuggingFace Spaces with built-in MCP server support
|
5 |
"""
|
6 |
|
7 |
import gradio as gr
|
8 |
import os
|
9 |
import logging
|
|
|
10 |
import json
|
11 |
+
import base64
|
12 |
+
import asyncio
|
13 |
+
from typing import Dict, Any, Tuple
|
14 |
from PIL import Image
|
15 |
import io
|
|
|
16 |
|
17 |
# Google Service Account Authentication Setup
|
18 |
def setup_google_credentials():
|
|
|
45 |
# Setup Google credentials on startup
|
46 |
setup_google_credentials()
|
47 |
|
48 |
+
# Google AI imports
|
49 |
try:
|
50 |
+
import google.generativeai as genai
|
51 |
+
from google import genai as genai_sdk
|
52 |
+
GEMINI_AVAILABLE = True
|
53 |
except ImportError:
|
54 |
+
GEMINI_AVAILABLE = False
|
55 |
|
56 |
# Configure logging
|
57 |
logging.basicConfig(level=logging.INFO)
|
58 |
logger = logging.getLogger(__name__)
|
59 |
|
60 |
+
# Get API keys - prioritize HuggingFace Secrets
|
61 |
+
GCP_KEYS = [
|
62 |
+
# Hugging Face Secrets (these are the primary ones for HF deployment)
|
63 |
+
os.getenv("GOOGLE_API_KEY"),
|
64 |
+
os.getenv("GEMINI_API_KEY"),
|
65 |
+
os.getenv("GCP_API_KEY"),
|
66 |
+
# Local development keys (fallback for local testing)
|
67 |
+
os.getenv("GCP_KEY_1"),
|
68 |
+
os.getenv("GCP_KEY_2"),
|
69 |
+
os.getenv("GCP_KEY_3"),
|
70 |
+
os.getenv("GCP_KEY_4"),
|
71 |
+
os.getenv("GCP_KEY_5"),
|
72 |
+
os.getenv("GCP_KEY_6")
|
73 |
+
]
|
74 |
+
|
75 |
+
GOOGLE_API_KEY = next((key for key in GCP_KEYS if key), None)
|
76 |
+
|
77 |
+
if GOOGLE_API_KEY and GEMINI_AVAILABLE:
|
78 |
+
genai.configure(api_key=GOOGLE_API_KEY)
|
79 |
+
logger.info("β
Google AI configured successfully")
|
80 |
+
|
81 |
+
# MCP-enabled functions for Agent1 (Image Generator)
|
82 |
+
def enhance_prompt_with_gemini(prompt: str, style: str) -> str:
|
83 |
+
"""
|
84 |
+
Use Gemini to enhance the user's prompt for better image generation.
|
85 |
+
|
86 |
+
Args:
|
87 |
+
prompt (str): The original marketing prompt
|
88 |
+
style (str): The desired image style
|
89 |
+
|
90 |
+
Returns:
|
91 |
+
str: Enhanced prompt optimized for image generation
|
92 |
+
"""
|
93 |
+
if not GEMINI_AVAILABLE or not GOOGLE_API_KEY:
|
94 |
+
# Basic enhancement without Gemini
|
95 |
+
style_enhancers = {
|
96 |
+
"realistic": "photorealistic, high detail, professional photography, sharp focus",
|
97 |
+
"artistic": "artistic masterpiece, creative composition, painterly style",
|
98 |
+
"cartoon": "cartoon style, vibrant colors, playful, animated character design",
|
99 |
+
"photographic": "professional photograph, high quality, detailed, commercial photography",
|
100 |
+
"illustration": "digital illustration, clean vector art, modern design"
|
101 |
+
}
|
102 |
+
enhancer = style_enhancers.get(style.lower(), "high quality, detailed")
|
103 |
+
return f"{prompt}, {enhancer}"
|
104 |
+
|
105 |
+
try:
|
106 |
+
enhancement_prompt = f"""
|
107 |
+
You are an expert prompt engineer for AI image generation. Take this marketing prompt and enhance it for optimal results.
|
108 |
+
|
109 |
+
Original prompt: "{prompt}"
|
110 |
+
Desired style: "{style}"
|
111 |
+
|
112 |
+
Please provide an enhanced version that:
|
113 |
+
1. Maintains the core marketing intent
|
114 |
+
2. Adds specific technical details for better image quality
|
115 |
+
3. Includes appropriate style descriptors for "{style}" style
|
116 |
+
4. Adds professional marketing composition guidance
|
117 |
+
5. Keeps the enhanced prompt under 150 words
|
118 |
+
|
119 |
+
Return only the enhanced prompt without explanation.
|
120 |
+
"""
|
121 |
+
|
122 |
+
model = genai.GenerativeModel('gemini-1.5-flash')
|
123 |
+
response = model.generate_content(enhancement_prompt)
|
124 |
+
enhanced = response.text.strip()
|
125 |
+
|
126 |
+
logger.info(f"Gemini enhanced prompt: {enhanced}")
|
127 |
+
return enhanced
|
128 |
+
|
129 |
+
except Exception as e:
|
130 |
+
logger.warning(f"Failed to enhance prompt with Gemini: {e}")
|
131 |
+
style_enhancers = {
|
132 |
+
"realistic": "photorealistic, high detail, professional photography",
|
133 |
+
"artistic": "artistic masterpiece, creative composition",
|
134 |
+
"cartoon": "cartoon style, vibrant colors, playful",
|
135 |
+
"photographic": "professional photograph, high quality, detailed",
|
136 |
+
"illustration": "digital illustration, clean design"
|
137 |
+
}
|
138 |
+
enhancer = style_enhancers.get(style.lower(), "high quality")
|
139 |
+
return f"{prompt}, {enhancer}"
|
140 |
+
|
141 |
+
def generate_marketing_image(prompt: str, style: str = "realistic") -> str:
|
142 |
+
"""
|
143 |
+
Generate a professional marketing image using Google Imagen3.
|
144 |
+
|
145 |
+
Args:
|
146 |
+
prompt (str): Description of the marketing image to generate
|
147 |
+
style (str): Art style for the image (realistic, artistic, cartoon, photographic, illustration)
|
148 |
+
|
149 |
+
Returns:
|
150 |
+
str: JSON string containing image data and metadata
|
151 |
+
"""
|
152 |
+
logger.info(f"π¨ Generating marketing image: {prompt}")
|
153 |
+
|
154 |
+
try:
|
155 |
+
# Enhance the prompt
|
156 |
+
enhanced_prompt = enhance_prompt_with_gemini(prompt, style)
|
157 |
+
|
158 |
+
# Try to generate with Google Genai SDK
|
159 |
+
if GEMINI_AVAILABLE and GOOGLE_API_KEY:
|
160 |
+
try:
|
161 |
+
logger.info("π¨ Using Google Genai SDK for image generation")
|
162 |
+
|
163 |
+
# Initialize the genai SDK client
|
164 |
+
client = genai_sdk.Client(api_key=GOOGLE_API_KEY)
|
165 |
+
|
166 |
+
# Generate image using Imagen 3 via SDK
|
167 |
+
result = client.models.generate_images(
|
168 |
+
model="imagen-3.0-generate-002",
|
169 |
+
prompt=enhanced_prompt,
|
170 |
+
config={
|
171 |
+
"number_of_images": 1,
|
172 |
+
"output_mime_type": "image/png"
|
173 |
+
}
|
174 |
+
)
|
175 |
+
|
176 |
+
# Check if we got a valid response with images
|
177 |
+
if result and hasattr(result, 'generated_images') and len(result.generated_images) > 0:
|
178 |
+
generated_image = result.generated_images[0]
|
179 |
+
|
180 |
+
if hasattr(generated_image, 'image') and hasattr(generated_image.image, 'image_bytes'):
|
181 |
+
# Convert image bytes to base64 data URL
|
182 |
+
image_bytes = generated_image.image.image_bytes
|
183 |
+
img_base64 = base64.b64encode(image_bytes).decode('utf-8')
|
184 |
+
|
185 |
+
# Determine MIME type from the response or default to PNG
|
186 |
+
mime_type = getattr(generated_image.image, 'mime_type', 'image/png')
|
187 |
+
image_url = f"data:{mime_type};base64,{img_base64}"
|
188 |
+
|
189 |
+
response_data = {
|
190 |
+
"success": True,
|
191 |
+
"image_url": image_url,
|
192 |
+
"prompt": prompt,
|
193 |
+
"enhanced_prompt": enhanced_prompt,
|
194 |
+
"style": style,
|
195 |
+
"generation_method": "google-genai-sdk",
|
196 |
+
"real_ai_generation": True
|
197 |
+
}
|
198 |
+
|
199 |
+
logger.info("β
Successfully generated real AI image with Google SDK!")
|
200 |
+
return json.dumps(response_data)
|
201 |
+
|
202 |
+
except Exception as e:
|
203 |
+
logger.error(f"Google SDK generation failed: {e}")
|
204 |
+
|
205 |
+
# Fallback: Generate a deterministic placeholder
|
206 |
+
logger.info("π Using placeholder URL fallback")
|
207 |
+
prompt_hash = abs(hash(enhanced_prompt)) % 10000
|
208 |
+
image_url = f"https://picsum.photos/seed/{prompt_hash}/1024/1024"
|
209 |
+
|
210 |
+
response_data = {
|
211 |
+
"success": True,
|
212 |
+
"image_url": image_url,
|
213 |
+
"prompt": prompt,
|
214 |
+
"enhanced_prompt": enhanced_prompt,
|
215 |
+
"style": style,
|
216 |
+
"generation_method": "placeholder",
|
217 |
+
"real_ai_generation": False
|
218 |
+
}
|
219 |
+
|
220 |
+
return json.dumps(response_data)
|
221 |
+
|
222 |
+
except Exception as e:
|
223 |
+
logger.error(f"Image generation failed: {e}")
|
224 |
+
return json.dumps({
|
225 |
+
"success": False,
|
226 |
+
"error": f"Generation failed: {str(e)}",
|
227 |
+
"prompt": prompt,
|
228 |
+
"style": style
|
229 |
+
})
|
230 |
|
231 |
+
def analyze_marketing_prompt(prompt: str, review_guidelines: str = "") -> str:
|
232 |
+
"""
|
233 |
+
Analyze a marketing prompt for quality, relevance, and compliance.
|
234 |
+
|
235 |
+
Args:
|
236 |
+
prompt (str): The marketing prompt to analyze
|
237 |
+
review_guidelines (str): Specific guidelines to check against
|
238 |
+
|
239 |
+
Returns:
|
240 |
+
str: JSON string containing detailed analysis and recommendations
|
241 |
+
"""
|
242 |
+
logger.info(f"π Analyzing marketing prompt: {prompt[:50]}...")
|
243 |
+
|
244 |
+
try:
|
245 |
+
word_count = len(prompt.split())
|
246 |
+
|
247 |
+
# Check for marketing-specific elements
|
248 |
+
marketing_keywords = [
|
249 |
+
"professional", "corporate", "business", "marketing", "brand", "commercial",
|
250 |
+
"office", "team", "collaboration", "presentation", "meeting", "workplace",
|
251 |
+
"customer", "service", "product", "showcase", "display", "advertising"
|
252 |
+
]
|
253 |
+
|
254 |
+
style_keywords = [
|
255 |
+
"realistic", "photographic", "artistic", "creative", "modern", "clean",
|
256 |
+
"minimalist", "professional", "high-quality", "detailed", "sharp"
|
257 |
+
]
|
258 |
+
|
259 |
+
composition_keywords = [
|
260 |
+
"lighting", "composition", "background", "foreground", "perspective",
|
261 |
+
"angle", "framing", "focus", "depth", "contrast", "colors"
|
262 |
+
]
|
263 |
+
|
264 |
+
# Count keyword categories
|
265 |
+
marketing_score = sum(1 for word in marketing_keywords if word.lower() in prompt.lower()) / len(marketing_keywords)
|
266 |
+
style_score = sum(1 for word in style_keywords if word.lower() in prompt.lower()) / len(style_keywords)
|
267 |
+
composition_score = sum(1 for word in composition_keywords if word.lower() in prompt.lower()) / len(composition_keywords)
|
268 |
+
|
269 |
+
# Base quality assessment
|
270 |
+
if word_count < 5:
|
271 |
+
base_quality = 0.3
|
272 |
+
quality_issues = ["Prompt is too short and lacks detail"]
|
273 |
+
elif word_count < 10:
|
274 |
+
base_quality = 0.5
|
275 |
+
quality_issues = ["Prompt could benefit from more descriptive details"]
|
276 |
+
elif word_count < 20:
|
277 |
+
base_quality = 0.7
|
278 |
+
quality_issues = []
|
279 |
+
elif word_count < 40:
|
280 |
+
base_quality = 0.8
|
281 |
+
quality_issues = []
|
282 |
+
else:
|
283 |
+
base_quality = 0.6
|
284 |
+
quality_issues = ["Prompt might be too complex - consider simplifying"]
|
285 |
+
|
286 |
+
# Adjust based on keyword presence
|
287 |
+
quality_adjustment = (marketing_score * 0.2 + style_score * 0.15 + composition_score * 0.15)
|
288 |
+
final_quality = min(1.0, base_quality + quality_adjustment)
|
289 |
+
|
290 |
+
# Generate specific feedback
|
291 |
+
missing_elements = []
|
292 |
+
if marketing_score < 0.1:
|
293 |
+
missing_elements.append("marketing context or business relevance")
|
294 |
+
if style_score < 0.1:
|
295 |
+
missing_elements.append("artistic style or visual quality descriptors")
|
296 |
+
if "english" in review_guidelines.lower() and "english" not in prompt.lower():
|
297 |
+
missing_elements.append("English language specification for text/signage")
|
298 |
+
|
299 |
+
present_elements = []
|
300 |
+
if marketing_score > 0.1:
|
301 |
+
present_elements.append("marketing/business context")
|
302 |
+
if style_score > 0.1:
|
303 |
+
present_elements.append("style descriptors")
|
304 |
+
if composition_score > 0.1:
|
305 |
+
present_elements.append("composition guidance")
|
306 |
+
|
307 |
+
# Calculate overall scores
|
308 |
+
relevance_score = min(1.0, final_quality + (marketing_score * 0.2))
|
309 |
+
safety_score = 0.95 # Generally high for marketing prompts
|
310 |
+
|
311 |
+
# Check for potentially problematic content
|
312 |
+
problematic_terms = ["violence", "inappropriate", "offensive", "controversial"]
|
313 |
+
for term in problematic_terms:
|
314 |
+
if term in prompt.lower():
|
315 |
+
safety_score = 0.7
|
316 |
+
break
|
317 |
+
|
318 |
+
overall_score = (final_quality * 0.4 + relevance_score * 0.4 + safety_score * 0.2)
|
319 |
+
|
320 |
+
# Generate recommendations
|
321 |
+
recommendations = []
|
322 |
+
|
323 |
+
if final_quality < 0.6:
|
324 |
+
recommendations.append("Consider adding more descriptive details about the desired image")
|
325 |
+
|
326 |
+
if marketing_score < 0.1:
|
327 |
+
recommendations.append("Add marketing context (e.g., professional, business, corporate)")
|
328 |
+
|
329 |
+
if "english" in review_guidelines.lower() and "english" not in prompt.lower():
|
330 |
+
recommendations.append("Add 'English signage' or 'English text' to meet language requirements")
|
331 |
+
|
332 |
+
if word_count < 10:
|
333 |
+
recommendations.append("Expand prompt with lighting, composition, or environmental details")
|
334 |
+
elif word_count > 50:
|
335 |
+
recommendations.append("Consider simplifying prompt while keeping key elements")
|
336 |
+
|
337 |
+
if not recommendations:
|
338 |
+
if overall_score > 0.8:
|
339 |
+
recommendations.append("Excellent prompt! Should generate high-quality marketing image")
|
340 |
+
else:
|
341 |
+
recommendations.append("Good prompt foundation - image should meet basic requirements")
|
342 |
+
|
343 |
+
analysis_result = {
|
344 |
+
"success": True,
|
345 |
+
"quality_score": round(final_quality, 2),
|
346 |
+
"relevance_score": round(relevance_score, 2),
|
347 |
+
"safety_score": round(safety_score, 2),
|
348 |
+
"overall_score": round(overall_score, 2),
|
349 |
+
"word_count": word_count,
|
350 |
+
"missing_elements": missing_elements,
|
351 |
+
"present_elements": present_elements,
|
352 |
+
"recommendations": recommendations[:5],
|
353 |
+
"analysis_method": "prompt_analysis"
|
354 |
+
}
|
355 |
+
|
356 |
+
return json.dumps(analysis_result)
|
357 |
+
|
358 |
+
except Exception as e:
|
359 |
+
logger.error(f"Prompt analysis failed: {e}")
|
360 |
+
return json.dumps({
|
361 |
+
"success": False,
|
362 |
+
"error": f"Analysis failed: {str(e)}",
|
363 |
+
"prompt": prompt
|
364 |
+
})
|
365 |
+
|
366 |
+
def generate_and_review_marketing_image(prompt: str, style: str = "realistic", review_guidelines: str = "") -> str:
|
367 |
+
"""
|
368 |
+
Complete workflow: Generate a marketing image and provide quality review.
|
369 |
+
|
370 |
+
Args:
|
371 |
+
prompt (str): Description of the marketing image to generate
|
372 |
+
style (str): Art style for the image (realistic, artistic, cartoon, photographic, illustration)
|
373 |
+
review_guidelines (str): Specific guidelines for marketing review
|
374 |
+
|
375 |
+
Returns:
|
376 |
+
str: JSON string containing image, review, and recommendations
|
377 |
+
"""
|
378 |
+
logger.info(f"π Starting complete marketing workflow for: {prompt}")
|
379 |
+
|
380 |
+
try:
|
381 |
+
# Step 1: Generate the image
|
382 |
+
generation_response = generate_marketing_image(prompt, style)
|
383 |
+
generation_data = json.loads(generation_response)
|
384 |
+
|
385 |
+
if not generation_data.get("success", False):
|
386 |
+
return generation_response # Return error
|
387 |
+
|
388 |
+
# Step 2: Analyze the prompt (marketing review)
|
389 |
+
analysis_response = analyze_marketing_prompt(prompt, review_guidelines)
|
390 |
+
analysis_data = json.loads(analysis_response)
|
391 |
+
|
392 |
+
# Combine results
|
393 |
+
workflow_result = {
|
394 |
+
"success": True,
|
395 |
+
"image": {
|
396 |
+
"url": generation_data.get("image_url", ""),
|
397 |
+
"data": generation_data.get("image_url", ""),
|
398 |
+
"prompt": prompt,
|
399 |
+
"style": style
|
400 |
+
},
|
401 |
+
"review": {
|
402 |
+
"quality_score": analysis_data.get("overall_score", 0.7),
|
403 |
+
"final_status": "passed" if analysis_data.get("overall_score", 0) > 0.7 else "needs_improvement",
|
404 |
+
"iterations": 1,
|
405 |
+
"passed": analysis_data.get("overall_score", 0) > 0.7,
|
406 |
+
"recommendations": analysis_data.get("recommendations", []),
|
407 |
+
"analysis_details": analysis_data
|
408 |
+
},
|
409 |
+
"metadata": {
|
410 |
+
"generation_method": generation_data.get("generation_method", "unknown"),
|
411 |
+
"real_ai_generation": generation_data.get("real_ai_generation", False),
|
412 |
+
"workflow_type": "gradio_mcp_server"
|
413 |
+
}
|
414 |
+
}
|
415 |
+
|
416 |
+
logger.info("β
Complete marketing workflow successful!")
|
417 |
+
return json.dumps(workflow_result)
|
418 |
+
|
419 |
+
except Exception as e:
|
420 |
+
logger.error(f"Complete workflow failed: {e}")
|
421 |
+
return json.dumps({
|
422 |
+
"success": False,
|
423 |
+
"error": f"Workflow failed: {str(e)}",
|
424 |
+
"prompt": prompt,
|
425 |
+
"style": style
|
426 |
+
})
|
427 |
+
|
428 |
+
# Gradio interface functions
|
429 |
+
def process_generated_image_and_results(api_response_str: str) -> Tuple[Image.Image, str]:
|
430 |
"""Process API response and return image and review text for Gradio display"""
|
431 |
try:
|
432 |
+
response_data = json.loads(api_response_str)
|
433 |
|
434 |
if not response_data.get('success', False):
|
435 |
return None, f"β Generation failed: {response_data.get('error', 'Unknown error')}"
|
|
|
454 |
quality_score = review_data.get('quality_score', 0)
|
455 |
passed = review_data.get('passed', False)
|
456 |
final_status = review_data.get('final_status', 'unknown')
|
|
|
457 |
recommendations = review_data.get('recommendations', [])
|
458 |
|
459 |
status_emoji = "π’" if passed else "π΄"
|
|
|
461 |
# Extract metadata about generation method
|
462 |
metadata = response_data.get('metadata', {})
|
463 |
generation_method = metadata.get('generation_method', 'unknown')
|
|
|
464 |
|
465 |
generation_info = ""
|
466 |
+
if generation_method == "google-genai-sdk":
|
467 |
+
generation_info = "π¨ **Generated with**: Google Imagen3 SDK (Real AI)\n"
|
|
|
|
|
468 |
elif generation_method == "placeholder":
|
469 |
generation_info = "π¨ **Generated with**: Placeholder (Fallback)\n"
|
470 |
|
471 |
+
review_text = f"""**π Marketing Review Results**
|
472 |
|
473 |
{generation_info}
|
474 |
**Quality Score:** {quality_score:.2f}/1.0
|
475 |
**Status:** {status_emoji} {final_status.upper()}
|
476 |
+
**Architecture:** Gradio MCP Server
|
|
|
477 |
|
478 |
**π‘ Recommendations:**
|
479 |
"""
|
|
|
483 |
review_text += f"{i}. {rec}\n"
|
484 |
else:
|
485 |
review_text += "β’ Image meets quality standards\n"
|
486 |
+
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
487 |
else:
|
488 |
review_text = "β οΈ Review data not available"
|
489 |
|
|
|
492 |
except Exception as e:
|
493 |
return None, f"β Error processing results: {str(e)}"
|
494 |
|
495 |
+
def gradio_generate_marketing_image(prompt: str, style: str, review_guidelines: str) -> Tuple[Image.Image, str]:
|
496 |
+
"""Gradio interface wrapper for complete marketing image generation"""
|
497 |
if not prompt.strip():
|
498 |
return None, "β οΈ Please enter a prompt to generate an image."
|
499 |
|
|
|
|
|
|
|
500 |
try:
|
501 |
+
# Use the complete workflow function
|
502 |
+
result_json = generate_and_review_marketing_image(prompt, style, review_guidelines)
|
503 |
+
return process_generated_image_and_results(result_json)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
504 |
except Exception as e:
|
505 |
+
error_message = f"β Error: {str(e)}"
|
506 |
logger.error(error_message)
|
507 |
return None, error_message
|
508 |
|
509 |
+
# Define suggested prompts
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
510 |
SUGGESTED_PROMPTS = {
|
511 |
"Modern office team collaboration": ("A modern office space with diverse professionals collaborating around a sleek conference table, natural lighting, professional attire, English signage visible", "realistic"),
|
512 |
"Executive boardroom meeting": ("Professional executive boardroom with polished conference table, city skyline view, business documents, English presentations on screens", "realistic"),
|
|
|
517 |
}
|
518 |
|
519 |
# Create Gradio interface
|
520 |
+
with gr.Blocks(title="Marketing Image Generator MCP", theme=gr.themes.Soft()) as demo:
|
521 |
gr.Markdown("""
|
522 |
+
# π¨ Marketing Image Generator
|
523 |
+
### Professional AI image generation with built-in MCP server support
|
524 |
|
525 |
+
**Gradio MCP Server** β **Google Imagen3** β **Marketing Review** β **Results**
|
526 |
+
|
527 |
+
*MCP Server available at: `/gradio_api/mcp/sse`*
|
528 |
""")
|
529 |
|
530 |
with gr.Row():
|
|
|
548 |
|
549 |
review_guidelines = gr.Textbox(
|
550 |
label="π Marketing Review Guidelines (Optional)",
|
551 |
+
placeholder="e.g., All text must be in English only, focus on professional appearance, ensure brand colors are prominent",
|
552 |
lines=3,
|
553 |
+
info="Provide specific marketing guidelines for review"
|
554 |
)
|
555 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
556 |
# Generate button
|
557 |
+
generate_btn = gr.Button("π Generate Marketing Image", variant="primary", size="lg")
|
558 |
|
559 |
# Status
|
560 |
+
gr.Markdown("π **Mode**: Gradio MCP Server")
|
561 |
+
gr.Markdown(f"π **API Status**: {'β
Configured' if GOOGLE_API_KEY else 'β No API Key'}")
|
|
|
|
|
|
|
|
|
562 |
|
563 |
with gr.Column(scale=2):
|
564 |
# Results display
|
565 |
+
gr.Markdown("### πΌοΈ Generated Image & Review")
|
566 |
|
567 |
image_output = gr.Image(
|
568 |
label="Generated Marketing Image",
|
|
|
572 |
)
|
573 |
|
574 |
review_output = gr.Markdown(
|
575 |
+
value="Click **Generate Marketing Image** to create your marketing image with automated review",
|
576 |
+
label="Marketing Review Results"
|
577 |
)
|
578 |
|
579 |
# Suggested prompts section
|
|
|
603 |
|
604 |
# Event handlers
|
605 |
generate_btn.click(
|
606 |
+
fn=gradio_generate_marketing_image,
|
607 |
+
inputs=[prompt, style, review_guidelines],
|
608 |
outputs=[image_output, review_output],
|
609 |
show_progress=True
|
610 |
)
|
611 |
|
|
|
|
|
|
|
|
|
|
|
612 |
# Footer
|
613 |
gr.Markdown("""
|
614 |
---
|
615 |
<div style='text-align: center; color: #666; font-size: 0.9rem;'>
|
616 |
+
<p>π¨ Marketing Image Generator | Gradio MCP Server</p>
|
617 |
+
<p>Image Generation + Marketing Review + MCP API</p>
|
618 |
+
<p>MCP Endpoint: <code>/gradio_api/mcp/sse</code></p>
|
619 |
</div>
|
620 |
""")
|
621 |
|
622 |
if __name__ == "__main__":
|
623 |
+
logger.info("π Starting Marketing Image Generator with MCP Server")
|
624 |
+
logger.info(f"π Google AI: {'β
Configured' if GOOGLE_API_KEY else 'β No API Key'}")
|
625 |
+
logger.info("π MCP Server will be available at /gradio_api/mcp/sse")
|
|
|
|
|
|
|
|
|
|
|
|
|
626 |
|
627 |
+
demo.launch(mcp_server=True)
|