Spaces:
Sleeping
Sleeping
Commit ·
e72b508
1
Parent(s): b98f6f7
- app/api/auth.py +17 -0
- app/services/evaluator.py +46 -30
app/api/auth.py
CHANGED
|
@@ -81,6 +81,23 @@ async def handle_auth(request: Request, req: AuthRequest):
|
|
| 81 |
if "token" in data:
|
| 82 |
log.info("[auth] token present (len=%d)", len(str(data["token"])))
|
| 83 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 84 |
return data
|
| 85 |
|
| 86 |
|
|
|
|
| 81 |
if "token" in data:
|
| 82 |
log.info("[auth] token present (len=%d)", len(str(data["token"])))
|
| 83 |
|
| 84 |
+
# Normalize response: promote access_token / session to top level
|
| 85 |
+
# if the edge function wrapped them inside a "data" key
|
| 86 |
+
if "access_token" not in data and "session" not in data:
|
| 87 |
+
nested = data.get("data")
|
| 88 |
+
if isinstance(nested, dict):
|
| 89 |
+
if "access_token" in nested:
|
| 90 |
+
data["access_token"] = nested["access_token"]
|
| 91 |
+
log.info("[auth] Promoted access_token from data.%s", "access_token")
|
| 92 |
+
if "session" in nested and isinstance(nested["session"], dict):
|
| 93 |
+
data["session"] = nested["session"]
|
| 94 |
+
log.info("[auth] Promoted session from data.session")
|
| 95 |
+
if "user" in nested and isinstance(nested["user"], dict) and "user_id" not in data:
|
| 96 |
+
uid = nested["user"].get("id") or nested["user"].get("user_id") or nested["user"].get("sub")
|
| 97 |
+
if uid:
|
| 98 |
+
data["user_id"] = uid
|
| 99 |
+
log.info("[auth] Promoted user_id from data.user")
|
| 100 |
+
|
| 101 |
return data
|
| 102 |
|
| 103 |
|
app/services/evaluator.py
CHANGED
|
@@ -168,39 +168,55 @@ async def ai_evaluate_code_fix(challenge_id: str, fixed_code: str, team_role: st
|
|
| 168 |
|
| 169 |
print(f"[ai-evaluate-code-fix] challenge={challenge_id} lang={language}")
|
| 170 |
|
| 171 |
-
|
| 172 |
-
|
| 173 |
-
|
| 174 |
-
|
| 175 |
-
|
| 176 |
-
|
| 177 |
-
|
| 178 |
-
|
| 179 |
-
|
| 180 |
-
|
| 181 |
-
|
| 182 |
-
|
| 183 |
-
|
| 184 |
-
|
| 185 |
-
|
| 186 |
-
|
| 187 |
-
|
| 188 |
-
|
| 189 |
-
|
| 190 |
-
|
|
|
|
|
|
|
| 191 |
|
| 192 |
-
|
| 193 |
-
|
|
|
|
| 194 |
|
| 195 |
-
|
| 196 |
-
|
| 197 |
-
|
| 198 |
-
|
| 199 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 200 |
|
| 201 |
-
|
| 202 |
-
return {"secured": False, "feedback": "AI response was not a JSON object"}
|
| 203 |
-
return evaluation
|
| 204 |
|
| 205 |
|
| 206 |
async def evaluate_code_fix(req, background_tasks: BackgroundTasks) -> dict:
|
|
|
|
| 168 |
|
| 169 |
print(f"[ai-evaluate-code-fix] challenge={challenge_id} lang={language}")
|
| 170 |
|
| 171 |
+
async def _call_ai(api_url: str, api_key: str, model: str) -> Optional[dict]:
|
| 172 |
+
"""Call an AI provider and return parsed evaluation dict, or None on failure."""
|
| 173 |
+
try:
|
| 174 |
+
async with httpx.AsyncClient(timeout=60) as client:
|
| 175 |
+
resp = await client.post(
|
| 176 |
+
api_url,
|
| 177 |
+
json={
|
| 178 |
+
"model": model,
|
| 179 |
+
"messages": [
|
| 180 |
+
{"role": "system", "content": "أنت مقيّم أكواد أمني. أعد JSON فقط."},
|
| 181 |
+
{"role": "user", "content": eval_prompt},
|
| 182 |
+
],
|
| 183 |
+
"temperature": 0.2,
|
| 184 |
+
"max_tokens": 1024,
|
| 185 |
+
},
|
| 186 |
+
headers={
|
| 187 |
+
"Content-Type": "application/json",
|
| 188 |
+
"Authorization": f"Bearer {api_key}",
|
| 189 |
+
},
|
| 190 |
+
)
|
| 191 |
+
except Exception:
|
| 192 |
+
return None
|
| 193 |
|
| 194 |
+
if resp.status_code != 200:
|
| 195 |
+
print(f"[ai-evaluate-code-fix] {api_url} returned {resp.status_code}")
|
| 196 |
+
return None
|
| 197 |
|
| 198 |
+
try:
|
| 199 |
+
content = resp.json()["choices"][0]["message"]["content"]
|
| 200 |
+
evaluation = parse_json_safe(content)
|
| 201 |
+
except Exception:
|
| 202 |
+
return None
|
| 203 |
+
|
| 204 |
+
if not isinstance(evaluation, dict):
|
| 205 |
+
return None
|
| 206 |
+
return evaluation
|
| 207 |
+
|
| 208 |
+
# Tier 1: Groq
|
| 209 |
+
evaluation = await _call_ai(GROQ_API_URL, GROQ_API_KEY, "llama-3.3-70b-versatile")
|
| 210 |
+
if evaluation is not None:
|
| 211 |
+
return evaluation
|
| 212 |
+
|
| 213 |
+
# Tier 2: Mistral (fallback)
|
| 214 |
+
print("[ai-evaluate-code-fix] Groq failed, trying Mistral...")
|
| 215 |
+
evaluation = await _call_ai(MISTRAL_API_URL, MISTRAL_API_KEY, MISTRAL_MODEL)
|
| 216 |
+
if evaluation is not None:
|
| 217 |
+
return evaluation
|
| 218 |
|
| 219 |
+
return {"secured": False, "feedback": "AI evaluation error: 401"}
|
|
|
|
|
|
|
| 220 |
|
| 221 |
|
| 222 |
async def evaluate_code_fix(req, background_tasks: BackgroundTasks) -> dict:
|