duckduckgo / app.py
Almaatla's picture
Upload 2 files
960de14 verified
from fastapi import FastAPI, Request, HTTPException
from fastapi.responses import HTMLResponse
from fastapi.templating import Jinja2Templates
from duckduckgo_search import DDGS
import logging
import uvicorn
from collections import deque
from datetime import datetime
app = FastAPI()
templates = Jinja2Templates(directory="templates")
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
# Global in-memory queue to store search results
# Stores dicts: {"query": str, "results": list, "timestamp": str}
response_queue = deque()
@app.get("/", response_class=HTMLResponse)
async def read_root(request: Request):
return templates.TemplateResponse("index.html", {"request": request})
@app.post("/api/queue")
async def queue_search(q: str):
"""
Performs the search immediately and pushes the result onto the in-memory queue.
"""
if not q:
return {"error": "Query parameter 'q' is required"}
try:
logger.info(f"Processing search for: {q}")
with DDGS() as ddgs:
# text() returns an iterator/generator in newer versions, convert to list
# backend="html" is removed in v7+
results = list(ddgs.text(q, max_results=10))
logger.info(f"Found {len(results)} results for '{q}'")
response_data = {
"query": q,
"results": results,
"timestamp": datetime.now().isoformat()
}
response_queue.append(response_data)
return {
"status": "queued",
"message": "Search completed and added to queue",
"queue_size": len(response_queue)
}
except Exception as e:
logger.error(f"Search error: {e}")
raise HTTPException(status_code=500, detail=str(e))
@app.get("/api/pop")
async def pop_result():
"""
Retrieves the next result from the queue (FIFO).
Returns the result and the number of remaining items.
"""
if not response_queue:
return {
"status": "empty",
"message": "No results in queue",
"remaining": 0
}
# Using popleft for FIFO (First-In-First-Out) behavior
# If you strictly wanted a stack (LIFO), use pop() instead
result = response_queue.popleft()
return {
"status": "success",
"data": result,
"remaining": len(response_queue)
}
@app.get("/api/status")
async def get_status():
"""Helper endpoint to check queue size"""
return {"queue_size": len(response_queue)}
# Keep the original endpoint for backward compatibility if needed,
# or it can be ignored.
@app.get("/api/search")
async def search(q: str):
if not q: return {"results": []}
try:
# DDGS context manager is recommended
with DDGS() as ddgs:
# text() returns an iterator/generator in newer versions, convert to list
# max_results is valid in newer versions of duckduckgo_search
results = list(ddgs.text(q, max_results=10))
logger.info(f"Direct search found {len(results)} results for '{q}'")
return {"results": results}
except Exception as e:
logger.error(f"Search error: {e}")
# In production we might want to hide the exact error, but for dev this is fine
raise HTTPException(status_code=500, detail=str(e))
if __name__ == "__main__":
uvicorn.run(app, host="0.0.0.0", port=7860)