invarlock 0.2.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (132) hide show
  1. invarlock/__init__.py +33 -0
  2. invarlock/__main__.py +10 -0
  3. invarlock/_data/runtime/profiles/ci_cpu.yaml +15 -0
  4. invarlock/_data/runtime/profiles/release.yaml +23 -0
  5. invarlock/_data/runtime/tiers.yaml +76 -0
  6. invarlock/adapters/__init__.py +102 -0
  7. invarlock/adapters/_capabilities.py +45 -0
  8. invarlock/adapters/auto.py +99 -0
  9. invarlock/adapters/base.py +530 -0
  10. invarlock/adapters/base_types.py +85 -0
  11. invarlock/adapters/hf_bert.py +852 -0
  12. invarlock/adapters/hf_gpt2.py +403 -0
  13. invarlock/adapters/hf_llama.py +485 -0
  14. invarlock/adapters/hf_mixin.py +383 -0
  15. invarlock/adapters/hf_onnx.py +112 -0
  16. invarlock/adapters/hf_t5.py +137 -0
  17. invarlock/adapters/py.typed +1 -0
  18. invarlock/assurance/__init__.py +43 -0
  19. invarlock/cli/__init__.py +8 -0
  20. invarlock/cli/__main__.py +8 -0
  21. invarlock/cli/_evidence.py +25 -0
  22. invarlock/cli/_json.py +75 -0
  23. invarlock/cli/adapter_auto.py +162 -0
  24. invarlock/cli/app.py +287 -0
  25. invarlock/cli/commands/__init__.py +26 -0
  26. invarlock/cli/commands/certify.py +403 -0
  27. invarlock/cli/commands/doctor.py +1358 -0
  28. invarlock/cli/commands/explain_gates.py +151 -0
  29. invarlock/cli/commands/export_html.py +100 -0
  30. invarlock/cli/commands/plugins.py +1331 -0
  31. invarlock/cli/commands/report.py +354 -0
  32. invarlock/cli/commands/run.py +4146 -0
  33. invarlock/cli/commands/verify.py +1040 -0
  34. invarlock/cli/config.py +396 -0
  35. invarlock/cli/constants.py +68 -0
  36. invarlock/cli/device.py +92 -0
  37. invarlock/cli/doctor_helpers.py +74 -0
  38. invarlock/cli/errors.py +6 -0
  39. invarlock/cli/overhead_utils.py +60 -0
  40. invarlock/cli/provenance.py +66 -0
  41. invarlock/cli/utils.py +41 -0
  42. invarlock/config.py +56 -0
  43. invarlock/core/__init__.py +62 -0
  44. invarlock/core/abi.py +15 -0
  45. invarlock/core/api.py +274 -0
  46. invarlock/core/auto_tuning.py +317 -0
  47. invarlock/core/bootstrap.py +226 -0
  48. invarlock/core/checkpoint.py +221 -0
  49. invarlock/core/contracts.py +73 -0
  50. invarlock/core/error_utils.py +64 -0
  51. invarlock/core/events.py +298 -0
  52. invarlock/core/exceptions.py +95 -0
  53. invarlock/core/registry.py +481 -0
  54. invarlock/core/retry.py +146 -0
  55. invarlock/core/runner.py +2041 -0
  56. invarlock/core/types.py +154 -0
  57. invarlock/edits/__init__.py +12 -0
  58. invarlock/edits/_edit_utils.py +249 -0
  59. invarlock/edits/_external_utils.py +268 -0
  60. invarlock/edits/noop.py +47 -0
  61. invarlock/edits/py.typed +1 -0
  62. invarlock/edits/quant_rtn.py +801 -0
  63. invarlock/edits/registry.py +166 -0
  64. invarlock/eval/__init__.py +23 -0
  65. invarlock/eval/bench.py +1207 -0
  66. invarlock/eval/bootstrap.py +50 -0
  67. invarlock/eval/data.py +2052 -0
  68. invarlock/eval/metrics.py +2167 -0
  69. invarlock/eval/primary_metric.py +767 -0
  70. invarlock/eval/probes/__init__.py +24 -0
  71. invarlock/eval/probes/fft.py +139 -0
  72. invarlock/eval/probes/mi.py +213 -0
  73. invarlock/eval/probes/post_attention.py +323 -0
  74. invarlock/eval/providers/base.py +67 -0
  75. invarlock/eval/providers/seq2seq.py +111 -0
  76. invarlock/eval/providers/text_lm.py +113 -0
  77. invarlock/eval/providers/vision_text.py +93 -0
  78. invarlock/eval/py.typed +1 -0
  79. invarlock/guards/__init__.py +18 -0
  80. invarlock/guards/_contracts.py +9 -0
  81. invarlock/guards/invariants.py +640 -0
  82. invarlock/guards/policies.py +805 -0
  83. invarlock/guards/py.typed +1 -0
  84. invarlock/guards/rmt.py +2097 -0
  85. invarlock/guards/spectral.py +1419 -0
  86. invarlock/guards/tier_config.py +354 -0
  87. invarlock/guards/variance.py +3298 -0
  88. invarlock/guards_ref/__init__.py +15 -0
  89. invarlock/guards_ref/rmt_ref.py +40 -0
  90. invarlock/guards_ref/spectral_ref.py +135 -0
  91. invarlock/guards_ref/variance_ref.py +60 -0
  92. invarlock/model_profile.py +353 -0
  93. invarlock/model_utils.py +221 -0
  94. invarlock/observability/__init__.py +10 -0
  95. invarlock/observability/alerting.py +535 -0
  96. invarlock/observability/core.py +546 -0
  97. invarlock/observability/exporters.py +565 -0
  98. invarlock/observability/health.py +588 -0
  99. invarlock/observability/metrics.py +457 -0
  100. invarlock/observability/py.typed +1 -0
  101. invarlock/observability/utils.py +553 -0
  102. invarlock/plugins/__init__.py +12 -0
  103. invarlock/plugins/hello_guard.py +33 -0
  104. invarlock/plugins/hf_awq_adapter.py +82 -0
  105. invarlock/plugins/hf_bnb_adapter.py +79 -0
  106. invarlock/plugins/hf_gptq_adapter.py +78 -0
  107. invarlock/plugins/py.typed +1 -0
  108. invarlock/py.typed +1 -0
  109. invarlock/reporting/__init__.py +7 -0
  110. invarlock/reporting/certificate.py +3221 -0
  111. invarlock/reporting/certificate_schema.py +244 -0
  112. invarlock/reporting/dataset_hashing.py +215 -0
  113. invarlock/reporting/guards_analysis.py +948 -0
  114. invarlock/reporting/html.py +32 -0
  115. invarlock/reporting/normalizer.py +235 -0
  116. invarlock/reporting/policy_utils.py +517 -0
  117. invarlock/reporting/primary_metric_utils.py +265 -0
  118. invarlock/reporting/render.py +1442 -0
  119. invarlock/reporting/report.py +903 -0
  120. invarlock/reporting/report_types.py +278 -0
  121. invarlock/reporting/utils.py +175 -0
  122. invarlock/reporting/validate.py +631 -0
  123. invarlock/security.py +176 -0
  124. invarlock/sparsity_utils.py +323 -0
  125. invarlock/utils/__init__.py +150 -0
  126. invarlock/utils/digest.py +45 -0
  127. invarlock-0.2.0.dist-info/METADATA +586 -0
  128. invarlock-0.2.0.dist-info/RECORD +132 -0
  129. invarlock-0.2.0.dist-info/WHEEL +5 -0
  130. invarlock-0.2.0.dist-info/entry_points.txt +20 -0
  131. invarlock-0.2.0.dist-info/licenses/LICENSE +201 -0
  132. invarlock-0.2.0.dist-info/top_level.txt +1 -0
invarlock/__init__.py ADDED
@@ -0,0 +1,33 @@
1
+ """
2
+ InvarLock: Edit‑agnostic robustness certificates for weight edits
3
+ =============================================================
4
+
5
+ Core runtime package — torch-independent utilities, configuration, and interfaces.
6
+
7
+ This package provides the foundation for the InvarLock GuardChain without heavy dependencies.
8
+ For torch-dependent functionality, see subpackages under `invarlock.*`:
9
+ - `invarlock.adapters`: Model adapters (HF GPT-2/BERT/LLaMA, auto)
10
+ - `invarlock.guards`: Safety mechanisms (invariants, spectral, RMT, variance)
11
+ - `invarlock.edits`: Built-in quantization and edit interfaces
12
+ - `invarlock.eval`: Metrics, guard-overhead checks, and certification
13
+ """
14
+
15
+ __version__ = "0.2.0"
16
+
17
+ # Core exports - torch-independent
18
+ from .config import CFG, Defaults, get_default_config
19
+
20
+ __all__ = ["__version__", "get_default_config", "Defaults", "CFG"]
21
+
22
+
23
+ def __getattr__(name: str): # pragma: no cover - thin lazy loader
24
+ """Lazily expose selected subpackages without importing heavy deps at init.
25
+
26
+ This keeps `import invarlock` torch-free while allowing patterns like
27
+ monkeypatching `invarlock.eval.*` in tests.
28
+ """
29
+ if name == "eval":
30
+ import importlib
31
+
32
+ return importlib.import_module(".eval", __name__)
33
+ raise AttributeError(name)
invarlock/__main__.py ADDED
@@ -0,0 +1,10 @@
1
+ from .cli.app import app
2
+
3
+
4
+ def main() -> None:
5
+ """Entry point for `python -m invarlock`."""
6
+ app()
7
+
8
+
9
+ if __name__ == "__main__":
10
+ main()
@@ -0,0 +1,15 @@
1
+ # CPU-only CI profile for telemetry/edge validation runs.
2
+ #
3
+ # Forces CPU execution and trims window counts slightly so telemetry runs
4
+ # finish quickly on developer machines while still exercising guards.
5
+
6
+ model:
7
+ device: "cpu"
8
+
9
+ dataset:
10
+ preview_n: 120
11
+ final_n: 120
12
+ stride: 512
13
+
14
+ context:
15
+ telemetry_profile: "ci_cpu"
@@ -0,0 +1,23 @@
1
+ # Release profile overrides for certification workflows.
2
+ #
3
+ # Applies denser evaluation coverage, longer bootstrap runs, and embeds
4
+ # policy snapshot metadata so artifacts can be audited after publication.
5
+
6
+ dataset:
7
+ preview_n: 400
8
+ final_n: 400
9
+
10
+ guards:
11
+ variance:
12
+ max_calib: 320
13
+
14
+ eval:
15
+ bootstrap:
16
+ replicates: 3200
17
+ alpha: 0.05
18
+ method: bca_paired_delta_log
19
+
20
+ context:
21
+ policy_snapshot:
22
+ tiers_file: src/invarlock/_data/runtime/tiers.yaml
23
+ profile: release
@@ -0,0 +1,76 @@
1
+ # Tier guard policy knobs for variance correction (balanced vs conservative)
2
+ #
3
+ # These values mirror the settings validated during the December 2025
4
+ # calibration runs. They should be kept in sync with the policy digest
5
+ # embedded in certificates and referenced by automation documentation.
6
+
7
+ balanced:
8
+ variance_guard:
9
+ deadband: 0.02
10
+ min_abs_adjust: 0.012
11
+ max_scale_step: 0.03
12
+ min_effect_lognll: 0.0009
13
+ predictive_one_sided: true
14
+ topk_backstop: 1
15
+ max_adjusted_modules: 1
16
+ tap: "transformer.h.*.mlp.c_proj"
17
+ predictive_gate: true
18
+ spectral_guard:
19
+ sigma_quantile: 0.95
20
+ deadband: 0.10
21
+ scope: all
22
+ max_caps: 5
23
+ max_spectral_norm: null
24
+ family_caps:
25
+ ffn: 3.834
26
+ attn: 3.423
27
+ embed: 3.1
28
+ other: 3.1
29
+ multiple_testing:
30
+ method: bh
31
+ alpha: 0.05
32
+ m: 4
33
+ rmt_guard:
34
+ deadband: 0.10
35
+ margin: 1.5
36
+ epsilon_default: 0.10
37
+ epsilon_by_family:
38
+ ffn: 0.10
39
+ attn: 0.08
40
+ embed: 0.12
41
+ other: 0.12
42
+
43
+ conservative:
44
+ variance_guard:
45
+ deadband: 0.03
46
+ min_abs_adjust: 0.02
47
+ max_scale_step: 0.015
48
+ min_effect_lognll: 0.0018
49
+ predictive_one_sided: false
50
+ topk_backstop: 0
51
+ max_adjusted_modules: 0
52
+ tap: "transformer.h.*.mlp.c_proj"
53
+ predictive_gate: true
54
+ spectral_guard:
55
+ sigma_quantile: 0.90
56
+ deadband: 0.05
57
+ scope: ffn
58
+ max_caps: 3
59
+ family_caps:
60
+ ffn: 2.3
61
+ attn: 2.6
62
+ embed: 2.8
63
+ other: 2.8
64
+ multiple_testing:
65
+ method: bonferroni
66
+ alpha: 0.02
67
+ m: 4
68
+ rmt_guard:
69
+ deadband: 0.05
70
+ margin: 1.3
71
+ epsilon_default: 0.06
72
+ epsilon_by_family:
73
+ ffn: 0.06
74
+ attn: 0.05
75
+ embed: 0.07
76
+ other: 0.07
@@ -0,0 +1,102 @@
1
+ """Adapter namespace (`invarlock.adapters`) exposing built-in adapters.
2
+
3
+ Provides lazy import of heavy submodules to avoid importing Transformers
4
+ stacks unless needed. Accessing adapter classes triggers on-demand import.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import importlib as _importlib
10
+ from typing import Any as _Any
11
+
12
+ from invarlock.core.abi import INVARLOCK_CORE_ABI as INVARLOCK_CORE_ABI
13
+
14
+ from .base import (
15
+ AdapterConfig,
16
+ AdapterInterface,
17
+ BaseAdapter,
18
+ DeviceManager,
19
+ )
20
+ from .base import (
21
+ PerformanceMetrics as BasePerformanceMetrics,
22
+ )
23
+
24
+ _LAZY_MAP = {
25
+ "HF_BERT_Adapter": ".hf_bert",
26
+ "HF_GPT2_Adapter": ".hf_gpt2",
27
+ "HF_LLaMA_Adapter": ".hf_llama",
28
+ "HF_T5_Adapter": ".hf_t5",
29
+ "HF_ORT_CausalLM_Adapter": ".hf_onnx",
30
+ "HF_Causal_Auto_Adapter": ".auto",
31
+ "HF_MLM_Auto_Adapter": ".auto",
32
+ }
33
+
34
+
35
+ def __getattr__(name: str) -> _Any: # pragma: no cover - simple lazy import
36
+ mod_name = _LAZY_MAP.get(name)
37
+ if not mod_name:
38
+ raise AttributeError(name)
39
+ module = _importlib.import_module(mod_name, __name__)
40
+ try:
41
+ return getattr(module, name)
42
+ except AttributeError as exc: # re-raise with module context
43
+ raise AttributeError(f"{name} not found in {mod_name}") from exc
44
+
45
+
46
+ # Simple quality label helper used by tests
47
+ def quality_label(ratio: float) -> str:
48
+ if ratio <= 1.10:
49
+ return "Excellent"
50
+ if ratio <= 1.25:
51
+ return "Good"
52
+ if ratio <= 1.40:
53
+ return "Fair"
54
+ return "Degraded"
55
+
56
+
57
+ class _RemovedComponent:
58
+ def __init__(self, name: str, replacement: str | None = None):
59
+ self._name = name
60
+ self._replacement = replacement
61
+
62
+ def __call__(self, *args, **kwargs):
63
+ raise NotImplementedError(
64
+ f"{self._name} is not available in InvarLock 1.0."
65
+ + (f" Use: {self._replacement}" if self._replacement else "")
66
+ )
67
+
68
+ def __getattr__(self, _): # pragma: no cover - simple passthrough
69
+ return _RemovedComponent(self._name, self._replacement)
70
+
71
+
72
+ # Placeholders for removed/legacy utilities referenced in tests
73
+ HF_Pythia_Adapter = _RemovedComponent("HF_Pythia_Adapter")
74
+ auto_tune_pruning_budget = _RemovedComponent("auto_tune_pruning_budget")
75
+ run_auto_invarlock = _RemovedComponent("run_auto_invarlock")
76
+ InvarLockPipeline = _RemovedComponent("InvarLockPipeline", "invarlock.cli.app:main")
77
+ InvarLockConfig = _RemovedComponent(
78
+ "InvarLockConfig", "invarlock.cli.config:InvarLockConfig"
79
+ )
80
+ run_invarlock_pipeline = _RemovedComponent(
81
+ "run_invarlock_pipeline", "invarlock.cli.run"
82
+ )
83
+ run_invarlock = _RemovedComponent("run_invarlock", "invarlock.cli.run")
84
+ quick_prune_gpt2 = _RemovedComponent("quick_prune_gpt2")
85
+
86
+ __all__ = [
87
+ "HF_GPT2_Adapter",
88
+ "HF_BERT_Adapter",
89
+ "HF_LLaMA_Adapter",
90
+ "HF_T5_Adapter",
91
+ "HF_ORT_CausalLM_Adapter",
92
+ "HF_Causal_Auto_Adapter",
93
+ "HF_MLM_Auto_Adapter",
94
+ "BaseAdapter",
95
+ "AdapterConfig",
96
+ "AdapterInterface",
97
+ "DeviceManager",
98
+ "BasePerformanceMetrics",
99
+ "quality_label",
100
+ "_RemovedComponent",
101
+ "INVARLOCK_CORE_ABI",
102
+ ]
@@ -0,0 +1,45 @@
1
+ """
2
+ Model capability probes (lightweight, no heavy imports).
3
+
4
+ Currently supports a simple MoE probe that scans module names to infer whether
5
+ the model likely contains mixture-of-experts structures (router/gating and
6
+ expert FFNs).
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ from typing import Any
12
+
13
+
14
+ def probe_moe_capabilities(model: Any) -> dict[str, Any]:
15
+ """Detect MoE-related modules in a model by name heuristics.
16
+
17
+ Returns:
18
+ {
19
+ 'moe': bool,
20
+ 'families': set[str], # e.g., {'router','expert_ffn'}
21
+ 'counts': dict[str,int], # occurrences per family
22
+ }
23
+ """
24
+ families: set[str] = set()
25
+ counts: dict[str, int] = {}
26
+ try:
27
+ for name, _module in model.named_modules():
28
+ lname = str(name).lower()
29
+ # Router/gating indicators
30
+ if any(
31
+ tok in lname
32
+ for tok in ("router", "routing", "gate", "gating", "dispatch", "switch")
33
+ ):
34
+ families.add("router")
35
+ counts["router"] = counts.get("router", 0) + 1
36
+ # Expert FFN indicators
37
+ if any(
38
+ tok in lname
39
+ for tok in ("experts", "expert", "moe", "mixture_of_experts")
40
+ ):
41
+ families.add("expert_ffn")
42
+ counts["expert_ffn"] = counts.get("expert_ffn", 0) + 1
43
+ except Exception:
44
+ pass
45
+ return {"moe": bool(families), "families": families, "counts": counts}
@@ -0,0 +1,99 @@
1
+ from __future__ import annotations
2
+
3
+ import importlib as _importlib
4
+ from typing import Any
5
+
6
+ from invarlock.core.api import ModelAdapter
7
+
8
+ from ..cli.adapter_auto import resolve_auto_adapter
9
+
10
+
11
+ class _DelegatingAdapter(ModelAdapter):
12
+ name = "auto_adapter"
13
+
14
+ def __init__(self) -> None:
15
+ self._delegate: ModelAdapter | None = None
16
+
17
+ def _ensure_delegate_from_id(self, model_id: str) -> ModelAdapter:
18
+ if self._delegate is not None:
19
+ return self._delegate
20
+ resolved = resolve_auto_adapter(model_id)
21
+ if resolved == "hf_llama":
22
+ HF_LLaMA_Adapter = _importlib.import_module(
23
+ ".hf_llama", __package__
24
+ ).HF_LLaMA_Adapter
25
+ self._delegate = HF_LLaMA_Adapter()
26
+ elif resolved == "hf_bert":
27
+ HF_BERT_Adapter = _importlib.import_module(
28
+ ".hf_bert", __package__
29
+ ).HF_BERT_Adapter
30
+ self._delegate = HF_BERT_Adapter()
31
+ else:
32
+ HF_GPT2_Adapter = _importlib.import_module(
33
+ ".hf_gpt2", __package__
34
+ ).HF_GPT2_Adapter
35
+ self._delegate = HF_GPT2_Adapter()
36
+ return self._delegate
37
+
38
+ def _ensure_delegate_from_model(self, model: Any) -> ModelAdapter:
39
+ # Best-effort: inspect class name
40
+ cls_name = getattr(model, "__class__", type(model)).__name__.lower()
41
+ if any(k in cls_name for k in ["llama", "mistral", "qwen", "yi"]):
42
+ HF_LLaMA_Adapter = _importlib.import_module(
43
+ ".hf_llama", __package__
44
+ ).HF_LLaMA_Adapter
45
+ self._delegate = HF_LLaMA_Adapter()
46
+ elif any(k in cls_name for k in ["bert", "roberta", "albert", "deberta"]):
47
+ HF_BERT_Adapter = _importlib.import_module(
48
+ ".hf_bert", __package__
49
+ ).HF_BERT_Adapter
50
+ self._delegate = HF_BERT_Adapter()
51
+ else:
52
+ HF_GPT2_Adapter = _importlib.import_module(
53
+ ".hf_gpt2", __package__
54
+ ).HF_GPT2_Adapter
55
+ self._delegate = HF_GPT2_Adapter()
56
+ return self._delegate
57
+
58
+ def can_handle(self, model: Any) -> bool: # pragma: no cover - trivial
59
+ return True
60
+
61
+ def describe(self, model: Any) -> dict[str, Any]:
62
+ delegate = self._delegate or self._ensure_delegate_from_model(model)
63
+ return delegate.describe(model)
64
+
65
+ def snapshot(self, model: Any) -> bytes:
66
+ delegate = self._delegate or self._ensure_delegate_from_model(model)
67
+ return delegate.snapshot(model)
68
+
69
+ def restore(self, model: Any, blob: bytes) -> None:
70
+ delegate = self._delegate or self._ensure_delegate_from_model(model)
71
+ return delegate.restore(model, blob)
72
+
73
+ def __getattr__(self, item: str): # pragma: no cover - passthrough
74
+ if item == "_delegate":
75
+ raise AttributeError(item)
76
+ delegate = self._delegate
77
+ if delegate is not None and hasattr(delegate, item):
78
+ return getattr(delegate, item)
79
+ raise AttributeError(item)
80
+
81
+
82
+ class HF_Causal_Auto_Adapter(_DelegatingAdapter):
83
+ name = "hf_causal_auto"
84
+
85
+ def load_model(self, model_id: str, device: str = "auto") -> Any:
86
+ delegate = self._ensure_delegate_from_id(model_id)
87
+ return delegate.load_model(model_id, device=device)
88
+
89
+
90
+ class HF_MLM_Auto_Adapter(_DelegatingAdapter):
91
+ name = "hf_mlm_auto"
92
+
93
+ def load_model(self, model_id: str, device: str = "auto") -> Any:
94
+ # Force BERT-like adapter for MLM families
95
+ HF_BERT_Adapter = _importlib.import_module(
96
+ ".hf_bert", __package__
97
+ ).HF_BERT_Adapter
98
+ self._delegate = HF_BERT_Adapter()
99
+ return self._delegate.load_model(model_id, device=device)