Skip to content

Commit d86838d

Browse files
authored
Merge pull request #6 from MVPandey/feature/mcts-optimization
MCTS Optimization: Added Redis caching and metrics tracking
2 parents 6e499ea + f4bca9a commit d86838d

46 files changed

Lines changed: 7329 additions & 82 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

app/api/monitoring.py

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
"""Monitoring endpoints for system health and metrics."""
2+
3+
import time
4+
5+
from fastapi import APIRouter, Response
6+
7+
from ..services.cache.redis_manager import redis_manager
8+
from ..services.cache.semantic_cache import semantic_cache
9+
from ..services.embeddings.embedding_service import embedding_service
10+
from ..utils.logger import logger
11+
from ..utils.metrics import metrics_collector
12+
13+
router = APIRouter(prefix="/monitoring", tags=["Monitoring"])
14+
15+
16+
@router.get("/cache/stats")
17+
async def get_cache_statistics():
18+
"""Get comprehensive cache performance statistics."""
19+
try:
20+
redis_info = await redis_manager.get_info()
21+
semantic_stats = semantic_cache.get_stats()
22+
embedding_stats = embedding_service.get_stats()
23+
24+
return {
25+
"status": "healthy",
26+
"redis": redis_info,
27+
"semantic_cache": semantic_stats,
28+
"embeddings": embedding_stats,
29+
"recommendations": _get_cache_recommendations(semantic_stats),
30+
}
31+
except Exception as e:
32+
logger.error(f"Failed to get cache statistics: {e}")
33+
return {
34+
"status": "error",
35+
"error": str(e),
36+
}
37+
38+
39+
@router.get("/cache/health")
40+
async def check_cache_health():
41+
"""Quick health check for cache systems."""
42+
try:
43+
redis_healthy = await redis_manager.exists("health_check")
44+
45+
return {
46+
"redis": "healthy" if redis_healthy or redis_manager._is_healthy else "unhealthy",
47+
"status": "healthy" if redis_healthy or redis_manager._is_healthy else "degraded",
48+
}
49+
except Exception as e:
50+
logger.error(f"Cache health check failed: {e}")
51+
return {
52+
"status": "unhealthy",
53+
"error": str(e),
54+
}
55+
56+
57+
@router.delete("/cache/clear")
58+
async def clear_cache():
59+
"""Clear all cache entries (admin operation)."""
60+
try:
61+
semantic_cleared = await semantic_cache.clear_all()
62+
63+
embedding_cleared = await embedding_service.clear_cache()
64+
65+
logger.info(
66+
"Cache cleared",
67+
extra={
68+
"semantic_entries": semantic_cleared,
69+
"embedding_entries": embedding_cleared,
70+
},
71+
)
72+
73+
return {
74+
"status": "success",
75+
"semantic_entries_cleared": semantic_cleared,
76+
"embedding_entries_cleared": embedding_cleared,
77+
"total_cleared": semantic_cleared + embedding_cleared,
78+
}
79+
except Exception as e:
80+
logger.error(f"Failed to clear cache: {e}")
81+
return {
82+
"status": "error",
83+
"error": str(e),
84+
}
85+
86+
87+
def _get_cache_recommendations(stats: dict) -> list[str]:
88+
"""Generate recommendations based on cache statistics."""
89+
recommendations = []
90+
91+
if stats["hit_rate"] < 0.2:
92+
recommendations.append(
93+
"Low cache hit rate. Consider adjusting similarity threshold or warming cache with common patterns."
94+
)
95+
96+
if stats["hit_rate"] > 0.9:
97+
recommendations.append("Very high cache hit rate. Consider reducing TTL to ensure fresh responses.")
98+
99+
if stats["total_requests"] > 10000:
100+
recommendations.append(
101+
"High cache usage. Monitor memory consumption and consider implementing cache size limits."
102+
)
103+
104+
return recommendations
105+
106+
107+
@router.get("/metrics")
108+
async def get_prometheus_metrics():
109+
"""Get Prometheus metrics in text format."""
110+
try:
111+
metrics_data = metrics_collector.get_metrics()
112+
return Response(content=metrics_data, media_type="text/plain")
113+
except Exception as e:
114+
logger.error(f"Failed to get Prometheus metrics: {e}")
115+
return Response(content=f"# Error: {str(e)}", media_type="text/plain", status_code=500)
116+
117+
118+
@router.get("/metrics/json")
119+
async def get_metrics_json():
120+
"""Get metrics in JSON format for easier consumption."""
121+
try:
122+
metrics_dict = metrics_collector.get_metrics_dict()
123+
return {
124+
"status": "success",
125+
"metrics": metrics_dict,
126+
"timestamp": int(time.time()),
127+
}
128+
except Exception as e:
129+
logger.error(f"Failed to get metrics: {e}")
130+
return {
131+
"status": "error",
132+
"error": str(e),
133+
}

app/main.py

Lines changed: 109 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,12 @@
1010

1111
from app.api import chat as chat_api
1212
from app.api import conversation_analysis as analysis_api
13+
from app.api import monitoring as monitoring_api
1314
from app.api import user as user_api
1415
from app.db.chat import db
16+
from app.services.cache.redis_manager import redis_manager
1517
from app.utils.logger import logger
18+
from app.utils.metrics import metrics_collector
1619

1720

1821
@asynccontextmanager
@@ -21,10 +24,21 @@ async def lifespan(app: FastAPI):
2124
Handles startup and shutdown events for the application.
2225
"""
2326
logger.info("Starting up...")
27+
2428
await db.create_db_and_tables()
2529
logger.info("Database tables created or already exist.")
30+
31+
await redis_manager.initialize()
32+
logger.info("Redis cache initialized.")
33+
34+
metrics_collector.initialize()
35+
logger.info("Metrics collector initialized.")
36+
2637
yield
38+
2739
logger.info("Shutting down...")
40+
await redis_manager.close()
41+
logger.info("Redis connections closed.")
2842

2943

3044
app = FastAPI(
@@ -142,12 +156,106 @@ async def general_exception_handler(request: Request, exc: Exception):
142156

143157
@app.get("/health")
144158
async def health_check():
145-
return {"status": "healthy"}
159+
"""Health check endpoint that validates all services."""
160+
import time
161+
162+
from .services.cache.redis_manager import redis_manager
163+
from .services.cache.semantic_cache import semantic_cache
164+
from .utils.config import app_settings
165+
166+
health_status = {
167+
"status": "healthy",
168+
"timestamp": int(time.time()),
169+
"version": app_settings.VERSION if hasattr(app_settings, "VERSION") else "unknown",
170+
"services": {},
171+
}
172+
173+
try:
174+
redis_healthy = redis_manager.is_healthy
175+
health_status["services"]["redis"] = {"status": "healthy" if redis_healthy else "unhealthy"}
176+
if not redis_healthy:
177+
health_status["status"] = "unhealthy"
178+
except Exception as e:
179+
health_status["services"]["redis"] = {"status": "unhealthy", "error": str(e)}
180+
health_status["status"] = "unhealthy"
181+
182+
try:
183+
cache_healthy = await semantic_cache.health_check()
184+
health_status["services"]["cache"] = {"status": "healthy" if cache_healthy else "unhealthy"}
185+
if not cache_healthy:
186+
health_status["status"] = "unhealthy"
187+
health_status["services"]["cache"]["error"] = "Cache health check failed"
188+
except Exception as e:
189+
health_status["services"]["cache"] = {"status": "unhealthy", "error": str(e)}
190+
health_status["status"] = "unhealthy"
191+
192+
if health_status["status"] == "unhealthy":
193+
return JSONResponse(status_code=503, content=health_status)
194+
195+
return health_status
196+
197+
198+
@app.get("/health/detailed")
199+
async def health_check_detailed():
200+
"""Detailed health check with additional service information."""
201+
202+
from .services.cache.redis_manager import redis_manager
203+
from .services.cache.semantic_cache import semantic_cache
204+
205+
health_status = await health_check()
206+
if isinstance(health_status, JSONResponse):
207+
health_status = health_status.body.decode()
208+
import json
209+
210+
health_status = json.loads(health_status)
211+
212+
try:
213+
if hasattr(redis_manager, "get_connection_info"):
214+
health_status["services"]["redis"]["connection_info"] = redis_manager.get_connection_info()
215+
except Exception:
216+
pass
217+
218+
try:
219+
if hasattr(semantic_cache, "get_stats"):
220+
health_status["services"]["cache"]["stats"] = await semantic_cache.get_stats()
221+
except Exception:
222+
pass
223+
224+
return health_status
225+
226+
227+
@app.get("/metrics")
228+
async def get_metrics():
229+
"""Prometheus metrics endpoint."""
230+
from fastapi import Response
231+
232+
from .utils.metrics import metrics_collector
233+
234+
try:
235+
metrics_data = metrics_collector.get_metrics()
236+
return Response(content=metrics_data, media_type="text/plain; version=0.0.4")
237+
except Exception as e:
238+
logger.error(f"Failed to get metrics: {e}")
239+
return Response(content=f"# Error: {str(e)}", media_type="text/plain; version=0.0.4", status_code=500)
240+
241+
242+
@app.get("/metrics/json")
243+
async def get_metrics_json():
244+
"""JSON metrics endpoint."""
245+
from .utils.metrics import metrics_collector
246+
247+
try:
248+
metrics_dict = metrics_collector.get_metrics_dict()
249+
return metrics_dict
250+
except Exception as e:
251+
logger.error(f"Failed to get metrics: {e}")
252+
return JSONResponse(status_code=500, content={"error": str(e)})
146253

147254

148255
app.include_router(user_api.router)
149256
app.include_router(chat_api.router)
150257
app.include_router(analysis_api.router)
258+
app.include_router(monitoring_api.router)
151259

152260

153261
def run_uvicorn():

app/services/cache/__init__.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
"""Cache services for the Conversational Analysis Engine."""
2+
3+
from .cache_metrics import cache_metrics, track_cache_operation
4+
from .eviction_policies import (
5+
EvictionPolicy,
6+
EvictionPolicyFactory,
7+
HybridEvictionPolicy,
8+
LFUEvictionPolicy,
9+
LRUEvictionPolicy,
10+
TTLEvictionPolicy,
11+
)
12+
from .redis_manager import redis_manager
13+
from .semantic_cache import semantic_cache
14+
from .similarity_strategies import (
15+
CosineSimilarityStrategy,
16+
DotProductSimilarityStrategy,
17+
EuclideanDistanceStrategy,
18+
HybridSimilarityStrategy,
19+
SimilarityStrategy,
20+
SimilarityStrategyFactory,
21+
)
22+
23+
__all__ = [
24+
"redis_manager",
25+
"semantic_cache",
26+
"cache_metrics",
27+
"track_cache_operation",
28+
"EvictionPolicy",
29+
"TTLEvictionPolicy",
30+
"LRUEvictionPolicy",
31+
"LFUEvictionPolicy",
32+
"HybridEvictionPolicy",
33+
"EvictionPolicyFactory",
34+
"SimilarityStrategy",
35+
"CosineSimilarityStrategy",
36+
"EuclideanDistanceStrategy",
37+
"DotProductSimilarityStrategy",
38+
"HybridSimilarityStrategy",
39+
"SimilarityStrategyFactory",
40+
]

0 commit comments

Comments
 (0)