invarlock 0.2.0__tar.gz → 0.3.1__tar.gz

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 (141) hide show
  1. {invarlock-0.2.0/src/invarlock.egg-info → invarlock-0.3.1}/PKG-INFO +2 -2
  2. {invarlock-0.2.0 → invarlock-0.3.1}/README.md +1 -1
  3. {invarlock-0.2.0 → invarlock-0.3.1}/pyproject.toml +1 -1
  4. {invarlock-0.2.0 → invarlock-0.3.1}/src/invarlock/__init__.py +1 -1
  5. {invarlock-0.2.0 → invarlock-0.3.1}/src/invarlock/_data/runtime/profiles/ci_cpu.yaml +5 -0
  6. {invarlock-0.2.0 → invarlock-0.3.1}/src/invarlock/adapters/__init__.py +13 -0
  7. invarlock-0.3.1/src/invarlock/adapters/auto.py +226 -0
  8. invarlock-0.3.1/src/invarlock/adapters/capabilities.py +421 -0
  9. {invarlock-0.2.0 → invarlock-0.3.1}/src/invarlock/adapters/hf_llama.py +2 -2
  10. {invarlock-0.2.0 → invarlock-0.3.1}/src/invarlock/adapters/hf_mixin.py +122 -1
  11. {invarlock-0.2.0 → invarlock-0.3.1}/src/invarlock/cli/commands/doctor.py +7 -1
  12. {invarlock-0.2.0 → invarlock-0.3.1}/src/invarlock/cli/commands/run.py +148 -2
  13. {invarlock-0.2.0 → invarlock-0.3.1}/src/invarlock/core/registry.py +34 -6
  14. {invarlock-0.2.0 → invarlock-0.3.1}/src/invarlock/guards/variance.py +41 -6
  15. {invarlock-0.2.0 → invarlock-0.3.1}/src/invarlock/plugins/hf_awq_adapter.py +22 -1
  16. invarlock-0.3.1/src/invarlock/plugins/hf_bnb_adapter.py +174 -0
  17. {invarlock-0.2.0 → invarlock-0.3.1}/src/invarlock/plugins/hf_gptq_adapter.py +24 -1
  18. {invarlock-0.2.0 → invarlock-0.3.1}/src/invarlock/reporting/certificate.py +155 -15
  19. {invarlock-0.2.0 → invarlock-0.3.1/src/invarlock.egg-info}/PKG-INFO +2 -2
  20. {invarlock-0.2.0 → invarlock-0.3.1}/src/invarlock.egg-info/SOURCES.txt +1 -0
  21. invarlock-0.2.0/src/invarlock/adapters/auto.py +0 -99
  22. invarlock-0.2.0/src/invarlock/plugins/hf_bnb_adapter.py +0 -79
  23. {invarlock-0.2.0 → invarlock-0.3.1}/LICENSE +0 -0
  24. {invarlock-0.2.0 → invarlock-0.3.1}/MANIFEST.in +0 -0
  25. {invarlock-0.2.0 → invarlock-0.3.1}/setup.cfg +0 -0
  26. {invarlock-0.2.0 → invarlock-0.3.1}/src/invarlock/__main__.py +0 -0
  27. {invarlock-0.2.0 → invarlock-0.3.1}/src/invarlock/_data/runtime/profiles/release.yaml +0 -0
  28. {invarlock-0.2.0 → invarlock-0.3.1}/src/invarlock/_data/runtime/tiers.yaml +0 -0
  29. {invarlock-0.2.0 → invarlock-0.3.1}/src/invarlock/adapters/_capabilities.py +0 -0
  30. {invarlock-0.2.0 → invarlock-0.3.1}/src/invarlock/adapters/base.py +0 -0
  31. {invarlock-0.2.0 → invarlock-0.3.1}/src/invarlock/adapters/base_types.py +0 -0
  32. {invarlock-0.2.0 → invarlock-0.3.1}/src/invarlock/adapters/hf_bert.py +0 -0
  33. {invarlock-0.2.0 → invarlock-0.3.1}/src/invarlock/adapters/hf_gpt2.py +0 -0
  34. {invarlock-0.2.0 → invarlock-0.3.1}/src/invarlock/adapters/hf_onnx.py +0 -0
  35. {invarlock-0.2.0 → invarlock-0.3.1}/src/invarlock/adapters/hf_t5.py +0 -0
  36. {invarlock-0.2.0 → invarlock-0.3.1}/src/invarlock/adapters/py.typed +0 -0
  37. {invarlock-0.2.0 → invarlock-0.3.1}/src/invarlock/assurance/__init__.py +0 -0
  38. {invarlock-0.2.0 → invarlock-0.3.1}/src/invarlock/cli/__init__.py +0 -0
  39. {invarlock-0.2.0 → invarlock-0.3.1}/src/invarlock/cli/__main__.py +0 -0
  40. {invarlock-0.2.0 → invarlock-0.3.1}/src/invarlock/cli/_evidence.py +0 -0
  41. {invarlock-0.2.0 → invarlock-0.3.1}/src/invarlock/cli/_json.py +0 -0
  42. {invarlock-0.2.0 → invarlock-0.3.1}/src/invarlock/cli/adapter_auto.py +0 -0
  43. {invarlock-0.2.0 → invarlock-0.3.1}/src/invarlock/cli/app.py +0 -0
  44. {invarlock-0.2.0 → invarlock-0.3.1}/src/invarlock/cli/commands/__init__.py +0 -0
  45. {invarlock-0.2.0 → invarlock-0.3.1}/src/invarlock/cli/commands/certify.py +0 -0
  46. {invarlock-0.2.0 → invarlock-0.3.1}/src/invarlock/cli/commands/explain_gates.py +0 -0
  47. {invarlock-0.2.0 → invarlock-0.3.1}/src/invarlock/cli/commands/export_html.py +0 -0
  48. {invarlock-0.2.0 → invarlock-0.3.1}/src/invarlock/cli/commands/plugins.py +0 -0
  49. {invarlock-0.2.0 → invarlock-0.3.1}/src/invarlock/cli/commands/report.py +0 -0
  50. {invarlock-0.2.0 → invarlock-0.3.1}/src/invarlock/cli/commands/verify.py +0 -0
  51. {invarlock-0.2.0 → invarlock-0.3.1}/src/invarlock/cli/config.py +0 -0
  52. {invarlock-0.2.0 → invarlock-0.3.1}/src/invarlock/cli/constants.py +0 -0
  53. {invarlock-0.2.0 → invarlock-0.3.1}/src/invarlock/cli/device.py +0 -0
  54. {invarlock-0.2.0 → invarlock-0.3.1}/src/invarlock/cli/doctor_helpers.py +0 -0
  55. {invarlock-0.2.0 → invarlock-0.3.1}/src/invarlock/cli/errors.py +0 -0
  56. {invarlock-0.2.0 → invarlock-0.3.1}/src/invarlock/cli/overhead_utils.py +0 -0
  57. {invarlock-0.2.0 → invarlock-0.3.1}/src/invarlock/cli/provenance.py +0 -0
  58. {invarlock-0.2.0 → invarlock-0.3.1}/src/invarlock/cli/utils.py +0 -0
  59. {invarlock-0.2.0 → invarlock-0.3.1}/src/invarlock/config.py +0 -0
  60. {invarlock-0.2.0 → invarlock-0.3.1}/src/invarlock/core/__init__.py +0 -0
  61. {invarlock-0.2.0 → invarlock-0.3.1}/src/invarlock/core/abi.py +0 -0
  62. {invarlock-0.2.0 → invarlock-0.3.1}/src/invarlock/core/api.py +0 -0
  63. {invarlock-0.2.0 → invarlock-0.3.1}/src/invarlock/core/auto_tuning.py +0 -0
  64. {invarlock-0.2.0 → invarlock-0.3.1}/src/invarlock/core/bootstrap.py +0 -0
  65. {invarlock-0.2.0 → invarlock-0.3.1}/src/invarlock/core/checkpoint.py +0 -0
  66. {invarlock-0.2.0 → invarlock-0.3.1}/src/invarlock/core/contracts.py +0 -0
  67. {invarlock-0.2.0 → invarlock-0.3.1}/src/invarlock/core/error_utils.py +0 -0
  68. {invarlock-0.2.0 → invarlock-0.3.1}/src/invarlock/core/events.py +0 -0
  69. {invarlock-0.2.0 → invarlock-0.3.1}/src/invarlock/core/exceptions.py +0 -0
  70. {invarlock-0.2.0 → invarlock-0.3.1}/src/invarlock/core/retry.py +0 -0
  71. {invarlock-0.2.0 → invarlock-0.3.1}/src/invarlock/core/runner.py +0 -0
  72. {invarlock-0.2.0 → invarlock-0.3.1}/src/invarlock/core/types.py +0 -0
  73. {invarlock-0.2.0 → invarlock-0.3.1}/src/invarlock/edits/__init__.py +0 -0
  74. {invarlock-0.2.0 → invarlock-0.3.1}/src/invarlock/edits/_edit_utils.py +0 -0
  75. {invarlock-0.2.0 → invarlock-0.3.1}/src/invarlock/edits/_external_utils.py +0 -0
  76. {invarlock-0.2.0 → invarlock-0.3.1}/src/invarlock/edits/noop.py +0 -0
  77. {invarlock-0.2.0 → invarlock-0.3.1}/src/invarlock/edits/py.typed +0 -0
  78. {invarlock-0.2.0 → invarlock-0.3.1}/src/invarlock/edits/quant_rtn.py +0 -0
  79. {invarlock-0.2.0 → invarlock-0.3.1}/src/invarlock/edits/registry.py +0 -0
  80. {invarlock-0.2.0 → invarlock-0.3.1}/src/invarlock/eval/__init__.py +0 -0
  81. {invarlock-0.2.0 → invarlock-0.3.1}/src/invarlock/eval/bench.py +0 -0
  82. {invarlock-0.2.0 → invarlock-0.3.1}/src/invarlock/eval/bootstrap.py +0 -0
  83. {invarlock-0.2.0 → invarlock-0.3.1}/src/invarlock/eval/data.py +0 -0
  84. {invarlock-0.2.0 → invarlock-0.3.1}/src/invarlock/eval/metrics.py +0 -0
  85. {invarlock-0.2.0 → invarlock-0.3.1}/src/invarlock/eval/primary_metric.py +0 -0
  86. {invarlock-0.2.0 → invarlock-0.3.1}/src/invarlock/eval/probes/__init__.py +0 -0
  87. {invarlock-0.2.0 → invarlock-0.3.1}/src/invarlock/eval/probes/fft.py +0 -0
  88. {invarlock-0.2.0 → invarlock-0.3.1}/src/invarlock/eval/probes/mi.py +0 -0
  89. {invarlock-0.2.0 → invarlock-0.3.1}/src/invarlock/eval/probes/post_attention.py +0 -0
  90. {invarlock-0.2.0 → invarlock-0.3.1}/src/invarlock/eval/providers/base.py +0 -0
  91. {invarlock-0.2.0 → invarlock-0.3.1}/src/invarlock/eval/providers/seq2seq.py +0 -0
  92. {invarlock-0.2.0 → invarlock-0.3.1}/src/invarlock/eval/providers/text_lm.py +0 -0
  93. {invarlock-0.2.0 → invarlock-0.3.1}/src/invarlock/eval/providers/vision_text.py +0 -0
  94. {invarlock-0.2.0 → invarlock-0.3.1}/src/invarlock/eval/py.typed +0 -0
  95. {invarlock-0.2.0 → invarlock-0.3.1}/src/invarlock/guards/__init__.py +0 -0
  96. {invarlock-0.2.0 → invarlock-0.3.1}/src/invarlock/guards/_contracts.py +0 -0
  97. {invarlock-0.2.0 → invarlock-0.3.1}/src/invarlock/guards/invariants.py +0 -0
  98. {invarlock-0.2.0 → invarlock-0.3.1}/src/invarlock/guards/policies.py +0 -0
  99. {invarlock-0.2.0 → invarlock-0.3.1}/src/invarlock/guards/py.typed +0 -0
  100. {invarlock-0.2.0 → invarlock-0.3.1}/src/invarlock/guards/rmt.py +0 -0
  101. {invarlock-0.2.0 → invarlock-0.3.1}/src/invarlock/guards/spectral.py +0 -0
  102. {invarlock-0.2.0 → invarlock-0.3.1}/src/invarlock/guards/tier_config.py +0 -0
  103. {invarlock-0.2.0 → invarlock-0.3.1}/src/invarlock/guards_ref/__init__.py +0 -0
  104. {invarlock-0.2.0 → invarlock-0.3.1}/src/invarlock/guards_ref/rmt_ref.py +0 -0
  105. {invarlock-0.2.0 → invarlock-0.3.1}/src/invarlock/guards_ref/spectral_ref.py +0 -0
  106. {invarlock-0.2.0 → invarlock-0.3.1}/src/invarlock/guards_ref/variance_ref.py +0 -0
  107. {invarlock-0.2.0 → invarlock-0.3.1}/src/invarlock/model_profile.py +0 -0
  108. {invarlock-0.2.0 → invarlock-0.3.1}/src/invarlock/model_utils.py +0 -0
  109. {invarlock-0.2.0 → invarlock-0.3.1}/src/invarlock/observability/__init__.py +0 -0
  110. {invarlock-0.2.0 → invarlock-0.3.1}/src/invarlock/observability/alerting.py +0 -0
  111. {invarlock-0.2.0 → invarlock-0.3.1}/src/invarlock/observability/core.py +0 -0
  112. {invarlock-0.2.0 → invarlock-0.3.1}/src/invarlock/observability/exporters.py +0 -0
  113. {invarlock-0.2.0 → invarlock-0.3.1}/src/invarlock/observability/health.py +0 -0
  114. {invarlock-0.2.0 → invarlock-0.3.1}/src/invarlock/observability/metrics.py +0 -0
  115. {invarlock-0.2.0 → invarlock-0.3.1}/src/invarlock/observability/py.typed +0 -0
  116. {invarlock-0.2.0 → invarlock-0.3.1}/src/invarlock/observability/utils.py +0 -0
  117. {invarlock-0.2.0 → invarlock-0.3.1}/src/invarlock/plugins/__init__.py +0 -0
  118. {invarlock-0.2.0 → invarlock-0.3.1}/src/invarlock/plugins/hello_guard.py +0 -0
  119. {invarlock-0.2.0 → invarlock-0.3.1}/src/invarlock/plugins/py.typed +0 -0
  120. {invarlock-0.2.0 → invarlock-0.3.1}/src/invarlock/py.typed +0 -0
  121. {invarlock-0.2.0 → invarlock-0.3.1}/src/invarlock/reporting/__init__.py +0 -0
  122. {invarlock-0.2.0 → invarlock-0.3.1}/src/invarlock/reporting/certificate_schema.py +0 -0
  123. {invarlock-0.2.0 → invarlock-0.3.1}/src/invarlock/reporting/dataset_hashing.py +0 -0
  124. {invarlock-0.2.0 → invarlock-0.3.1}/src/invarlock/reporting/guards_analysis.py +0 -0
  125. {invarlock-0.2.0 → invarlock-0.3.1}/src/invarlock/reporting/html.py +0 -0
  126. {invarlock-0.2.0 → invarlock-0.3.1}/src/invarlock/reporting/normalizer.py +0 -0
  127. {invarlock-0.2.0 → invarlock-0.3.1}/src/invarlock/reporting/policy_utils.py +0 -0
  128. {invarlock-0.2.0 → invarlock-0.3.1}/src/invarlock/reporting/primary_metric_utils.py +0 -0
  129. {invarlock-0.2.0 → invarlock-0.3.1}/src/invarlock/reporting/render.py +0 -0
  130. {invarlock-0.2.0 → invarlock-0.3.1}/src/invarlock/reporting/report.py +0 -0
  131. {invarlock-0.2.0 → invarlock-0.3.1}/src/invarlock/reporting/report_types.py +0 -0
  132. {invarlock-0.2.0 → invarlock-0.3.1}/src/invarlock/reporting/utils.py +0 -0
  133. {invarlock-0.2.0 → invarlock-0.3.1}/src/invarlock/reporting/validate.py +0 -0
  134. {invarlock-0.2.0 → invarlock-0.3.1}/src/invarlock/security.py +0 -0
  135. {invarlock-0.2.0 → invarlock-0.3.1}/src/invarlock/sparsity_utils.py +0 -0
  136. {invarlock-0.2.0 → invarlock-0.3.1}/src/invarlock/utils/__init__.py +0 -0
  137. {invarlock-0.2.0 → invarlock-0.3.1}/src/invarlock/utils/digest.py +0 -0
  138. {invarlock-0.2.0 → invarlock-0.3.1}/src/invarlock.egg-info/dependency_links.txt +0 -0
  139. {invarlock-0.2.0 → invarlock-0.3.1}/src/invarlock.egg-info/entry_points.txt +0 -0
  140. {invarlock-0.2.0 → invarlock-0.3.1}/src/invarlock.egg-info/requires.txt +0 -0
  141. {invarlock-0.2.0 → invarlock-0.3.1}/src/invarlock.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: invarlock
3
- Version: 0.2.0
3
+ Version: 0.3.1
4
4
  Summary: Edit‑agnostic robustness certificates for weight edits (InvarLock framework)
5
5
  Author-email: InvarLock Team <oss@invarlock.dev>
6
6
  Maintainer-email: InvarLock Maintainers <support@invarlock.dev>
@@ -112,7 +112,7 @@ they don’t, roll back safely.
112
112
  Technical: edit‑agnostic guard pipeline (invariants → spectral → RMT →
113
113
  variance) producing a machine‑readable Safety Certificate.
114
114
 
115
- > **Status:** 0.2.0 (pre‑1.0). Until 1.0, **minor** releases may be
115
+ > **Status:** 0.3.1 (pre‑1.0). Until 1.0, **minor** releases may be
116
116
  > breaking. See CLI help and the CHANGELOG for updates.
117
117
 
118
118
  [![CI](https://img.shields.io/github/actions/workflow/status/invarlock/invarlock/ci.yml?branch=main&logo=github&label=CI)](https://github.com/invarlock/invarlock/actions/workflows/ci.yml)
@@ -6,7 +6,7 @@ they don’t, roll back safely.
6
6
  Technical: edit‑agnostic guard pipeline (invariants → spectral → RMT →
7
7
  variance) producing a machine‑readable Safety Certificate.
8
8
 
9
- > **Status:** 0.2.0 (pre‑1.0). Until 1.0, **minor** releases may be
9
+ > **Status:** 0.3.1 (pre‑1.0). Until 1.0, **minor** releases may be
10
10
  > breaking. See CLI help and the CHANGELOG for updates.
11
11
 
12
12
  [![CI](https://img.shields.io/github/actions/workflow/status/invarlock/invarlock/ci.yml?branch=main&logo=github&label=CI)](https://github.com/invarlock/invarlock/actions/workflows/ci.yml)
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "invarlock"
7
- version = "0.2.0"
7
+ version = "0.3.1"
8
8
  description = "Edit‑agnostic robustness certificates for weight edits (InvarLock framework)"
9
9
  authors = [{ name = "InvarLock Team", email = "oss@invarlock.dev" }]
10
10
  maintainers = [{ name = "InvarLock Maintainers", email = "support@invarlock.dev" }]
@@ -12,7 +12,7 @@ For torch-dependent functionality, see subpackages under `invarlock.*`:
12
12
  - `invarlock.eval`: Metrics, guard-overhead checks, and certification
13
13
  """
14
14
 
15
- __version__ = "0.2.0"
15
+ __version__ = "0.3.1"
16
16
 
17
17
  # Core exports - torch-independent
18
18
  from .config import CFG, Defaults, get_default_config
@@ -11,5 +11,10 @@ dataset:
11
11
  final_n: 120
12
12
  stride: 512
13
13
 
14
+ primary_metric:
15
+ acceptance_range:
16
+ min: 0.95
17
+ max: 1.15
18
+
14
19
  context:
15
20
  telemetry_profile: "ci_cpu"
@@ -20,6 +20,13 @@ from .base import (
20
20
  from .base import (
21
21
  PerformanceMetrics as BasePerformanceMetrics,
22
22
  )
23
+ from .capabilities import (
24
+ ModelCapabilities,
25
+ QuantizationConfig,
26
+ QuantizationMethod,
27
+ detect_capabilities_from_model,
28
+ detect_quantization_from_config,
29
+ )
23
30
 
24
31
  _LAZY_MAP = {
25
32
  "HF_BERT_Adapter": ".hf_bert",
@@ -99,4 +106,10 @@ __all__ = [
99
106
  "quality_label",
100
107
  "_RemovedComponent",
101
108
  "INVARLOCK_CORE_ABI",
109
+ # Capabilities
110
+ "ModelCapabilities",
111
+ "QuantizationConfig",
112
+ "QuantizationMethod",
113
+ "detect_capabilities_from_model",
114
+ "detect_quantization_from_config",
102
115
  ]
@@ -0,0 +1,226 @@
1
+ from __future__ import annotations
2
+
3
+ import importlib as _importlib
4
+ import json
5
+ from pathlib import Path
6
+ from typing import Any
7
+
8
+ from invarlock.core.api import ModelAdapter
9
+
10
+ from ..cli.adapter_auto import resolve_auto_adapter
11
+
12
+
13
+ def _detect_quantization_from_path(model_id: str) -> str | None:
14
+ """
15
+ Detect quantization method from a local checkpoint path.
16
+
17
+ Returns:
18
+ Quantization adapter name ("hf_bnb", "hf_awq", "hf_gptq") or None.
19
+ """
20
+ path = Path(model_id)
21
+ if not path.exists():
22
+ return None
23
+
24
+ config_path = path / "config.json"
25
+ if not config_path.exists():
26
+ return None
27
+
28
+ try:
29
+ config_data = json.loads(config_path.read_text())
30
+ quant_cfg = config_data.get("quantization_config", {})
31
+
32
+ if not quant_cfg:
33
+ return None
34
+
35
+ quant_method = quant_cfg.get("quant_method", "").lower()
36
+
37
+ if quant_method == "awq":
38
+ return "hf_awq"
39
+ elif quant_method == "gptq":
40
+ return "hf_gptq"
41
+ elif (
42
+ quant_method == "bitsandbytes"
43
+ or quant_cfg.get("load_in_8bit")
44
+ or quant_cfg.get("load_in_4bit")
45
+ ):
46
+ return "hf_bnb"
47
+
48
+ except Exception:
49
+ pass
50
+
51
+ return None
52
+
53
+
54
+ def _detect_quantization_from_model(model: Any) -> str | None:
55
+ """
56
+ Detect quantization method from a loaded model instance.
57
+
58
+ Returns:
59
+ Quantization adapter name ("hf_bnb", "hf_awq", "hf_gptq") or None.
60
+ """
61
+ config = getattr(model, "config", None)
62
+ if config is None:
63
+ return None
64
+
65
+ quant_cfg = getattr(config, "quantization_config", None)
66
+ if quant_cfg is None:
67
+ # Check for BNB attributes on the model itself
68
+ if getattr(model, "is_loaded_in_8bit", False) or getattr(
69
+ model, "is_loaded_in_4bit", False
70
+ ):
71
+ return "hf_bnb"
72
+ return None
73
+
74
+ # Handle dict-style config
75
+ if isinstance(quant_cfg, dict):
76
+ quant_method = quant_cfg.get("quant_method", "").lower()
77
+ if quant_method == "awq":
78
+ return "hf_awq"
79
+ elif quant_method == "gptq":
80
+ return "hf_gptq"
81
+ elif (
82
+ quant_method == "bitsandbytes"
83
+ or quant_cfg.get("load_in_8bit")
84
+ or quant_cfg.get("load_in_4bit")
85
+ ):
86
+ return "hf_bnb"
87
+ else:
88
+ # Object-style config
89
+ cfg_class = quant_cfg.__class__.__name__
90
+ if cfg_class in ("AWQConfig",):
91
+ return "hf_awq"
92
+ elif cfg_class in ("GPTQConfig",):
93
+ return "hf_gptq"
94
+ elif cfg_class in ("BitsAndBytesConfig", "BnbConfig"):
95
+ return "hf_bnb"
96
+ # Check attributes
97
+ if getattr(quant_cfg, "load_in_8bit", False) or getattr(
98
+ quant_cfg, "load_in_4bit", False
99
+ ):
100
+ return "hf_bnb"
101
+
102
+ return None
103
+
104
+
105
+ class _DelegatingAdapter(ModelAdapter):
106
+ name = "auto_adapter"
107
+
108
+ def __init__(self) -> None:
109
+ self._delegate: ModelAdapter | None = None
110
+
111
+ def _load_adapter(self, adapter_name: str) -> ModelAdapter:
112
+ """Load an adapter by name."""
113
+ if adapter_name == "hf_llama":
114
+ HF_LLaMA_Adapter = _importlib.import_module(
115
+ ".hf_llama", __package__
116
+ ).HF_LLaMA_Adapter
117
+ return HF_LLaMA_Adapter()
118
+ elif adapter_name == "hf_bert":
119
+ HF_BERT_Adapter = _importlib.import_module(
120
+ ".hf_bert", __package__
121
+ ).HF_BERT_Adapter
122
+ return HF_BERT_Adapter()
123
+ elif adapter_name == "hf_gpt2":
124
+ HF_GPT2_Adapter = _importlib.import_module(
125
+ ".hf_gpt2", __package__
126
+ ).HF_GPT2_Adapter
127
+ return HF_GPT2_Adapter()
128
+ elif adapter_name == "hf_bnb":
129
+ HF_BNB_Adapter = _importlib.import_module(
130
+ "invarlock.plugins.hf_bnb_adapter"
131
+ ).HF_BNB_Adapter
132
+ return HF_BNB_Adapter()
133
+ elif adapter_name == "hf_awq":
134
+ HF_AWQ_Adapter = _importlib.import_module(
135
+ "invarlock.plugins.hf_awq_adapter"
136
+ ).HF_AWQ_Adapter
137
+ return HF_AWQ_Adapter()
138
+ elif adapter_name == "hf_gptq":
139
+ HF_GPTQ_Adapter = _importlib.import_module(
140
+ "invarlock.plugins.hf_gptq_adapter"
141
+ ).HF_GPTQ_Adapter
142
+ return HF_GPTQ_Adapter()
143
+ else:
144
+ # Default to GPT2 adapter
145
+ HF_GPT2_Adapter = _importlib.import_module(
146
+ ".hf_gpt2", __package__
147
+ ).HF_GPT2_Adapter
148
+ return HF_GPT2_Adapter()
149
+
150
+ def _ensure_delegate_from_id(self, model_id: str) -> ModelAdapter:
151
+ if self._delegate is not None:
152
+ return self._delegate
153
+
154
+ # First check for quantization in local checkpoint
155
+ quant_adapter = _detect_quantization_from_path(model_id)
156
+ if quant_adapter:
157
+ self._delegate = self._load_adapter(quant_adapter)
158
+ return self._delegate
159
+
160
+ # Fall back to architecture-based resolution
161
+ resolved = resolve_auto_adapter(model_id)
162
+ self._delegate = self._load_adapter(resolved)
163
+ return self._delegate
164
+
165
+ def _ensure_delegate_from_model(self, model: Any) -> ModelAdapter:
166
+ if self._delegate is not None:
167
+ return self._delegate
168
+
169
+ # First check for quantization on the loaded model
170
+ quant_adapter = _detect_quantization_from_model(model)
171
+ if quant_adapter:
172
+ self._delegate = self._load_adapter(quant_adapter)
173
+ return self._delegate
174
+
175
+ # Fall back to class name inspection
176
+ cls_name = getattr(model, "__class__", type(model)).__name__.lower()
177
+ if any(k in cls_name for k in ["llama", "mistral", "qwen", "yi"]):
178
+ self._delegate = self._load_adapter("hf_llama")
179
+ elif any(k in cls_name for k in ["bert", "roberta", "albert", "deberta"]):
180
+ self._delegate = self._load_adapter("hf_bert")
181
+ else:
182
+ self._delegate = self._load_adapter("hf_gpt2")
183
+ return self._delegate
184
+
185
+ def can_handle(self, model: Any) -> bool: # pragma: no cover - trivial
186
+ return True
187
+
188
+ def describe(self, model: Any) -> dict[str, Any]:
189
+ delegate = self._delegate or self._ensure_delegate_from_model(model)
190
+ return delegate.describe(model)
191
+
192
+ def snapshot(self, model: Any) -> bytes:
193
+ delegate = self._delegate or self._ensure_delegate_from_model(model)
194
+ return delegate.snapshot(model)
195
+
196
+ def restore(self, model: Any, blob: bytes) -> None:
197
+ delegate = self._delegate or self._ensure_delegate_from_model(model)
198
+ return delegate.restore(model, blob)
199
+
200
+ def __getattr__(self, item: str): # pragma: no cover - passthrough
201
+ if item == "_delegate":
202
+ raise AttributeError(item)
203
+ delegate = self._delegate
204
+ if delegate is not None and hasattr(delegate, item):
205
+ return getattr(delegate, item)
206
+ raise AttributeError(item)
207
+
208
+
209
+ class HF_Causal_Auto_Adapter(_DelegatingAdapter):
210
+ name = "hf_causal_auto"
211
+
212
+ def load_model(self, model_id: str, device: str = "auto") -> Any:
213
+ delegate = self._ensure_delegate_from_id(model_id)
214
+ return delegate.load_model(model_id, device=device)
215
+
216
+
217
+ class HF_MLM_Auto_Adapter(_DelegatingAdapter):
218
+ name = "hf_mlm_auto"
219
+
220
+ def load_model(self, model_id: str, device: str = "auto") -> Any:
221
+ # Force BERT-like adapter for MLM families
222
+ HF_BERT_Adapter = _importlib.import_module(
223
+ ".hf_bert", __package__
224
+ ).HF_BERT_Adapter
225
+ self._delegate = HF_BERT_Adapter()
226
+ return self._delegate.load_model(model_id, device=device)