Spaces:
Running
Running
refactor: decouple usage increment from rate limit checks, add session refresh error handling.
Browse files- quiz_generator/routes.py +6 -2
- rag/rag.py +1 -1
- store.py +41 -11
- summary_generator/routes.py +6 -2
quiz_generator/routes.py
CHANGED
|
@@ -3,7 +3,7 @@ import logging
|
|
| 3 |
from fastapi import APIRouter, HTTPException, Depends
|
| 4 |
from typing import Optional
|
| 5 |
from src.quiz_generator.quiz import smart_quiz_generator
|
| 6 |
-
from src.store import get_material, get_chunks, get_summary, save_quiz, get_quizzes, save_quiz_result, get_quiz_results,
|
| 7 |
from src.dependencies import get_current_user_id, get_current_user
|
| 8 |
from src.config import settings
|
| 9 |
from .schemas import QuizRequest, QuizResponse, SaveQuizResultRequest
|
|
@@ -35,7 +35,7 @@ async def generate_quiz(
|
|
| 35 |
):
|
| 36 |
# Rate limit check
|
| 37 |
user_email = current_user.get("email") if isinstance(current_user, dict) else getattr(current_user, "email", None)
|
| 38 |
-
if not
|
| 39 |
raise HTTPException(429, "Daily limit of 20 requests reached. Come back tomorrow!")
|
| 40 |
|
| 41 |
body.difficulty = body.difficulty.capitalize()
|
|
@@ -109,6 +109,10 @@ async def generate_quiz(
|
|
| 109 |
model_name=settings.model_name,
|
| 110 |
)
|
| 111 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 112 |
return QuizResponse(quiz=quiz, quiz_id=saved["id"])
|
| 113 |
except ValueError as e:
|
| 114 |
logger.warning(f"Validation error in generate_quiz: {str(e)}")
|
|
|
|
| 3 |
from fastapi import APIRouter, HTTPException, Depends
|
| 4 |
from typing import Optional
|
| 5 |
from src.quiz_generator.quiz import smart_quiz_generator
|
| 6 |
+
from src.store import get_material, get_chunks, get_summary, save_quiz, get_quizzes, save_quiz_result, get_quiz_results, check_daily_limit, increment_daily_usage, ADMIN_EMAILS
|
| 7 |
from src.dependencies import get_current_user_id, get_current_user
|
| 8 |
from src.config import settings
|
| 9 |
from .schemas import QuizRequest, QuizResponse, SaveQuizResultRequest
|
|
|
|
| 35 |
):
|
| 36 |
# Rate limit check
|
| 37 |
user_email = current_user.get("email") if isinstance(current_user, dict) else getattr(current_user, "email", None)
|
| 38 |
+
if not check_daily_limit(user_id, email=user_email, limit=20):
|
| 39 |
raise HTTPException(429, "Daily limit of 20 requests reached. Come back tomorrow!")
|
| 40 |
|
| 41 |
body.difficulty = body.difficulty.capitalize()
|
|
|
|
| 109 |
model_name=settings.model_name,
|
| 110 |
)
|
| 111 |
|
| 112 |
+
# Only increment limit if the quiz was generated successfully and saved without error
|
| 113 |
+
if not (user_email and user_email in ADMIN_EMAILS):
|
| 114 |
+
increment_daily_usage(user_id)
|
| 115 |
+
|
| 116 |
return QuizResponse(quiz=quiz, quiz_id=saved["id"])
|
| 117 |
except ValueError as e:
|
| 118 |
logger.warning(f"Validation error in generate_quiz: {str(e)}")
|
rag/rag.py
CHANGED
|
@@ -163,7 +163,7 @@ def get_quiz_llm():
|
|
| 163 |
model=settings.model_name,
|
| 164 |
api_key=settings.gemini_api_key,
|
| 165 |
temperature=0.3,
|
| 166 |
-
max_output_tokens=
|
| 167 |
timeout=300,
|
| 168 |
)
|
| 169 |
|
|
|
|
| 163 |
model=settings.model_name,
|
| 164 |
api_key=settings.gemini_api_key,
|
| 165 |
temperature=0.3,
|
| 166 |
+
max_output_tokens=12000,
|
| 167 |
timeout=300,
|
| 168 |
)
|
| 169 |
|
store.py
CHANGED
|
@@ -646,16 +646,11 @@ def get_or_create_memory(memory_id: Optional[str] = None, seed_messages: list[di
|
|
| 646 |
return mem, mid
|
| 647 |
|
| 648 |
|
| 649 |
-
def
|
| 650 |
"""
|
| 651 |
-
|
| 652 |
-
|
| 653 |
-
|
| 654 |
-
Bypasses the database RPC (which runs in UTC and resets at 3 AM Egypt time)
|
| 655 |
-
to perform the date check in python using Egypt local timezone (UTC+3),
|
| 656 |
-
ensuring limits reset exactly at 12 AM Egypt time.
|
| 657 |
"""
|
| 658 |
-
# Guard: never apply limit to admin emails
|
| 659 |
if email and email in ADMIN_EMAILS:
|
| 660 |
return True
|
| 661 |
|
|
@@ -678,16 +673,51 @@ def check_and_increment_daily_limit(user_id: str, email: Optional[str] = None, l
|
|
| 678 |
count = 0
|
| 679 |
if count >= limit:
|
| 680 |
return False
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 681 |
|
| 682 |
_robust_execute(
|
| 683 |
_table_supabase("profiles")
|
| 684 |
.update({"daily_requests": count + 1, "last_request_date": today})
|
| 685 |
.eq("id", user_id)
|
| 686 |
)
|
| 687 |
-
return True
|
| 688 |
except Exception as e:
|
| 689 |
-
logger.error(f"
|
| 690 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 691 |
|
| 692 |
|
| 693 |
def get_usage(user_id: str) -> dict:
|
|
|
|
| 646 |
return mem, mid
|
| 647 |
|
| 648 |
|
| 649 |
+
def check_daily_limit(user_id: str, email: Optional[str] = None, limit: int = 20) -> bool:
|
| 650 |
"""
|
| 651 |
+
Checks if the user is under the daily limit. Returns True if allowed, False if exceeded.
|
| 652 |
+
Does NOT increment the count.
|
|
|
|
|
|
|
|
|
|
|
|
|
| 653 |
"""
|
|
|
|
| 654 |
if email and email in ADMIN_EMAILS:
|
| 655 |
return True
|
| 656 |
|
|
|
|
| 673 |
count = 0
|
| 674 |
if count >= limit:
|
| 675 |
return False
|
| 676 |
+
return True
|
| 677 |
+
except Exception as e:
|
| 678 |
+
logger.error(f"Rate limit check failed: {e}")
|
| 679 |
+
return True
|
| 680 |
+
|
| 681 |
+
|
| 682 |
+
def increment_daily_usage(user_id: str) -> None:
|
| 683 |
+
"""
|
| 684 |
+
Increments the daily request count for the user.
|
| 685 |
+
"""
|
| 686 |
+
today = _get_today_date_str()
|
| 687 |
+
try:
|
| 688 |
+
result = _robust_execute(
|
| 689 |
+
_table_supabase("profiles")
|
| 690 |
+
.select("daily_requests, last_request_date")
|
| 691 |
+
.eq("id", user_id)
|
| 692 |
+
)
|
| 693 |
+
if not result.data:
|
| 694 |
+
return
|
| 695 |
+
profile = result.data[0] if result.data else None
|
| 696 |
+
if not profile:
|
| 697 |
+
return
|
| 698 |
+
|
| 699 |
+
last_date = profile.get("last_request_date")
|
| 700 |
+
count = profile.get("daily_requests", 0) or 0
|
| 701 |
+
if last_date != today:
|
| 702 |
+
count = 0
|
| 703 |
|
| 704 |
_robust_execute(
|
| 705 |
_table_supabase("profiles")
|
| 706 |
.update({"daily_requests": count + 1, "last_request_date": today})
|
| 707 |
.eq("id", user_id)
|
| 708 |
)
|
|
|
|
| 709 |
except Exception as e:
|
| 710 |
+
logger.error(f"Failed to increment daily usage: {e}")
|
| 711 |
+
|
| 712 |
+
|
| 713 |
+
def check_and_increment_daily_limit(user_id: str, email: Optional[str] = None, limit: int = 20) -> bool:
|
| 714 |
+
"""
|
| 715 |
+
Check limit and increment if allowed. Deprecated/kept for compatibility.
|
| 716 |
+
"""
|
| 717 |
+
allowed = check_daily_limit(user_id, email, limit)
|
| 718 |
+
if allowed and not (email and email in ADMIN_EMAILS):
|
| 719 |
+
increment_daily_usage(user_id)
|
| 720 |
+
return allowed
|
| 721 |
|
| 722 |
|
| 723 |
def get_usage(user_id: str) -> dict:
|
summary_generator/routes.py
CHANGED
|
@@ -4,7 +4,7 @@ import logging
|
|
| 4 |
from fastapi import APIRouter, HTTPException, Depends
|
| 5 |
|
| 6 |
from src.summary_generator.summary import summarizer, web_summarizer
|
| 7 |
-
from src.store import get_material, get_chunks, save_summary, get_summary as get_stored_summary,
|
| 8 |
from src.dependencies import get_current_user_id, get_current_user
|
| 9 |
from src.config import settings
|
| 10 |
from .schemas import SummarizeRequest, SummarizeResponse
|
|
@@ -22,7 +22,7 @@ async def generate_summary(
|
|
| 22 |
):
|
| 23 |
# Rate limit check
|
| 24 |
user_email = current_user.get("email") if isinstance(current_user, dict) else getattr(current_user, "email", None)
|
| 25 |
-
if not
|
| 26 |
raise HTTPException(429, "Daily limit of 20 requests reached. Come back tomorrow!")
|
| 27 |
|
| 28 |
mat = get_material(body.material_id)
|
|
@@ -58,6 +58,10 @@ async def generate_summary(
|
|
| 58 |
model_name=settings.model_name,
|
| 59 |
)
|
| 60 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 61 |
return SummarizeResponse(summary=summary, time_taken=elapsed)
|
| 62 |
except Exception as e:
|
| 63 |
raise HTTPException(500, f"Summarization failed: {e}")
|
|
|
|
| 4 |
from fastapi import APIRouter, HTTPException, Depends
|
| 5 |
|
| 6 |
from src.summary_generator.summary import summarizer, web_summarizer
|
| 7 |
+
from src.store import get_material, get_chunks, save_summary, get_summary as get_stored_summary, check_daily_limit, increment_daily_usage
|
| 8 |
from src.dependencies import get_current_user_id, get_current_user
|
| 9 |
from src.config import settings
|
| 10 |
from .schemas import SummarizeRequest, SummarizeResponse
|
|
|
|
| 22 |
):
|
| 23 |
# Rate limit check
|
| 24 |
user_email = current_user.get("email") if isinstance(current_user, dict) else getattr(current_user, "email", None)
|
| 25 |
+
if not check_daily_limit(user_id, email=user_email, limit=20):
|
| 26 |
raise HTTPException(429, "Daily limit of 20 requests reached. Come back tomorrow!")
|
| 27 |
|
| 28 |
mat = get_material(body.material_id)
|
|
|
|
| 58 |
model_name=settings.model_name,
|
| 59 |
)
|
| 60 |
|
| 61 |
+
# Only increment limit if the summary was generated successfully and saved without error
|
| 62 |
+
if not (user_email and user_email in settings.admin_emails if hasattr(settings, "admin_emails") else False):
|
| 63 |
+
increment_daily_usage(user_id)
|
| 64 |
+
|
| 65 |
return SummarizeResponse(summary=summary, time_taken=elapsed)
|
| 66 |
except Exception as e:
|
| 67 |
raise HTTPException(500, f"Summarization failed: {e}")
|