Spaces:
Running
Running
import gradio as gr | |
import requests | |
import base64 | |
import os | |
import io | |
from PIL import Image | |
# --- OpenAI API Call Logic --- | |
def call_openai_image_api(prompt: str, api_key: str, input_image: Image.Image | None = None): | |
""" | |
Calls the appropriate OpenAI Image API (generation or edit) | |
based on whether an input image is provided. | |
Args: | |
prompt: The text prompt for image generation or editing. | |
api_key: The OpenAI API key. | |
input_image: A PIL Image object for editing, or None for generation. | |
Returns: | |
A tuple containing: | |
- original_image (PIL.Image or None): The original image if editing, else None. | |
- result_image (PIL.Image or None): The generated/edited image, or None on error. | |
- status_message (str): A message indicating success or error details. | |
""" | |
if not api_key: | |
return None, None, "Error: OpenAI API Key is missing." | |
if not prompt: | |
return None, None, "Error: Prompt cannot be empty." | |
headers = {"Authorization": f"Bearer {api_key}"} | |
# Hypothetical model name from the original code. Replace with "dall-e-2" or "dall-e-3" if needed. | |
model = "gpt-image-1" # Using the model specified in the original code | |
size = "1024x1024" | |
response = None # Initialize response variable | |
try: | |
if input_image: | |
# --- Image Editing --- | |
if not isinstance(input_image, Image.Image): | |
return None, None, "Error: Invalid image provided for editing." | |
# Convert PIL Image to bytes for the API request | |
byte_stream = io.BytesIO() | |
input_image.save(byte_stream, format="PNG") # Save PIL image to bytes buffer [[1]] | |
byte_stream.seek(0) # Rewind buffer to the beginning | |
files = { | |
"image": ("input_image.png", byte_stream, "image/png"), | |
} | |
# CORRECTED data dictionary: removed 'response_format' | |
data = { | |
"prompt": prompt, | |
"model": model, | |
"size": size, | |
# "response_format": "b64_json", # <-- THIS LINE IS REMOVED | |
} | |
api_url = "https://api.openai.com/v1/images/edits" | |
print("Calling OpenAI Image Edit API...") # Debug print | |
response = requests.post(api_url, headers=headers, files=files, data=data) | |
else: | |
# --- Image Generation --- | |
# (This part remains the same as it uses response_format correctly via json payload) | |
headers["Content-Type"] = "application/json" | |
payload = { | |
"prompt": prompt, | |
"model": model, | |
"response_format": "b64_json", # Keep this for generation | |
"size": size, | |
"n": 1, # Generate one image | |
} | |
api_url = "https://api.openai.com/v1/images/generations" | |
print("Calling OpenAI Image Generation API...") # Debug print | |
response = requests.post(api_url, headers=headers, json=payload) | |
print(f"API Response Status Code: {response.status_code}") # Debug print | |
response.raise_for_status() # Raise HTTPError for bad responses (4xx or 5xx) | |
# Process successful response | |
response_data = response.json() | |
# Ensure the expected data structure is present | |
if not response_data.get("data") or not isinstance(response_data["data"], list) or len(response_data["data"]) == 0: | |
return input_image, None, f"Error: Unexpected API response format - 'data' array missing or empty: {response_data}" | |
if not response_data["data"][0].get("b64_json"): | |
return input_image, None, f"Error: Unexpected API response format - 'b64_json' key missing: {response_data}" | |
img_b64 = response_data["data"][0]["b64_json"] | |
img_bytes = base64.b64decode(img_b64) # Decode base64 string [[1]] | |
result_image = Image.open(io.BytesIO(img_bytes)) # Convert bytes to PIL Image [[1]] | |
print("Image processed successfully.") # Debug print | |
return input_image, result_image, "Success!" | |
except requests.exceptions.RequestException as e: | |
error_message = f"API Request Error: {e}" | |
# Check if response exists before trying to access its attributes/methods | |
if response is not None: | |
try: | |
# Attempt to get more specific error from OpenAI response | |
error_detail = response.json() | |
error_message += f"\nAPI Error Details: {error_detail}" | |
except requests.exceptions.JSONDecodeError: | |
# Fallback if response is not JSON | |
error_message += f"\nRaw Response Text: {response.text}" | |
except Exception as json_e: | |
error_message += f"\nError parsing JSON response: {json_e}\nRaw Response Text: {response.text}" | |
print(error_message) # Debug print | |
return input_image, None, error_message | |
except Exception as e: | |
error_message = f"An unexpected error occurred: {e}" | |
print(error_message) # Debug print | |
return input_image, None, error_message | |
# --- Gradio Interface Setup --- | |
# Check for API key in environment variables | |
api_key_env = os.environ.get("OPENAI_API_KEY") | |
api_key_present_info = "OpenAI API Key found in environment variables." if api_key_env else "OpenAI API Key not found in environment variables. Please enter it below." | |
def process_image_request(prompt_input, api_key_input, uploaded_image): | |
""" | |
Wrapper function for Gradio interface. | |
Determines the API key to use (input field first, then environment variable). | |
Calls the main API function. | |
""" | |
# Prioritize the API key entered in the input field | |
final_api_key = api_key_input if api_key_input else api_key_env | |
# Call the actual API logic | |
original_img, result_img, status = call_openai_image_api(prompt_input, final_api_key, uploaded_image) | |
# Return values for the Gradio output components | |
# If generating (original_img is None), return None for the original image display | |
# If editing, return the uploaded image (original_img) for the original image display | |
return original_img, result_img, status | |
# Build the Gradio interface using Blocks for more layout control [[7]] | |
with gr.Blocks() as demo: | |
gr.Markdown("# OpenAI GPT-Image-1 Text-to-Image Demo") # App title | |
gr.Markdown("Enter a prompt to generate an image, or upload an image and enter a prompt to edit it.") | |
with gr.Row(): | |
with gr.Column(scale=1): | |
prompt_input = gr.Textbox(label="Image Description (Prompt)", lines=3, placeholder="e.g., A futuristic cityscape at sunset") # Text input for prompt | |
gr.Markdown(f"*{api_key_present_info}*") | |
api_key_input = gr.Textbox(label="OpenAI API Key", type="password", placeholder="Enter your key if not set in environment") # Password input for API key | |
uploaded_image_input = gr.Image(type="pil", label="Upload Image to Edit (Optional)") # Image upload [[4]] | |
submit_button = gr.Button("Generate / Edit Image") | |
with gr.Column(scale=2): | |
status_output = gr.Textbox(label="Status", interactive=False) | |
with gr.Row(): | |
original_image_output = gr.Image(label="Original Image", interactive=False) | |
result_image_output = gr.Image(label="Generated / Edited Image", interactive=False) # Display output image | |
# Connect the button click event to the processing function | |
submit_button.click( | |
fn=process_image_request, | |
inputs=[prompt_input, api_key_input, uploaded_image_input], | |
outputs=[original_image_output, result_image_output, status_output] | |
) | |
# Launch the Gradio app [[2]] | |
if __name__ == "__main__": | |
demo.launch() | |