import os import tempfile from PIL import Image import gradio as gr from google import genai from google.genai import types # Helpers def save_binary_file(file_name, data): with open(file_name, "wb") as f: f.write(data) def generate_edit(prompt, pil_image, api_key, model="gemini-2.0-flash-exp"): client = genai.Client(api_key=(api_key.strip() if api_key and api_key.strip() != "" else os.environ.get("GEMINI_API_KEY"))) with tempfile.NamedTemporaryFile(suffix=".png", delete=False) as tmp_img: image_path = tmp_img.name pil_image.save(image_path) files = [client.files.upload(file=image_path)] contents = [ types.Content( role="user", parts=[ types.Part.from_uri(file_uri=files[0].uri, mime_type=files[0].mime_type), types.Part.from_text(text=prompt), ], ), ] generate_content_config = types.GenerateContentConfig( temperature=1, top_p=0.95, top_k=40, max_output_tokens=8192, response_modalities=["image", "text"], response_mime_type="text/plain", ) text_response = "" image_out_path = None with tempfile.NamedTemporaryFile(suffix=".png", delete=False) as tmp_out: out_path = tmp_out.name for chunk in client.models.generate_content_stream( model=model, contents=contents, config=generate_content_config, ): if not chunk.candidates or not chunk.candidates[0].content or not chunk.candidates[0].content.parts: continue candidate = chunk.candidates[0].content.parts[0] if candidate.inline_data: save_binary_file(out_path, candidate.inline_data.data) image_out_path = out_path break else: text_response += chunk.text + "\n" del files return image_out_path, text_response def process_image_and_prompt(pil_image, prompt, api_key): try: image_path, text_out = generate_edit(prompt, pil_image, api_key) if image_path: img = Image.open(image_path) if img.mode == "RGBA": img = img.convert("RGB") return img # Return only the image on success else: # If no image generated, raise an error for Gradio popup raise gr.Error(f"⚠️ Image generation failed: {text_out.strip() if text_out.strip() else 'No specific error message.'}") except Exception as e: # Catch any other exceptions and re-raise as Gradio error raise gr.Error(f"❌ Generation failed: {str(e)}") def reset_inputs(api_key_value=None): # Reset all inputs, keeping API key unchanged return None, "", api_key_value or "" # Styles with gradient background for body/app container css_style = """ :root { --bg: #0f111a; --panel: #1b1e28; --text: #e9eefc; --muted: #9fb3c8; --accent: #6a8efd; } body, .app-container { /* Gradient background instead of solid */ background: linear-gradient(135deg, #2a3a67, #1b254b); color: var(--text); } .header-block { width: 100%; display: flex; justify-content: center; padding: 8px 0; } .header-gradient { width: 100%; padding: 20px 0; border-radius: 12px; background: linear-gradient(90deg, #6a8efd, #44abc7); text-align: center; } .header-title { margin: 0; font-size: 2.6rem; font-weight: 900; color: #fff; text-shadow: 0 2px 8px rgba(0,0,0,.25); } .header-subtitle { margin-top: 6px; font-size: 1.05rem; color: #e8f0ff; } .main { display: flex; gap: 20px; align-items: stretch; padding: 0 12px; } .sidebar { width: 320px; background: #1a1e2a; padding: 14px; border-radius: 12px; min-height: 360px; box-shadow: 0 2px 10px rgb(0 0 0 / 0.25); } .sidebar h2 { color: #8ab4ff; font-size: 1rem; margin: 6px 0; } .sidebar ul { margin: 0; padding-left: 18px; color: #d6e3ff; line-height: 1.9; } .sidebar a { color: #97b7ff; text-decoration: none; } .sidebar a:hover { text-decoration: underline; } .main-panel { flex: 1; display: flex; flex-direction: column; gap: 12px; } .section-header { font-weight: 800; font-size: 1.04rem; color: #cbd5e1; margin: 6px 0; } .layout-two-col { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; } @media (max-width: 1100px) { .layout-two-col { grid-template-columns: 1fr; } } #output-viewport { display: flex; justify-content: center; align-items: center; min-height: 260px; } #output-image { display: flex; justify-content: center; align-items: center; } #output-image img { max-width: 100%; max-height: 420px; object-fit: contain; border-radius: 12px; background: #23252b; } """ # Layout with gr.Blocks(css=css_style) as app: gr.HTML("""