Spaces:
Running
Running
| """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() | |