"""Environment inspection -> evidence/environment.json (no secret values are printed, only presence).""" from __future__ import annotations import json, os, platform, subprocess, sys, shutil, re from pathlib import Path from .util import utc_now, ROOT, write_json def _sysctl(k): try: return subprocess.check_output(["sysctl", "-n", k], text=True).strip() except Exception: return None def _env_key_present(name: str) -> bool: if os.environ.get(name): return True for p in [ROOT / ".env", Path.home() / "Desktop/Recherche-UQO/immobilier/agentic-valuation-quebec/.env"]: if p.exists() and re.search(rf"^{name}=\S+", p.read_text(), re.M): return True return False def doctor() -> dict: import mlx.core as mx, mlx_lm, numpy, psutil info = { "timestamp_utc": utc_now(), "hostname": platform.node(), "os": platform.platform(), "macos_version": platform.mac_ver()[0], "cpu": _sysctl("machdep.cpu.brand_string"), "cores": os.cpu_count(), "memory_bytes": int(_sysctl("hw.memsize") or 0), "disk_free_bytes": shutil.disk_usage(str(ROOT)).free, "python": sys.version, "executable": sys.executable, "mlx": mx.__version__, "mlx_lm": mlx_lm.__version__, "numpy": numpy.__version__, "metal_available": bool(mx.metal.is_available()) if hasattr(mx, "metal") else None, "device_info": {k: (v if isinstance(v, (int, str)) else str(v)) for k, v in (mx.device_info().items() if hasattr(mx, "device_info") else [])}, "credentials_present": {k: _env_key_present(k) for k in ["ANTHROPIC_API_KEY", "OPENAI_API_KEY", "HF_TOKEN"]}, "hf_cache_models": sorted(p.name for p in (Path.home() / ".cache/huggingface/hub").glob("models--*")), "latex": {t: shutil.which(t) for t in ["latexmk", "pdflatex", "lualatex", "bibtex"]}, "git_commit": None, } try: import torch; info["torch"] = torch.__version__; info["torch_mps"] = bool(torch.backends.mps.is_available()) except Exception as e: info["torch"] = f"unavailable: {type(e).__name__}" try: import llmlingua; info["llmlingua"] = getattr(llmlingua, "__version__", "installed") except Exception: info["llmlingua"] = None try: info["git_commit"] = subprocess.check_output(["git", "-C", str(ROOT), "rev-parse", "HEAD"], text=True, stderr=subprocess.DEVNULL).strip() except Exception: pass return info def main(output: str): info = doctor(); write_json(output, info) print(json.dumps({k: info[k] for k in ["hostname", "cpu", "memory_bytes", "mlx", "mlx_lm", "python", "credentials_present"]}, indent=1)) return 0