app_views.py 1.5 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344
  1. import json
  2. import threading
  3. import time
  4. from datetime import datetime
  5. from pathlib import Path
  6. from fastapi import FastAPI
  7. from pydantic import BaseModel, Field
  8. class SearchRequest(BaseModel):
  9. keywords: list[str] = Field(min_length=1, max_length=100)
  10. class JsonlQueryLogger:
  11. def __init__(self, path: str | Path):
  12. self.path = Path(path)
  13. self.path.parent.mkdir(parents=True, exist_ok=True)
  14. self._lock = threading.Lock()
  15. def write(self, query: str, candidates: list[dict], elapsed_ms: float) -> None:
  16. entry = {
  17. "time": datetime.now().astimezone().isoformat(timespec="milliseconds"),
  18. "query": query,
  19. "candidates": candidates,
  20. "elapsed_ms": round(elapsed_ms, 3),
  21. }
  22. line = json.dumps(entry, ensure_ascii=False, separators=(",", ":")) + "\n"
  23. with self._lock:
  24. with self.path.open("a", encoding="utf-8", newline="") as file:
  25. file.write(line)
  26. def register_routes(app: FastAPI, matcher, query_logger):
  27. @app.post("/v1/search")
  28. def search(request: SearchRequest) -> dict[str, list[dict]]:
  29. results = []
  30. for keyword in request.keywords:
  31. started_at = time.perf_counter()
  32. candidates = matcher.search(keyword, limit=3)
  33. elapsed_ms = (time.perf_counter() - started_at) * 1000
  34. query_logger.write(keyword, candidates, elapsed_ms)
  35. results.append({"query": keyword, "candidates": candidates})
  36. return {"results": results}