ak0601 commited on
Commit
95127dd
·
verified ·
1 Parent(s): 9504503

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +160 -160
app.py CHANGED
@@ -1,161 +1,161 @@
1
-
2
- from fastapi import FastAPI, File, UploadFile, Form, HTTPException
3
- from fastapi.responses import JSONResponse
4
- import tempfile
5
- from dotenv import load_dotenv
6
- import os
7
- import google.generativeai as genai # Correct import alias
8
- import json
9
- import logging # Added for better debugging
10
-
11
- load_dotenv()
12
- # Configure logging
13
- logging.basicConfig(level=logging.INFO)
14
- logger = logging.getLogger(__name__)
15
-
16
- app = FastAPI()
17
-
18
- # --- Configuration ---
19
- # Load API Key securely (e.g., from environment variable)
20
- # Replace with your actual key retrieval method
21
- API_KEY = os.getenv("GOOGLE_API_KEY") # Use environment variable or replace directly
22
-
23
- if not API_KEY:
24
- logger.error("GEMINI_API_KEY environment variable not set.")
25
- # You might want to raise an exception or exit here in a real application
26
- # For now, we'll let it proceed but it will fail later if the placeholder key is invalid
27
-
28
- # Configure the Gemini client globally
29
- try:
30
- genai.configure(api_key=API_KEY)
31
- logger.info("Google Gemini client configured successfully.")
32
- except Exception as e:
33
- logger.error(f"Failed to configure Google Gemini client: {e}")
34
- # Handle configuration error appropriately
35
-
36
- # Initialize the Generative Model globally
37
- # Use a model that supports image input, like gemini-1.5-flash-latest or gemini-pro-vision
38
- # gemini-1.5-flash is generally recommended now
39
- try:
40
- model = genai.GenerativeModel("gemini-2.0-flash") # Using the recommended flash model
41
- logger.info(f"Google Gemini model '{model.model_name}' initialized.")
42
- except Exception as e:
43
- logger.error(f"Failed to initialize Google Gemini model: {e}")
44
- # Handle model initialization error appropriately
45
-
46
- # --- FastAPI Endpoint ---
47
- @app.post("/rate-outfit/")
48
- async def rate_outfit(image: UploadFile = File(...), category: str = Form(...),occasion: str = Form(...),Place: str = Form(...),type_of_feedback: str = Form(...)):
49
- logger.info(f"Received request to rate outfit. Category: {category}, Image: {image.filename}, Content-Type: {image.content_type}")
50
-
51
- if image.content_type not in ["image/jpeg", "image/png", "image/jpg"]:
52
- logger.warning(f"Invalid image content type: {image.content_type}")
53
- raise HTTPException(status_code=400, detail="Please upload a valid image file (jpeg, png, jpg).")
54
-
55
- tmp_path = None # Initialize tmp_path
56
- try:
57
- # Save image to temp file safely
58
- # Using a context manager ensures the file is closed properly
59
- with tempfile.NamedTemporaryFile(delete=False, suffix=os.path.splitext(image.filename)[1]) as tmp:
60
- content = await image.read()
61
- tmp.write(content)
62
- tmp_path = tmp.name
63
- logger.info(f"Image saved temporarily to: {tmp_path}")
64
-
65
- # Upload image to Gemini using the recommended function
66
- logger.info("Uploading image to Gemini...")
67
- # The new API uses genai.upload_file directly
68
- uploaded_file = genai.upload_file(path=tmp_path, display_name=image.filename)
69
- logger.info(f"Image uploaded successfully: {uploaded_file.name}")
70
-
71
-
72
- # Define the prompt clearly
73
- prompt = (
74
- f"You are an AI fashion assistant. Based on the category '{category}', analyze the provided image."
75
- "The user is going for an ocassion of {occasion} at {Place}, so it want {type_of_feedback} kind of feedback from you, so answer accordingly. "
76
- "Extract the following information and provide the response ONLY as a valid JSON object, without any surrounding text, markdown formatting (like ```json), or explanations. "
77
- "The JSON object should follow this exact schema: "
78
- '{"Tag": "A short, catchy caption phrase based on the image, including a relevant emoji.", '
79
- '"Feedback": "Concise advice (1-2 sentences) on how the look could be improved or styled differently."}'
80
- " --- IMPORTANT SAFETY CHECK: If the image contains nudity, offensive content, any religious context, political figure, or anything inappropriate for a fashion context, respond ONLY with the following JSON: "
81
- '{"error": "Please upload an appropriate image"} --- '
82
- "Focus on being concise and eye-catching."
83
- )
84
-
85
- # Prepare content for the model (prompt first, then file)
86
- # Ensure the uploaded file object is used, not just the path
87
- content_parts = [prompt, uploaded_file] # Pass the UploadedFile object
88
-
89
- logger.info("Generating content with Gemini model...")
90
- # Generate content
91
- response = model.generate_content(content_parts)
92
- logger.info("Received response from Gemini.")
93
- # logger.debug(f"Raw Gemini response text: {response.text}") # Optional: Log raw response for debugging
94
-
95
- # Clean and parse the response
96
- text_response = response.text.strip()
97
-
98
- # Robust cleaning: Remove potential markdown code blocks
99
- if text_response.startswith("```json"):
100
- text_response = text_response[7:] # Remove ```json\n
101
- if text_response.endswith("```"):
102
- text_response = text_response[:-3] # Remove ```
103
- text_response = text_response.strip() # Strip again after removing markdown
104
-
105
- logger.info(f"Cleaned Gemini response text: {text_response}")
106
-
107
- # Attempt to parse the cleaned JSON
108
- try:
109
- result = json.loads(text_response)
110
- # Validate if the result contains expected keys or the error key
111
- if "error" in result:
112
- logger.warning(f"Gemini detected inappropriate image: {result['error']}")
113
- # Return a different status code for client-side handling? (e.g., 400 Bad Request)
114
- # raise HTTPException(status_code=400, detail=result['error'])
115
- # Or just return the error JSON as requested by some flows:
116
- return JSONResponse(content=result, status_code=200) # Or 400 depending on desired API behavior
117
- elif "Tag" not in result or "Feedback" not in result:
118
- logger.error(f"Gemini response missing expected keys 'Tag' or 'Feedback'. Got: {result}")
119
- raise HTTPException(status_code=500, detail="AI response format error: Missing expected keys.")
120
-
121
- logger.info(f"Successfully parsed Gemini response: {result}")
122
- return JSONResponse(content=result)
123
-
124
- except json.JSONDecodeError as json_err:
125
- logger.error(f"Failed to decode JSON response from Gemini: {json_err}")
126
- logger.error(f"Invalid JSON string received: {text_response}")
127
- raise HTTPException(status_code=500, detail="AI response format error: Invalid JSON.")
128
- except Exception as parse_err: # Catch other potential errors during parsing/validation
129
- logger.error(f"Error processing Gemini response: {parse_err}")
130
- raise HTTPException(status_code=500, detail="Error processing AI response.")
131
-
132
-
133
- except genai.types.generation_types.BlockedPromptException as block_err:
134
- logger.warning(f"Gemini blocked the prompt or response due to safety settings: {block_err}")
135
- # Return a generic safety message or the specific error JSON
136
- error_response = {"error": "Request blocked due to safety policies. Please ensure the image is appropriate."}
137
- # It's often better to return a 400 Bad Request here
138
- return JSONResponse(content=error_response, status_code=400)
139
-
140
- except Exception as e:
141
- logger.error(f"An unexpected error occurred: {e}", exc_info=True) # Log full traceback
142
- # Generic error for security reasons, details are logged
143
- raise HTTPException(status_code=500, detail="An internal server error occurred.")
144
-
145
- finally:
146
- # Cleanup temp image file if it was created
147
- if tmp_path and os.path.exists(tmp_path):
148
- try:
149
- os.remove(tmp_path)
150
- logger.info(f"Temporary file {tmp_path} removed.")
151
- except OSError as e:
152
- logger.error(f"Error removing temporary file {tmp_path}: {e}")
153
-
154
- # --- To Run (if this is the main script) ---
155
- if __name__ == "__main__":
156
- import uvicorn
157
- # # Remember to set the GEMINI_API_KEY environment variable before running
158
- # Example (Linux/macOS): export GEMINI_API_KEY='your_actual_api_key'
159
- # # Example (Windows CMD): set GEMINI_API_KEY=your_actual_api_key
160
- # # Example (Windows PowerShell): $env:GEMINI_API_KEY='your_actual_api_key'
161
  uvicorn.run(app, host="0.0.0.0", port=8000)
 
1
+
2
+ from fastapi import FastAPI, File, UploadFile, Form, HTTPException
3
+ from fastapi.responses import JSONResponse
4
+ import tempfile
5
+ from dotenv import load_dotenv
6
+ import os
7
+ import google.generativeai as genai # Correct import alias
8
+ import json
9
+ import logging # Added for better debugging
10
+
11
+ load_dotenv()
12
+ # Configure logging
13
+ logging.basicConfig(level=logging.INFO)
14
+ logger = logging.getLogger(__name__)
15
+
16
+ app = FastAPI()
17
+
18
+ # --- Configuration ---
19
+ # Load API Key securely (e.g., from environment variable)
20
+ # Replace with your actual key retrieval method
21
+ API_KEY = os.getenv("GOOGLE_API_KEY") # Use environment variable or replace directly
22
+
23
+ if not API_KEY:
24
+ logger.error("GEMINI_API_KEY environment variable not set.")
25
+ # You might want to raise an exception or exit here in a real application
26
+ # For now, we'll let it proceed but it will fail later if the placeholder key is invalid
27
+
28
+ # Configure the Gemini client globally
29
+ try:
30
+ genai.configure(api_key=API_KEY)
31
+ logger.info("Google Gemini client configured successfully.")
32
+ except Exception as e:
33
+ logger.error(f"Failed to configure Google Gemini client: {e}")
34
+ # Handle configuration error appropriately
35
+
36
+ # Initialize the Generative Model globally
37
+ # Use a model that supports image input, like gemini-1.5-flash-latest or gemini-pro-vision
38
+ # gemini-1.5-flash is generally recommended now
39
+ try:
40
+ model = genai.GenerativeModel("gemini-2.0-flash") # Using the recommended flash model
41
+ logger.info(f"Google Gemini model '{model.model_name}' initialized.")
42
+ except Exception as e:
43
+ logger.error(f"Failed to initialize Google Gemini model: {e}")
44
+ # Handle model initialization error appropriately
45
+
46
+ # --- FastAPI Endpoint ---
47
+ @app.post("/rate-outfit/")
48
+ async def rate_outfit(image: UploadFile = File(...), category: str = Form(...),occasion: str = Form(...),Place: str = Form(...),type_of_feedback: str = Form(...)):
49
+ logger.info(f"Received request to rate outfit. Category: {category}, Image: {image.filename}, Content-Type: {image.content_type}")
50
+
51
+ if image.content_type not in ["image/jpeg", "image/png", "image/jpg"]:
52
+ logger.warning(f"Invalid image content type: {image.content_type}")
53
+ raise HTTPException(status_code=400, detail="Please upload a valid image file (jpeg, png, jpg).")
54
+
55
+ tmp_path = None # Initialize tmp_path
56
+ try:
57
+ # Save image to temp file safely
58
+ # Using a context manager ensures the file is closed properly
59
+ with tempfile.NamedTemporaryFile(delete=False, suffix=os.path.splitext(image.filename)[1]) as tmp:
60
+ content = await image.read()
61
+ tmp.write(content)
62
+ tmp_path = tmp.name
63
+ logger.info(f"Image saved temporarily to: {tmp_path}")
64
+
65
+ # Upload image to Gemini using the recommended function
66
+ logger.info("Uploading image to Gemini...")
67
+ # The new API uses genai.upload_file directly
68
+ uploaded_file = genai.upload_file(path=tmp_path, display_name=image.filename)
69
+ logger.info(f"Image uploaded successfully: {uploaded_file.name}")
70
+
71
+
72
+ # Define the prompt clearly
73
+ prompt = (
74
+ f"You are an AI fashion assistant. Based on the category '{category}', analyze the provided image."
75
+ "The user is going for an ocassion of {occasion} at {Place}, so it want {type_of_feedback} kind of feedback from you, so answer accordingly. Be very enthusiastic and excited "
76
+ "Extract the following information and provide the response ONLY as a valid JSON object, without any surrounding text, markdown formatting (like ```json), or explanations. "
77
+ "The JSON object should follow this exact schema: "
78
+ '{"Tag": "A short, catchy caption phrase based on the image, including a relevant emoji.", '
79
+ '"Feedback": "Concise advice (1-2 sentences) on how the look could be improved or styled differently."}'
80
+ " --- IMPORTANT SAFETY CHECK: If the image contains nudity, offensive content, any religious context, political figure, or anything inappropriate for a fashion context, respond ONLY with the following JSON: "
81
+ '{"error": "Please upload an appropriate image"} --- '
82
+ "Focus on being concise and eye-catching."
83
+ )
84
+
85
+ # Prepare content for the model (prompt first, then file)
86
+ # Ensure the uploaded file object is used, not just the path
87
+ content_parts = [prompt, uploaded_file] # Pass the UploadedFile object
88
+
89
+ logger.info("Generating content with Gemini model...")
90
+ # Generate content
91
+ response = model.generate_content(content_parts)
92
+ logger.info("Received response from Gemini.")
93
+ # logger.debug(f"Raw Gemini response text: {response.text}") # Optional: Log raw response for debugging
94
+
95
+ # Clean and parse the response
96
+ text_response = response.text.strip()
97
+
98
+ # Robust cleaning: Remove potential markdown code blocks
99
+ if text_response.startswith("```json"):
100
+ text_response = text_response[7:] # Remove ```json\n
101
+ if text_response.endswith("```"):
102
+ text_response = text_response[:-3] # Remove ```
103
+ text_response = text_response.strip() # Strip again after removing markdown
104
+
105
+ logger.info(f"Cleaned Gemini response text: {text_response}")
106
+
107
+ # Attempt to parse the cleaned JSON
108
+ try:
109
+ result = json.loads(text_response)
110
+ # Validate if the result contains expected keys or the error key
111
+ if "error" in result:
112
+ logger.warning(f"Gemini detected inappropriate image: {result['error']}")
113
+ # Return a different status code for client-side handling? (e.g., 400 Bad Request)
114
+ # raise HTTPException(status_code=400, detail=result['error'])
115
+ # Or just return the error JSON as requested by some flows:
116
+ return JSONResponse(content=result, status_code=200) # Or 400 depending on desired API behavior
117
+ elif "Tag" not in result or "Feedback" not in result:
118
+ logger.error(f"Gemini response missing expected keys 'Tag' or 'Feedback'. Got: {result}")
119
+ raise HTTPException(status_code=500, detail="AI response format error: Missing expected keys.")
120
+
121
+ logger.info(f"Successfully parsed Gemini response: {result}")
122
+ return JSONResponse(content=result)
123
+
124
+ except json.JSONDecodeError as json_err:
125
+ logger.error(f"Failed to decode JSON response from Gemini: {json_err}")
126
+ logger.error(f"Invalid JSON string received: {text_response}")
127
+ raise HTTPException(status_code=500, detail="AI response format error: Invalid JSON.")
128
+ except Exception as parse_err: # Catch other potential errors during parsing/validation
129
+ logger.error(f"Error processing Gemini response: {parse_err}")
130
+ raise HTTPException(status_code=500, detail="Error processing AI response.")
131
+
132
+
133
+ except genai.types.generation_types.BlockedPromptException as block_err:
134
+ logger.warning(f"Gemini blocked the prompt or response due to safety settings: {block_err}")
135
+ # Return a generic safety message or the specific error JSON
136
+ error_response = {"error": "Request blocked due to safety policies. Please ensure the image is appropriate."}
137
+ # It's often better to return a 400 Bad Request here
138
+ return JSONResponse(content=error_response, status_code=400)
139
+
140
+ except Exception as e:
141
+ logger.error(f"An unexpected error occurred: {e}", exc_info=True) # Log full traceback
142
+ # Generic error for security reasons, details are logged
143
+ raise HTTPException(status_code=500, detail="An internal server error occurred.")
144
+
145
+ finally:
146
+ # Cleanup temp image file if it was created
147
+ if tmp_path and os.path.exists(tmp_path):
148
+ try:
149
+ os.remove(tmp_path)
150
+ logger.info(f"Temporary file {tmp_path} removed.")
151
+ except OSError as e:
152
+ logger.error(f"Error removing temporary file {tmp_path}: {e}")
153
+
154
+ # --- To Run (if this is the main script) ---
155
+ if __name__ == "__main__":
156
+ import uvicorn
157
+ # # Remember to set the GEMINI_API_KEY environment variable before running
158
+ # Example (Linux/macOS): export GEMINI_API_KEY='your_actual_api_key'
159
+ # # Example (Windows CMD): set GEMINI_API_KEY=your_actual_api_key
160
+ # # Example (Windows PowerShell): $env:GEMINI_API_KEY='your_actual_api_key'
161
  uvicorn.run(app, host="0.0.0.0", port=8000)