- gateway: separate config, models, state, audit, guardrail, embeddings, store, metrics, cleanup and routes; keep main.py as FastAPI bootstrap - extension: split client/config, six tools, config command and rules; preserve jiti entrypoint and registrations - Dockerfile copies the complete gateway module set - tests: update monkeypatch boundaries for modular config/state
38 lines
1.1 KiB
Python
38 lines
1.1 KiB
Python
"""Stato runtime condiviso tra bootstrap e route."""
|
|
from __future__ import annotations
|
|
|
|
import contextvars
|
|
import hashlib
|
|
import json
|
|
import time
|
|
from typing import Any
|
|
|
|
from qdrant_client import QdrantClient
|
|
|
|
from config import QDRANT_API_KEY, QDRANT_URL
|
|
|
|
qdrant = QdrantClient(url=QDRANT_URL, api_key=QDRANT_API_KEY)
|
|
request_id: contextvars.ContextVar[str] = contextvars.ContextVar("request_id", default="-")
|
|
ratelimit: dict[str, list[float]] = {}
|
|
status_ratelimit: dict[str, list[float]] = {}
|
|
idempotency: dict[str, dict[str, Any]] = {}
|
|
meta_cache: dict[str, Any] = {}
|
|
STATUS_RATE_LIMIT_PER_MIN = 30
|
|
IDEMPOTENCY_TTL_SECONDS = 24 * 3600
|
|
|
|
|
|
def payload_hash(body: Any) -> str:
|
|
canonical = json.dumps(body.model_dump(), sort_keys=True, default=str)
|
|
return hashlib.sha256(canonical.encode()).hexdigest()
|
|
|
|
|
|
def idempotency_cleanup() -> None:
|
|
now = time.time()
|
|
expired = [k for k, v in idempotency.items() if now - v["ts"] > IDEMPOTENCY_TTL_SECONDS]
|
|
for key in expired:
|
|
idempotency.pop(key, None)
|
|
|
|
|
|
def invalidate_meta() -> None:
|
|
meta_cache.clear()
|