invarlock 0.3.5__py3-none-any.whl → 0.3.7__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 (74) hide show
  1. invarlock/__init__.py +2 -2
  2. invarlock/_data/runtime/tiers.yaml +57 -30
  3. invarlock/adapters/__init__.py +11 -15
  4. invarlock/adapters/auto.py +35 -40
  5. invarlock/adapters/capabilities.py +2 -2
  6. invarlock/adapters/hf_causal.py +418 -0
  7. invarlock/adapters/{hf_onnx.py → hf_causal_onnx.py} +3 -3
  8. invarlock/adapters/hf_mixin.py +25 -4
  9. invarlock/adapters/{hf_bert.py → hf_mlm.py} +4 -11
  10. invarlock/adapters/{hf_t5.py → hf_seq2seq.py} +9 -9
  11. invarlock/calibration/spectral_null.py +15 -10
  12. invarlock/calibration/variance_ve.py +0 -2
  13. invarlock/cli/adapter_auto.py +31 -21
  14. invarlock/cli/app.py +73 -2
  15. invarlock/cli/commands/calibrate.py +6 -2
  16. invarlock/cli/commands/certify.py +651 -91
  17. invarlock/cli/commands/doctor.py +11 -11
  18. invarlock/cli/commands/explain_gates.py +57 -8
  19. invarlock/cli/commands/plugins.py +13 -9
  20. invarlock/cli/commands/report.py +233 -69
  21. invarlock/cli/commands/run.py +1066 -244
  22. invarlock/cli/commands/verify.py +154 -15
  23. invarlock/cli/config.py +22 -6
  24. invarlock/cli/doctor_helpers.py +4 -5
  25. invarlock/cli/output.py +193 -0
  26. invarlock/cli/provenance.py +1 -1
  27. invarlock/core/api.py +45 -5
  28. invarlock/core/auto_tuning.py +65 -20
  29. invarlock/core/bootstrap.py +1 -1
  30. invarlock/core/contracts.py +7 -1
  31. invarlock/core/registry.py +11 -13
  32. invarlock/core/runner.py +425 -75
  33. invarlock/edits/quant_rtn.py +65 -37
  34. invarlock/eval/bench.py +3 -16
  35. invarlock/eval/data.py +82 -51
  36. invarlock/eval/metrics.py +63 -2
  37. invarlock/eval/primary_metric.py +23 -0
  38. invarlock/eval/tail_stats.py +230 -0
  39. invarlock/eval/tasks/__init__.py +12 -0
  40. invarlock/eval/tasks/classification.py +48 -0
  41. invarlock/eval/tasks/qa.py +36 -0
  42. invarlock/eval/tasks/text_generation.py +102 -0
  43. invarlock/guards/_estimators.py +154 -0
  44. invarlock/guards/invariants.py +19 -10
  45. invarlock/guards/policies.py +16 -6
  46. invarlock/guards/rmt.py +627 -546
  47. invarlock/guards/spectral.py +348 -110
  48. invarlock/guards/tier_config.py +32 -30
  49. invarlock/guards/variance.py +7 -31
  50. invarlock/guards_ref/rmt_ref.py +23 -23
  51. invarlock/model_profile.py +90 -42
  52. invarlock/observability/health.py +6 -6
  53. invarlock/observability/metrics.py +108 -0
  54. invarlock/reporting/certificate.py +384 -55
  55. invarlock/reporting/certificate_schema.py +3 -2
  56. invarlock/reporting/dataset_hashing.py +15 -2
  57. invarlock/reporting/guards_analysis.py +350 -277
  58. invarlock/reporting/html.py +55 -5
  59. invarlock/reporting/normalizer.py +13 -0
  60. invarlock/reporting/policy_utils.py +38 -36
  61. invarlock/reporting/primary_metric_utils.py +71 -17
  62. invarlock/reporting/render.py +852 -431
  63. invarlock/reporting/report.py +40 -4
  64. invarlock/reporting/report_types.py +11 -3
  65. invarlock/reporting/telemetry.py +86 -0
  66. invarlock/reporting/validate.py +1 -18
  67. {invarlock-0.3.5.dist-info → invarlock-0.3.7.dist-info}/METADATA +27 -13
  68. {invarlock-0.3.5.dist-info → invarlock-0.3.7.dist-info}/RECORD +72 -65
  69. {invarlock-0.3.5.dist-info → invarlock-0.3.7.dist-info}/WHEEL +1 -1
  70. {invarlock-0.3.5.dist-info → invarlock-0.3.7.dist-info}/entry_points.txt +5 -3
  71. invarlock/adapters/hf_gpt2.py +0 -404
  72. invarlock/adapters/hf_llama.py +0 -487
  73. {invarlock-0.3.5.dist-info → invarlock-0.3.7.dist-info}/licenses/LICENSE +0 -0
  74. {invarlock-0.3.5.dist-info → invarlock-0.3.7.dist-info}/top_level.txt +0 -0
@@ -5,6 +5,7 @@ InvarLock Guards - Invariants
5
5
  Invariant checking for model edits to ensure structural integrity.
6
6
  """
7
7
 
8
+ import hashlib
8
9
  from typing import Any
9
10
 
10
11
  import torch
@@ -33,6 +34,7 @@ class InvariantsGuard(Guard):
33
34
  self.on_fail = on_fail
34
35
  self.prepared = False
35
36
  self.baseline_checks: dict[str, Any] = {}
37
+ self.last_current_checks: dict[str, Any] = {}
36
38
  self.profile_checks: tuple[str, ...] = ()
37
39
 
38
40
  def prepare(
@@ -102,6 +104,10 @@ class InvariantsGuard(Guard):
102
104
  "action": outcome.action,
103
105
  "violations": outcome.violations,
104
106
  "metrics": outcome.metrics,
107
+ "details": {
108
+ "baseline_checks": self.baseline_checks,
109
+ "current_checks": self.last_current_checks,
110
+ },
105
111
  }
106
112
 
107
113
  def finalize(self, model: Any) -> GuardOutcome:
@@ -125,6 +131,7 @@ class InvariantsGuard(Guard):
125
131
 
126
132
  # Check current invariants
127
133
  current_checks = self._capture_invariants(model, None)
134
+ self.last_current_checks = current_checks
128
135
  violations: list[dict[str, Any]] = []
129
136
  tokenizer_mismatches: list[dict[str, Any]] = []
130
137
 
@@ -354,14 +361,14 @@ class InvariantsGuard(Guard):
354
361
  except Exception:
355
362
  pass
356
363
 
357
- # LLaMA style (model.embed_tokens <-> lm_head)
364
+ # Decoder embed_tokens style (model.embed_tokens <-> lm_head)
358
365
  try:
359
- llama_model = getattr(model, "model", None)
360
- embed_tokens = getattr(llama_model, "embed_tokens", None)
366
+ decoder_model = getattr(model, "model", None)
367
+ embed_tokens = getattr(decoder_model, "embed_tokens", None)
361
368
  embed_weight = getattr(embed_tokens, "weight", None)
362
- llama_head_weight = getattr(getattr(model, "lm_head", None), "weight", None)
363
- if embed_weight is not None and llama_head_weight is not None:
364
- weight_tying_flags["llama"] = _is_tied(embed_weight, llama_head_weight)
369
+ head_weight = getattr(getattr(model, "lm_head", None), "weight", None)
370
+ if embed_weight is not None and head_weight is not None:
371
+ weight_tying_flags["embed_tokens"] = _is_tied(embed_weight, head_weight)
365
372
  except Exception:
366
373
  pass
367
374
 
@@ -376,8 +383,10 @@ class InvariantsGuard(Guard):
376
383
  structure_items = []
377
384
  for name, module in model.named_modules():
378
385
  structure_items.append(f"{name}:{type(module).__name__}")
379
- structure_hash = hash(tuple(structure_items))
380
- checks["structure_hash"] = structure_hash
386
+ canonical = "\n".join(sorted(structure_items))
387
+ checks["structure_hash"] = hashlib.sha256(
388
+ canonical.encode("utf-8")
389
+ ).hexdigest()[:16]
381
390
  except Exception:
382
391
  checks["structure_hash"] = 0
383
392
 
@@ -424,7 +433,7 @@ class InvariantsGuard(Guard):
424
433
  return "bert" in model_type or has_cls_decoder
425
434
 
426
435
  if name in {"rope_rotary_embedding", "rotary_embedding"}:
427
- # Detect rotary embeddings used by LLaMA-style models
436
+ # Detect rotary embeddings used by RoPE-style models
428
437
  if hasattr(model, "model") and hasattr(model.model, "layers"):
429
438
  first_layer = model.model.layers[0] if model.model.layers else None
430
439
  else:
@@ -443,7 +452,7 @@ class InvariantsGuard(Guard):
443
452
  model_type = getattr(config, "model_type", "") if config else ""
444
453
  return any(
445
454
  keyword in model_type
446
- for keyword in ("gpt", "llama", "mistral", "opt", "phi")
455
+ for keyword in ("gpt", "mistral", "mixtral", "qwen", "opt", "phi")
447
456
  )
448
457
 
449
458
  return True
@@ -15,7 +15,7 @@ from typing import Any, Literal
15
15
 
16
16
  try: # Python 3.12+
17
17
  from typing import NotRequired, TypedDict
18
- except ImportError: # Legacy fallback
18
+ except ImportError: # Python <3.12 fallback
19
19
  from typing import NotRequired
20
20
 
21
21
  from typing_extensions import TypedDict
@@ -40,6 +40,7 @@ SPECTRAL_CONSERVATIVE: SpectralPolicy = {
40
40
  "scope": "ffn", # FFN layers only (safest)
41
41
  "correction_enabled": True,
42
42
  "max_caps": 3,
43
+ "max_spectral_norm": None,
43
44
  "multiple_testing": {"method": "bonferroni", "alpha": 0.02, "m": 4},
44
45
  }
45
46
 
@@ -50,6 +51,7 @@ SPECTRAL_BALANCED: SpectralPolicy = {
50
51
  "scope": "ffn", # FFN layers only
51
52
  "correction_enabled": False,
52
53
  "max_caps": 5,
54
+ "max_spectral_norm": None,
53
55
  "multiple_testing": {"method": "bh", "alpha": 0.05, "m": 4},
54
56
  }
55
57
 
@@ -60,6 +62,7 @@ SPECTRAL_AGGRESSIVE: SpectralPolicy = {
60
62
  "scope": "all", # All layers including attention
61
63
  "correction_enabled": True,
62
64
  "max_caps": 8,
65
+ "max_spectral_norm": None,
63
66
  "multiple_testing": {"method": "bh", "alpha": 0.1, "m": 4},
64
67
  }
65
68
 
@@ -70,6 +73,7 @@ SPECTRAL_ATTN_AWARE: SpectralPolicy = {
70
73
  "scope": "attn", # Attention layers only
71
74
  "correction_enabled": False,
72
75
  "max_caps": 5,
76
+ "max_spectral_norm": None,
73
77
  "multiple_testing": {"method": "bh", "alpha": 0.05, "m": 4},
74
78
  }
75
79
 
@@ -81,7 +85,8 @@ RMT_CONSERVATIVE: RMTPolicyDict = {
81
85
  "deadband": 0.05, # 5% deadband - strict threshold
82
86
  "margin": 1.3, # Lower margin for conservative detection
83
87
  "correct": True, # Enable automatic correction
84
- "epsilon": {"attn": 0.05, "ffn": 0.06, "embed": 0.07, "other": 0.07},
88
+ "epsilon_default": 0.06,
89
+ "epsilon_by_family": {"attn": 0.05, "ffn": 0.06, "embed": 0.07, "other": 0.07},
85
90
  }
86
91
 
87
92
  # Balanced RMT policy - good for most use cases
@@ -90,7 +95,8 @@ RMT_BALANCED: RMTPolicyDict = {
90
95
  "deadband": 0.10, # 10% deadband - reasonable tolerance
91
96
  "margin": 1.5, # Standard margin for outlier detection
92
97
  "correct": False, # Monitor-only by default
93
- "epsilon": {"attn": 0.08, "ffn": 0.10, "embed": 0.12, "other": 0.12},
98
+ "epsilon_default": 0.10,
99
+ "epsilon_by_family": {"attn": 0.08, "ffn": 0.10, "embed": 0.12, "other": 0.12},
94
100
  }
95
101
 
96
102
  # Aggressive RMT policy - for research/experimental use
@@ -99,7 +105,8 @@ RMT_AGGRESSIVE: RMTPolicyDict = {
99
105
  "deadband": 0.15, # 15% deadband - more permissive
100
106
  "margin": 1.8, # Higher margin allows more deviation
101
107
  "correct": True, # Enable automatic correction
102
- "epsilon": {"attn": 0.15, "ffn": 0.15, "embed": 0.15, "other": 0.15},
108
+ "epsilon_default": 0.15,
109
+ "epsilon_by_family": {"attn": 0.15, "ffn": 0.15, "embed": 0.15, "other": 0.15},
103
110
  }
104
111
 
105
112
  # === Variance Guard Policies ===
@@ -276,6 +283,8 @@ def get_spectral_policy(
276
283
  policy["scope"] = tier_config["scope"]
277
284
  if "max_caps" in tier_config:
278
285
  policy["max_caps"] = tier_config["max_caps"]
286
+ if "max_spectral_norm" in tier_config:
287
+ policy["max_spectral_norm"] = tier_config["max_spectral_norm"]
279
288
  if "family_caps" in tier_config:
280
289
  policy["family_caps"] = tier_config["family_caps"]
281
290
  if "multiple_testing" in tier_config:
@@ -390,9 +399,10 @@ def get_rmt_policy(name: str = "balanced", *, use_yaml: bool = True) -> RMTPolic
390
399
  policy["deadband"] = tier_config["deadband"]
391
400
  if "margin" in tier_config:
392
401
  policy["margin"] = tier_config["margin"]
393
- # Use epsilon_by_family as the epsilon dict
402
+ if "epsilon_default" in tier_config:
403
+ policy["epsilon_default"] = tier_config["epsilon_default"]
394
404
  if "epsilon_by_family" in tier_config:
395
- policy["epsilon"] = tier_config["epsilon_by_family"]
405
+ policy["epsilon_by_family"] = tier_config["epsilon_by_family"]
396
406
  except Exception:
397
407
  # Fallback to hardcoded values on any error
398
408
  pass