ai / src /tools /image.py
hadadrjt's picture
ai: Set proper permissions.
970d47c
#
# SPDX-FileCopyrightText: Hadad <[email protected]>
# SPDX-License-Identifier: Apache-2.0
#
import asyncio # Import asyncio for asynchronous programming and managing event loops
import httpx # Import httpx for async HTTP requests with HTTP/1.1 and HTTP/2 support
import aiohttp # Import aiohttp for alternative async HTTP client capabilities
from urllib.parse import quote # Import quote to safely encode URL path components
from typing import Optional # Import Optional for type hinting parameters that may be None
from src.utils.ip_generator import generate_ip # Import custom utility to generate random IP addresses for request headers
from src.utils.tools import initialize_tools # Import utility to initialize and get tool endpoints
# Define a class named ImageGeneration to encapsulate functionalities related to generating image content
class ImageGeneration:
# This class provides methods to create image files based on text instructions
"""
Class to handle asynchronous image generation requests to an external service.
Attributes:
FORMATS (dict): Maps image format names to (width, height) tuples.
Methods:
create_image: Async method to generate an image URL from a text prompt,
retrying until successful, using httpx and aiohttp.
"""
# Supported image formats with their dimensions (width, height)
FORMATS = {
"default": (1024, 1024),
"square": (1024, 1024),
"landscape": (1024, 768),
"landscape_large": (1440, 1024),
"portrait": (768, 1024),
"portrait_large": (1024, 1440),
}
@staticmethod
async def create_image(
generate_image_instruction: str, # Text description for the image to generate
image_format: str = "default", # Format key from FORMATS dict
model: Optional[str] = "flux-realism", # Model name for generation, default 'flux-realism'
seed: Optional[int] = None, # Optional seed for reproducible randomness
nologo: bool = True, # Whether to exclude logo watermark
private: bool = True, # Whether the image should be private
enhance: bool = True, # Whether to apply enhancement filters
) -> str:
"""
Asynchronously generate an image URL by sending requests to the image generation service.
Uses httpx for initial requests and aiohttp as fallback, retrying indefinitely until success.
Args:
generate_image_instruction (str): Text prompt describing the desired image.
image_format (str): Key for image dimensions.
model (Optional[str]): Model to use for generation.
seed (Optional[int]): Seed for randomization control.
nologo (bool): Flag to exclude logo watermark.
private (bool): Flag to mark image as private.
enhance (bool): Flag to apply image enhancement.
Returns:
str: URL of the generated image on success.
Raises:
ValueError: If image_format is invalid.
"""
# Validate image format key
if image_format not in ImageGeneration.FORMATS:
raise ValueError("Invalid image format.")
# Extract width and height for the requested format
width, height = ImageGeneration.FORMATS[image_format]
# Initialize tools and get image generation service endpoint URL
_, image_tool, _ = initialize_tools()
# Encode instruction safely for URL path usage
generate_image_instruct = quote(generate_image_instruction)
# Construct the full URL endpoint for image generation
url = f"{image_tool}{generate_image_instruct}"
# Prepare query parameters with image size, model, flags as strings
params = {
"width": width,
"height": height,
"model": model,
"nologo": "true" if nologo else "false",
"private": "true" if private else "false",
"enhance": "true" if enhance else "false",
}
# Add seed parameter if provided
if seed is not None:
params["seed"] = seed
# Prepare headers
headers = {
"X-Forwarded-For": generate_ip() # Random IP address for request header to simulate client origin
}
# Use httpx.AsyncClient with no timeout for initial requests
async with httpx.AsyncClient(timeout=None) as client:
while True:
try:
# Send GET request to the image generation endpoint
resp = await client.get(url, params=params, headers=headers)
# If response is successful, return the final URL
if resp.status_code == 200:
return str(resp.url)
except httpx.HTTPError:
# On httpx errors, fallback to aiohttp for robustness
pass
# Fallback retry with aiohttp client
async with aiohttp.ClientSession() as session:
try:
async with session.get(url, params=params, headers=headers) as resp:
if resp.status == 200:
# Return the final URL (aiohttp does not provide direct URL property)
return str(resp.url)
except aiohttp.ClientError:
# Ignore aiohttp errors and retry
pass
# Wait 15 seconds before retrying to avoid overwhelming the server
await asyncio.sleep(15)