abinazebinoy commited on
Commit
5fdaa50
·
1 Parent(s): 48337b7

feat(phase-34): add api/routes/self_learning.py with 4 endpoints

Browse files

GET /self-learning/patterns -- PatternLearner discovers new investigation motifs
GET /self-learning/weights -- WeightOptimizer current investigator weights
GET /self-learning/audit -- SelfAudit scraper health check (30s timeout)
GET /self-learning/schema -- SchemaLearner pending fields not in schema.py

All 4 modules in ai/self_learning/ were fully implemented. None were
reachable via any API endpoint. Now wired.

Files changed (1) hide show
  1. api/routes/self_learning.py +105 -0
api/routes/self_learning.py ADDED
@@ -0,0 +1,105 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ BharatGraph - Phase 34: Self-Learning API
3
+ GET /self-learning/patterns -- discover new investigation patterns from graph
4
+ GET /self-learning/weights -- current optimised investigator weights
5
+ GET /self-learning/audit -- scraper health check (which sources are live)
6
+ GET /self-learning/schema -- newly detected fields not yet in schema
7
+
8
+ Pure ASCII.
9
+ """
10
+ import os, sys
11
+ sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
12
+
13
+ from datetime import datetime
14
+ from fastapi import APIRouter, Depends, Header
15
+ from fastapi import HTTPException
16
+ from loguru import logger
17
+
18
+ from api.dependencies import get_db
19
+
20
+ router = APIRouter(prefix="/self-learning", tags=["SelfLearning"])
21
+
22
+
23
+ def _require_admin(x_admin_secret: str = Header(default="")):
24
+ secret = os.getenv("ADMIN_SECRET", "")
25
+ if secret and x_admin_secret != secret:
26
+ raise HTTPException(status_code=403, detail="Forbidden")
27
+
28
+
29
+ @router.get("/patterns")
30
+ def discover_patterns(driver=Depends(get_db)):
31
+ """
32
+ Run the PatternLearner against the current graph to discover
33
+ new investigation motifs not yet in the hardcoded pattern list.
34
+ Returns confirmed patterns (found >= 5 times) and newly discovered motifs.
35
+ """
36
+ logger.info("[SelfLearning] pattern discovery run")
37
+ try:
38
+ from ai.self_learning.pattern_learner import PatternLearner
39
+ pl = PatternLearner(driver=driver)
40
+ result = pl.discover_patterns()
41
+ result["analyzed_at"] = datetime.now().isoformat()
42
+ return result
43
+ except Exception as e:
44
+ logger.error(f"[SelfLearning] pattern discovery error: {type(e).__name__}")
45
+ return {"status": "error", "detail": str(type(e).__name__),
46
+ "analyzed_at": datetime.now().isoformat()}
47
+
48
+
49
+ @router.get("/weights")
50
+ def get_investigator_weights():
51
+ """
52
+ Return the current optimised investigator weights.
53
+ Weights are updated after each investigation outcome is recorded.
54
+ The base weights are overridden by the weight file if it exists.
55
+ """
56
+ logger.info("[SelfLearning] weight lookup")
57
+ try:
58
+ from ai.self_learning.weight_optimizer import WeightOptimizer
59
+ wo = WeightOptimizer()
60
+ return {
61
+ "weights": wo._load_weights(),
62
+ "outcome_count": len(wo._load_outcomes()),
63
+ "analyzed_at": datetime.now().isoformat(),
64
+ }
65
+ except Exception as e:
66
+ logger.error(f"[SelfLearning] weights error: {type(e).__name__}")
67
+ return {"status": "error", "detail": str(type(e).__name__)}
68
+
69
+
70
+ @router.get("/audit")
71
+ def scraper_audit():
72
+ """
73
+ Run a health check against all registered scrapers.
74
+ Tests whether each source URL is reachable and returns parseable data.
75
+ Expensive -- allow 30 seconds. Use sparingly.
76
+ """
77
+ logger.info("[SelfLearning] scraper audit")
78
+ try:
79
+ from ai.self_learning.self_audit import run
80
+ result = run(timeout_secs=25)
81
+ result["analyzed_at"] = datetime.now().isoformat()
82
+ return result
83
+ except Exception as e:
84
+ logger.error(f"[SelfLearning] audit error: {type(e).__name__}")
85
+ return {"status": "error", "detail": str(type(e).__name__)}
86
+
87
+
88
+ @router.get("/schema")
89
+ def pending_schema_fields(driver=Depends(get_db)):
90
+ """
91
+ Return fields that have appeared in scraped records but are not yet
92
+ defined in graph/schema.py. Helps developers identify what new data
93
+ sources are emitting.
94
+ """
95
+ logger.info("[SelfLearning] schema detection")
96
+ try:
97
+ from ai.self_learning.schema_learner import SchemaLearner
98
+ sl = SchemaLearner()
99
+ return {
100
+ "pending_fields": sl.get_pending(),
101
+ "analyzed_at": datetime.now().isoformat(),
102
+ }
103
+ except Exception as e:
104
+ logger.error(f"[SelfLearning] schema error: {type(e).__name__}")
105
+ return {"status": "error", "detail": str(type(e).__name__)}