diff --git a/.gitignore b/.gitignore index 4aaf5e7..ff6fa49 100644 --- a/.gitignore +++ b/.gitignore @@ -4,7 +4,7 @@ __pycache__/ *.pyo *.pyd -# Virtual env +# Virtual environment .venv/ venv/ env/ @@ -23,26 +23,19 @@ htmlcov/ .vscode/ .idea/ -# Runtime +# Runtime databases adaptive_runtime.db *.db *.sqlite *.sqlite3 +# Backup files +*.bak + # Logs *.log # OS .DS_Store Thumbs.db -# Runtime database -adaptive_runtime.db -*.db -*.sqlite -# Python cache -__pycache__/ -.pytest_cache/ - -# Virtual environment -.venv/ diff --git a/adaptive_runtime/core/confidence_engine.py.bak b/adaptive_runtime/core/confidence_engine.py.bak deleted file mode 100644 index 5917d56..0000000 --- a/adaptive_runtime/core/confidence_engine.py.bak +++ /dev/null @@ -1,110 +0,0 @@ -""" -Confidence Engine - adaptive probabilistic confidence scoring. -""" - -import math -from collections import deque -from dataclasses import dataclass, field -from typing import Deque - -from pydantic import BaseModel -from ..observability.logger import get_logger -from ..observability.metrics import metrics - -logger = get_logger("confidence_engine") - - -@dataclass -class OutcomeRecord: - success: bool - confidence_at_time: float - context_risk: str - - -class ConfidenceResult(BaseModel): - confidence: float - decay_factor: float - history_weight: float - context_adjustment: float - final: float - - -class ConfidenceEngine: - """ - Calculates adaptive confidence using: - - base score from event severity - - contextual adjustment from risk level - - historical outcome weighting (exponential decay) - - configurable decay over time - """ - - RISK_WEIGHTS = { - "low": 1.00, - "medium": 0.90, - "high": 0.75, - "critical": 0.55, - } - - def __init__( - self, - base_confidence: float = 0.75, - decay_rate: float = 0.05, - history_window: int = 50, - ): - self._base = base_confidence - self._decay_rate = decay_rate - self._history: Deque[OutcomeRecord] = deque(maxlen=history_window) - self._call_count = 0 - - - def calculate(self, event: dict, context_risk: str) -> ConfidenceResult: - self._call_count += 1 - - severity = float(event.get("severity", 0.5)) - base = max(0.1, self._base - severity * 0.2) - - decay = self._decay_factor() - hist_weight = self._history_weight(context_risk) - ctx_adj = self.RISK_WEIGHTS.get(context_risk, 0.8) - - raw = base * decay * hist_weight * ctx_adj - final = round(min(max(raw, 0.05), 0.99), 4) - - result = ConfidenceResult( - confidence=round(base, 4), - decay_factor=round(decay, 4), - history_weight=round(hist_weight, 4), - context_adjustment=round(ctx_adj, 4), - final=final, - ) - metrics.record("confidence.final", final) - logger.info( - "Confidence → base=%.2f decay=%.2f hist=%.2f ctx=%.2f final=%.4f", - base, decay, hist_weight, ctx_adj, final, - ) - return result - - def record_outcome(self, success: bool, confidence: float, context_risk: str) -> None: - """Feed outcome back for adaptive weighting.""" - self._history.append(OutcomeRecord( - success=success, - confidence_at_time=confidence, - context_risk=context_risk, - )) - logger.debug("Outcome recorded: success=%s conf=%.3f", success, confidence) - - - def _decay_factor(self) -> float: - """Confidence decays slightly as call volume grows (simulate drift).""" - return math.exp(-self._decay_rate * (self._call_count // 20)) - - def _history_weight(self, context_risk: str) -> float: - """Adjust weight based on past success rate for the same risk tier.""" - relevant = [r for r in self._history if r.context_risk == context_risk] - if len(relevant) < 3: - return 1.0 - success_rate = sum(1 for r in relevant if r.success) / len(relevant) - # Map [0, 1] success rate → [0.6, 1.1] weight - return 0.6 + success_rate * 0.5 - - diff --git a/adaptive_runtime/core/context_engine.py.bak b/adaptive_runtime/core/context_engine.py.bak deleted file mode 100644 index 403d4ef..0000000 --- a/adaptive_runtime/core/context_engine.py.bak +++ /dev/null @@ -1,132 +0,0 @@ -""" -Context Engine - transforms raw events into contextual understanding. -""" - -from pydantic import BaseModel, Field -from ..observability.logger import get_logger -from ..observability.metrics import metrics - -logger = get_logger("context_engine") - - - -class RawEvent(BaseModel): - type: str - severity: float = 0.0 - cpu: float = 0.0 - memory: float = 0.0 - error_rate: float = 0.0 - latency_ms: float = 0.0 - extra: dict = Field(default_factory=dict) - - -class ContextResult(BaseModel): - risk: str # low | medium | high | critical - stability: str # stable | degraded | low | critical - context: str # symbolic label - pressure_score: float - tags: list[str] - - - -_RISK_RULES: list[tuple[float, str]] = [ - (0.85, "critical"), - (0.60, "high"), - (0.35, "medium"), - (0.00, "low"), -] - -_STABILITY_RULES: list[tuple[float, str]] = [ - (0.85, "critical"), - (0.65, "low"), - (0.40, "degraded"), - (0.00, "stable"), -] - -_CONTEXT_MAP: dict[str, str] = { - "service_overload": "resource_pressure", - "anomaly_detected": "anomaly_signal", - "memory_leak": "resource_pressure", - "timeout": "latency_issue", - "auth_failure": "security_signal", - "recovery_needed": "self_healing", - "degraded_service": "service_degradation", -} - - - -class ContextEngine: - """ - Classifies events and scores runtime context. - - Uses lightweight rule-based scoring - no ML dependencies. - """ - - def __init__(self, custom_context_map: dict[str, str] | None = None): - self._ctx_map = {**_CONTEXT_MAP, **(custom_context_map or {})} - - def analyze(self, event: dict) -> ContextResult: - raw = RawEvent(**{k: v for k, v in event.items() if k in RawEvent.model_fields or k == "extra"}, - extra={k: v for k, v in event.items() if k not in RawEvent.model_fields}) - - pressure = self._pressure_score(raw) - risk = self._classify(pressure, _RISK_RULES) - stability = self._classify(pressure, _STABILITY_RULES) - context_label = self._ctx_map.get(raw.type, f"unknown_{raw.type}") - tags = self._build_tags(raw, risk) - - result = ContextResult( - risk=risk, - stability=stability, - context=context_label, - pressure_score=round(pressure, 4), - tags=tags, - ) - - metrics.record("context.pressure", pressure) - logger.info( - "Context → risk=%s stability=%s ctx=%s pressure=%.2f", - risk, stability, context_label, pressure, - ) - return result - - - def _pressure_score(self, raw: RawEvent) -> float: - """Weighted composite score in [0, 1].""" - weights = { - "severity": 0.35, - "cpu": 0.20, - "memory": 0.20, - "error_rate": 0.15, - "latency_ms": 0.10, - } - score = ( - raw.severity * weights["severity"] - + (raw.cpu / 100) * weights["cpu"] - + (raw.memory / 100) * weights["memory"] - + raw.error_rate * weights["error_rate"] - + min(raw.latency_ms / 5000, 1.0) * weights["latency_ms"] - ) - return min(score, 1.0) - - @staticmethod - def _classify(score: float, rules: list[tuple[float, str]]) -> str: - for threshold, label in rules: - if score >= threshold: - return label - return rules[-1][1] - - @staticmethod - def _build_tags(raw: RawEvent, risk: str) -> list[str]: - tags = [f"risk:{risk}", f"event:{raw.type}"] - if raw.cpu > 80: - tags.append("cpu:high") - if raw.memory > 80: - tags.append("memory:high") - if raw.error_rate > 0.5: - tags.append("errors:elevated") - if raw.latency_ms > 2000: - tags.append("latency:high") - return tags - - diff --git a/adaptive_runtime/core/decision_engine.py.bak b/adaptive_runtime/core/decision_engine.py.bak deleted file mode 100644 index bf9265b..0000000 --- a/adaptive_runtime/core/decision_engine.py.bak +++ /dev/null @@ -1,96 +0,0 @@ -""" -Decision Engine - generates adaptive runtime decisions. -""" - -from pydantic import BaseModel -from ..observability.logger import get_logger -from ..observability.metrics import metrics - -logger = get_logger("decision_engine") - - -class DecisionResult(BaseModel): - action: str - confidence: float - reason: str - priority: str # low | normal | high | critical - metadata: dict = {} - - -# Each rule: (context_label, risk_level, min_confidence) → action -# Evaluated top-to-bottom; first match wins. - -_RULES: list[tuple[str | None, str | None, float, str, str]] = [ - # (context, risk, min_conf, action, reason) - ("resource_pressure", "critical", 0.0, "scale_up_immediate", "critical_resource_pressure"), - ("resource_pressure", "high", 0.70, "restart_service", "high_resource_pressure"), - ("resource_pressure", "high", 0.0, "throttle_requests", "resource_pressure_low_confidence"), - ("resource_pressure", "medium", 0.0, "optimize_resources", "medium_resource_pressure"), - ("anomaly_signal", "critical", 0.0, "isolate_component", "critical_anomaly"), - ("anomaly_signal", "high", 0.65, "rollback_deployment", "anomaly_with_confidence"), - ("anomaly_signal", "high", 0.0, "increase_monitoring", "anomaly_low_confidence"), - ("anomaly_signal", None, 0.0, "flag_for_review", "anomaly_detected"), - ("latency_issue", "high", 0.0, "optimize_query", "latency_degradation"), - ("latency_issue", None, 0.0, "cache_warmup", "latency_signal"), - ("security_signal", None, 0.0, "trigger_security_audit", "security_event"), - ("self_healing", None, 0.0, "run_recovery", "recovery_requested"), - ("service_degradation","high", 0.0, "circuit_breaker_open", "service_degraded_high"), - ("service_degradation", None, 0.0, "health_check", "service_degraded"), -] - -_PRIORITY_MAP = { - "critical": "critical", - "high": "high", - "medium": "normal", - "low": "low", -} - - -class DecisionEngine: - """ - Matches context + risk + confidence against a rule table to produce - a ranked, explainable action. No ML required. - """ - - def __init__(self, custom_rules: list | None = None): - self._rules = (custom_rules or []) + _RULES - - def decide( - self, - event: dict, - context_label: str, - risk: str, - confidence: float, - ) -> DecisionResult: - action, reason = self._match(context_label, risk, confidence) - priority = _PRIORITY_MAP.get(risk, "normal") - - result = DecisionResult( - action=action, - confidence=round(confidence, 4), - reason=reason, - priority=priority, - metadata={ - "event_type": event.get("type"), - "context": context_label, - "risk": risk, - }, - ) - metrics.record("decision.confidence", confidence) - logger.info( - "Decision → action=%s confidence=%.3f reason=%s priority=%s", - action, confidence, reason, priority, - ) - return result - - - def _match(self, context: str, risk: str, confidence: float) -> tuple[str, str]: - for ctx_rule, risk_rule, min_conf, action, reason in self._rules: - ctx_ok = ctx_rule is None or ctx_rule == context - risk_ok = risk_rule is None or risk_rule == risk - conf_ok = confidence >= min_conf - if ctx_ok and risk_ok and conf_ok: - return action, reason - return "monitor_and_wait", "no_matching_rule" - - diff --git a/adaptive_runtime/runtime/cache.py.bak b/adaptive_runtime/runtime/cache.py.bak deleted file mode 100644 index 65c8f48..0000000 --- a/adaptive_runtime/runtime/cache.py.bak +++ /dev/null @@ -1,52 +0,0 @@ -""" -Lightweight TTL-based in-memory cache for the runtime. -""" - -import time -from threading import Lock - - -class TTLCache: - """ - Simple thread-safe in-memory cache with per-entry TTL. - Suitable for deduplicating events, caching context results, etc. - """ - - def __init__(self, default_ttl: float = 60.0): - self._store: dict[str, tuple[any, float]] = {} # key → (value, expires_at) - self._default_ttl = default_ttl - self._lock = Lock() - - def set(self, key: str, value, ttl: float | None = None) -> None: - expires = time.monotonic() + (ttl if ttl is not None else self._default_ttl) - with self._lock: - self._store[key] = (value, expires) - - def get(self, key: str): - with self._lock: - entry = self._store.get(key) - if entry is None: - return None - value, expires = entry - if time.monotonic() > expires: - del self._store[key] - return None - return value - - def delete(self, key: str) -> None: - with self._lock: - self._store.pop(key, None) - - def purge_expired(self) -> int: - now = time.monotonic() - with self._lock: - expired = [k for k, (_, exp) in self._store.items() if now > exp] - for k in expired: - del self._store[k] - return len(expired) - - def __len__(self) -> int: - self.purge_expired() - return len(self._store) - - diff --git a/adaptive_runtime/runtime/runtime_manager.py.bak b/adaptive_runtime/runtime/runtime_manager.py.bak deleted file mode 100644 index f3a878b..0000000 --- a/adaptive_runtime/runtime/runtime_manager.py.bak +++ /dev/null @@ -1,184 +0,0 @@ -""" -Runtime Manager - central orchestrator of all engines. - -Usage: - from adaptive_runtime import Runtime - - runtime = Runtime() - await runtime.start() - result = await runtime.process({"type": "service_overload", "severity": 0.82}) - await runtime.stop() -""" - -import asyncio - -from pydantic import BaseModel - -from ..core.state_engine import StateEngine -from ..core.context_engine import ContextEngine -from ..core.confidence_engine import ConfidenceEngine -from ..core.decision_engine import DecisionEngine -from ..core.recovery_engine import RecoveryEngine - -from .event_bus import EventBus -from .cache import TTLCache - -from ..storage.sqlite_store import SQLiteStore -from ..storage.memory_store import MemoryStore - -from ..observability.logger import get_logger -from ..observability.metrics import metrics - -logger = get_logger("runtime") - - -class RuntimeResult(BaseModel): - event_type: str - context: dict - confidence: float - action: str - reason: str - priority: str - state_persisted: bool - checkpoint_created: bool - - -class Runtime: - """ - Adaptive Runtime - wires all engines together into a single - event-driven processing loop. - - Args: - agent_id: Logical identity of the running agent. - persist: If True (default) use SQLiteStore, else MemoryStore. - checkpoint_every: Create a snapshot every N processed events. - """ - - def __init__( - self, - agent_id: str = "default", - persist: bool = True, - checkpoint_every: int = 10, - ): - self.agent_id = agent_id - self._checkpoint_every = checkpoint_every - self._event_count = 0 - self._started = False - - # Storage - self._store = SQLiteStore() if persist else MemoryStore() - - # Engines - self._state = StateEngine(self._store, agent_id) - self._context = ContextEngine() - self._confidence = ConfidenceEngine() - self._decision = DecisionEngine() - self._recovery = RecoveryEngine(self._store, agent_id) - - # Supporting components - self.bus = EventBus() - self._cache = TTLCache(default_ttl=30.0) - - - async def start(self) -> None: - """Connect storage and restore any previous state.""" - await self._store.connect() - await self._state.load_state() - self._started = True - logger.info("Runtime started agent_id='%s'", self.agent_id) - - async def stop(self) -> None: - await self._store.close() - self._started = False - logger.info("Runtime stopped agent_id='%s'", self.agent_id) - - - async def process(self, event: dict) -> RuntimeResult: - """ - Full pipeline: - Event → Context → Confidence → Decision → State → Recovery - """ - if not self._started: - await self.start() - - etype = event.get("type", "unknown") - logger.info("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━") - logger.info("Event received: %s", etype) - - self._event_count += 1 - metrics.record("runtime.events", 1) - - # 1. Context analysis - ctx_result = self._context.analyze(event) - logger.info( - "[Context Engine] risk=%s stability=%s pressure=%.2f", - ctx_result.risk, ctx_result.stability, ctx_result.pressure_score, - ) - - # 2. Confidence evaluation - conf_result = self._confidence.calculate(event, ctx_result.risk) - logger.info("[Confidence Engine] confidence=%.4f", conf_result.final) - - # 3. Decision - decision = self._decision.decide( - event, - ctx_result.context, - ctx_result.risk, - conf_result.final, - ) - logger.info("[Decision Engine] ACTION: %s", decision.action.upper()) - - # 4. State persistence - patch = { - "last_event": etype, - "last_action": decision.action, - "last_risk": ctx_result.risk, - "last_confidence": conf_result.final, - } - await self._state.patch_state(patch) - logger.info("[State Engine] State persisted") - - # Log event to store - await self._store.log_event( - self.agent_id, etype, event, - outcome={"action": decision.action, "confidence": conf_result.final}, - ) - - # 5. Checkpoint (periodic) - checkpoint_created = False - if self._event_count % self._checkpoint_every == 0: - await self._recovery.create_checkpoint(self._state.current) - checkpoint_created = True - logger.info("[Recovery Engine] Checkpoint created (#%d)", self._event_count) - - # Publish to bus (non-blocking, best-effort) - asyncio.create_task( - self.bus.publish({**event, "_decision": decision.model_dump()}) - ) - - result = RuntimeResult( - event_type=etype, - context=ctx_result.model_dump(), - confidence=conf_result.final, - action=decision.action, - reason=decision.reason, - priority=decision.priority, - state_persisted=True, - checkpoint_created=checkpoint_created, - ) - logger.info("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n") - return result - - - async def recover(self) -> dict: - """Manually trigger crash recovery.""" - return await self._recovery.crash_recovery(self._state) - - - def metrics_summary(self) -> dict: - return metrics.summary() - - async def event_history(self, limit: int = 20) -> list[dict]: - return await self._store.recent_events(self.agent_id, limit) - - diff --git a/diagnose.py b/diagnose.py deleted file mode 100644 index adfa24d..0000000 --- a/diagnose.py +++ /dev/null @@ -1,53 +0,0 @@ -""" -diagnose.py ------------ -Prints hex bytes around corrupt characters in the source files. -Run from project root: - python diagnose.py -""" -import os - -FILES = [ - "adaptive_runtime/core/confidence_engine.py", - "adaptive_runtime/runtime/runtime_manager.py", -] - -for fpath in FILES: - if not os.path.exists(fpath): - print(f"NOT FOUND: {fpath}") - continue - - with open(fpath, "rb") as f: - raw = f.read() - - print(f"\n{'='*60}") - print(f"FILE: {fpath}") - print(f"Size: {len(raw)} bytes") - - # Print first non-ASCII sequence found - i = 0 - shown = 0 - while i < len(raw) and shown < 5: - if raw[i] > 0x7f: - start = max(0, i - 4) - chunk = raw[start:i+12] - try: - context = raw[max(0,i-20):i+20].decode("cp1252", errors="replace") - except Exception: - context = "?" - print(f" pos={i:5d} hex={chunk.hex(' ')}") - print(f" context (cp1252): {repr(context)}") - shown += 1 - i += 3 - else: - i += 1 - - if shown == 0: - print(" No non-ASCII bytes found — file may already be clean UTF-8") - # Show a sample of bytes around known keywords - for keyword in [b"arrow", b"Event", b"Confidence", b"base="]: - idx = raw.find(keyword) - if idx >= 0: - chunk = raw[max(0,idx-5):idx+20] - print(f" '{keyword.decode()}' at pos {idx}: {chunk.hex(' ')}") - break diff --git a/fix_encoding.py b/fix_encoding.py deleted file mode 100644 index 0b10dc1..0000000 --- a/fix_encoding.py +++ /dev/null @@ -1,222 +0,0 @@ -""" -fix_encoding.py v3 --------------------- -Fixes mojibake in adaptive_runtime source files. - -Two corruption patterns found in this repo: - - Pattern A — ÔåÆ (arrow →, U+2192) - This is UTF-8 bytes E2 86 92 mis-read as cp1252, - then written back as those 3 cp1252 chars. - The file now contains the cp1252 bytes for Ô å Æ. - cp1252: D4 E5 C6 (or nearby variant) - - Pattern B — ├óÔÇØ┬ü (box drawing ═, U+2550) - UTF-8 bytes E2 95 90 mis-read as cp1252. - -Strategy: open file as cp1252, find the mojibake strings, -replace with correct Unicode, save as UTF-8. - -Run from project root: - python fix_encoding.py -""" - -import os -import shutil - -TARGET_DIR = "adaptive_runtime" - -# --------------------------------------------------------------------------- -# String-level replacements (read file as cp1252, fix, write as UTF-8) -# Key = what you see when file is decoded as cp1252 -# Val = correct Unicode character -# --------------------------------------------------------------------------- -STRING_FIXES = [ - # Pattern A: arrow right → - # When UTF-8 E2 86 92 is decoded as cp1252: â†' - # After a second round of mis-encoding, may appear as ÔåÆ or â†' - ("\u00e2\u0086\u0092", "\u2192"), # â†' (most common form) - ("\u00d4\u00e5\u00c6", "\u2192"), # ÔåÆ (second-round corruption) - # Fallback variants seen in CMD output - ("\u0413\u00f3\u00d4\u00c7\u00e1\u00d4\u00c7\u00d6", "\u2192"), - - # Pattern B: box drawing ═ (U+2550) - # UTF-8 E2 95 90 decoded as cp1252: â• - ("\u00e2\u0095\u0090", "\u2550"), - # CMD shows ├óÔÇØ┬ü — cp1252 bytes for that sequence: - ("\u251c\u00f3\u00d4\u00c7\u00d8\u00b0\u00fc", "\u2550"), - - # Pattern B alt: the 3-byte cp1252 read of E2 95 90 - # E2=â 95=\x95(bullet in cp1252) 90=\x90(unused, often ?) - ("\u00e2\u0095\u0090", "\u2550"), - - # em dash — (U+2014) UTF-8: E2 80 94 - ("\u00e2\u0080\u0094", "\u2014"), - - # box drawing ─ (U+2500) UTF-8: E2 94 80 - ("\u00e2\u0094\u0080", "\u2500"), -] - -# Also do a direct byte-level pass for the ═ separator lines -# Runtime_manager uses a long line of ═ chars; bytes after first fix attempt: -BOX_EQ_BYTE_PATTERNS = [ - # Original corrupt form (before any fix attempt) - b"\xc3\xa2\xe2\x80\xa2\xc2\xac", - # After partial fix attempt, may be stored as these cp1252 bytes - b"\xe2\x95\x90", # actual UTF-8 for ═ — this is already correct, skip -] - - -def fix_via_cp1252(filepath: str) -> int: - """Read as cp1252, apply string replacements, write as UTF-8.""" - try: - with open(filepath, "r", encoding="cp1252") as f: - content = f.read() - except Exception as e: - print(f" WARNING: could not read {filepath} as cp1252: {e}") - return 0 - - original = content - for bad, good in STRING_FIXES: - content = content.replace(bad, good) - - if content != original: - shutil.copy2(filepath, filepath + ".bak") - with open(filepath, "w", encoding="utf-8") as f: - f.write(content) - changed = sum(original.count(bad) for bad, _ in STRING_FIXES) - return max(changed, 1) - return 0 - - -def fix_via_bytes(filepath: str) -> int: - """Byte-level pass for ═ separator lines that survived string pass.""" - with open(filepath, "rb") as f: - raw = f.read() - original = raw - - # After the cp1252 pass the file is UTF-8. - # ÔåÆ in cp1252 is bytes D4 E5 C6 - raw = raw.replace(b"\xd4\xe5\xc6", "\u2192".encode("utf-8")) - # ├óÔÇØ┬ü read as raw cp1252 bytes - raw = raw.replace(b"\xc3\xa2\xe2\x80\xa2\xc2\xac", "\u2550".encode("utf-8")) - # â†' as raw bytes (E2 86 92 misread) - raw = raw.replace(b"\xc3\xa2\xe2\x80\xa0\xe2\x80\x99", "\u2192".encode("utf-8")) - - if raw != original: - with open(filepath, "wb") as f: - f.write(raw) - return raw.count("\u2192".encode()) + raw.count("\u2550".encode()) - return 0 - - -def get_hex_sample(filepath: str, search: bytes = b"\xe2\x86\x92") -> str: - """Return hex of first 6 bytes around search pattern, or first 12 bytes.""" - with open(filepath, "rb") as f: - raw = f.read() - idx = raw.find(search) - if idx >= 0: - start = max(0, idx - 2) - return raw[start:start+9].hex(" ") - # fallback: find any high byte - for i, b in enumerate(raw): - if b > 0x7f: - return raw[i:i+9].hex(" ") - return "(no high bytes found)" - - -def probe_encoding(filepath: str) -> str: - with open(filepath, "rb") as f: - raw = f.read(256) - if raw.startswith(b"\xef\xbb\xbf"): - return "UTF-8 BOM" - try: - raw.decode("utf-8") - return "UTF-8" - except UnicodeDecodeError: - return "non-UTF-8" - - -def main(): - if not os.path.isdir(TARGET_DIR): - print(f"ERROR: '{TARGET_DIR}' not found. Run from project root.") - return - - # --- Diagnostic first --- - print("=" * 60) - print("DIAGNOSTIC: encoding and byte samples") - print("=" * 60) - problem_files = [] - for root, _, files in os.walk(TARGET_DIR): - for fname in sorted(files): - if not fname.endswith(".py"): - continue - fpath = os.path.join(root, fname) - enc = probe_encoding(fpath) - with open(fpath, "rb") as f: - raw = f.read() - has_problem = any(b > 0x7f for b in raw[:50]) or b"\xd4\xe5\xc6" in raw or b"\xc3\xa2" in raw - # Check for known corrupt sequences - corrupt = b"\xd4\xe5\xc6" in raw or b"\xc3\xa2\xe2\x80\xa2\xc2\xac" in raw or b"\xc3\xa2\xe2\x80\xa0" in raw - if corrupt: - problem_files.append(fpath) - sample = get_hex_sample(fpath) - print(f" CORRUPT {os.path.relpath(fpath):50s} {enc} hex={sample}") - else: - print(f" ok {os.path.relpath(fpath):50s} {enc}") - - print() - - if not problem_files: - # Try a different approach: check for ÔåÆ as cp1252-decoded text - print("No known byte patterns found.") - print("Trying cp1252 text scan for ÔåÆ and ├óÔÇØ┬ü ...\n") - problem_files = [] - for root, _, files in os.walk(TARGET_DIR): - for fname in sorted(files): - if not fname.endswith(".py"): - continue - fpath = os.path.join(root, fname) - try: - with open(fpath, "r", encoding="cp1252") as f: - content = f.read() - # Look for any of the corrupt char sequences - if any(bad in content for bad, _ in STRING_FIXES): - problem_files.append(fpath) - print(f" cp1252 corrupt: {os.path.relpath(fpath)}") - except Exception: - pass - - if not problem_files: - print("No corrupt sequences found by any method.") - print("Files may already be clean after previous fix run.") - print("Check GitHub to confirm characters render correctly.") - return - - print(f"\nFixing {len(problem_files)} file(s)...\n") - total_fixed = 0 - for fpath in problem_files: - n = fix_via_cp1252(fpath) - if n: - print(f" FIXED (string pass): {os.path.relpath(fpath)} ({n} replacements)") - total_fixed += n - n2 = fix_via_bytes(fpath) - if n2: - print(f" FIXED (byte pass): {os.path.relpath(fpath)} ({n2} replacements)") - total_fixed += n2 - - print() - print("=" * 60) - if total_fixed: - print(f"Done. {total_fixed} replacement(s) made.") - print("Originals backed up as .bak") - print() - print("Verify:") - print(r' findstr /s /n "Ô" adaptive_runtime\*.py adaptive_runtime\*\*.py') - print("Expected: no output") - else: - print("No replacements made. Files may already be clean.") - - -if __name__ == "__main__": - main() diff --git a/verify.py b/verify.py deleted file mode 100644 index 0f791ff..0000000 --- a/verify.py +++ /dev/null @@ -1,40 +0,0 @@ -""" -verify.py - restore remaining .bak files and verify all UTF-8 clean -Run: python verify.py -""" -import os -import shutil - -FILES = [ - "adaptive_runtime/core/confidence_engine.py", - "adaptive_runtime/core/context_engine.py", - "adaptive_runtime/core/decision_engine.py", - "adaptive_runtime/runtime/cache.py", - "adaptive_runtime/runtime/runtime_manager.py", -] - -print("Step 1 — Restore .bak files (skip if no .bak exists)") -for f in FILES: - bak = f + ".bak" - if os.path.exists(bak): - shutil.copy2(bak, f) - print(f" restored: {f}") - else: - print(f" no bak: {f} (skipped)") - -print() -print("Step 2 — Verify all files are valid UTF-8") -all_ok = True -for f in FILES: - try: - open(f, encoding="utf-8").read() - print(f" OK {f}") - except UnicodeDecodeError as e: - print(f" FAIL {f} — {e}") - all_ok = False - -print() -if all_ok: - print("All files clean. Safe to commit to GitHub.") -else: - print("Some files still have issues. Paste output here for next fix.")