|
|
|
|
|
import gradio as gr |
|
from dotenv import load_dotenv |
|
from clarifier_agent import clarifier_agent |
|
from research_manager import ResearchManagerAgent |
|
from agents import Runner |
|
from collections import defaultdict |
|
from datetime import datetime |
|
import time |
|
import logging |
|
|
|
load_dotenv(override=True) |
|
|
|
|
|
class RateLimiter: |
|
|
|
def __init__(self, max_requests=2, time_window=60, daily_quota=10): |
|
self.max_requests = max_requests |
|
self.time_window = time_window |
|
self.request_history = defaultdict(list) |
|
self.daily_quota = daily_quota |
|
self.daily_counts = defaultdict(lambda: {'date': self._today(), 'count': 0}) |
|
|
|
def _today(self): |
|
return datetime.utcnow().strftime('%Y-%m-%d') |
|
|
|
def is_rate_limited(self, user_id): |
|
now = time.time() |
|
self.request_history[user_id] = [ |
|
t for t in self.request_history[user_id] if now - t < self.time_window |
|
] |
|
if len(self.request_history[user_id]) >= self.max_requests: |
|
return True |
|
self.request_history[user_id].append(now) |
|
return False |
|
|
|
def is_quota_exceeded(self, user_id): |
|
today = self._today() |
|
user_quota = self.daily_counts[user_id] |
|
if user_quota['date'] != today: |
|
user_quota['date'] = today |
|
user_quota['count'] = 0 |
|
if user_quota['count'] >= self.daily_quota: |
|
return True |
|
user_quota['count'] += 1 |
|
self.daily_counts[user_id] = user_quota |
|
return False |
|
|
|
rate_limiter = RateLimiter() |
|
logger = logging.getLogger(__name__) |
|
logger.setLevel(logging.DEBUG) |
|
|
|
async def get_user_id(request: gr.Request = None): |
|
user_id = "default_user" |
|
if request is not None: |
|
try: |
|
forwarded = request.headers.get("X-Forwarded-For") |
|
if forwarded: |
|
user_id = forwarded.split(",")[0].strip() |
|
else: |
|
user_id = getattr(request.client, 'host', 'default_user') |
|
except Exception: |
|
pass |
|
logger.debug(f"[RateLimiter] user_id={user_id}") |
|
return user_id |
|
|
|
|
|
async def get_clarifying_questions(query, request: gr.Request = None): |
|
user_id = await get_user_id(request) |
|
if rate_limiter.is_rate_limited(user_id): |
|
return ["Rate limit exceeded. Please wait a minute."], "", "", "" |
|
if rate_limiter.is_quota_exceeded(user_id): |
|
return ["Daily quota exceeded. Try again tomorrow."], "", "", "" |
|
|
|
result = await Runner.run(clarifier_agent, input=query) |
|
return result.final_output.questions |
|
|
|
|
|
async def run_with_handoff(query, q1, q2, q3, a1, a2, a3, request: gr.Request = None): |
|
user_id = await get_user_id(request) |
|
if rate_limiter.is_rate_limited(user_id): |
|
yield "Rate limit exceeded. Please wait a minute." |
|
return |
|
if rate_limiter.is_quota_exceeded(user_id): |
|
yield "You have reached your daily quota. Try again tomorrow." |
|
return |
|
|
|
questions = [q1, q2, q3] |
|
answers = [a1, a2, a3] |
|
async for chunk in ResearchManagerAgent().run( |
|
query, |
|
questions, |
|
answers, |
|
): |
|
yield chunk |
|
|
|
with gr.Blocks(theme=gr.themes.Default(primary_hue="sky")) as ui: |
|
gr.Markdown("# π Deep Research Agent (Clarify β‘οΈ Research β‘οΈ Email)") |
|
|
|
query = gr.Textbox(label="π What would you like to research?") |
|
|
|
get_questions_btn = gr.Button("Generate Clarifying Questions", variant="primary") |
|
|
|
clar_q1 = gr.Textbox(label="Clarifying Question 1", interactive=False) |
|
clar_q2 = gr.Textbox(label="Clarifying Question 2", interactive=False) |
|
clar_q3 = gr.Textbox(label="Clarifying Question 3", interactive=False) |
|
|
|
answer_1 = gr.Textbox(label="Your Answer to Q1") |
|
answer_2 = gr.Textbox(label="Your Answer to Q2") |
|
answer_3 = gr.Textbox(label="Your Answer to Q3") |
|
|
|
submit_answers_btn = gr.Button("Submit & Run Full Research") |
|
report = gr.Markdown(label="π Research Report") |
|
|
|
|
|
query.submit( |
|
fn=get_clarifying_questions, |
|
inputs=query, |
|
outputs=[clar_q1, clar_q2, clar_q3] |
|
).then(lambda *_: "", outputs=report) |
|
|
|
get_questions_btn.click( |
|
fn=get_clarifying_questions, |
|
inputs=query, |
|
outputs=[clar_q1, clar_q2, clar_q3] |
|
).then(lambda *_: "", outputs=report) |
|
|
|
|
|
submit_answers_btn.click( |
|
fn=run_with_handoff, |
|
inputs=[query, clar_q1, clar_q2, clar_q3, answer_1, answer_2, answer_3], |
|
outputs=report |
|
) |
|
|
|
ui.launch(inbrowser=True) |
|
|