File size: 11,342 Bytes
f088210
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
"""Enhanced RAG-Powered Analytics Agent with Community Insights.

Integrates retrieval-augmented generation for context-aware analytics,
versioning hooks per SCHEMA_VERSIONING.md, and rollback capabilities.
Enhanced with patterns from bziiit/AGENT_ANALYSE_RAG and fahmiaziz/rag-sql-agent.
"""

import os
import json
import logging
from datetime import datetime
from typing import Dict, List, Optional, Any, Tuple
from dataclasses import dataclass, asdict
import sys

# Import RAG pipeline for context retrieval
from smolagents.rag_pipeline import RAGPipeline, RetrievalConfig
from schema_registry import SchemaRegistry

logger = logging.getLogger(__name__)

@dataclass
class AnalyticsConfig:
    """Configuration for RAG-powered analytics."""
    rag_enabled: bool = True
    top_k_docs: int = 5
    similarity_threshold: float = 0.7
    enable_versioning: bool = True
    schema_version: str = "1.0.0"
    rollback_enabled: bool = True
    audit_trail: bool = True

@dataclass
class AnalyticsQuery:
    """Structured analytics query with RAG context."""
    query_id: str
    query_text: str
    context_docs: List[Dict[str, Any]]
    filters: Dict[str, Any]
    aggregations: List[str]
    timestamp: str
    schema_version: str

@dataclass
class AnalyticsResult:
    """Enhanced analytics result with citations and provenance."""
    query_id: str
    data: Dict[str, Any]
    citations: List[Dict[str, Any]]
    confidence_score: float
    schema_version: str
    rollback_checkpoint: Optional[str]
    timestamp: str

class RAGAnalyticsAgent:
    """Enhanced Analytics Agent with RAG, versioning, and rollback."""
    
    def __init__(
        self,
        config: Optional[AnalyticsConfig] = None,
        rag_pipeline: Optional[RAGPipeline] = None,
        schema_registry: Optional[SchemaRegistry] = None
    ):
        self.config = config or AnalyticsConfig()
        self.rag_pipeline = rag_pipeline
        self.schema_registry = schema_registry
        self.checkpoints: Dict[str, Dict] = {}
        
        logger.info(f"Initialized RAGAnalyticsAgent with config: {self.config}")
    
    def create_checkpoint(self, checkpoint_id: str, state: Dict[str, Any]) -> None:
        """Create rollback checkpoint per SCHEMA_VERSIONING.md."""
        self.checkpoints[checkpoint_id] = {
            "state": state,
            "timestamp": datetime.utcnow().isoformat(),
            "schema_version": self.config.schema_version
        }
        logger.info(f"Created checkpoint: {checkpoint_id}")
    
    def rollback_to_checkpoint(self, checkpoint_id: str) -> Optional[Dict[str, Any]]:
        """Rollback to a previous checkpoint."""
        if checkpoint_id in self.checkpoints:
            checkpoint = self.checkpoints[checkpoint_id]
            logger.info(f"Rolling back to checkpoint: {checkpoint_id}")
            return checkpoint["state"]
        logger.warning(f"Checkpoint not found: {checkpoint_id}")
        return None
    
    def retrieve_context(
        self,
        query_text: str,
        top_k: Optional[int] = None
    ) -> List[Dict[str, Any]]:
        """Retrieve relevant context using RAG pipeline."""
        if not self.config.rag_enabled or not self.rag_pipeline:
            return []
        
        top_k = top_k or self.config.top_k_docs
        
        try:
            results = self.rag_pipeline.retrieve(
                query=query_text,
                top_k=top_k,
                threshold=self.config.similarity_threshold
            )
            
            context_docs = [
                {
                    "content": r.get("content", ""),
                    "source": r.get("metadata", {}).get("source", "unknown"),
                    "score": r.get("score", 0.0),
                    "metadata": r.get("metadata", {})
                }
                for r in results
            ]
            
            logger.info(f"Retrieved {len(context_docs)} context documents")
            return context_docs
            
        except Exception as e:
            logger.error(f"Context retrieval failed: {e}")
            return []
    
    def validate_schema(
        self,
        data: Dict[str, Any],
        schema_version: Optional[str] = None
    ) -> Tuple[bool, List[str]]:
        """Validate data against schema version."""
        if not self.config.enable_versioning or not self.schema_registry:
            return True, []
        
        schema_version = schema_version or self.config.schema_version
        
        try:
            is_valid = self.schema_registry.validate(data, schema_version)
            errors = [] if is_valid else ["Schema validation failed"]
            return is_valid, errors
        except Exception as e:
            logger.error(f"Schema validation error: {e}")
            return False, [str(e)]
    
    def analyze(
        self,
        query_text: str,
        data_source: Dict[str, Any],
        filters: Optional[Dict[str, Any]] = None,
        aggregations: Optional[List[str]] = None
    ) -> AnalyticsResult:
        """Perform RAG-enhanced analytics with versioning and rollback."""
        query_id = f"analytics_{datetime.utcnow().timestamp()}"
        
        # Create checkpoint before analysis
        if self.config.rollback_enabled:
            self.create_checkpoint(
                f"{query_id}_pre",
                {"data_source": data_source, "filters": filters}
            )
        
        # Retrieve context
        context_docs = self.retrieve_context(query_text)
        
        # Build query
        query = AnalyticsQuery(
            query_id=query_id,
            query_text=query_text,
            context_docs=context_docs,
            filters=filters or {},
            aggregations=aggregations or [],
            timestamp=datetime.utcnow().isoformat(),
            schema_version=self.config.schema_version
        )
        
        # Perform analysis (simplified - extend with actual analytics logic)
        try:
            analysis_data = self._execute_analysis(query, data_source)
            
            # Validate result schema
            is_valid, errors = self.validate_schema(analysis_data)
            if not is_valid:
                logger.error(f"Schema validation failed: {errors}")
                # Attempt rollback
                if self.config.rollback_enabled:
                    rollback_state = self.rollback_to_checkpoint(f"{query_id}_pre")
                    if rollback_state:
                        logger.info("Successfully rolled back to pre-analysis state")
            
            # Build citations from context
            citations = [
                {
                    "source": doc["source"],
                    "relevance_score": doc["score"],
                    "excerpt": doc["content"][:200]
                }
                for doc in context_docs
            ]
            
            # Calculate confidence based on context quality
            avg_score = sum(d["score"] for d in context_docs) / len(context_docs) if context_docs else 0.5
            
            result = AnalyticsResult(
                query_id=query_id,
                data=analysis_data,
                citations=citations,
                confidence_score=avg_score,
                schema_version=self.config.schema_version,
                rollback_checkpoint=f"{query_id}_pre" if self.config.rollback_enabled else None,
                timestamp=datetime.utcnow().isoformat()
            )
            
            # Audit trail
            if self.config.audit_trail:
                self._log_audit(query, result)
            
            return result
            
        except Exception as e:
            logger.error(f"Analysis failed: {e}")
            # Return error result with rollback info
            return AnalyticsResult(
                query_id=query_id,
                data={"error": str(e)},
                citations=[],
                confidence_score=0.0,
                schema_version=self.config.schema_version,
                rollback_checkpoint=f"{query_id}_pre" if self.config.rollback_enabled else None,
                timestamp=datetime.utcnow().isoformat()
            )
    
    def _execute_analysis(
        self,
        query: AnalyticsQuery,
        data_source: Dict[str, Any]
    ) -> Dict[str, Any]:
        """Execute the actual analysis logic (extend as needed)."""
        # Simplified analytics - enhance with real logic
        results = {
            "query_id": query.query_id,
            "metrics": {},
            "insights": [],
            "context_applied": len(query.context_docs) > 0
        }
        
        # Apply filters
        filtered_data = data_source
        for key, value in query.filters.items():
            if key in filtered_data:
                filtered_data = {k: v for k, v in filtered_data.items() if v == value}
        
        # Apply aggregations
        for agg in query.aggregations:
            if agg == "count":
                results["metrics"]["count"] = len(filtered_data)
            elif agg == "sum":
                results["metrics"]["sum"] = sum(v for v in filtered_data.values() if isinstance(v, (int, float)))
        
        # Extract insights from RAG context
        if query.context_docs:
            for doc in query.context_docs[:3]:  # Top 3 docs
                results["insights"].append({
                    "source": doc["source"],
                    "insight": doc["content"][:150]
                })
        
        return results
    
    def _log_audit(
        self,
        query: AnalyticsQuery,
        result: AnalyticsResult
    ) -> None:
        """Log audit trail for compliance."""
        audit_entry = {
            "timestamp": datetime.utcnow().isoformat(),
            "query_id": query.query_id,
            "query_text": query.query_text,
            "schema_version": query.schema_version,
            "confidence_score": result.confidence_score,
            "citations_count": len(result.citations)
        }
        logger.info(f"Audit: {json.dumps(audit_entry)}")
    
    def get_version_info(self) -> Dict[str, Any]:
        """Get current version and rollback information."""
        return {
            "schema_version": self.config.schema_version,
            "checkpoints": list(self.checkpoints.keys()),
            "versioning_enabled": self.config.enable_versioning,
            "rollback_enabled": self.config.rollback_enabled
        }

# Example usage
if __name__ == "__main__":
    logging.basicConfig(level=logging.INFO)
    
    # Initialize agent
    config = AnalyticsConfig(
        rag_enabled=True,
        top_k_docs=5,
        enable_versioning=True,
        rollback_enabled=True
    )
    
    agent = RAGAnalyticsAgent(config=config)
    
    # Example query
    result = agent.analyze(
        query_text="What are the top engagement metrics for Q4?",
        data_source={"engagement": 100, "reach": 500},
        aggregations=["count", "sum"]
    )
    
    print(f"\nAnalysis Result:")
    print(f"Query ID: {result.query_id}")
    print(f"Confidence: {result.confidence_score}")
    print(f"Citations: {len(result.citations)}")
    print(f"Rollback checkpoint: {result.rollback_checkpoint}")
    print(f"\nVersion Info: {json.dumps(agent.get_version_info(), indent=2)}")