2026-05-18 16:32:42 +08:00
|
|
|
"""Define API routes for status."""
|
|
|
|
|
|
2026-05-21 23:53:15 +08:00
|
|
|
import time
|
|
|
|
|
from typing import Any
|
|
|
|
|
|
2026-05-14 15:07:34 +08:00
|
|
|
from fastapi import APIRouter
|
2026-05-18 16:32:42 +08:00
|
|
|
|
|
|
|
|
from app.config.settings import settings
|
2026-05-21 23:53:15 +08:00
|
|
|
from app.shared.bootstrap import (
|
|
|
|
|
get_bm25_retriever,
|
|
|
|
|
get_binary_store,
|
|
|
|
|
get_conversation_store,
|
|
|
|
|
get_document_query_service,
|
|
|
|
|
get_vector_index,
|
|
|
|
|
)
|
2026-05-14 15:07:34 +08:00
|
|
|
|
|
|
|
|
router = APIRouter(prefix="/status", tags=["系统状态"])
|
|
|
|
|
|
2026-05-21 23:53:15 +08:00
|
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
# Simple TTL cache for /stats (avoids O(N) doc scan on every request)
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
_stats_cache: dict[str, Any] = {}
|
|
|
|
|
_stats_cache_time: float = 0.0
|
|
|
|
|
_STATS_TTL_SECONDS: float = 10.0
|
|
|
|
|
|
2026-05-14 15:07:34 +08:00
|
|
|
|
|
|
|
|
@router.get("/stats")
|
|
|
|
|
async def get_stats():
|
2026-05-21 23:53:15 +08:00
|
|
|
"""Return document statistics (cached for 10 s)."""
|
|
|
|
|
global _stats_cache, _stats_cache_time
|
|
|
|
|
now = time.time()
|
|
|
|
|
if _stats_cache and (now - _stats_cache_time) < _STATS_TTL_SECONDS:
|
|
|
|
|
return _stats_cache
|
|
|
|
|
|
2026-05-18 16:32:42 +08:00
|
|
|
documents = get_document_query_service().list_documents()
|
2026-05-21 23:53:15 +08:00
|
|
|
indexed = sum(1 for d in documents if d.status.value == "indexed")
|
|
|
|
|
failed = sum(1 for d in documents if d.status.value == "failed")
|
|
|
|
|
_stats_cache = {
|
2026-05-18 16:32:42 +08:00
|
|
|
"documents_total": len(documents),
|
|
|
|
|
"documents_indexed": indexed,
|
|
|
|
|
"documents_failed": failed,
|
2026-05-21 23:53:15 +08:00
|
|
|
"chunks_total": sum(d.chunk_count for d in documents),
|
2026-05-18 16:32:42 +08:00
|
|
|
}
|
2026-05-21 23:53:15 +08:00
|
|
|
_stats_cache_time = now
|
|
|
|
|
return _stats_cache
|
2026-05-14 15:07:34 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.get("/config")
|
|
|
|
|
async def get_config():
|
2026-05-21 23:53:15 +08:00
|
|
|
"""Return system configuration."""
|
2026-05-18 16:32:42 +08:00
|
|
|
return {
|
|
|
|
|
"embedding_model": settings.embedding_model,
|
|
|
|
|
"embedding_dim": settings.embedding_dim,
|
|
|
|
|
"embedding_base_url": settings.embedding_base_url,
|
|
|
|
|
"milvus_collection": settings.milvus_collection,
|
2026-05-18 22:30:28 +08:00
|
|
|
"parser_backend": settings.parser_backend,
|
|
|
|
|
"chunk_backend": settings.chunk_backend,
|
|
|
|
|
"artifact_prefix": settings.document_parse_artifact_prefix,
|
|
|
|
|
"parser_failure_mode": settings.parser_failure_mode,
|
2026-05-18 16:32:42 +08:00
|
|
|
"llm_provider": settings.llm_provider,
|
|
|
|
|
"llm_model": settings.llm_model,
|
|
|
|
|
"document_metadata_path": settings.document_metadata_path,
|
|
|
|
|
}
|
2026-05-14 15:07:34 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.get("/milvus/health")
|
|
|
|
|
async def milvus_health():
|
2026-05-21 23:53:15 +08:00
|
|
|
"""Return Milvus health (kept for backwards compat)."""
|
2026-05-18 16:32:42 +08:00
|
|
|
return get_vector_index().health()
|
2026-05-21 23:53:15 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.get("/health")
|
|
|
|
|
async def get_health():
|
|
|
|
|
"""Return aggregate health of all backend services."""
|
|
|
|
|
# --- Milvus ---
|
|
|
|
|
try:
|
|
|
|
|
milvus_info = get_vector_index().health()
|
|
|
|
|
milvus_status = "ok" if milvus_info.get("connected") else "error"
|
|
|
|
|
except Exception as exc: # noqa: BLE001
|
|
|
|
|
milvus_info = {}
|
|
|
|
|
milvus_status = "error"
|
|
|
|
|
milvus_info["error"] = str(exc)
|
|
|
|
|
|
|
|
|
|
# --- MinIO ---
|
|
|
|
|
try:
|
|
|
|
|
minio_connected = get_binary_store().client.connected
|
|
|
|
|
minio_status = "ok" if minio_connected else "error"
|
|
|
|
|
except Exception: # noqa: BLE001
|
|
|
|
|
minio_status = "error"
|
|
|
|
|
minio_connected = False
|
|
|
|
|
|
|
|
|
|
# --- BM25 ---
|
|
|
|
|
bm25 = get_bm25_retriever()
|
|
|
|
|
|
|
|
|
|
# --- Sessions ---
|
|
|
|
|
try:
|
|
|
|
|
session_count = len(get_conversation_store().list_sessions())
|
|
|
|
|
except Exception: # noqa: BLE001
|
|
|
|
|
session_count = 0
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
"milvus": {"status": milvus_status, **milvus_info},
|
|
|
|
|
"minio": {"status": minio_status, "connected": minio_connected},
|
|
|
|
|
"bm25": {"available": bm25 is not None},
|
|
|
|
|
"reranker": {
|
|
|
|
|
"enabled": settings.reranker_enabled,
|
|
|
|
|
"model": settings.reranker_model if settings.reranker_enabled else None,
|
|
|
|
|
},
|
|
|
|
|
"sessions": {
|
|
|
|
|
"active": session_count,
|
|
|
|
|
"max": settings.session_max_sessions,
|
|
|
|
|
},
|
|
|
|
|
}
|