| 1234567891011121314151617181920212223242526272829303132333435363738394041424344 |
- import json
- import threading
- import time
- from datetime import datetime
- from pathlib import Path
- from fastapi import FastAPI
- from pydantic import BaseModel, Field
- class SearchRequest(BaseModel):
- keywords: list[str] = Field(min_length=1, max_length=100)
- class JsonlQueryLogger:
- def __init__(self, path: str | Path):
- self.path = Path(path)
- self.path.parent.mkdir(parents=True, exist_ok=True)
- self._lock = threading.Lock()
- def write(self, query: str, candidates: list[dict], elapsed_ms: float) -> None:
- entry = {
- "time": datetime.now().astimezone().isoformat(timespec="milliseconds"),
- "query": query,
- "candidates": candidates,
- "elapsed_ms": round(elapsed_ms, 3),
- }
- line = json.dumps(entry, ensure_ascii=False, separators=(",", ":")) + "\n"
- with self._lock:
- with self.path.open("a", encoding="utf-8", newline="") as file:
- file.write(line)
- def register_routes(app: FastAPI, matcher, query_logger):
- @app.post("/v1/search")
- def search(request: SearchRequest) -> dict[str, list[dict]]:
- results = []
- for keyword in request.keywords:
- started_at = time.perf_counter()
- candidates = matcher.search(keyword, limit=3)
- elapsed_ms = (time.perf_counter() - started_at) * 1000
- query_logger.write(keyword, candidates, elapsed_ms)
- results.append({"query": keyword, "candidates": candidates})
- return {"results": results}
|