AI Blog Writer - Implement modular architecture with research, outline, and core services

This commit is contained in:
ajaysi
2025-09-12 16:53:16 +05:30
parent c0a366269d
commit 2ae0c4a8b9
29 changed files with 3210 additions and 907 deletions

View File

@@ -9,6 +9,8 @@ Based on Google AI's official grounding documentation.
import os
import json
import re
import time
import asyncio
from typing import List, Dict, Any, Optional
from datetime import datetime
from loguru import logger
@@ -104,6 +106,29 @@ class GeminiGroundedProvider:
)
except asyncio.TimeoutError:
raise Exception(f"Gemini API request timed out after {self.timeout} seconds")
except Exception as api_error:
# Handle specific Google API errors with retry logic
error_str = str(api_error)
if "503" in error_str and "overloaded" in error_str:
# Conservative retry for overloaded service (expensive API calls)
response = await self._retry_with_backoff(
lambda: self._make_api_request(grounded_prompt, config),
max_retries=1, # Only 1 retry to avoid excessive costs
base_delay=5 # Longer delay
)
elif "429" in error_str:
# Conservative retry for rate limits
response = await self._retry_with_backoff(
lambda: self._make_api_request(grounded_prompt, config),
max_retries=1, # Only 1 retry
base_delay=10 # Much longer delay for rate limits
)
elif "401" in error_str or "403" in error_str:
raise Exception("Authentication failed. Please check your API credentials.")
elif "400" in error_str:
raise Exception("Invalid request. Please check your input parameters.")
else:
raise Exception(f"Google AI service error: {error_str}")
# Process the grounded response
result = self._process_grounded_response(response, content_type)
@@ -112,9 +137,47 @@ class GeminiGroundedProvider:
return result
except Exception as e:
logger.error(f"❌ Error generating grounded content: {str(e)}")
# Log error without causing secondary exceptions
try:
logger.error(f"❌ Error generating grounded content: {str(e)}")
except:
# Fallback to print if logging fails
print(f"Error generating grounded content: {str(e)}")
raise
async def _make_api_request(self, grounded_prompt: str, config: Any):
"""Make the actual API request to Gemini."""
import concurrent.futures
loop = asyncio.get_event_loop()
with concurrent.futures.ThreadPoolExecutor() as executor:
return await asyncio.wait_for(
loop.run_in_executor(
executor,
lambda: self.client.models.generate_content(
model="gemini-2.5-flash",
contents=grounded_prompt,
config=config,
)
),
timeout=self.timeout
)
async def _retry_with_backoff(self, func, max_retries: int = 3, base_delay: float = 1.0):
"""Retry a function with exponential backoff."""
for attempt in range(max_retries + 1):
try:
return await func()
except Exception as e:
if attempt == max_retries:
# Last attempt failed, raise the error
raise e
# Calculate delay with exponential backoff
delay = base_delay * (2 ** attempt)
logger.warning(f"Attempt {attempt + 1} failed, retrying in {delay} seconds: {str(e)}")
await asyncio.sleep(delay)
def _build_grounded_prompt(self, prompt: str, content_type: str) -> str:
"""
Build a prompt optimized for grounded content generation.