import gradio as gr import openai import os from dotenv import load_dotenv import logging import datetime import json import re import random # Set up logging with more detailed configuration log_dir = "logs" if not os.path.exists(log_dir): os.makedirs(log_dir) log_filename = os.path.join(log_dir, f"svg_generator_{datetime.datetime.now().strftime('%Y%m%d_%H%M%S')}.log") logging.basicConfig( filename=log_filename, level=logging.INFO, format='%(asctime)s - %(levelname)s - [%(filename)s:%(lineno)d] - %(message)s' ) # Add console handler to see logs in terminal console_handler = logging.StreamHandler() console_handler.setLevel(logging.INFO) console_formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s') console_handler.setFormatter(console_formatter) logging.getLogger().addHandler(console_handler) # Load environment variables load_dotenv() logging.info("Environment variables loaded") # Configure OpenAI API api_key = os.getenv("OPENAI_API_KEY") if not api_key: logging.error("OPENAI_API_KEY not found in environment variables!") raise ValueError("OPENAI_API_KEY not found!") client = openai.OpenAI(api_key=api_key) logging.info("OpenAI client initialized successfully") def clean_svg_content(content): """Clean and validate SVG content""" # Remove any leading/trailing whitespace content = content.strip() # Log original content for debugging logging.info(f"Original content starts with: {content[:100]}...") # Remove XML declaration if present content = re.sub(r'<\?xml[^>]+\?>\s*', '', content) # Remove DOCTYPE if present content = re.sub(r']+>\s*', '', content) # Remove any comments content = re.sub(r'\s*', '', content) # Remove any empty lines content = '\n'.join(line for line in content.splitlines() if line.strip()) # Log cleaned content logging.info(f"Cleaned content starts with: {content[:100]}...") return content def validate_svg(content): """Validate SVG content""" # Check if content starts with ]', content): logging.warning(f"Content doesn't start with SVG tag. Content starts with: {content[:100]}") return False # Check if content has matching closing tag if not content.strip().endswith(''): logging.warning("Content doesn't end with closing SVG tag") return False return True def generate_svg(prompt): """Generate SVG using the fine-tuned GPT-4 model""" try: logging.info(f"Sending request to OpenAI API with prompt: {prompt[:100]}...") logging.info(f"Using model: ft:gpt-4o-mini-2024-07-18:personal::AyNMN2ax") response = client.chat.completions.create( model="ft:gpt-4o-mini-2024-07-18:personal::AyNMN2ax", messages=[ {"role": "system", "content": """in this add this Create a well-aligned and meaningful SVG generator prompt. Ensure it includes appropriate alignment and content instructions. # Output Format The SVG should be generated with structured alignment and include meaningful attributes such as colors, dimensions, and positioning. # Steps [optional] 1. Define the shapes and their attributes (e.g., circles, rectangles). 2. Set the colors and dimensions. 3. Align the objects to create a cohesive design. 4. Review and adjust for meaningfulness and symmetry. # Examples [optional] Example Input: - Shapes: Circle, Rectangle - Colors: Blue, Red - Sizes: 100x100, 50x50 - Positioning: Center Example Output: ```svg ``` # Notes [optional] - Ensure the SVG is valid and adheres to SVG standards. - Ensure in output only generate the svg only in svg you can create comment it's okay - Consider accessibility aspects such as color contrast. - Review the design for visual balance and clarity. - IMPORTANT: Return ONLY the raw SVG code without any markdown formatting or explanations. The response should start with ."""}, {"role": "user", "content": prompt} ], temperature=1, # Reduced for more consistent output max_tokens=2000, ) # Log the response details logging.info(f"Response received from OpenAI API") logging.info(f"Response finish reason: {response.choices[0].finish_reason}") # Get and clean the SVG content svg_content = response.choices[0].message.content logging.info(f"Raw SVG length: {len(svg_content)} characters") # Clean the SVG content cleaned_svg = clean_svg_content(svg_content) logging.info(f"Cleaned SVG length: {len(cleaned_svg)} characters") # Validate the cleaned SVG if not validate_svg(cleaned_svg): error_msg = "Invalid SVG format received from API" logging.error(error_msg) return f""" Error: {error_msg} Please try again with a different prompt """ return cleaned_svg except openai.APIError as e: error_msg = f"OpenAI API Error: {str(e)}" logging.error(error_msg) return f""" {error_msg} """ except openai.APIConnectionError as e: error_msg = f"Connection Error: {str(e)}" logging.error(error_msg) return f""" {error_msg} """ except openai.RateLimitError as e: error_msg = f"Rate Limit Error: {str(e)}" logging.error(error_msg) return f""" {error_msg} """ except Exception as e: error_msg = f"Unexpected Error: {str(e)}" logging.error(error_msg, exc_info=True) return f""" {error_msg} """ def create_preview_svg(svg_content): """Create a preview version of the SVG with proper sizing""" # Force width and height to 1080 for full size preview svg_content = re.sub(r'width="[^"]+"', 'width="1080"', svg_content) svg_content = re.sub(r'height="[^"]+"', 'height="1080"', svg_content) # Ensure viewBox is present if 'viewBox' not in svg_content: svg_content = svg_content.replace(' {svg} """ def generate_similar_prompts(example_prompt): """Generate 10 similar testimonial prompts based on an example prompt using GPT-4 mini""" system_prompt = """You are a creative testimonial prompt generator. Based on the following example prompt, generate 10 unique testimonial prompts that start with 'Create' and are similar in style but with variations in:""" user_prompt = f"Example prompt: {example_prompt}\n\nGenerate 10 similar prompts that start with 'Create', with good alignment and default positioning" try: logging.info("Sending request to OpenAI API for similar prompts...") response = client.chat.completions.create( model="gpt-4o-mini-2024-07-18", messages=[ {"role": "system", "content": system_prompt}, {"role": "user", "content": user_prompt} ], temperature=0.7, max_tokens=500 ) prompts_text = response.choices[0].message.content.strip() logging.info("Received similar prompts from OpenAI API") prompts_list = [line.strip() for line in prompts_text.splitlines() if line.strip()] # Ensure each prompt includes quality requirements and SVG code request quality_prompts = [] for prompt in prompts_list: if "good alignment" not in prompt.lower() and "default positioning" not in prompt.lower(): prompt += " create svg code with good alignment, default word positioning and high SVG quality. Please provide complete SVG code." quality_prompts.append(prompt) if len(quality_prompts) < 10: extra_count = 10 - len(quality_prompts) unique_prompts = [f"{example_prompt} - variation {i+1} with good alignment, default word positioning and high SVG quality. Please provide complete SVG code." for i in range(extra_count)] quality_prompts += unique_prompts else: quality_prompts = quality_prompts[:10] # Add default SVG code request to all prompts final_prompts = [] for prompt in quality_prompts: if "create svg code" not in prompt.lower(): prompt += " Please create complete SVG code with proper structure and elements." final_prompts.append(prompt) return final_prompts except Exception as e: error_msg = f"Error generating similar prompts: {str(e)}" logging.error(error_msg, exc_info=True) return [f"{example_prompt} - variation {i+1} with good alignment, default word positioning and high SVG quality. Please provide complete SVG code." for i in range(10)] def generate_random_prompt(user_prompt=None): """ Generate a unique prompt based on user's input prompt. If no prompt is provided, use a default example. Ensures all generated prompts maintain quality standards. """ if not user_prompt: user_prompt = "Design a modern testimonial with a clean layout, balanced typography, and vibrant color accents." # Add quality requirements to the base prompt base_prompt = f"{user_prompt} The design must have good alignment, balanced composition, and professional SVG quality." # Generate variations based on the enhanced base prompt prompts = generate_similar_prompts(base_prompt) # Ensure each prompt includes quality requirements quality_prompts = [] for prompt in prompts: if "good alignment" not in prompt.lower() and "svg quality" not in prompt.lower(): prompt += " Ensure good alignment and high SVG quality." quality_prompts.append(prompt) return random.choice(quality_prompts) def process_all_prompts(*prompts): """Process all prompts and return SVG results with preview and full-view versions""" preview_results = [] fullview_results = [] logging.info(f"Starting to process {len(prompts)} prompts") for i, prompt in enumerate(prompts, 1): logging.info(f"\n{'='*50}") logging.info(f"Processing prompt {i} of {len(prompts)}") try: if prompt.strip(): modified_prompt = prompt.strip() + " The design must have good alignment and be visually appealing." logging.info(f"Prompt {i} content: {modified_prompt[:100]}...") svg = generate_svg(modified_prompt) preview_svg = create_preview_svg(svg) logging.info(f"Successfully generated SVG for prompt {i}") logging.info(f"SVG size: {len(svg)} characters") preview_results.append(preview_svg) fullview_results.append(svg) else: default_svg = """ No prompt provided """ logging.info(f"Empty prompt {i} received - using default SVG") preview_results.append(default_svg) fullview_results.append(default_svg) except Exception as e: error_msg = f"Error processing prompt {i}: {str(e)}" logging.error(error_msg, exc_info=True) error_svg = f""" Error generating SVG {error_msg} """ preview_results.append(error_svg) fullview_results.append(error_svg) logging.info(f"Completed processing prompt {i}") logging.info(f"{'='*50}\n") logging.info(f"Completed processing all {len(prompts)} prompts") return tuple(preview_results) + (fullview_results,) def generate_svg_wrapper(prompt): """ Wrapper function to handle SVG generation and create preview Returns the preview SVG, full SVG, and a status message for display """ if not prompt: return None, None, "❌ Please enter a prompt first!" modified_prompt = prompt.strip() + " The design must have good alignment and be visually appealing." svg = generate_svg(modified_prompt) preview_svg = create_preview_svg(svg) return preview_svg, svg, "✅ Generated SVG for prompt!" def generate_svg_and_show_code_fn(prompt): """Wrapper function to generate an SVG and immediately show the SVG code below the preview.""" preview, code, status = generate_svg_wrapper(prompt) return preview, gr.update(value=code, visible=True), status def generate_multiple_svgs(*args): # Use the values from prompt boxes instead of generating new ones prompts = [arg for arg in args if arg and isinstance(arg, str)] num_galleries = len(svg_galleries) num_code_boxes = len(code_boxes) if not prompts: empty_preview = '
Generate prompts first
' outputs = [] # Add status and container updates outputs.append("❌ Please generate and edit prompts first before generating designs!") # status message outputs.append(gr.update(visible=False)) # multi_svg_container outputs.append(gr.update(visible=True)) # single_svg_container # Add gallery outputs with visibility updates for _ in range(num_galleries): outputs.append(gr.update(value=empty_preview, visible=False)) # Add code outputs with visibility updates for _ in range(num_code_boxes): outputs.append(gr.update(value="", visible=False)) return outputs try: logging.info(f"Generating SVGs for {len(prompts)} prompts") gallery_outputs = [] code_outputs = [] for prompt in prompts: if prompt.strip(): preview_svg, full_svg, _ = generate_svg_wrapper(prompt) gallery_outputs.append(gr.update(value=preview_svg, visible=True)) code_outputs.append(gr.update(value=full_svg, visible=False)) else: gallery_outputs.append(gr.update(value='
No prompt provided
', visible=True)) code_outputs.append(gr.update(value="", visible=False)) outputs = [] # Add status and container updates outputs.append("✅ Generated designs based on your edited prompts!") # status message outputs.append(gr.update(visible=True)) # multi_svg_container outputs.append(gr.update(visible=False)) # single_svg_container # Add gallery outputs for i in range(num_galleries): if i < len(gallery_outputs): outputs.append(gallery_outputs[i]) else: outputs.append(gr.update(value='
No design
', visible=False)) # Add code outputs for i in range(num_code_boxes): if i < len(code_outputs): outputs.append(code_outputs[i]) else: outputs.append(gr.update(value="", visible=False)) logging.info("Successfully generated all SVGs") return outputs except Exception as e: error_msg = f"Error generating SVGs: {str(e)}" logging.error(error_msg, exc_info=True) error_preview = '
Error generating SVG
' outputs = [] # Add status and container updates outputs.append(f"❌ {error_msg}") # status message outputs.append(gr.update(visible=False)) # multi_svg_container outputs.append(gr.update(visible=True)) # single_svg_container # Add gallery outputs with visibility updates for _ in range(num_galleries): outputs.append(gr.update(value=error_preview, visible=False)) # Add code outputs with visibility updates for _ in range(num_code_boxes): outputs.append(gr.update(value="", visible=False)) return outputs # Create the Gradio interface with gr.Blocks(theme=gr.themes.Soft()) as app: gr.Markdown("# SVG Generator with Similar Prompt Generation") gr.Markdown("### Step 1: Enter an example prompt to generate similar prompts") example_prompt_input = gr.Textbox(label="Example Prompt", placeholder="Enter an example testimonial prompt here...", lines=3) generate_examples_btn = gr.Button("Generate 10 Similar Prompts") gr.Markdown("### Step 2: Review and edit the generated prompts") prompts = [gr.Textbox(label=f"Prompt {i+1}", placeholder=f"Generated prompt {i+1}...") for i in range(10)] # When button is clicked, populate the 10 prompt textboxes generate_examples_btn.click(fn=generate_similar_prompts, inputs=example_prompt_input, outputs=prompts) gr.Markdown("### Step 3: Generate SVGs for the above prompts") generate_btn = gr.Button("Generate SVGs", variant="primary", size="lg") # Store full-view SVGs fullview_svgs = gr.State([]) preview_outputs = [] # Create grid layout for SVG previews with gr.Row(): with gr.Column(scale=1): for i in range(0, 5): # First column with gr.Group(): preview = gr.HTML(label=f"SVG {i+1}", container=True) preview_outputs.append(preview) # Add a button that opens the full view in a new tab def create_view_fn(index): def view_fn(svgs): if svgs and len(svgs) > index: return f""" Full Size SVG {index + 1}
{svgs[index]}

SVG Code

{svgs[index].replace('<', '<').replace('>', '>')}
""" return "" return view_fn with gr.Row(): view_btn = gr.Button(f"View Full Size {i+1}", size="sm") full_view = gr.HTML(visible=False) view_btn.click( fn=create_view_fn(i), inputs=[fullview_svgs], outputs=[full_view], js=""" async function(svg_html) { if (svg_html) { const win = window.open("", "_blank"); win.document.write(svg_html); } return ""; } """ ) with gr.Column(scale=1): for i in range(5, 10): # Second column with gr.Group(): preview = gr.HTML(label=f"SVG {i+1}", container=True) preview_outputs.append(preview) with gr.Row(): view_btn = gr.Button(f"View Full Size {i+1}", size="sm") full_view = gr.HTML(visible=False) view_btn.click( fn=create_view_fn(i), inputs=[fullview_svgs], outputs=[full_view], js=""" async function(svg_html) { if (svg_html) { const win = window.open("", "_blank"); win.document.write(svg_html); } return ""; } """ ) # Add some CSS to style the previews gr.HTML(""" """) generate_btn.click( fn=process_all_prompts, inputs=prompts, outputs=preview_outputs + [fullview_svgs] ) # Launch the app if __name__ == "__main__": logging.info("Starting SVG Generator application") try: with gr.Blocks(theme=gr.themes.Soft()) as demo: gr.Markdown(""" # 🎨 SVG Testimonial Generator ### How to use: 1. Enter your own prompt OR generate a random prompt 2. Review the prompt and generate designs 3. View, download, or check the code for each design """) # Define containers and lists first multi_svg_container = gr.Row(visible=False) single_svg_container = gr.Row() svg_galleries = [] code_boxes = [] code_visibilities = [] # Initialize code_visibilities list # Create galleries, code boxes, and visibility states for i in range(10): svg_galleries.append(gr.HTML( label=f"Preview {i+1}", value='
Waiting for generation...
', visible=False )) code_box = gr.Textbox( label=f"SVG Code {i+1}", lines=5, visible=False, show_copy_button=True ) code_boxes.append(code_box) code_visibilities.append(gr.update(visible=False)) # Add visibility state for each code box with gr.Row(): with gr.Column(scale=3): prompt_input = gr.Textbox( label="✍️ Enter Your Design Prompt", lines=2, placeholder="Example: Design a modern testimonial with a dark background, white text, and customer quote from John Doe about excellent service." ) with gr.Row(): with gr.Column(scale=1): random_prompt_btn = gr.Button("🎲 Generate Random Prompt", variant="secondary", size="lg") with gr.Column(scale=1): generate_designs_btn = gr.Button("🎨 Generate 10 Designs", variant="primary", size="lg") with gr.Column(scale=1): generate_single_btn = gr.Button("🚀 Generate Single SVG", variant="primary", size="lg") # Status message for user feedback status_message = gr.Markdown("") # Container for editable prompts with gr.Row() as prompts_container: with gr.Column(): prompt_boxes = [] svg_previews = [] generate_buttons = [] # Store generate buttons for i in range(10): with gr.Row(): prompt_boxes.append(gr.Textbox( label=f"✍️ Prompt {i+1}", lines=2, interactive=True, visible=False )) gen_btn = gr.Button(f"🎨 Generate SVG {i+1}", visible=False) generate_buttons.append(gen_btn) # Store button reference svg_preview = gr.HTML(visible=False) svg_previews.append(svg_preview) def create_generate_fn(index): def generate_fn(prompt): if not prompt: return gr.update(visible=False), "❌ Please enter a prompt first!" try: svg_content = generate_svg(prompt) preview_svg = create_preview_svg(svg_content) return gr.update(value=preview_svg, visible=True), f"✅ Generated SVG for prompt {index + 1}!" except Exception as e: return gr.update(visible=False), f"❌ Error generating SVG: {str(e)}" return generate_fn gen_btn.click( fn=create_generate_fn(i), inputs=[prompt_boxes[i]], outputs=[svg_previews[i], status_message] ) # Add Generate All button with gr.Row(): generate_all_btn = gr.Button("🎨 Generate All SVGs", variant="primary", size="lg", visible=False) def generate_and_show_prompts(current_prompt): prompts = generate_similar_prompts(current_prompt) if current_prompt else [generate_random_prompt() for _ in range(10)] outputs = [] # Add prompt box updates for prompt in prompts: outputs.append(gr.update(value=prompt, visible=True)) # Add status message outputs.append("✅ Generated prompts! Click individual Generate buttons or Generate All to create designs!") # Add generate all button visibility outputs.append(gr.update(visible=True)) # Add generate button visibilities for _ in range(len(generate_buttons)): outputs.append(gr.update(visible=True)) return outputs # Event handler for random prompt generation random_prompt_btn.click( fn=generate_and_show_prompts, inputs=[prompt_input], outputs=prompt_boxes + [status_message, generate_all_btn] + generate_buttons ) # Event handler for Generate All button generate_all_btn.click( fn=generate_multiple_svgs, inputs=prompt_boxes, outputs=[status_message, multi_svg_container, single_svg_container] + svg_galleries + code_boxes ) # Event handler for Generate Designs button generate_designs_btn.click( fn=generate_multiple_svgs, inputs=prompt_boxes, outputs=[status_message, multi_svg_container, single_svg_container] + svg_galleries + code_boxes ) # Single SVG preview container with gr.Row() as single_svg_container: with gr.Column(min_width=1100): svg_preview = gr.HTML( label="Preview", value='
Enter prompt and click Generate
' ) svg_code_box = gr.Textbox( label="SVG Code", lines=10, visible=False, show_copy_button=True ) download_btn = gr.Button("💾 Download", variant="secondary") file_output = gr.File(label="Download", visible=False) # Now, register the single SVG generation click event AFTER svg_code_box is defined: generate_single_btn.click( fn=generate_svg_and_show_code_fn, inputs=[prompt_input], outputs=[svg_preview, svg_code_box, status_message] ) # Container for random prompts display (can be removed since we now use textboxes) with gr.Row(visible=False) as random_prompts_container: random_prompts_box = gr.Dataframe( headers=["#", "Random Prompts"], label="Generated Random Prompts", interactive=False ) # Container for multiple SVGs with gr.Row(visible=False) as multi_svg_container: with gr.Column(min_width=1100): svg_galleries = [] code_boxes = [] code_visibilities = [] view_code_buttons = [] download_buttons = [] # Initialize download_buttons list for i in range(10): with gr.Group(): gr.Markdown(f"### Design {i+1}") # Preview container with fixed size with gr.Column(min_width=1080): # Preview svg_galleries.append(gr.HTML( label=f"Preview {i+1}", value='
Waiting for generation...
' )) with gr.Row(): # View Code button view_code_btn = gr.Button(f"📝 View Code {i+1}", variant="secondary") view_code_buttons.append(view_code_btn) # Download button download_btn = gr.Button(f"💾 Download {i+1}", variant="secondary") # Code view code_box = gr.Textbox( label=f"SVG Code {i+1}", lines=5, visible=False, show_copy_button=True ) code_boxes.append(code_box) code_visibilities.append(gr.update(visible=False)) # Hidden file output file_output = gr.File(label=f"Download {i+1}", visible=False) download_buttons.append((download_btn, file_output)) # Add separator gr.Markdown("---") def toggle_code_visibility(evt: gr.SelectData): """Toggle visibility of code box when view code button is clicked""" index = evt.index return gr.update(visible=True) if index < len(code_boxes) else gr.update(visible=False) # Add click handlers for each view code button for i, (view_btn, code_box) in enumerate(zip(view_code_buttons, code_boxes)): view_btn.click( fn=lambda: gr.update(visible=True), inputs=None, outputs=[code_box] ) def download_svg(svg_code, index): """Download SVG file""" if not svg_code: return None temp_file = f"testimonial_{index+1}.svg" with open(temp_file, "w", encoding="utf-8") as f: f.write(svg_code) return temp_file # Add click handlers for download buttons for i, (download_btn, file_output) in enumerate(download_buttons): download_btn.click( fn=lambda x, idx=i: download_svg(x, idx), inputs=[code_boxes[i]], outputs=[file_output] ) # Add click handler for single SVG download download_btn.click( fn=lambda x: download_svg(x, 0), inputs=[svg_code_box], outputs=[file_output] ) demo.launch( server_port=7860, # Default Gradio port share=True, # Creates a shareable link show_error=True ) logging.info("Application launched successfully") except Exception as e: logging.error(f"Error launching application: {str(e)}", exc_info=True)