pro580's picture
Fix rate limiter to use X-Forwarded-For header behind HF proxy
e323466
Raw
History Blame Contribute Delete
3.81 kB
"""Gradio web interface for the two-stage customer support agent."""
import sys
import time
import collections
import threading
from pathlib import Path
import yaml
# Rate limiter: 5 requests per 60 seconds per IP
_rate_lock = threading.Lock()
_request_log: dict[str, collections.deque] = collections.defaultdict(lambda: collections.deque())
RATE_LIMIT = 5
RATE_WINDOW = 60 # seconds
def _is_rate_limited(ip: str) -> bool:
now = time.time()
with _rate_lock:
dq = _request_log[ip]
while dq and now - dq[0] > RATE_WINDOW:
dq.popleft()
if len(dq) >= RATE_LIMIT:
return True
dq.append(now)
return False
sys.path.insert(0, str(Path(__file__).parent))
with open("config/config.yaml") as f:
cfg = yaml.safe_load(f)
print("Loading models... (takes ~10 seconds on first run)")
try:
from src.pipeline.agent import build_agent
agent = build_agent(cfg)
LOAD_ERROR = None
print("Models loaded. Ready.")
except Exception as e:
agent = None
LOAD_ERROR = str(e)
print(f"WARNING: Could not load agent: {e}")
import gradio as gr
def handle_query(query: str, request: gr.Request):
"""Run the full pipeline on a user query and return display values."""
forwarded_for = request.headers.get("x-forwarded-for") or request.headers.get("x-real-ip")
ip = forwarded_for.split(",")[0].strip() if forwarded_for else (request.client.host if request.client else "unknown")
if _is_rate_limited(ip):
return "-", "-", f"Rate limit reached: max {RATE_LIMIT} requests per {RATE_WINDOW}s. Please wait.", "⏳ Rate limited"
if agent is None:
return "Error", "-", f"Agent failed to load: {LOAD_ERROR}", "❌ Setup error"
if not query.strip():
return "-", "-", "Please type a question first.", "-"
result = agent.resolve(query)
intent = result["predicted_intent"].replace("_", " ").title()
confidence = f"{result['confidence']:.0%}"
response = result["response"]
if result["requires_human"]:
status = "⚠️ Low confidence - consider human review"
else:
status = "✅ High confidence"
return intent, confidence, response, status
with gr.Blocks(title="Customer Support Agent", theme=gr.themes.Soft()) as demo:
gr.Markdown("""
# Customer Support Agent
**Two-stage ML pipeline:** fine-tuned DistilBERT intent classifier -> Claude response generator
Type a customer support question and the agent will:
1. Classify what kind of issue it is
2. Generate a relevant support response
""")
query_input = gr.Textbox(
label="Customer Query",
placeholder="e.g. My bill seems wrong, I was charged twice this month",
lines=3,
)
submit_btn = gr.Button("Get Response", variant="primary")
with gr.Row():
intent_out = gr.Textbox(label="Detected Intent", scale=2)
confidence_out = gr.Textbox(label="Confidence", scale=1)
status_out = gr.Textbox(label="Status", scale=2)
response_out = gr.Textbox(label="Generated Response", lines=7)
for trigger in [submit_btn.click, query_input.submit]:
trigger(
fn=handle_query,
inputs=query_input,
outputs=[intent_out, confidence_out, response_out, status_out],
)
gr.Markdown("""
---
**Try these examples:**
- *"I can't log in to my account, I forgot my password"*
- *"How much does the premium plan cost?"*
- *"The app keeps crashing every time I open it"*
- *"I want to cancel my subscription"*
**Intent categories:** Billing Issue · Account Access · Technical Support ·
Product Inquiry · Cancellation Request · General Feedback
""")
if __name__ == "__main__":
demo.launch()