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
@@ -0,0 +1,354 @@
1
+ """
2
+ Tier Configuration Loader
3
+ =========================
4
+
5
+ Loads guard policy values from the calibrated tiers.yaml source of truth.
6
+ Provides fallback to hardcoded defaults if the YAML file is unavailable.
7
+
8
+ The tiers.yaml file contains calibration values derived from pilot runs
9
+ (November 2025 certification). This loader ensures code and config stay
10
+ synchronized.
11
+ """
12
+
13
+ from __future__ import annotations
14
+
15
+ import logging
16
+ import warnings
17
+ from functools import lru_cache
18
+ from pathlib import Path
19
+ from typing import Any, Literal
20
+
21
+ logger = logging.getLogger(__name__)
22
+
23
+ # Path to bundled tiers.yaml
24
+ _TIERS_YAML_PATH = Path(__file__).parent.parent / "_data" / "runtime" / "tiers.yaml"
25
+
26
+ # Hardcoded fallbacks (November 2025 calibration values)
27
+ # These are used if tiers.yaml cannot be loaded
28
+ _FALLBACK_CONFIG: dict[str, dict[str, Any]] = {
29
+ "balanced": {
30
+ "variance_guard": {
31
+ "deadband": 0.02,
32
+ "min_abs_adjust": 0.012,
33
+ "max_scale_step": 0.03,
34
+ "min_effect_lognll": 0.0009,
35
+ "predictive_one_sided": True,
36
+ "topk_backstop": 1,
37
+ "max_adjusted_modules": 1,
38
+ },
39
+ "spectral_guard": {
40
+ "sigma_quantile": 0.95,
41
+ "deadband": 0.10,
42
+ "scope": "all",
43
+ "max_caps": 5,
44
+ "max_spectral_norm": None,
45
+ "family_caps": {
46
+ "ffn": 2.5,
47
+ "attn": 2.8,
48
+ "embed": 3.0,
49
+ "other": 3.0,
50
+ },
51
+ "multiple_testing": {
52
+ "method": "bh",
53
+ "alpha": 0.05,
54
+ "m": 4,
55
+ },
56
+ },
57
+ "rmt_guard": {
58
+ "deadband": 0.10,
59
+ "margin": 1.5,
60
+ "epsilon_default": 0.10,
61
+ "epsilon_by_family": {
62
+ "ffn": 0.10,
63
+ "attn": 0.08,
64
+ "embed": 0.12,
65
+ "other": 0.12,
66
+ },
67
+ },
68
+ },
69
+ "conservative": {
70
+ "variance_guard": {
71
+ "deadband": 0.03,
72
+ "min_abs_adjust": 0.02,
73
+ "max_scale_step": 0.015,
74
+ "min_effect_lognll": 0.0018,
75
+ "predictive_one_sided": False,
76
+ "topk_backstop": 0,
77
+ "max_adjusted_modules": 0,
78
+ },
79
+ "spectral_guard": {
80
+ "sigma_quantile": 0.90,
81
+ "deadband": 0.05,
82
+ "scope": "ffn",
83
+ "max_caps": 3,
84
+ "family_caps": {
85
+ "ffn": 2.3,
86
+ "attn": 2.6,
87
+ "embed": 2.8,
88
+ "other": 2.8,
89
+ },
90
+ "multiple_testing": {
91
+ "method": "bonferroni",
92
+ "alpha": 0.02,
93
+ "m": 4,
94
+ },
95
+ },
96
+ "rmt_guard": {
97
+ "deadband": 0.05,
98
+ "margin": 1.3,
99
+ "epsilon_default": 0.06,
100
+ "epsilon_by_family": {
101
+ "ffn": 0.06,
102
+ "attn": 0.05,
103
+ "embed": 0.07,
104
+ "other": 0.07,
105
+ },
106
+ },
107
+ },
108
+ "aggressive": {
109
+ "variance_guard": {
110
+ "deadband": 0.12,
111
+ "min_effect_lognll": 0.0005,
112
+ },
113
+ "spectral_guard": {
114
+ "sigma_quantile": 0.98,
115
+ "deadband": 0.15,
116
+ "scope": "all",
117
+ "max_caps": 8,
118
+ "family_caps": {
119
+ "ffn": 3.0,
120
+ "attn": 3.2,
121
+ "embed": 3.5,
122
+ "other": 3.5,
123
+ },
124
+ "multiple_testing": {
125
+ "method": "bh",
126
+ "alpha": 0.1,
127
+ "m": 4,
128
+ },
129
+ },
130
+ "rmt_guard": {
131
+ "deadband": 0.15,
132
+ "margin": 1.8,
133
+ "epsilon_default": 0.15,
134
+ "epsilon_by_family": {
135
+ "ffn": 0.15,
136
+ "attn": 0.15,
137
+ "embed": 0.15,
138
+ "other": 0.15,
139
+ },
140
+ },
141
+ },
142
+ }
143
+
144
+ TierName = Literal["balanced", "conservative", "aggressive"]
145
+ GuardType = Literal["spectral_guard", "rmt_guard", "variance_guard"]
146
+
147
+
148
+ def _load_yaml() -> dict[str, Any] | None:
149
+ """Load tiers.yaml if available."""
150
+ try:
151
+ import yaml
152
+ except ImportError:
153
+ logger.debug("PyYAML not installed; using fallback tier config")
154
+ return None
155
+
156
+ if not _TIERS_YAML_PATH.exists():
157
+ logger.debug("tiers.yaml not found at %s; using fallback", _TIERS_YAML_PATH)
158
+ return None
159
+
160
+ try:
161
+ with open(_TIERS_YAML_PATH) as f:
162
+ data = yaml.safe_load(f)
163
+ if not isinstance(data, dict):
164
+ logger.warning("tiers.yaml did not parse as dict; using fallback")
165
+ return None
166
+ return data
167
+ except Exception as exc:
168
+ logger.warning("Failed to load tiers.yaml: %s; using fallback", exc)
169
+ return None
170
+
171
+
172
+ @lru_cache(maxsize=1)
173
+ def load_tier_config() -> dict[str, dict[str, Any]]:
174
+ """
175
+ Load tier configuration from tiers.yaml with fallback to hardcoded defaults.
176
+
177
+ Returns:
178
+ Dict mapping tier names to guard configurations.
179
+ Structure: {tier: {guard_type: {param: value}}}
180
+
181
+ The result is cached for efficiency. Call clear_tier_config_cache() to reload.
182
+ """
183
+ yaml_data = _load_yaml()
184
+ if yaml_data is None:
185
+ return _FALLBACK_CONFIG.copy()
186
+
187
+ # Merge yaml data with fallbacks for any missing keys
188
+ result: dict[str, dict[str, Any]] = {}
189
+ for tier in ("balanced", "conservative", "aggressive"):
190
+ tier_data = yaml_data.get(tier, {})
191
+ fallback_tier = _FALLBACK_CONFIG.get(tier, {})
192
+
193
+ result[tier] = {}
194
+ for guard in ("spectral_guard", "rmt_guard", "variance_guard"):
195
+ guard_data = tier_data.get(guard, {})
196
+ fallback_guard = fallback_tier.get(guard, {})
197
+
198
+ # Deep merge: YAML values override fallbacks
199
+ merged = _deep_merge(fallback_guard, guard_data)
200
+ result[tier][guard] = merged
201
+
202
+ return result
203
+
204
+
205
+ def _deep_merge(base: dict[str, Any], override: dict[str, Any]) -> dict[str, Any]:
206
+ """Recursively merge override into base, returning new dict."""
207
+ result = base.copy()
208
+ for key, value in override.items():
209
+ if key in result and isinstance(result[key], dict) and isinstance(value, dict):
210
+ result[key] = _deep_merge(result[key], value)
211
+ else:
212
+ result[key] = value
213
+ return result
214
+
215
+
216
+ def clear_tier_config_cache() -> None:
217
+ """Clear cached tier config to force reload on next access."""
218
+ load_tier_config.cache_clear()
219
+
220
+
221
+ def get_tier_guard_config(
222
+ tier: TierName,
223
+ guard: GuardType,
224
+ ) -> dict[str, Any]:
225
+ """
226
+ Get configuration for a specific tier and guard type.
227
+
228
+ Args:
229
+ tier: Tier name ("balanced", "conservative", "aggressive")
230
+ guard: Guard type ("spectral_guard", "rmt_guard", "variance_guard")
231
+
232
+ Returns:
233
+ Guard configuration dict with all calibrated values.
234
+
235
+ Example:
236
+ >>> config = get_tier_guard_config("balanced", "rmt_guard")
237
+ >>> config["epsilon_by_family"]["ffn"]
238
+ 0.10
239
+ """
240
+ config = load_tier_config()
241
+ tier_config = config.get(tier, config.get("balanced", {}))
242
+ return tier_config.get(guard, {}).copy()
243
+
244
+
245
+ def get_spectral_caps(tier: TierName = "balanced") -> dict[str, float]:
246
+ """Get spectral κ caps for a tier (family -> kappa value)."""
247
+ config = get_tier_guard_config(tier, "spectral_guard")
248
+ return config.get("family_caps", {}).copy()
249
+
250
+
251
+ def get_rmt_epsilon(tier: TierName = "balanced") -> dict[str, float]:
252
+ """Get RMT ε values for a tier (family -> epsilon value)."""
253
+ config = get_tier_guard_config(tier, "rmt_guard")
254
+ return config.get("epsilon_by_family", {}).copy()
255
+
256
+
257
+ def get_variance_min_effect(tier: TierName = "balanced") -> float:
258
+ """Get VE min_effect_lognll for a tier."""
259
+ config = get_tier_guard_config(tier, "variance_guard")
260
+ return config.get("min_effect_lognll", 0.0009)
261
+
262
+
263
+ def check_drift(
264
+ *,
265
+ silent: bool = False,
266
+ ) -> dict[str, list[str]]:
267
+ """
268
+ Check for drift between tiers.yaml and hardcoded fallbacks.
269
+
270
+ This helps detect when tiers.yaml has been updated but hardcoded
271
+ fallbacks haven't been synchronized.
272
+
273
+ Args:
274
+ silent: If True, don't emit warnings (just return drift info)
275
+
276
+ Returns:
277
+ Dict of tier -> list of drift descriptions.
278
+ Empty dict means no drift detected.
279
+
280
+ Example:
281
+ >>> drift = check_drift()
282
+ >>> if drift:
283
+ ... print("Drift detected:", drift)
284
+ """
285
+ yaml_data = _load_yaml()
286
+ if yaml_data is None:
287
+ # No YAML means we're using fallbacks exclusively
288
+ return {}
289
+
290
+ drifts: dict[str, list[str]] = {}
291
+
292
+ for tier in ("balanced", "conservative", "aggressive"):
293
+ tier_drifts: list[str] = []
294
+ yaml_tier = yaml_data.get(tier, {})
295
+ fallback_tier = _FALLBACK_CONFIG.get(tier, {})
296
+
297
+ for guard in ("spectral_guard", "rmt_guard", "variance_guard"):
298
+ yaml_guard = yaml_tier.get(guard, {})
299
+ fallback_guard = fallback_tier.get(guard, {})
300
+
301
+ drift_keys = _find_drifts(yaml_guard, fallback_guard, prefix=f"{guard}.")
302
+ tier_drifts.extend(drift_keys)
303
+
304
+ if tier_drifts:
305
+ drifts[tier] = tier_drifts
306
+ if not silent:
307
+ warnings.warn(
308
+ f"Tier config drift detected in '{tier}': "
309
+ f"{', '.join(tier_drifts[:3])}{'...' if len(tier_drifts) > 3 else ''}. "
310
+ "Consider updating hardcoded fallbacks in tier_config.py",
311
+ UserWarning,
312
+ stacklevel=2,
313
+ )
314
+
315
+ return drifts
316
+
317
+
318
+ def _find_drifts(
319
+ yaml_data: dict[str, Any],
320
+ fallback_data: dict[str, Any],
321
+ prefix: str = "",
322
+ ) -> list[str]:
323
+ """Find keys where YAML differs from fallback."""
324
+ drifts: list[str] = []
325
+
326
+ all_keys = set(yaml_data.keys()) | set(fallback_data.keys())
327
+ for key in all_keys:
328
+ yaml_val = yaml_data.get(key)
329
+ fallback_val = fallback_data.get(key)
330
+
331
+ # Skip if fallback doesn't have this key (YAML-only keys are fine)
332
+ if fallback_val is None:
333
+ continue
334
+
335
+ if isinstance(yaml_val, dict) and isinstance(fallback_val, dict):
336
+ nested = _find_drifts(yaml_val, fallback_val, prefix=f"{prefix}{key}.")
337
+ drifts.extend(nested)
338
+ elif yaml_val != fallback_val:
339
+ drifts.append(f"{prefix}{key}: yaml={yaml_val} vs fallback={fallback_val}")
340
+
341
+ return drifts
342
+
343
+
344
+ __all__ = [
345
+ "load_tier_config",
346
+ "clear_tier_config_cache",
347
+ "get_tier_guard_config",
348
+ "get_spectral_caps",
349
+ "get_rmt_epsilon",
350
+ "get_variance_min_effect",
351
+ "check_drift",
352
+ "TierName",
353
+ "GuardType",
354
+ ]