sushilideaclan01 commited on
Commit
dd2695a
Β·
1 Parent(s): 0fbc3f9

feat: Convert image generation and specific service calls to asynchronous operations with timeouts using `AsyncOpenAI` and `asyncio.to_thread`.

Browse files
Files changed (2) hide show
  1. services/generator.py +4 -2
  2. services/image.py +31 -16
services/generator.py CHANGED
@@ -2058,7 +2058,8 @@ CONCEPT: {concept.get('name', 'Custom Concept')}
2058
 
2059
  # Step 1: Researcher
2060
  print("πŸ” Step 1: Researching psychology triggers, angles, and concepts...")
2061
- researcher_output = third_flow_service.researcher(
 
2062
  target_audience=target_audience,
2063
  offer=offer,
2064
  niche=niche_display
@@ -2085,7 +2086,8 @@ CONCEPT: {concept.get('name', 'Custom Concept')}
2085
  # Step 3: Creative Director
2086
  print(f"🎨 Step 3: Creating {num_strategies} creative strategy/strategies...")
2087
  print(f"πŸ“‹ Parameters: num_strategies={num_strategies}, num_images={num_images}")
2088
- creative_strategies = third_flow_service.creative_director(
 
2089
  researcher_output=researcher_output,
2090
  book_knowledge=book_knowledge,
2091
  ads_knowledge=ads_knowledge,
 
2058
 
2059
  # Step 1: Researcher
2060
  print("πŸ” Step 1: Researching psychology triggers, angles, and concepts...")
2061
+ researcher_output = await asyncio.to_thread(
2062
+ third_flow_service.researcher,
2063
  target_audience=target_audience,
2064
  offer=offer,
2065
  niche=niche_display
 
2086
  # Step 3: Creative Director
2087
  print(f"🎨 Step 3: Creating {num_strategies} creative strategy/strategies...")
2088
  print(f"πŸ“‹ Parameters: num_strategies={num_strategies}, num_images={num_images}")
2089
+ creative_strategies = await asyncio.to_thread(
2090
+ third_flow_service.creative_director,
2091
  researcher_output=researcher_output,
2092
  book_knowledge=book_knowledge,
2093
  ads_knowledge=ads_knowledge,
services/image.py CHANGED
@@ -15,7 +15,8 @@ sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
15
 
16
  import replicate
17
  import httpx
18
- from openai import OpenAI
 
19
  from config import settings
20
 
21
 
@@ -141,7 +142,7 @@ class ImageService:
141
  # Initialize OpenAI client for gpt-image-1.5 support
142
  self.openai_client = None
143
  if hasattr(settings, 'openai_api_key') and settings.openai_api_key:
144
- self.openai_client = OpenAI(api_key=settings.openai_api_key)
145
 
146
  def _fetch_image(self, url: str) -> Optional[bytes]:
147
  """Fetch image from URL with retry logic."""
@@ -161,7 +162,7 @@ class ImageService:
161
  if attempt == RETRY_ATTEMPTS - 1:
162
  print(f"Failed to fetch image from {url}: {e}")
163
  return None
164
- time.sleep(1)
165
  return None
166
 
167
  async def load_image(
@@ -294,15 +295,19 @@ class ImageService:
294
  print("Generating image with gpt-image-1.5")
295
  size_str = f"{width}x{height}"
296
 
297
- result = self.openai_client.images.generate(
298
- model="gpt-image-1.5",
299
- prompt=prompt,
300
- quality="auto",
301
- background="auto",
302
- moderation="auto",
303
- size=size_str,
304
- output_format="jpeg",
305
- output_compression=90,
 
 
 
 
306
  )
307
 
308
  if result.data and len(result.data) > 0:
@@ -313,6 +318,9 @@ class ImageService:
313
  return image_bytes, "gpt-image-1.5", None
314
 
315
  raise Exception("No image data returned from OpenAI API")
 
 
 
316
 
317
  except Exception as e:
318
  print(f"OpenAI image generation failed: {e}")
@@ -358,8 +366,16 @@ class ImageService:
358
  try:
359
  print(f"Generating image with {current_model} (attempt {attempt + 1})")
360
 
361
- # Use official Replicate client
362
- output = self.client.run(cfg["id"], input=input_data)
 
 
 
 
 
 
 
 
363
 
364
  # Extract image bytes and URL
365
  image_bytes, image_url = self._extract_image_from_output(output)
@@ -372,7 +388,7 @@ class ImageService:
372
  last_error = e
373
  if attempt < RETRY_ATTEMPTS - 1:
374
  print(f"Attempt {attempt + 1} failed: {e}, retrying...")
375
- time.sleep(2 ** attempt) # Exponential backoff
376
  continue
377
 
378
  # Model failed, try next in fallback chain
@@ -409,7 +425,6 @@ class ImageService:
409
  except Exception as e:
410
  last_error = e
411
  if attempt < max_retries:
412
- import asyncio
413
  await asyncio.sleep(2)
414
  continue
415
 
 
15
 
16
  import replicate
17
  import httpx
18
+ from openai import AsyncOpenAI
19
+ import asyncio
20
  from config import settings
21
 
22
 
 
142
  # Initialize OpenAI client for gpt-image-1.5 support
143
  self.openai_client = None
144
  if hasattr(settings, 'openai_api_key') and settings.openai_api_key:
145
+ self.openai_client = AsyncOpenAI(api_key=settings.openai_api_key)
146
 
147
  def _fetch_image(self, url: str) -> Optional[bytes]:
148
  """Fetch image from URL with retry logic."""
 
162
  if attempt == RETRY_ATTEMPTS - 1:
163
  print(f"Failed to fetch image from {url}: {e}")
164
  return None
165
+ time.sleep(1) # Sync fetch so sync sleep is fine here or use asyncio.sleep
166
  return None
167
 
168
  async def load_image(
 
295
  print("Generating image with gpt-image-1.5")
296
  size_str = f"{width}x{height}"
297
 
298
+ # Use a timeout for OpenAI image generation
299
+ result = await asyncio.wait_for(
300
+ self.openai_client.images.generate(
301
+ model="gpt-image-1.5",
302
+ prompt=prompt,
303
+ quality="auto",
304
+ background="auto",
305
+ moderation="auto",
306
+ size=size_str,
307
+ output_format="jpeg",
308
+ output_compression=90,
309
+ ),
310
+ timeout=120.0
311
  )
312
 
313
  if result.data and len(result.data) > 0:
 
318
  return image_bytes, "gpt-image-1.5", None
319
 
320
  raise Exception("No image data returned from OpenAI API")
321
+ except asyncio.TimeoutError:
322
+ print("Timed out generating image with gpt-image-1.5")
323
+ raise Exception("Timeout: Image generation with gpt-image-1.5 took too long")
324
 
325
  except Exception as e:
326
  print(f"OpenAI image generation failed: {e}")
 
366
  try:
367
  print(f"Generating image with {current_model} (attempt {attempt + 1})")
368
 
369
+ # Use official Replicate client - offload blocking call to thread and add timeout
370
+ try:
371
+ # Wrap the blocking call in a thread and add a 3-minute timeout
372
+ output = await asyncio.wait_for(
373
+ asyncio.to_thread(self.client.run, cfg["id"], input=input_data),
374
+ timeout=180.0 # 3 minutes timeout
375
+ )
376
+ except asyncio.TimeoutError:
377
+ print(f"Timed out generating image with {current_model} after 180 seconds")
378
+ raise Exception(f"Timeout: Image generation with {current_model} took too long")
379
 
380
  # Extract image bytes and URL
381
  image_bytes, image_url = self._extract_image_from_output(output)
 
388
  last_error = e
389
  if attempt < RETRY_ATTEMPTS - 1:
390
  print(f"Attempt {attempt + 1} failed: {e}, retrying...")
391
+ await asyncio.sleep(2 ** attempt) # Exponential backoff (use await for async sleep)
392
  continue
393
 
394
  # Model failed, try next in fallback chain
 
425
  except Exception as e:
426
  last_error = e
427
  if attempt < max_retries:
 
428
  await asyncio.sleep(2)
429
  continue
430