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.
- invarlock/__init__.py +2 -2
- invarlock/_data/runtime/tiers.yaml +57 -30
- invarlock/adapters/__init__.py +11 -15
- invarlock/adapters/auto.py +35 -40
- invarlock/adapters/capabilities.py +2 -2
- invarlock/adapters/hf_causal.py +418 -0
- invarlock/adapters/{hf_onnx.py → hf_causal_onnx.py} +3 -3
- invarlock/adapters/hf_mixin.py +25 -4
- invarlock/adapters/{hf_bert.py → hf_mlm.py} +4 -11
- invarlock/adapters/{hf_t5.py → hf_seq2seq.py} +9 -9
- invarlock/calibration/spectral_null.py +15 -10
- invarlock/calibration/variance_ve.py +0 -2
- invarlock/cli/adapter_auto.py +31 -21
- invarlock/cli/app.py +73 -2
- invarlock/cli/commands/calibrate.py +6 -2
- invarlock/cli/commands/certify.py +651 -91
- invarlock/cli/commands/doctor.py +11 -11
- invarlock/cli/commands/explain_gates.py +57 -8
- invarlock/cli/commands/plugins.py +13 -9
- invarlock/cli/commands/report.py +233 -69
- invarlock/cli/commands/run.py +1066 -244
- invarlock/cli/commands/verify.py +154 -15
- invarlock/cli/config.py +22 -6
- invarlock/cli/doctor_helpers.py +4 -5
- invarlock/cli/output.py +193 -0
- invarlock/cli/provenance.py +1 -1
- invarlock/core/api.py +45 -5
- invarlock/core/auto_tuning.py +65 -20
- invarlock/core/bootstrap.py +1 -1
- invarlock/core/contracts.py +7 -1
- invarlock/core/registry.py +11 -13
- invarlock/core/runner.py +425 -75
- invarlock/edits/quant_rtn.py +65 -37
- invarlock/eval/bench.py +3 -16
- invarlock/eval/data.py +82 -51
- invarlock/eval/metrics.py +63 -2
- invarlock/eval/primary_metric.py +23 -0
- invarlock/eval/tail_stats.py +230 -0
- invarlock/eval/tasks/__init__.py +12 -0
- invarlock/eval/tasks/classification.py +48 -0
- invarlock/eval/tasks/qa.py +36 -0
- invarlock/eval/tasks/text_generation.py +102 -0
- invarlock/guards/_estimators.py +154 -0
- invarlock/guards/invariants.py +19 -10
- invarlock/guards/policies.py +16 -6
- invarlock/guards/rmt.py +627 -546
- invarlock/guards/spectral.py +348 -110
- invarlock/guards/tier_config.py +32 -30
- invarlock/guards/variance.py +7 -31
- invarlock/guards_ref/rmt_ref.py +23 -23
- invarlock/model_profile.py +90 -42
- invarlock/observability/health.py +6 -6
- invarlock/observability/metrics.py +108 -0
- invarlock/reporting/certificate.py +384 -55
- invarlock/reporting/certificate_schema.py +3 -2
- invarlock/reporting/dataset_hashing.py +15 -2
- invarlock/reporting/guards_analysis.py +350 -277
- invarlock/reporting/html.py +55 -5
- invarlock/reporting/normalizer.py +13 -0
- invarlock/reporting/policy_utils.py +38 -36
- invarlock/reporting/primary_metric_utils.py +71 -17
- invarlock/reporting/render.py +852 -431
- invarlock/reporting/report.py +40 -4
- invarlock/reporting/report_types.py +11 -3
- invarlock/reporting/telemetry.py +86 -0
- invarlock/reporting/validate.py +1 -18
- {invarlock-0.3.5.dist-info → invarlock-0.3.7.dist-info}/METADATA +27 -13
- {invarlock-0.3.5.dist-info → invarlock-0.3.7.dist-info}/RECORD +72 -65
- {invarlock-0.3.5.dist-info → invarlock-0.3.7.dist-info}/WHEEL +1 -1
- {invarlock-0.3.5.dist-info → invarlock-0.3.7.dist-info}/entry_points.txt +5 -3
- invarlock/adapters/hf_gpt2.py +0 -404
- invarlock/adapters/hf_llama.py +0 -487
- {invarlock-0.3.5.dist-info → invarlock-0.3.7.dist-info}/licenses/LICENSE +0 -0
- {invarlock-0.3.5.dist-info → invarlock-0.3.7.dist-info}/top_level.txt +0 -0
invarlock/guards/invariants.py
CHANGED
|
@@ -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
|
-
#
|
|
364
|
+
# Decoder embed_tokens style (model.embed_tokens <-> lm_head)
|
|
358
365
|
try:
|
|
359
|
-
|
|
360
|
-
embed_tokens = getattr(
|
|
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
|
-
|
|
363
|
-
if embed_weight is not None and
|
|
364
|
-
weight_tying_flags["
|
|
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
|
-
|
|
380
|
-
checks["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
|
|
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", "
|
|
455
|
+
for keyword in ("gpt", "mistral", "mixtral", "qwen", "opt", "phi")
|
|
447
456
|
)
|
|
448
457
|
|
|
449
458
|
return True
|
invarlock/guards/policies.py
CHANGED
|
@@ -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: #
|
|
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
|
-
"
|
|
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
|
-
"
|
|
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
|
-
"
|
|
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
|
-
|
|
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["
|
|
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
|