Spaces:
Paused
Paused
| # validation_layer.py (Mythos-Killer v9.0 "मिथोस-सर्वर" — Validation Layer) | |
| # यह फ़ाइल "सच्चाई की अदालत" (Court of Truth) है। | |
| # यह चार वैलिडेटर्स (Static, Dynamic, Consensus, Adversarial) के स्कोर को | |
| # कॉन्टेक्स्ट-अवेयर (जोखिम के हिसाब से वज़न बदलने वाले) कॉन्फिडेंस इंजन में | |
| # डालकर एक अंतिम ट्रुथ स्कोर और फैसला लौटाती है। | |
| # साथ ही, फेल होने पर रिट्री लॉजिक और सबूत (एविडेंस) कलेक्टर भी शामिल है। | |
| import asyncio | |
| import hashlib | |
| import json | |
| import os | |
| import re | |
| from typing import Dict, Any, List, Optional, Tuple | |
| from datetime import datetime | |
| from dataclasses import dataclass, field | |
| # बाहरी लाइब्रेरी (pip install करनी होंगी) | |
| import httpx | |
| # E2B सैंडबॉक्स (अगर उपलब्ध हो) | |
| try: | |
| from e2b_code_interpreter import Sandbox | |
| E2B_AVAILABLE = True | |
| except ImportError: | |
| E2B_AVAILABLE = False | |
| Sandbox = None | |
| # हफ़्तों के घटक (फ़ॉलबैक के लिए try-except) | |
| try: | |
| from event_bus import get_event_bus, Events | |
| EVENT_BUS_AVAILABLE = True | |
| except ImportError: | |
| EVENT_BUS_AVAILABLE = False | |
| Events = None | |
| try: | |
| from router import SmartModelRouter | |
| ROUTER_AVAILABLE = True | |
| except ImportError: | |
| ROUTER_AVAILABLE = False | |
| class ValidationResult: | |
| """एक पूर्ण वैलिडेशन का परिणाम""" | |
| truth_score: float = 0.0 | |
| decision: str = "❓ Uncertain" | |
| status: str = "retry" # accept / accept_with_warning / retry / reject | |
| static_score: float = 0.0 | |
| dynamic_score: float = 0.0 | |
| consensus_score: float = 0.0 | |
| adversarial_score: float = 0.0 | |
| evidence: List[Dict] = field(default_factory=list) | |
| feedback: str = "" | |
| run_id: str = "" | |
| timestamp: str = "" | |
| class ValidationOrchestrator: | |
| """ | |
| v9.0 का "सच्चाई का कोर्ट" — यह चार वैलिडेटर्स के स्कोर को | |
| कॉन्टेक्स्ट-अवेयर कॉन्फिडेंस इंजन में डालकर अंतिम फैसला लेता है। | |
| """ | |
| # चारों वैलिडेटर्स के डिफ़ॉल्ट वज़न (जोखिम के हिसाब से बदलेंगे) | |
| DEFAULT_WEIGHTS = { | |
| "static": 0.20, | |
| "dynamic": 0.40, | |
| "consensus": 0.20, | |
| "adversarial": 0.20, | |
| } | |
| # जोखिम-अनुसार वज़न | |
| HIGH_RISK_WEIGHTS = { | |
| "static": 0.10, | |
| "dynamic": 0.50, | |
| "consensus": 0.15, | |
| "adversarial": 0.25, | |
| } | |
| LOW_RISK_WEIGHTS = { | |
| "static": 0.40, | |
| "dynamic": 0.20, | |
| "consensus": 0.30, | |
| "adversarial": 0.10, | |
| } | |
| # निर्णय थ्रेशोल्ड | |
| ACCEPT_THRESHOLD = 90 | |
| WARNING_THRESHOLD = 70 | |
| UNCERTAIN_THRESHOLD = 50 | |
| def __init__( | |
| self, | |
| router: Optional[Any] = None, | |
| event_bus=None, | |
| piston_url: str = "https://emkc.org/api/v2/piston/execute", | |
| e2b_api_key: Optional[str] = None, | |
| ): | |
| self.router = router | |
| self.event_bus = event_bus | |
| self.piston_url = piston_url | |
| self.e2b_api_key = e2b_api_key or os.environ.get("E2B_API_KEY") | |
| self.e2b_available = E2B_AVAILABLE and self.e2b_api_key is not None | |
| if self.event_bus is None and EVENT_BUS_AVAILABLE: | |
| try: | |
| self.event_bus = get_event_bus() | |
| except Exception: | |
| pass | |
| self.stats = { | |
| "total_validations": 0, | |
| "accepted": 0, | |
| "rejected": 0, | |
| "retried": 0, | |
| "started_at": datetime.now().isoformat(), | |
| } | |
| # ─── मुख्य वैलिडेशन पाइपलाइन ────────────────────────────── | |
| async def run_validation_pipeline( | |
| self, | |
| meta_reasoner_claim: Dict[str, Any], | |
| risk_level: str = "medium", | |
| max_retries: int = 3, | |
| ) -> ValidationResult: | |
| self.stats["total_validations"] += 1 | |
| run_id = hashlib.md5( | |
| json.dumps(meta_reasoner_claim, sort_keys=True, ensure_ascii=False).encode() | |
| ).hexdigest()[:12] | |
| print(f"⚖️ Validation Court: Trial started for claim: {meta_reasoner_claim.get('title', run_id)}") | |
| weights = self._get_weights_for_risk(risk_level) | |
| for attempt in range(1, max_retries + 1): | |
| # ─── Tier 1: Fast Checks (Static + Consensus) — एक साथ ─── | |
| static_task = asyncio.create_task(self._static_validator(meta_reasoner_claim)) | |
| consensus_task = asyncio.create_task(self._consensus_validator(meta_reasoner_claim)) | |
| static_score, consensus_score = await asyncio.gather(static_task, consensus_task) | |
| # Task 4 Fix: स्कोर claim में सेव करें ताकि consensus/adversarial उठा सकें | |
| meta_reasoner_claim["_static_score"] = static_score | |
| # ─── Tier 2: Dynamic Validator ─── | |
| dynamic_score = await self._dynamic_validator(meta_reasoner_claim) | |
| # Task 4 Fix: dynamic score भी सेव करें | |
| meta_reasoner_claim["_dynamic_score"] = dynamic_score | |
| # ─── Tier 3: Adversarial Validator ─── | |
| adversarial_score = await self._adversarial_validator(meta_reasoner_claim) | |
| # ─── कॉन्टेक्स्ट-अवेयर कॉन्फिडेंस इंजन ─── | |
| truth_score = self._calculate_confidence( | |
| static_score, dynamic_score, consensus_score, adversarial_score, weights | |
| ) | |
| # ─── निर्णय लें ─── | |
| decision, status = self._make_decision(truth_score) | |
| # ─── एविडेंस इकट्ठा करें ─── | |
| evidence = self._collect_evidence( | |
| meta_reasoner_claim, static_score, dynamic_score, | |
| consensus_score, adversarial_score, truth_score, decision | |
| ) | |
| if status == "retry" and attempt < max_retries: | |
| print(f"⚠️ Validation Court: Score {truth_score:.1f} — retrying ({attempt}/{max_retries})") | |
| meta_reasoner_claim["_validation_feedback"] = ( | |
| f"Dynamic score was {dynamic_score}, Adversarial was {adversarial_score}. " | |
| f"Please strengthen the exploit payload or provide more execution evidence." | |
| ) | |
| continue | |
| result = ValidationResult( | |
| truth_score=truth_score, | |
| decision=decision, | |
| status=status, | |
| static_score=static_score, | |
| dynamic_score=dynamic_score, | |
| consensus_score=consensus_score, | |
| adversarial_score=adversarial_score, | |
| evidence=evidence, | |
| feedback=meta_reasoner_claim.get("_validation_feedback", ""), | |
| run_id=run_id, | |
| timestamp=datetime.now().isoformat(), | |
| ) | |
| if status in ("accept", "accept_with_warning"): | |
| self.stats["accepted"] += 1 | |
| elif status == "reject": | |
| self.stats["rejected"] += 1 | |
| else: | |
| self.stats["retried"] += 1 | |
| if self.event_bus and EVENT_BUS_AVAILABLE: | |
| self.event_bus.emit_sync("validation.completed", { | |
| "run_id": run_id, | |
| "truth_score": truth_score, | |
| "status": status, | |
| "timestamp": datetime.now().isoformat(), | |
| }) | |
| return result | |
| # सारे रिट्री विफल | |
| return ValidationResult( | |
| truth_score=0.0, | |
| decision="❌ Reject (Max Retries)", | |
| status="reject", | |
| feedback="All retries exhausted.", | |
| run_id=run_id, | |
| timestamp=datetime.now().isoformat(), | |
| ) | |
| # ─── वज़न चयन (कॉन्टेक्स्ट-अवेयर) ────────────────────────── | |
| def _get_weights_for_risk(self, risk_level: str) -> Dict[str, float]: | |
| """जोखिम स्तर के हिसाब से वैलिडेशन वज़न चुनता है।""" | |
| risk_lower = risk_level.lower() | |
| if risk_lower in ("high", "critical"): | |
| return dict(self.HIGH_RISK_WEIGHTS) | |
| elif risk_lower == "low": | |
| return dict(self.LOW_RISK_WEIGHTS) | |
| else: | |
| return dict(self.DEFAULT_WEIGHTS) | |
| # ─── कॉन्फिडेंस इंजन ────────────────────────────────────── | |
| def _calculate_confidence( | |
| self, | |
| static: float, | |
| dynamic: float, | |
| consensus: float, | |
| adversarial: float, | |
| weights: Dict[str, float], | |
| ) -> float: | |
| """वज़न के हिसाब से फाइनल ट्रुथ स्कोर निकालता है।""" | |
| score = ( | |
| (static * weights["static"]) | |
| + (dynamic * weights["dynamic"]) | |
| + (consensus * weights["consensus"]) | |
| + (adversarial * weights["adversarial"]) | |
| ) | |
| return round(min(score, 100.0), 1) | |
| def _make_decision(self, truth_score: float) -> Tuple[str, str]: | |
| """ट्रुथ स्कोर के आधार पर फैसला और स्टेटस लौटाता है।""" | |
| if truth_score >= self.ACCEPT_THRESHOLD: | |
| return "✅ Confirmed — Exploit Validated Successfully", "accept" | |
| elif truth_score >= self.WARNING_THRESHOLD: | |
| return "⚠️ Likely True — Accept with Caution", "accept_with_warning" | |
| elif truth_score >= self.UNCERTAIN_THRESHOLD: | |
| return "❓ Uncertain — Needs Further Investigation", "retry" | |
| else: | |
| return "❌ Reject — Unable to Verify Claim", "reject" | |
| # ─── 1. STATIC VALIDATOR (बिना रन किए कोड चेक) ────────────── | |
| async def _static_validator(self, claim: Dict[str, Any]) -> float: | |
| """ | |
| Task 3 Fix: खतरनाक पैटर्न मिलने पर BONUS देता है (penalty नहीं), | |
| क्योंकि यह exploit/hacking code checker है — | |
| dangerous code = exploit असली है। | |
| """ | |
| print("🔍 Static Check: Scanning for exploit patterns...") | |
| score = 50.0 # न्यूट्रल बेस से शुरू | |
| code = claim.get("code", claim.get("generated_code", "")) | |
| if not code: | |
| return 50.0 | |
| # ─── खतरनाक पैटर्न = BONUS (exploit real होने का सबूत) ── | |
| dangerous_patterns = [ | |
| (r"os\.system\s*\(", 25, "os.system() — command injection vector confirmed"), | |
| (r"subprocess\.call\s*\(", 20, "subprocess.call() — shell injection vector confirmed"), | |
| (r"eval\s*\(", 30, "eval() — arbitrary code execution confirmed"), | |
| (r"exec\s*\(", 30, "exec() — arbitrary code execution confirmed"), | |
| (r"__import__\s*\(", 15, "__import__() — dynamic import exploit confirmed"), | |
| (r"pickle\.loads?\s*\(", 25, "pickle.load() — deserialization exploit confirmed"), | |
| (r"sql\s*=\s*.*\+.*user", 20, "SQL concatenation — injection exploit confirmed"), | |
| (r"password\s*=\s*['\"].*['\"]", 15, "Hardcoded credential — leak vector confirmed"), | |
| ] | |
| for pattern, bonus, reason in dangerous_patterns: | |
| if re.search(pattern, code, re.IGNORECASE): | |
| score = min(100.0, score + bonus) | |
| print(f" 💀 {reason} (+{bonus})") | |
| # ─── अच्छे पैटर्न की जाँच (बोनस) ────────────────── | |
| good_patterns = [ | |
| (r"try\s*:", 5, "try-except block found"), | |
| (r"with\s+.*\s+as\s+", 3, "context manager (with statement) found"), | |
| (r"\.strip\(\s*\)", 3, "input sanitization (.strip()) found"), | |
| ] | |
| for pattern, bonus, reason in good_patterns: | |
| if re.search(pattern, code, re.IGNORECASE): | |
| score = min(100.0, score + bonus) | |
| print(f" ✅ {reason} (+{bonus})") | |
| return max(0.0, score) | |
| # ─── 2. DYNAMIC VALIDATOR (सैंडबॉक्स में रन करना) ─────────── | |
| async def _dynamic_validator(self, claim: Dict[str, Any]) -> float: | |
| """ | |
| कोड को Piston API या E2B सैंडबॉक्स में चलाकर जाँचता है। | |
| यह सबसे भरोसेमंद वैलिडेशन है। | |
| """ | |
| print("💥 Dynamic Check: Executing in sandbox...") | |
| code = claim.get("code", claim.get("generated_code", "")) | |
| if not code: | |
| return 0.0 | |
| use_e2b = self.e2b_available and ( | |
| "import" in code or "open(" in code or "requests" in code | |
| ) | |
| try: | |
| if use_e2b: | |
| return await self._run_e2b(code, claim) | |
| else: | |
| return await self._run_piston(code, claim) | |
| except Exception as e: | |
| print(f" ❌ Dynamic execution failed: {e}") | |
| return 0.0 | |
| async def _run_piston(self, code: str, claim: Dict[str, Any]) -> float: | |
| """Piston API पर कोड चलाएँ।""" | |
| payload = { | |
| "language": "python", | |
| "version": "*", | |
| "files": [{"name": "main.py", "content": code}], | |
| "stdin": "", | |
| "args": [], | |
| "compile_timeout": 10000, | |
| "run_timeout": 5000, | |
| } | |
| try: | |
| async with httpx.AsyncClient(timeout=15.0) as client: | |
| resp = await client.post(self.piston_url, json=payload) | |
| if resp.status_code == 200: | |
| data = resp.json() | |
| run_data = data.get("run", {}) | |
| stderr = run_data.get("stderr", "") | |
| signal = run_data.get("signal") | |
| if signal is None and not stderr: | |
| print(" ✅ Piston execution succeeded") | |
| return 95.0 | |
| elif stderr: | |
| print(f" ⚠️ Piston execution had errors: {stderr[:100]}") | |
| return 40.0 | |
| else: | |
| return 70.0 | |
| else: | |
| print(f" ❌ Piston API returned {resp.status_code}") | |
| return 10.0 | |
| except httpx.TimeoutException: | |
| print(" ❌ Piston execution timed out") | |
| return 20.0 | |
| except Exception as e: | |
| print(f" ❌ Piston execution error: {e}") | |
| return 0.0 | |
| async def _run_e2b(self, code: str, claim: Dict[str, Any]) -> float: | |
| """ | |
| Task 2 Fix: E2B Sandbox को asyncio.to_thread में wrap करके | |
| async-safe तरीके से चलाएँ। | |
| """ | |
| if not self.e2b_available or Sandbox is None: | |
| return await self._run_piston(code, claim) | |
| def _blocking_e2b_run(): | |
| with Sandbox(api_key=self.e2b_api_key, timeout=30) as sandbox: | |
| execution = sandbox.run_code(code, language="python") | |
| error = execution.error | |
| return error | |
| try: | |
| error = await asyncio.to_thread(_blocking_e2b_run) | |
| if error is None: | |
| print(" ✅ E2B execution succeeded") | |
| return 98.0 | |
| else: | |
| print(f" ⚠️ E2B execution error: {str(error)[:100]}") | |
| return 35.0 | |
| except Exception as e: | |
| print(f" ❌ E2B sandbox error: {e}") | |
| return await self._run_piston(code, claim) | |
| # ─── 3. CONSENSUS VALIDATOR (मल्टी-मॉडल जूरी) ──────────────── | |
| async def _consensus_validator(self, claim: Dict[str, Any]) -> float: | |
| """ | |
| Task 2 Fix: self.router.call_async() से async call करता है। | |
| Task 4 Fix: fallback में claim["_static_score"] इस्तेमाल करता है। | |
| """ | |
| print("🤖 Consensus Check: Asking the jury...") | |
| if not self.router or not ROUTER_AVAILABLE: | |
| static_score = claim.get("_static_score", 50.0) | |
| return min(static_score, 85.0) | |
| title = claim.get("title", "Unknown claim") | |
| code = claim.get("code", claim.get("generated_code", ""))[:500] | |
| prompt = f"""You are a code security reviewer. Analyze this claim: | |
| Title: {title} | |
| Code (first 500 chars): | |
| {code} | |
| Is this claim likely valid? Answer ONLY with a number 0-100. | |
| 0 = completely false, 100 = completely true. | |
| Do NOT explain.""" | |
| scores = [] | |
| jury_models = ["google/gemini-flash-1.5-8b", "meta-llama/llama-3.2-3b-instruct"] | |
| for model in jury_models: | |
| try: | |
| response = await self.router.call_async( | |
| model=model, | |
| prompt=prompt, | |
| hidden_thinking=True, | |
| task_type="coding", | |
| ) | |
| match = re.search(r'\b(\d{1,3})\b', str(response)) | |
| if match: | |
| score = float(match.group(1)) | |
| score = max(0.0, min(100.0, score)) | |
| scores.append(score) | |
| except Exception as e: | |
| print(f" ⚠️ Consensus model {model} failed: {e}") | |
| if not scores: | |
| return 50.0 | |
| consensus_score = sum(scores) / len(scores) | |
| print(f" ✅ Consensus score: {consensus_score:.1f} ({len(scores)} models)") | |
| return round(consensus_score, 1) | |
| # ─── 4. ADVERSARIAL VALIDATOR (रेड टीम — गलत साबित करो) ────── | |
| async def _adversarial_validator(self, claim: Dict[str, Any]) -> float: | |
| """ | |
| Task 2 Fix: self.router.call_async() से async call करता है। | |
| Task 4 Fix: fallback में claim["_dynamic_score"] इस्तेमाल करता है। | |
| """ | |
| print("⚔️ Adversarial Check: Trying to disprove claim...") | |
| if not self.router or not ROUTER_AVAILABLE: | |
| dynamic_score = claim.get("_dynamic_score", 50.0) | |
| return max(0.0, dynamic_score - 10.0) | |
| title = claim.get("title", "Unknown claim") | |
| code = claim.get("code", claim.get("generated_code", ""))[:500] | |
| prompt = f"""You are an adversarial security tester. Try to find flaws in this claim: | |
| Title: {title} | |
| Code (first 500 chars): | |
| {code} | |
| Rate how resistant this claim is to adversarial testing. | |
| 0 = easily disproven, 100 = very robust. | |
| Answer ONLY with a number 0-100. Do NOT explain.""" | |
| try: | |
| response = await self.router.call_async( | |
| model="deepseek/deepseek-r1", | |
| prompt=prompt, | |
| hidden_thinking=True, | |
| task_type="coding", | |
| ) | |
| match = re.search(r'\b(\d{1,3})\b', str(response)) | |
| if match: | |
| score = float(match.group(1)) | |
| score = max(0.0, min(100.0, score)) | |
| print(f" ✅ Adversarial score: {score:.1f}") | |
| return score | |
| except Exception as e: | |
| print(f" ⚠️ Adversarial model failed: {e}") | |
| return 50.0 | |
| # ─── एविडेंस कलेक्टर ──────────────────────────────────── | |
| def _collect_evidence( | |
| self, | |
| claim: Dict[str, Any], | |
| static: float, | |
| dynamic: float, | |
| consensus: float, | |
| adversarial: float, | |
| truth: float, | |
| decision: str, | |
| ) -> List[Dict]: | |
| """वैलिडेशन के सबूत इकट्ठा करता है।""" | |
| return [ | |
| { | |
| "type": "static_analysis", | |
| "score": static, | |
| "timestamp": datetime.now().isoformat(), | |
| }, | |
| { | |
| "type": "dynamic_execution", | |
| "score": dynamic, | |
| "sandbox": "E2B" if self.e2b_available else "Piston", | |
| "timestamp": datetime.now().isoformat(), | |
| }, | |
| { | |
| "type": "consensus_check", | |
| "score": consensus, | |
| "timestamp": datetime.now().isoformat(), | |
| }, | |
| { | |
| "type": "adversarial_test", | |
| "score": adversarial, | |
| "timestamp": datetime.now().isoformat(), | |
| }, | |
| { | |
| "type": "final_verdict", | |
| "truth_score": truth, | |
| "decision": decision, | |
| "claim_title": claim.get("title", "Unknown"), | |
| "timestamp": datetime.now().isoformat(), | |
| }, | |
| ] | |
| # ─── स्टैट्स ───────────────────────────────────────────── | |
| def get_stats(self) -> Dict[str, Any]: | |
| """ऑर्केस्ट्रेटर के रनटाइम स्टैट्स लौटाता है।""" | |
| return dict(self.stats) |