llm-ready-data / app /api /v1 /web_search.py
light-infer-chat's picture
ok
f02b0c0
Raw
History Blame Contribute Delete
7.77 kB
from __future__ import annotations
import time
from typing import Optional
from fastapi import APIRouter, Depends, Query
from app.api.deps import require_auth
from app.core.logger import get_logger
from app.models.schemas import (
WebSearchAutocompleteRequest,
WebSearchAutocompleteResponse,
WebSearchConfigResponse,
WebSearchEngineDescriptionsResponse,
WebSearchRequest,
WebSearchResponse,
WebSearchStatsResponse,
)
from app.services.web_search_service import WebSearchService
router = APIRouter()
_logger = get_logger(__name__)
def get_search_service() -> WebSearchService:
return WebSearchService()
@router.post(
"/web-search",
response_model=WebSearchResponse,
summary="Search the web using SearXNG meta search engine",
)
async def web_search_post(
body: WebSearchRequest,
token: str = Depends(require_auth),
svc: WebSearchService = Depends(get_search_service),
) -> WebSearchResponse:
_logger.info("Web search (POST): q=%s, cat=%s", body.q, body.categories)
start = time.perf_counter()
result = await svc.search(
query=body.q,
categories=body.categories,
language=body.language,
pageno=body.pageno,
time_range=body.time_range,
safesearch=body.safesearch,
engines=body.engines,
max_results=body.max_results,
)
elapsed_ms = round((time.perf_counter() - start) * 1000, 2)
return WebSearchResponse(
success=result.get("success", False),
time_ms=elapsed_ms,
query=result.get("query", body.q),
number_of_results=result.get("number_of_results", 0),
results=result.get("results", []),
suggestions=result.get("suggestions", []),
infoboxes=result.get("infoboxes", []),
error=result.get("error"),
)
@router.get(
"/web-search",
response_model=WebSearchResponse,
summary="Search the web via query parameters",
)
async def web_search_get(
q: str = Query(..., min_length=1, max_length=500, description="Search query"),
categories: str = Query("general", description="Comma-separated categories"),
language: str = Query("en", description="Language code"),
pageno: int = Query(1, ge=1, le=100, description="Page number"),
time_range: Optional[str] = Query(None, description="Time range: day, week, month, year"),
safesearch: int = Query(0, ge=0, le=2, description="Safe search level"),
engines: Optional[str] = Query(None, description="Comma-separated engines"),
max_results: int = Query(10, ge=1, le=50, description="Max results"),
token: str = Depends(require_auth),
svc: WebSearchService = Depends(get_search_service),
) -> WebSearchResponse:
_logger.info("Web search (GET): q=%s, cat=%s", q, categories)
start = time.perf_counter()
result = await svc.search(
query=q,
categories=categories,
language=language,
pageno=pageno,
time_range=time_range,
safesearch=safesearch,
engines=engines,
max_results=max_results,
)
elapsed_ms = round((time.perf_counter() - start) * 1000, 2)
return WebSearchResponse(
success=result.get("success", False),
time_ms=elapsed_ms,
query=result.get("query", q),
number_of_results=result.get("number_of_results", 0),
results=result.get("results", []),
suggestions=result.get("suggestions", []),
infoboxes=result.get("infoboxes", []),
error=result.get("error"),
)
@router.get(
"/web-search/config",
response_model=WebSearchConfigResponse,
summary="Get SearXNG instance configuration (engines, categories, plugins)",
)
async def web_search_config(
token: str = Depends(require_auth),
svc: WebSearchService = Depends(get_search_service),
) -> WebSearchConfigResponse:
_logger.info("Web search config request")
start = time.perf_counter()
result = await svc.get_config()
elapsed_ms = round((time.perf_counter() - start) * 1000, 2)
return WebSearchConfigResponse(
success=result.get("success", False),
time_ms=elapsed_ms,
instance_name=result.get("instance_name"),
version=result.get("version"),
engines=result.get("engines", []),
categories=result.get("categories", []),
plugins=result.get("plugins", []),
error=result.get("error"),
)
@router.get(
"/web-search/autocomplete",
response_model=WebSearchAutocompleteResponse,
summary="Get autocomplete suggestions for a query prefix",
)
async def web_search_autocomplete(
q: str = Query(..., min_length=1, max_length=200, description="Query prefix"),
token: str = Depends(require_auth),
svc: WebSearchService = Depends(get_search_service),
) -> WebSearchAutocompleteResponse:
_logger.info("Web search autocomplete: q=%s", q)
start = time.perf_counter()
result = await svc.autocomplete(q)
elapsed_ms = round((time.perf_counter() - start) * 1000, 2)
return WebSearchAutocompleteResponse(
success=result.get("success", False),
time_ms=elapsed_ms,
query=result.get("query", q),
suggestions=result.get("suggestions", []),
error=result.get("error"),
)
@router.post(
"/web-search/autocomplete",
response_model=WebSearchAutocompleteResponse,
summary="Get autocomplete suggestions (POST)",
)
async def web_search_autocomplete_post(
body: WebSearchAutocompleteRequest,
token: str = Depends(require_auth),
svc: WebSearchService = Depends(get_search_service),
) -> WebSearchAutocompleteResponse:
_logger.info("Web search autocomplete (POST): q=%s", body.q)
start = time.perf_counter()
result = await svc.autocomplete(body.q)
elapsed_ms = round((time.perf_counter() - start) * 1000, 2)
return WebSearchAutocompleteResponse(
success=result.get("success", False),
time_ms=elapsed_ms,
query=result.get("query", body.q),
suggestions=result.get("suggestions", []),
error=result.get("error"),
)
@router.get(
"/web-search/engine-descriptions",
response_model=WebSearchEngineDescriptionsResponse,
summary="Get all SearXNG engine descriptions",
)
async def web_search_engine_descriptions(
token: str = Depends(require_auth),
svc: WebSearchService = Depends(get_search_service),
) -> WebSearchEngineDescriptionsResponse:
_logger.info("Web search engine descriptions")
start = time.perf_counter()
result = await svc.get_engine_descriptions()
elapsed_ms = round((time.perf_counter() - start) * 1000, 2)
return WebSearchEngineDescriptionsResponse(
success=result.get("success", False),
time_ms=elapsed_ms,
engines=result.get("engines"),
error=result.get("error"),
)
@router.get(
"/web-search/stats",
response_model=WebSearchStatsResponse,
summary="Get SearXNG instance statistics",
)
async def web_search_stats(
token: str = Depends(require_auth),
svc: WebSearchService = Depends(get_search_service),
) -> WebSearchStatsResponse:
_logger.info("Web search stats request")
start = time.perf_counter()
result = await svc.get_stats()
elapsed_ms = round((time.perf_counter() - start) * 1000, 2)
return WebSearchStatsResponse(
success=result.get("success", False),
time_ms=elapsed_ms,
stats=result.get("stats"),
error=result.get("error"),
)
@router.get(
"/web-search/health",
summary="Check SearXNG instance health",
)
async def web_search_health(
token: str = Depends(require_auth),
svc: WebSearchService = Depends(get_search_service),
) -> dict:
_logger.info("Web search health check")
result = await svc.health()
return result