AkashKumarave commited on
Commit
7fe76c7
·
verified ·
1 Parent(s): 84a350d

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +53 -138
app.py CHANGED
@@ -2,7 +2,7 @@ import logging
2
  from fastapi import FastAPI, UploadFile, File, HTTPException, Form, Request
3
  from fastapi.middleware.cors import CORSMiddleware
4
  from fastapi.responses import FileResponse, JSONResponse
5
- import requests
6
  import base64
7
  import os
8
  from pathlib import Path
@@ -21,7 +21,7 @@ logging.basicConfig(level=logging.INFO)
21
  logger = logging.getLogger(__name__)
22
 
23
  # Initialize FastAPI app
24
- app = FastAPI(title="Gemini 2.5 Flash Image Generator API with Razorpay")
25
 
26
  # Enable CORS for the frontend
27
  app.add_middleware(
@@ -39,14 +39,18 @@ app.add_middleware(
39
  )
40
 
41
  # ===== API CONFIGURATION =====
42
- GEMINI_API_KEY = os.getenv("GEMINI_API_KEY", "AIzaSyDL5Rilo7ptJpUOZdY6wy8PJYUcVcnDADs")
43
- API_BASE_URL = "https://generativelanguage.googleapis.com"
44
- CREATE_TASK_ENDPOINT = f"{API_BASE_URL}/v1beta/models/gemini-2.5-flash-image-preview:generateContent?key={GEMINI_API_KEY}"
 
 
 
 
45
 
46
  # ===== RAZORPAY CONFIGURATION =====
47
  RAZORPAY_KEY_ID = os.getenv("RAZORPAY_KEY_ID")
48
  RAZORPAY_KEY_SECRET = os.getenv("RAZORPAY_KEY_SECRET")
49
- razorpay_client = razorpay.Client(auth=(RAZORPAY_KEY_ID, RAZORPAY_KEY_SECRET))
50
 
51
  # ===== SUPABASE CONFIGURATION =====
52
  SUPABASE_URL = os.getenv("SUPABASE_URL")
@@ -64,14 +68,6 @@ class VerifyPaymentRequest(BaseModel):
64
  user_id: Optional[str] = None
65
 
66
  # ===== IMAGE PROCESSING =====
67
- def prepare_image_base64(image_content: bytes):
68
- """Convert image bytes to base64 without prefix"""
69
- try:
70
- return base64.b64encode(image_content).decode('utf-8')
71
- except Exception as e:
72
- logger.error(f"Image processing failed: {str(e)}")
73
- raise HTTPException(status_code=500, detail=f"Image processing failed: {str(e)}")
74
-
75
  def validate_image(image_content: bytes):
76
  """Validate image meets API requirements"""
77
  try:
@@ -90,79 +86,39 @@ def validate_image(image_content: bytes):
90
  # ===== API FUNCTIONS =====
91
  def create_multi_image_task(subject_images: List[bytes], prompt: str):
92
  """Create image generation task with Gemini API (up to two images)"""
93
- headers = {
94
- "Content-Type": "application/json"
95
- }
96
- subject_image_list = []
97
- for idx, img_content in enumerate(subject_images):
98
- _, img_format = validate_image(img_content)
99
- base64_img = prepare_image_base64(img_content)
100
- if base64_img:
101
- subject_image_list.append({
102
  "inline_data": {
103
- "mime_type": f"image/{img_format}",
104
- "data": base64_img
105
  }
106
  })
 
 
107
 
108
- # Use a more descriptive prompt structure as per documentation
109
- enhanced_prompt = f"A photorealistic composition combining elements from the provided images: {prompt}. Ensure the scene is cohesive, with soft, natural lighting and a balanced aspect ratio of 16:9."
110
-
111
- payload = {
112
- "contents": [
113
- {
114
- "parts": [
115
- {"text": enhanced_prompt},
116
- *subject_image_list
117
- ]
118
- }
119
- ],
120
- "generationConfig": {
121
- "response_mime_type": "image/png"
122
- },
123
- "safetySettings": [
124
- {
125
- "category": "HARM_CATEGORY_DANGEROUS_CONTENT",
126
- "threshold": "BLOCK_NONE"
127
- },
128
- {
129
- "category": "HARM_CATEGORY_HATE_SPEECH",
130
- "threshold": "BLOCK_NONE"
131
- },
132
- {
133
- "category": "HARM_CATEGORY_SEXUALLY_EXPLICIT",
134
- "threshold": "BLOCK_NONE"
135
- },
136
- {
137
- "category": "HARM_CATEGORY_HARASSMENT",
138
- "threshold": "BLOCK_NONE"
139
- }
140
- ]
141
- }
142
-
143
- max_retries = 1
144
- for attempt in range(max_retries + 1):
145
- try:
146
- logger.info(f"Sending request to Gemini API (attempt {attempt + 1}): {payload}")
147
- response = requests.post(CREATE_TASK_ENDPOINT, json=payload, headers=headers)
148
- response.raise_for_status()
149
- data = response.json()
150
- logger.info(f"API response: {data}")
151
- if "safetyRatings" in data:
152
- logger.info(f"Safety ratings: {data['safetyRatings']}")
153
- if not data.get("candidates") or not data["candidates"][0].get("content"):
154
- raise HTTPException(status_code=500, detail="No valid content returned from API")
155
- return data
156
- except requests.exceptions.RequestException as e:
157
- logger.error(f"API request failed: {str(e)}")
158
- if hasattr(e, 'response') and e.response:
159
- logger.error(f"API response: {e.response.text}")
160
- if e.response.status_code in [429, 500] and attempt < max_retries:
161
- time.sleep(2 ** attempt) # Exponential backoff
162
- continue
163
- raise HTTPException(status_code=500, detail=f"API Error: {str(e)}")
164
 
165
- # ===== MAIN PROCESSING =====
166
  async def generate_image(subject_images: List[bytes], prompt: str):
167
  """Handle complete image generation workflow"""
168
  if len(subject_images) != 2:
@@ -171,64 +127,23 @@ async def generate_image(subject_images: List[bytes], prompt: str):
171
  for img_content in subject_images:
172
  validate_image(img_content)
173
 
174
- task_response = create_multi_image_task(subject_images, prompt)
175
  try:
176
- # Check for error in response
177
- if "error" in task_response:
178
- error_message = task_response["error"].get("message", "Unknown error")
179
- error_code = task_response["error"].get("code", 500)
180
- logger.error(f"API returned error: {error_code} - {error_message}")
181
- raise HTTPException(status_code=500, detail=f"API error: {error_code} - {error_message}")
182
-
183
- # Check response structure
184
- if "candidates" not in task_response or not task_response["candidates"]:
185
- logger.error(f"Invalid response structure: {task_response}")
186
- raise HTTPException(status_code=500, detail="Invalid API response: No candidates found")
187
-
188
- candidate = task_response["candidates"][0]
189
- if "content" not in candidate or "parts" not in candidate["content"]:
190
- logger.error(f"Invalid content structure: {candidate}")
191
- raise HTTPException(status_code=500, detail="Invalid API response: No content parts found")
192
-
193
- parts = candidate["content"]["parts"]
194
  logger.info(f"Response parts: {parts}")
195
- # Find the part with inline_data or file_uri
196
- image_base64 = None
197
- file_uri = None
198
- text_response = None
199
  for part in parts:
200
- if "inline_data" in part and "data" in part["inline_data"]:
201
- image_base64 = part["inline_data"]["data"]
202
- if not image_base64:
203
- logger.warning("Empty inline_data.data received")
204
- elif "fileUri" in part:
205
- file_uri = part["fileUri"]
206
- logger.info(f"File URI found: {file_uri}")
207
- elif "text" in part:
208
- text_response = part["text"]
209
- logger.info(f"Text part found: {text_response}")
210
-
211
- if not image_base64 and not file_uri:
212
- error_detail = text_response or "No image data (inline_data or fileUri) found in API response"
213
- if image_base64 == "":
214
- error_detail = f"Empty inline_data.data returned by API: {text_response or 'No additional details'}"
215
- logger.error(f"No image data in response parts: {parts}")
216
- raise HTTPException(status_code=500, detail=f"API error: {error_detail}")
217
-
218
- if file_uri:
219
- # Download image from file_uri
220
- logger.info(f"Downloading image from {file_uri}")
221
- response = requests.get(file_uri)
222
- response.raise_for_status()
223
- image_data = response.content
224
- else:
225
- # Decode base64 image
226
- try:
227
- image_data = base64.b64decode(image_base64)
228
- except Exception as e:
229
- logger.error(f"Failed to decode base64 image: {str(e)}")
230
- raise HTTPException(status_code=500, detail=f"Failed to decode image data: {str(e)}")
231
-
232
  output_dir = Path("/tmp")
233
  output_dir.mkdir(exist_ok=True)
234
  output_path = output_dir / f"gemini_output_{int(time.time())}.png"
@@ -365,7 +280,7 @@ async def verify_payment_endpoint(
365
  @app.get("/")
366
  async def index():
367
  return {
368
- "status": "Gemini 2.5 Flash Image Generator API with Razorpay is running",
369
  "endpoints": {
370
  "generate": "POST /generate",
371
  "create_order": "POST /create-razorpay-order",
 
2
  from fastapi import FastAPI, UploadFile, File, HTTPException, Form, Request
3
  from fastapi.middleware.cors import CORSMiddleware
4
  from fastapi.responses import FileResponse, JSONResponse
5
+ import google.generativeai as genai
6
  import base64
7
  import os
8
  from pathlib import Path
 
21
  logger = logging.getLogger(__name__)
22
 
23
  # Initialize FastAPI app
24
+ app = FastAPI(title="Gemini Image Generator API with Razorpay")
25
 
26
  # Enable CORS for the frontend
27
  app.add_middleware(
 
39
  )
40
 
41
  # ===== API CONFIGURATION =====
42
+ GEMINI_API_KEY = os.getenv("GEMINI_API_KEY")
43
+ if not GEMINI_API_KEY:
44
+ logger.error("GEMINI_API_KEY is not set")
45
+ raise HTTPException(status_code=500, detail="GEMINI_API_KEY is not set")
46
+
47
+ genai.configure(api_key=GEMINI_API_KEY)
48
+ MODEL_NAME = "gemini-1.5-flash" # Use a valid model (verify in Google's documentation)
49
 
50
  # ===== RAZORPAY CONFIGURATION =====
51
  RAZORPAY_KEY_ID = os.getenv("RAZORPAY_KEY_ID")
52
  RAZORPAY_KEY_SECRET = os.getenv("RAZORPAY_KEY_SECRET")
53
+ razorpay_client = razorpay.Client(auth=(RAZORPAY_KEY_ID, RAZORPAY_KEY_SECRET)) if RAZORPAY_KEY_ID and RAZORPAY_KEY_SECRET else None
54
 
55
  # ===== SUPABASE CONFIGURATION =====
56
  SUPABASE_URL = os.getenv("SUPABASE_URL")
 
68
  user_id: Optional[str] = None
69
 
70
  # ===== IMAGE PROCESSING =====
 
 
 
 
 
 
 
 
71
  def validate_image(image_content: bytes):
72
  """Validate image meets API requirements"""
73
  try:
 
86
  # ===== API FUNCTIONS =====
87
  def create_multi_image_task(subject_images: List[bytes], prompt: str):
88
  """Create image generation task with Gemini API (up to two images)"""
89
+ try:
90
+ model = genai.GenerativeModel(MODEL_NAME)
91
+ parts = []
92
+ for img_content in subject_images:
93
+ _, img_format = validate_image(img_content)
94
+ parts.append({
 
 
 
95
  "inline_data": {
96
+ "data": base64.b64encode(img_content).decode('utf-8'),
97
+ "mime_type": f"image/{img_format}"
98
  }
99
  })
100
+ enhanced_prompt = f"A photorealistic composition combining elements from the provided images: {prompt}. Ensure the scene is cohesive, with soft, natural lighting and a balanced aspect ratio of 16:9."
101
+ parts.append({"text": enhanced_prompt})
102
 
103
+ logger.info(f"Sending request to Gemini API with prompt: {prompt}")
104
+ response = model.generate_content(
105
+ parts,
106
+ generation_config={"response_mime_type": "image/png"},
107
+ safety_settings=[
108
+ {"category": "HARM_CATEGORY_DANGEROUS_CONTENT", "threshold": "BLOCK_NONE"},
109
+ {"category": "HARM_CATEGORY_HATE_SPEECH", "threshold": "BLOCK_NONE"},
110
+ {"category": "HARM_CATEGORY_SEXUALLY_EXPLICIT", "threshold": "BLOCK_NONE"},
111
+ {"category": "HARM_CATEGORY_HARASSMENT", "threshold": "BLOCK_NONE"}
112
+ ]
113
+ )
114
+ logger.info(f"API response: {response}")
115
+ if not response.candidates or not response.candidates[0].content:
116
+ raise HTTPException(status_code=500, detail="No valid content returned from API")
117
+ return response
118
+ except Exception as e:
119
+ logger.error(f"API request failed: {str(e)}")
120
+ raise HTTPException(status_code=500, detail=f"API Error: {str(e)}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
121
 
 
122
  async def generate_image(subject_images: List[bytes], prompt: str):
123
  """Handle complete image generation workflow"""
124
  if len(subject_images) != 2:
 
127
  for img_content in subject_images:
128
  validate_image(img_content)
129
 
130
+ response = create_multi_image_task(subject_images, prompt)
131
  try:
132
+ candidate = response.candidates[0]
133
+ parts = candidate.content.parts
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
134
  logger.info(f"Response parts: {parts}")
135
+
136
+ image_data = None
 
 
137
  for part in parts:
138
+ if hasattr(part, 'inline_data') and part.inline_data.data:
139
+ image_data = part.inline_data.data
140
+ break
141
+ elif hasattr(part, 'text'):
142
+ logger.info(f"Text part found: {part.text}")
143
+
144
+ if not image_data:
145
+ raise HTTPException(status_code=500, detail="No image data found in API response")
146
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
147
  output_dir = Path("/tmp")
148
  output_dir.mkdir(exist_ok=True)
149
  output_path = output_dir / f"gemini_output_{int(time.time())}.png"
 
280
  @app.get("/")
281
  async def index():
282
  return {
283
+ "status": "Gemini Image Generator API with Razorpay is running",
284
  "endpoints": {
285
  "generate": "POST /generate",
286
  "create_order": "POST /create-razorpay-order",