Update main.py
Browse files
main.py
CHANGED
|
@@ -105,7 +105,7 @@ async def generate_audio(text, filename):
|
|
| 105 |
if os.path.exists(filename): os.remove(filename)
|
| 106 |
return None
|
| 107 |
|
| 108 |
-
async def safe_generate(prompt, retries=
|
| 109 |
if client is None: raise Exception("AI Client is not initialized.")
|
| 110 |
for attempt in range(retries):
|
| 111 |
try:
|
|
@@ -113,7 +113,7 @@ async def safe_generate(prompt, retries=3):
|
|
| 113 |
return await loop.run_in_executor(None, lambda: client.predict(prompt=prompt, api_name="/generate_questions"))
|
| 114 |
except Exception as e:
|
| 115 |
if attempt == retries - 1: raise e
|
| 116 |
-
await asyncio.sleep(
|
| 117 |
|
| 118 |
def parse_question_output(raw_output: str):
|
| 119 |
if not raw_output: return None, None
|
|
@@ -156,10 +156,10 @@ async def refill_specific_pool(track_id: int, difficulty: int, count: int, sessi
|
|
| 156 |
"created_at": firestore.SERVER_TIMESTAMP
|
| 157 |
})
|
| 158 |
success_count += 1
|
| 159 |
-
print(f"Successfully added question {success_count}/{count}")
|
| 160 |
-
await asyncio.sleep(
|
| 161 |
except Exception as e:
|
| 162 |
-
print(f"Refill
|
| 163 |
await asyncio.sleep(5)
|
| 164 |
|
| 165 |
# =========================================
|
|
@@ -167,12 +167,22 @@ async def refill_specific_pool(track_id: int, difficulty: int, count: int, sessi
|
|
| 167 |
# =========================================
|
| 168 |
@app.post("/generate-session")
|
| 169 |
async def generate_session(request: GenerateSessionRequest, background_tasks: BackgroundTasks):
|
| 170 |
-
s_type,
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 171 |
query = db.collection("questions_pool").where(filter=FieldFilter("session_type", "==", s_type))
|
| 172 |
|
| 173 |
if s_type == 1: # Technical
|
| 174 |
if t_id is None: raise HTTPException(status_code=400, detail="trackName is required for technical sessions.")
|
| 175 |
query = query.where(filter=FieldFilter("track_id", "==", t_id)).where(filter=FieldFilter("difficulty", "==", diff))
|
|
|
|
|
|
|
|
|
|
| 176 |
|
| 177 |
docs = query.limit(10).get()
|
| 178 |
final_questions = []
|
|
@@ -189,7 +199,7 @@ async def generate_session(request: GenerateSessionRequest, background_tasks: Ba
|
|
| 189 |
snap = query.count().get()
|
| 190 |
current_count = snap[0][0].value
|
| 191 |
if current_count < 50:
|
| 192 |
-
print(f"Stock is low ({current_count}).
|
| 193 |
await refill_specific_pool(t_id if s_type == 1 else -1, diff, 50 - current_count, s_type)
|
| 194 |
|
| 195 |
background_tasks.add_task(check_and_refill_background)
|
|
@@ -199,6 +209,38 @@ async def generate_session(request: GenerateSessionRequest, background_tasks: Ba
|
|
| 199 |
|
| 200 |
return {"session_id": request.sessionId, "questions": final_questions}
|
| 201 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 202 |
@app.post("/cleanup-audio")
|
| 203 |
async def cleanup_audio(request: CleanupRequest, background_tasks: BackgroundTasks):
|
| 204 |
def delete_job(urls):
|
|
@@ -210,22 +252,6 @@ async def cleanup_audio(request: CleanupRequest, background_tasks: BackgroundTas
|
|
| 210 |
background_tasks.add_task(delete_job, request.audioUrls)
|
| 211 |
return {"message": "Cloudinary cleanup process initiated."}
|
| 212 |
|
| 213 |
-
@app.get("/system-cleanup")
|
| 214 |
-
async def system_cleanup(background_tasks: BackgroundTasks):
|
| 215 |
-
"""Manually trigger a cleanup for broken records in Firestore"""
|
| 216 |
-
def run_cleanup():
|
| 217 |
-
docs = db.collection("questions_pool").get()
|
| 218 |
-
count = 0
|
| 219 |
-
for doc in docs:
|
| 220 |
-
data = doc.to_dict()
|
| 221 |
-
if not data.get("audio_url"):
|
| 222 |
-
db.collection("questions_pool").document(doc.id).delete()
|
| 223 |
-
count += 1
|
| 224 |
-
print(f"Manual cleanup finished. Removed {count} broken records.")
|
| 225 |
-
|
| 226 |
-
background_tasks.add_task(run_cleanup)
|
| 227 |
-
return {"message": "System cleanup task started in background."}
|
| 228 |
-
|
| 229 |
@app.get("/health")
|
| 230 |
async def health():
|
| 231 |
return {"status": "active", "ai_model_connected": client is not None}
|
|
|
|
| 105 |
if os.path.exists(filename): os.remove(filename)
|
| 106 |
return None
|
| 107 |
|
| 108 |
+
async def safe_generate(prompt, retries=5):
|
| 109 |
if client is None: raise Exception("AI Client is not initialized.")
|
| 110 |
for attempt in range(retries):
|
| 111 |
try:
|
|
|
|
| 113 |
return await loop.run_in_executor(None, lambda: client.predict(prompt=prompt, api_name="/generate_questions"))
|
| 114 |
except Exception as e:
|
| 115 |
if attempt == retries - 1: raise e
|
| 116 |
+
await asyncio.sleep(5)
|
| 117 |
|
| 118 |
def parse_question_output(raw_output: str):
|
| 119 |
if not raw_output: return None, None
|
|
|
|
| 156 |
"created_at": firestore.SERVER_TIMESTAMP
|
| 157 |
})
|
| 158 |
success_count += 1
|
| 159 |
+
print(f"Successfully added {track_text} question {success_count}/{count}")
|
| 160 |
+
await asyncio.sleep(2)
|
| 161 |
except Exception as e:
|
| 162 |
+
print(f"Refill error: {e}")
|
| 163 |
await asyncio.sleep(5)
|
| 164 |
|
| 165 |
# =========================================
|
|
|
|
| 167 |
# =========================================
|
| 168 |
@app.post("/generate-session")
|
| 169 |
async def generate_session(request: GenerateSessionRequest, background_tasks: BackgroundTasks):
|
| 170 |
+
s_type, t_id = request.sessionType, request.trackName
|
| 171 |
+
|
| 172 |
+
# FORCED ADJUSTMENT: Behavioral always uses difficulty 0
|
| 173 |
+
if s_type == 0:
|
| 174 |
+
diff = 0
|
| 175 |
+
else:
|
| 176 |
+
diff = request.difficultyLevel
|
| 177 |
+
|
| 178 |
query = db.collection("questions_pool").where(filter=FieldFilter("session_type", "==", s_type))
|
| 179 |
|
| 180 |
if s_type == 1: # Technical
|
| 181 |
if t_id is None: raise HTTPException(status_code=400, detail="trackName is required for technical sessions.")
|
| 182 |
query = query.where(filter=FieldFilter("track_id", "==", t_id)).where(filter=FieldFilter("difficulty", "==", diff))
|
| 183 |
+
else:
|
| 184 |
+
# For behavioral, we filter specifically by the forced difficulty 0
|
| 185 |
+
query = query.where(filter=FieldFilter("difficulty", "==", 0))
|
| 186 |
|
| 187 |
docs = query.limit(10).get()
|
| 188 |
final_questions = []
|
|
|
|
| 199 |
snap = query.count().get()
|
| 200 |
current_count = snap[0][0].value
|
| 201 |
if current_count < 50:
|
| 202 |
+
print(f"Stock for {('Behavioral' if s_type==0 else TECH_CATEGORIES[t_id])} is low ({current_count}). Refilling...")
|
| 203 |
await refill_specific_pool(t_id if s_type == 1 else -1, diff, 50 - current_count, s_type)
|
| 204 |
|
| 205 |
background_tasks.add_task(check_and_refill_background)
|
|
|
|
| 209 |
|
| 210 |
return {"session_id": request.sessionId, "questions": final_questions}
|
| 211 |
|
| 212 |
+
@app.get("/admin/prefill-all")
|
| 213 |
+
async def prefill_all(background_tasks: BackgroundTasks):
|
| 214 |
+
"""Checks and refills ALL tracks and behavioral questions to 50 items each."""
|
| 215 |
+
async def run_sync():
|
| 216 |
+
print("Starting Global Smart Prefill...")
|
| 217 |
+
|
| 218 |
+
# 1. Sync Behavioral
|
| 219 |
+
beh_snap = db.collection("questions_pool").where(filter=FieldFilter("session_type", "==", 0)).count().get()
|
| 220 |
+
beh_count = beh_snap[0][0].value
|
| 221 |
+
if beh_count < 50:
|
| 222 |
+
print(f"Syncing Behavioral: adding {50-beh_count}")
|
| 223 |
+
await refill_specific_pool(-1, 0, 50 - beh_count, 0)
|
| 224 |
+
|
| 225 |
+
# 2. Sync Technical
|
| 226 |
+
for t_id, t_name in TECH_CATEGORIES.items():
|
| 227 |
+
for d_id, d_name in DIFFICULTY_MAP.items():
|
| 228 |
+
query = db.collection("questions_pool")\
|
| 229 |
+
.where(filter=FieldFilter("session_type", "==", 1))\
|
| 230 |
+
.where(filter=FieldFilter("track_id", "==", t_id))\
|
| 231 |
+
.where(filter=FieldFilter("difficulty", "==", d_id))
|
| 232 |
+
|
| 233 |
+
snap = query.count().get()
|
| 234 |
+
current = snap[0][0].value
|
| 235 |
+
if current < 50:
|
| 236 |
+
print(f"Syncing {t_name} ({d_name}): adding {50-current}")
|
| 237 |
+
await refill_specific_pool(t_id, d_id, 50 - current, 1)
|
| 238 |
+
|
| 239 |
+
print("Global Smart Prefill Completed!")
|
| 240 |
+
|
| 241 |
+
background_tasks.add_task(run_sync)
|
| 242 |
+
return {"message": "Global prefill process started in the background."}
|
| 243 |
+
|
| 244 |
@app.post("/cleanup-audio")
|
| 245 |
async def cleanup_audio(request: CleanupRequest, background_tasks: BackgroundTasks):
|
| 246 |
def delete_job(urls):
|
|
|
|
| 252 |
background_tasks.add_task(delete_job, request.audioUrls)
|
| 253 |
return {"message": "Cloudinary cleanup process initiated."}
|
| 254 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 255 |
@app.get("/health")
|
| 256 |
async def health():
|
| 257 |
return {"status": "active", "ai_model_connected": client is not None}
|