refactor: lifespan al posto di @app.on_event (deprecato) + bump v2.5.0

- startup (collection+indici) e cleanup task nel context manager lifespan
- shutdown: cancel del cleanup task
- verificato sul server: startup OK, scrittura OK (istanza di test)
This commit is contained in:
Matteo Benedetto
2026-08-16 19:17:19 +02:00
parent 25ccb13f5b
commit ec0fdb972a
+32 -28
View File
@@ -26,6 +26,7 @@ import os
import time
import uuid
from collections import Counter
from contextlib import asynccontextmanager
from datetime import datetime, timezone
from typing import Any, Literal, Optional
@@ -53,7 +54,37 @@ MAX_TEXT_LEN = int(os.environ.get("MAX_TEXT_LEN", "8000"))
logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(message)s")
log = logging.getLogger("memory-gateway")
app = FastAPI(title="Memory Gateway", version="2.4.0")
@asynccontextmanager
async def lifespan(_app: FastAPI):
"""Startup/shutdown: crea collection e indici, avvia il cleanup periodico."""
collections = qdrant.get_collections().collections
if not any(c.name == COLLECTION for c in collections):
qdrant.create_collection(
collection_name=COLLECTION,
vectors_config=qm.VectorParams(size=EMBED_DIM, distance=qm.Distance.COSINE),
)
for field in ("agent_id", "project_id", "scope", "kind", "supersedes_id", "superseded_by"):
qdrant.create_payload_index(
collection_name=COLLECTION,
field_name=field,
field_schema=qm.PayloadSchemaType.KEYWORD,
)
log.info("collection %s creata con indici", COLLECTION)
else:
log.info("collection %s già esistente", COLLECTION)
cleanup_task = asyncio.create_task(_cleanup_loop())
try:
yield
finally:
cleanup_task.cancel()
try:
await cleanup_task
except asyncio.CancelledError:
pass
app = FastAPI(title="Memory Gateway", version="2.5.0", lifespan=lifespan)
qdrant = QdrantClient(url=QDRANT_URL, api_key=QDRANT_API_KEY)
# Rate limit in-memory: {key: [timestamps]}
@@ -165,28 +196,6 @@ async def embed(text: str) -> list[float]:
return r.json()["embeddings"][0]
# ---------------------------------------------------------------------------
# Startup: crea collection e indici se non esistono
# ---------------------------------------------------------------------------
@app.on_event("startup")
def startup() -> None:
collections = qdrant.get_collections().collections
if not any(c.name == COLLECTION for c in collections):
qdrant.create_collection(
collection_name=COLLECTION,
vectors_config=qm.VectorParams(size=EMBED_DIM, distance=qm.Distance.COSINE),
)
for field in ("agent_id", "project_id", "scope", "kind", "supersedes_id", "superseded_by"):
qdrant.create_payload_index(
collection_name=COLLECTION,
field_name=field,
field_schema=qm.PayloadSchemaType.KEYWORD,
)
log.info("collection %s creata con indici", COLLECTION)
else:
log.info("collection %s già esistente", COLLECTION)
# ---------------------------------------------------------------------------
# Endpoints
# ---------------------------------------------------------------------------
@@ -425,10 +434,5 @@ async def _cleanup_loop() -> None:
await asyncio.sleep(3600)
@app.on_event("startup")
async def start_cleanup() -> None:
asyncio.create_task(_cleanup_loop())
if __name__ == "__main__":
uvicorn.run(app, host="0.0.0.0", port=8080)