import gradio as gr from datetime import datetime, timedelta from collections import defaultdict import threading rate_limit_data = defaultdict(list) lock = threading.Lock() UNAUTH_RATE_LIMIT = 3 AUTH_RATE_LIMIT = 30 RATE_LIMIT_WINDOW = 60 def clean_old_entries(user_id): """Remove entries older than the rate limit window""" current_time = datetime.now() cutoff_time = current_time - timedelta(seconds=RATE_LIMIT_WINDOW) rate_limit_data[user_id] = [ timestamp for timestamp in rate_limit_data[user_id] if timestamp > cutoff_time ] def get_user_identifier(profile: gr.OAuthProfile | None, request: gr.Request) -> tuple[str, bool]: """Get user identifier and whether they're authenticated""" if profile is not None: return profile.username, True else: if request: return f"ip_{request.client.host}", False return "ip_unknown", False def check_rate_limit(user_id: str, is_authenticated: bool) -> tuple[bool, int, int]: """ Check if user has exceeded rate limit Returns: (can_proceed, clicks_used, max_clicks) """ with lock: clean_old_entries(user_id) max_clicks = AUTH_RATE_LIMIT if is_authenticated else UNAUTH_RATE_LIMIT clicks_used = len(rate_limit_data[user_id]) can_proceed = clicks_used < max_clicks return can_proceed, clicks_used, max_clicks def add_click(user_id: str): """Add a click timestamp for the user""" with lock: rate_limit_data[user_id].append(datetime.now()) def update_status(profile: gr.OAuthProfile | None, request: gr.Request) -> str: """Update the status message showing current rate limit info""" user_id, is_authenticated = get_user_identifier(profile, request) _, clicks_used, max_clicks = check_rate_limit(user_id, is_authenticated) if is_authenticated: return f"✅ You are logged in as '{profile.username}'. You have clicked {clicks_used} times this minute. You have {max_clicks} total clicks per minute." # type: ignore else: return f"⚠️ You are not logged in. You have clicked {clicks_used} times this minute. You have {max_clicks} total clicks per minute." def run_action(profile: gr.OAuthProfile | None, request: gr.Request) -> tuple[str, str]: """Handle the run button click with rate limiting""" user_id, is_authenticated = get_user_identifier(profile, request) can_proceed, clicks_used, max_clicks = check_rate_limit(user_id, is_authenticated) if not can_proceed: result = f"❌ Rate limit exceeded! You've used all {max_clicks} clicks for this minute. Please wait before trying again." status = update_status(profile, request) return result, status add_click(user_id) _, new_clicks_used, _ = check_rate_limit(user_id, is_authenticated) result = f"✅ Action executed successfully! (Click #{new_clicks_used})" status = update_status(profile, request) return result, status with gr.Blocks(title="Rate Limiting Demo") as demo: gr.Markdown("# Rate Limiting Demo App") gr.Markdown("This app demonstrates rate limiting based on authentication status.") gr.LoginButton() status_text = gr.Markdown("Loading status...") with gr.Row(): run_btn = gr.Button("🚀 Run Action", variant="primary", scale=1) result_text = gr.Markdown("") demo.load(update_status, inputs=None, outputs=status_text) run_btn.click( run_action, inputs=None, outputs=[result_text, status_text] ) gr.Markdown("---") gr.Markdown(""" ### Rate Limits: - **Not logged in:** 3 clicks per minute (based on IP address) - **Logged in:** 30 clicks per minute (based on HF username) ### How it works: - Click the **Login** button to authenticate with Hugging Face - Click the **Run Action** button to test the rate limiting - The system tracks your clicks over a rolling 1-minute window """) if __name__ == "__main__": demo.launch()