mirror of
https://github.com/elder-plinius/OBLITERATUS.git
synced 2026-08-28 13:50:30 +02:00
test: establish Gate 1 quality baseline (#90)
Establishes the mandatory testing, coverage, repeatability, mutation, packaging, supply-chain, and AIWG workspace baseline before feature integration.
This commit is contained in:
@@ -0,0 +1,131 @@
|
||||
"""Boundary contracts for evaluation baselines, adapters, and public reports."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from types import SimpleNamespace
|
||||
from unittest.mock import Mock
|
||||
|
||||
import pytest
|
||||
import torch
|
||||
from torch import nn
|
||||
|
||||
from obliteratus.evaluation.baselines import (
|
||||
direction_specificity_test,
|
||||
random_direction_ablation,
|
||||
)
|
||||
from obliteratus.evaluation.evaluator import Evaluator
|
||||
|
||||
|
||||
class _Encoding(dict):
|
||||
def to(self, _device):
|
||||
return self
|
||||
|
||||
|
||||
class _ClassificationDataset:
|
||||
def __init__(self, texts, labels):
|
||||
self.texts = list(texts)
|
||||
self.labels = list(labels)
|
||||
self.selected = None
|
||||
|
||||
def __len__(self):
|
||||
return len(self.texts)
|
||||
|
||||
def __getitem__(self, key):
|
||||
if isinstance(key, slice):
|
||||
return {"text": self.texts[key], "label": self.labels[key]}
|
||||
return {"text": self.texts[key], "label": self.labels[key]}
|
||||
|
||||
def select(self, indices):
|
||||
indices = list(indices)
|
||||
self.selected = indices
|
||||
return _ClassificationDataset(
|
||||
[self.texts[index] for index in indices],
|
||||
[self.labels[index] for index in indices],
|
||||
)
|
||||
|
||||
|
||||
class _ClassificationModel(nn.Module):
|
||||
def __init__(self, batches):
|
||||
super().__init__()
|
||||
self.anchor = nn.Parameter(torch.zeros(()))
|
||||
self.batches = list(batches)
|
||||
|
||||
def forward(self, **_encodings):
|
||||
return SimpleNamespace(logits=torch.tensor(self.batches.pop(0)))
|
||||
|
||||
|
||||
def test_evaluator_dispatches_classification_and_rejects_unknown_tasks():
|
||||
dataset = _ClassificationDataset(["a", "b", "ignored"], [1, 0, 1])
|
||||
tokenizer = Mock(return_value=_Encoding(input_ids=torch.ones((2, 1), dtype=torch.long)))
|
||||
model = _ClassificationModel([[[0.0, 2.0], [3.0, 0.0]]])
|
||||
handle = SimpleNamespace(model=model, tokenizer=tokenizer, task="classification")
|
||||
|
||||
result = Evaluator(
|
||||
handle,
|
||||
dataset,
|
||||
metrics=["accuracy", "f1"],
|
||||
batch_size=2,
|
||||
max_samples=2,
|
||||
).evaluate()
|
||||
|
||||
assert dataset.selected == [0, 1]
|
||||
assert result == {"accuracy": 1.0, "f1": 1.0}
|
||||
tokenizer.assert_called_once()
|
||||
|
||||
handle.task = "unsupported"
|
||||
with pytest.raises(ValueError, match="Unsupported task: unsupported"):
|
||||
Evaluator(handle, dataset).evaluate()
|
||||
|
||||
|
||||
def test_classification_returns_only_requested_metrics():
|
||||
dataset = _ClassificationDataset(["a"], [0])
|
||||
tokenizer = Mock(return_value=_Encoding(input_ids=torch.ones((1, 1), dtype=torch.long)))
|
||||
model = _ClassificationModel([[[2.0, 0.0]]])
|
||||
handle = SimpleNamespace(model=model, tokenizer=tokenizer, task="classification")
|
||||
|
||||
assert Evaluator(handle, dataset, metrics=["accuracy"]).evaluate() == {"accuracy": 1.0}
|
||||
|
||||
|
||||
def _pipeline(**overrides):
|
||||
values = {
|
||||
"_strong_layers": [0, 1],
|
||||
"refusal_directions": {0: torch.tensor([1.0, 0.0]), 1: torch.tensor([0.0, 1.0])},
|
||||
"_harmful_means": {0: torch.tensor([2.0, 0.0]), 1: torch.tensor([0.0, 4.0])},
|
||||
"_harmless_means": {0: torch.tensor([0.5, 0.0]), 1: torch.tensor([0.0, 1.0])},
|
||||
}
|
||||
values.update(overrides)
|
||||
return SimpleNamespace(**values)
|
||||
|
||||
|
||||
def test_random_direction_baseline_handles_missing_and_cleared_activations():
|
||||
missing = _pipeline(_strong_layers=[], refusal_directions={})
|
||||
assert "no directions" in random_direction_ablation(missing).details["error"].lower()
|
||||
|
||||
cleared = _pipeline(_harmful_means={})
|
||||
assert "activations cleared" in random_direction_ablation(cleared).details["error"]
|
||||
|
||||
|
||||
def test_random_direction_baseline_is_seeded_and_reports_trial_statistics():
|
||||
first = random_direction_ablation(_pipeline(), n_trials=4, seed=17)
|
||||
second = random_direction_ablation(_pipeline(), n_trials=4, seed=17)
|
||||
|
||||
assert first == second
|
||||
assert first.baseline_name == "random_direction"
|
||||
assert first.n_trials == 4
|
||||
assert len(first.refusal_rates) == 4
|
||||
assert first.refusal_rate == first.mean_refusal_rate
|
||||
assert first.std_refusal_rate >= 0
|
||||
assert first.details == {"hidden_dim": 2, "n_strong_layers": 2}
|
||||
|
||||
|
||||
def test_direction_specificity_covers_missing_partial_and_complete_inputs():
|
||||
assert direction_specificity_test(_pipeline(_strong_layers=[], refusal_directions={})) == {
|
||||
"error": "No directions available"
|
||||
}
|
||||
partial = _pipeline(_harmless_means={})
|
||||
assert "activations cleared" in direction_specificity_test(partial)["error"]
|
||||
|
||||
result = direction_specificity_test(_pipeline())
|
||||
assert result["harmful_projection"] == 3.0
|
||||
assert result["harmless_projection"] == 0.75
|
||||
assert result["specificity_ratio"] == 4.0
|
||||
@@ -0,0 +1,133 @@
|
||||
"""Pure contracts for the lm-eval adapter and public report boundary."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import sys
|
||||
from types import SimpleNamespace
|
||||
from unittest.mock import Mock
|
||||
|
||||
import matplotlib.pyplot as plt
|
||||
import pytest
|
||||
|
||||
from obliteratus.reporting.report import (
|
||||
AblationReport,
|
||||
AblationResult,
|
||||
_sanitize_public_value,
|
||||
)
|
||||
from obliteratus.evaluation import lm_eval_integration as LM_EVAL
|
||||
|
||||
|
||||
def test_lm_eval_missing_dependency_has_actionable_error(monkeypatch):
|
||||
monkeypatch.setitem(sys.modules, "lm_eval", None)
|
||||
with pytest.raises(ImportError, match="pip install lm-eval>=0.4.0"):
|
||||
LM_EVAL.run_benchmarks("model")
|
||||
|
||||
|
||||
def test_lm_eval_preserves_measured_zero_and_falls_back_to_numeric_metric(monkeypatch):
|
||||
simple_evaluate = Mock(return_value={
|
||||
"results": {
|
||||
"zero": {"acc,none": 0.0, "acc_norm,none": 0.75},
|
||||
"normalized": {"acc_norm,none": 0.6},
|
||||
"fallback": {"alias": "name", "stderr": 0.02, "score": 0.4},
|
||||
"empty": {"alias": "empty"},
|
||||
}
|
||||
})
|
||||
monkeypatch.setitem(sys.modules, "lm_eval", SimpleNamespace(simple_evaluate=simple_evaluate))
|
||||
|
||||
scores = LM_EVAL.run_benchmarks(
|
||||
"org/model",
|
||||
tasks=["zero", "normalized", "fallback", "empty"],
|
||||
device="cpu",
|
||||
batch_size=3,
|
||||
num_fewshot=2,
|
||||
limit=5,
|
||||
)
|
||||
|
||||
assert scores == {"zero": 0.0, "normalized": 0.6, "fallback": 0.02}
|
||||
simple_evaluate.assert_called_once_with(
|
||||
model="hf",
|
||||
model_args="pretrained=org/model,device=cpu",
|
||||
tasks=["zero", "normalized", "fallback", "empty"],
|
||||
batch_size=3,
|
||||
num_fewshot=2,
|
||||
limit=5,
|
||||
)
|
||||
|
||||
|
||||
def test_lm_eval_defaults_and_model_comparison(monkeypatch):
|
||||
simple_evaluate = Mock(return_value={"results": {"task": {"acc,none": 0.5}}})
|
||||
monkeypatch.setitem(sys.modules, "lm_eval", SimpleNamespace(simple_evaluate=simple_evaluate))
|
||||
assert LM_EVAL.run_benchmarks("model", tasks=["task"]) == {"task": 0.5}
|
||||
assert simple_evaluate.call_args.kwargs["model_args"] == "pretrained=model"
|
||||
|
||||
responses = iter([{"a": 0.8, "shared": 0.5}, {"b": 0.4, "shared": 0.7}])
|
||||
monkeypatch.setattr(
|
||||
LM_EVAL,
|
||||
"run_benchmarks",
|
||||
lambda *_args, **_kwargs: next(responses),
|
||||
)
|
||||
assert LM_EVAL.compare_models("original", "abliterated") == {
|
||||
"a": {"original": 0.8, "abliterated": 0.0, "delta": -0.8},
|
||||
"b": {"original": 0.0, "abliterated": 0.4, "delta": 0.4},
|
||||
"shared": {"original": 0.5, "abliterated": 0.7, "delta": pytest.approx(0.2)},
|
||||
}
|
||||
|
||||
|
||||
def test_report_sanitizes_sequences_objects_windows_paths_and_long_labels():
|
||||
custom = SimpleNamespace(value="/private/path/item")
|
||||
sanitized = _sanitize_public_value({
|
||||
"items": ("C:\\private\\model.bin", custom),
|
||||
"api-key": "must disappear",
|
||||
"finite": 1.5,
|
||||
"infinite": float("inf"),
|
||||
})
|
||||
assert "api-key" not in sanitized
|
||||
assert sanitized["finite"] == 1.5
|
||||
assert sanitized["infinite"] is None
|
||||
assert sanitized["items"][0] == "model.bin"
|
||||
assert "private/path" not in sanitized["items"][1]
|
||||
|
||||
report = AblationReport(model_name="x" * 100)
|
||||
assert report.to_dict()["model_name"].endswith("...")
|
||||
assert len(report.to_dict()["model_name"]) == 80
|
||||
|
||||
|
||||
def test_report_summary_empty_and_populated(capsys):
|
||||
AblationReport("empty").print_summary()
|
||||
assert "No ablation results" in capsys.readouterr().out
|
||||
|
||||
report = AblationReport("model")
|
||||
report.add_baseline({"score": 0.0, "missing": None})
|
||||
report.add_result(AblationResult("s", "c", "d", {"score": 1.0, "missing": None}))
|
||||
report.print_summary()
|
||||
output = capsys.readouterr().out
|
||||
assert "Ablation Results: model" in output
|
||||
assert "unavailable" in output
|
||||
|
||||
|
||||
def test_report_plot_boundaries(monkeypatch, tmp_path):
|
||||
report = AblationReport("model")
|
||||
report.add_baseline({"score": 2.0})
|
||||
report.add_result(AblationResult("s", "positive", "d", {"score": 3.0}))
|
||||
report.add_result(AblationResult("s", "negative", "d", {"score": 1.0}))
|
||||
|
||||
impact = tmp_path / "nested" / "impact.png"
|
||||
impact.parent.mkdir()
|
||||
report.plot_impact(output_path=impact)
|
||||
assert impact.stat().st_size > 0
|
||||
|
||||
heatmap = tmp_path / "heatmap.png"
|
||||
report.plot_heatmap(heatmap)
|
||||
assert heatmap.stat().st_size > 0
|
||||
|
||||
show = Mock()
|
||||
monkeypatch.setattr(plt, "show", show)
|
||||
report.plot_impact(metric="score")
|
||||
report.plot_heatmap()
|
||||
assert show.call_count == 2
|
||||
|
||||
no_delta = AblationReport("model", baseline_metrics={"score": None})
|
||||
no_delta.add_result(AblationResult("s", "c", "d", {"score": 1.0}))
|
||||
with pytest.raises(ValueError, match="No delta column"):
|
||||
no_delta.plot_impact("score")
|
||||
no_delta.plot_heatmap()
|
||||
@@ -19,8 +19,21 @@ def test_mutation_campaign_preloads_native_modules_before_covered_line_discovery
|
||||
assert "mutate_only_covered_lines = true" in mutmut_config
|
||||
assert '"obliteratus/runtime_contracts.py"' in mutmut_config
|
||||
assert '"obliteratus/persistence_contracts.py"' in mutmut_config
|
||||
assert '"obliteratus/evaluation/lm_eval_integration.py"' in mutmut_config
|
||||
assert '"obliteratus/reporting/report.py"' not in mutmut_config
|
||||
assert '"tests/test_runtime_contracts.py"' in mutmut_config
|
||||
assert '"tests/test_persistence_contracts.py"' in mutmut_config
|
||||
assert '"tests/test_lm_eval_reporting_contracts.py"' in mutmut_config
|
||||
assert '"tests/test_telemetry_failure_contracts.py"' not in mutmut_config
|
||||
assert '"tests/test_evaluation_reporting_contracts.py"' in Path(
|
||||
"scripts/run_repeat_gate.py",
|
||||
).read_text()
|
||||
assert '"tests/test_lm_eval_reporting_contracts.py"' in Path(
|
||||
"scripts/run_repeat_gate.py",
|
||||
).read_text()
|
||||
assert '"tests/test_telemetry_failure_contracts.py"' in Path(
|
||||
"scripts/run_repeat_gate.py",
|
||||
).read_text()
|
||||
assert "import torch, yaml; from mutmut.__main__ import cli; cli()" in workflow
|
||||
|
||||
|
||||
@@ -54,9 +67,9 @@ def _coverage():
|
||||
"obliteratus/pure.py": {
|
||||
"summary": {
|
||||
"num_statements": 100,
|
||||
"covered_lines": 80,
|
||||
"num_branches": 20,
|
||||
"covered_branches": 15,
|
||||
"covered_lines": 90,
|
||||
"num_branches": 100,
|
||||
"covered_branches": 78,
|
||||
},
|
||||
},
|
||||
"obliteratus/external.py": {
|
||||
@@ -76,8 +89,8 @@ def test_policy_and_exact_mature_floors_pass():
|
||||
assert quality.validate_policy(policy) == []
|
||||
measurement, failures = quality.validate_mature_cpu_scope(_coverage(), policy)
|
||||
assert failures == []
|
||||
assert measurement["line_percent"] == 80
|
||||
assert measurement["branch_percent"] == 75
|
||||
assert measurement["line_percent"] == 90
|
||||
assert measurement["branch_percent"] == 78
|
||||
|
||||
|
||||
def test_floor_regression_requires_structured_reviewed_exception():
|
||||
@@ -109,10 +122,10 @@ def test_exclusions_require_unique_traceable_environment_boundaries():
|
||||
def test_mature_scope_rejects_regression_and_stale_exclusion():
|
||||
policy = _policy()
|
||||
report = _coverage()
|
||||
report["files"]["obliteratus/pure.py"]["summary"]["covered_lines"] = 79
|
||||
report["files"]["obliteratus/pure.py"]["summary"]["covered_lines"] = 89
|
||||
_, failures = quality.validate_mature_cpu_scope(report, policy)
|
||||
assert failures == [
|
||||
"mature CPU line coverage 79.00% is below the 80.00% floor",
|
||||
"mature CPU line coverage 89.00% is below the 90.00% floor",
|
||||
]
|
||||
del report["files"]["obliteratus/external.py"]
|
||||
_, failures = quality.measure_mature_cpu_scope(report, policy)
|
||||
|
||||
@@ -0,0 +1,295 @@
|
||||
"""Failure, storage, and Hub boundary contracts for opt-in telemetry."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from types import SimpleNamespace
|
||||
from unittest.mock import Mock
|
||||
|
||||
import pytest
|
||||
import torch
|
||||
|
||||
import obliteratus.telemetry as telemetry
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def _restore_telemetry_globals(monkeypatch, tmp_path):
|
||||
monkeypatch.setattr(telemetry, "TELEMETRY_FILE", tmp_path / "telemetry.jsonl")
|
||||
monkeypatch.setattr(telemetry, "_TELEMETRY_DIR", tmp_path)
|
||||
monkeypatch.setattr(telemetry, "_TELEMETRY_REPO", "")
|
||||
monkeypatch.setattr(telemetry, "_hub_repo_created", False)
|
||||
monkeypatch.setattr(telemetry, "_hub_sync_last", 0.0)
|
||||
monkeypatch.setattr(telemetry, "_restore_done", False)
|
||||
telemetry._sync_in_progress.clear()
|
||||
|
||||
|
||||
def test_public_text_sanitizes_windows_paths_and_truncates():
|
||||
assert telemetry._sanitize_public_text(r"C:\private\model.bin") == "model.bin"
|
||||
assert telemetry._sanitize_public_text("x" * 20, max_len=8) == "xxxxx..."
|
||||
assert telemetry._sanitize_public_value(object())
|
||||
|
||||
|
||||
def test_telemetry_directory_prefers_explicit_and_home(monkeypatch, tmp_path):
|
||||
explicit = tmp_path / "explicit"
|
||||
monkeypatch.setenv("OBLITERATUS_DATA_DIR", str(explicit))
|
||||
monkeypatch.setattr(telemetry, "_ON_HF_SPACES", False)
|
||||
assert telemetry._telemetry_dir() == explicit
|
||||
|
||||
monkeypatch.setattr(telemetry, "_test_writable", lambda path: path.name == ".obliteratus")
|
||||
monkeypatch.setattr(Path, "home", staticmethod(lambda: tmp_path / "home"))
|
||||
assert telemetry._telemetry_dir() == tmp_path / "home" / ".obliteratus"
|
||||
|
||||
|
||||
def test_telemetry_directory_retries_hf_mount_then_uses_it(monkeypatch):
|
||||
monkeypatch.delenv("OBLITERATUS_DATA_DIR", raising=False)
|
||||
monkeypatch.setattr(telemetry, "_ON_HF_SPACES", True)
|
||||
monkeypatch.setattr(Path, "exists", lambda self: str(self) == "/data")
|
||||
attempts = iter([False, True])
|
||||
monkeypatch.setattr(telemetry, "_test_writable", lambda _path: next(attempts))
|
||||
sleep = Mock()
|
||||
monkeypatch.setattr(telemetry.time, "sleep", sleep)
|
||||
|
||||
assert telemetry._telemetry_dir() == Path("/data/obliteratus")
|
||||
sleep.assert_called_once_with(1)
|
||||
|
||||
|
||||
def test_telemetry_directory_has_ephemeral_fallback(monkeypatch, tmp_path):
|
||||
monkeypatch.delenv("OBLITERATUS_DATA_DIR", raising=False)
|
||||
monkeypatch.setattr(telemetry, "_ON_HF_SPACES", False)
|
||||
monkeypatch.setattr(telemetry, "_test_writable", lambda _path: False)
|
||||
monkeypatch.setattr(Path, "home", staticmethod(lambda: tmp_path / "home"))
|
||||
assert telemetry._telemetry_dir() == Path("/tmp/obliteratus_telemetry")
|
||||
|
||||
|
||||
class _HubApi:
|
||||
instances: list["_HubApi"] = []
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
self.kwargs = kwargs
|
||||
self.create_repo = Mock()
|
||||
self.repo_info = Mock()
|
||||
self.upload_file = Mock()
|
||||
self.list_repo_files = Mock(return_value=[])
|
||||
self.instances.append(self)
|
||||
|
||||
|
||||
def _hub_module(api_class=_HubApi, **members):
|
||||
return SimpleNamespace(HfApi=api_class, **members)
|
||||
|
||||
|
||||
def test_ensure_hub_repo_create_and_existing_fallback(monkeypatch):
|
||||
_HubApi.instances.clear()
|
||||
monkeypatch.setitem(sys.modules, "huggingface_hub", _hub_module())
|
||||
assert telemetry._ensure_hub_repo("org/data") is True
|
||||
_HubApi.instances[-1].create_repo.assert_called_once()
|
||||
assert telemetry._ensure_hub_repo("org/data") is True
|
||||
assert len(_HubApi.instances) == 1
|
||||
|
||||
telemetry._hub_repo_created = False
|
||||
|
||||
class ExistingApi(_HubApi):
|
||||
def __init__(self, **kwargs):
|
||||
super().__init__(**kwargs)
|
||||
self.create_repo.side_effect = RuntimeError("cannot create")
|
||||
|
||||
monkeypatch.setitem(sys.modules, "huggingface_hub", _hub_module(ExistingApi))
|
||||
assert telemetry._ensure_hub_repo("org/data") is True
|
||||
ExistingApi.instances[-1].repo_info.assert_called_once()
|
||||
|
||||
|
||||
def test_ensure_hub_repo_fails_closed(monkeypatch):
|
||||
class FailingApi(_HubApi):
|
||||
def __init__(self, **kwargs):
|
||||
super().__init__(**kwargs)
|
||||
self.create_repo.side_effect = RuntimeError("create")
|
||||
self.repo_info.side_effect = RuntimeError("lookup")
|
||||
|
||||
monkeypatch.setitem(sys.modules, "huggingface_hub", _hub_module(FailingApi))
|
||||
assert telemetry._ensure_hub_repo("org/data") is False
|
||||
|
||||
monkeypatch.setitem(sys.modules, "huggingface_hub", None)
|
||||
assert telemetry._ensure_hub_repo("org/data") is False
|
||||
|
||||
|
||||
def test_background_sync_short_circuits_and_uploads(monkeypatch, tmp_path):
|
||||
telemetry._sync_in_progress.set()
|
||||
telemetry._sync_to_hub_bg()
|
||||
telemetry._sync_in_progress.clear()
|
||||
|
||||
telemetry._sync_to_hub_bg()
|
||||
assert not telemetry._sync_in_progress.is_set()
|
||||
|
||||
telemetry._TELEMETRY_REPO = "org/data"
|
||||
telemetry._sync_to_hub_bg()
|
||||
assert not telemetry._sync_in_progress.is_set()
|
||||
|
||||
telemetry.TELEMETRY_FILE.write_text("{}\n")
|
||||
_HubApi.instances.clear()
|
||||
monkeypatch.setitem(sys.modules, "huggingface_hub", _hub_module())
|
||||
monkeypatch.setattr(telemetry, "_ensure_hub_repo", lambda _repo: True)
|
||||
monkeypatch.setattr(telemetry, "_instance_slug", lambda: "slug")
|
||||
telemetry._sync_to_hub_bg()
|
||||
_HubApi.instances[-1].upload_file.assert_called_once_with(
|
||||
path_or_fileobj=str(telemetry.TELEMETRY_FILE),
|
||||
path_in_repo="data/slug.jsonl",
|
||||
repo_id="org/data",
|
||||
repo_type="dataset",
|
||||
commit_message="Auto-sync telemetry from slug",
|
||||
)
|
||||
assert not telemetry._sync_in_progress.is_set()
|
||||
|
||||
|
||||
def test_sync_scheduler_enforces_configuration_enablement_and_debounce(monkeypatch):
|
||||
thread = Mock()
|
||||
monkeypatch.setattr(telemetry.threading, "Thread", Mock(return_value=thread))
|
||||
monkeypatch.setattr(telemetry, "is_enabled", lambda: True)
|
||||
|
||||
telemetry._schedule_hub_sync()
|
||||
thread.start.assert_not_called()
|
||||
|
||||
telemetry._TELEMETRY_REPO = "org/data"
|
||||
monkeypatch.setattr(telemetry, "is_enabled", lambda: False)
|
||||
telemetry._schedule_hub_sync()
|
||||
thread.start.assert_not_called()
|
||||
|
||||
monkeypatch.setattr(telemetry, "is_enabled", lambda: True)
|
||||
monkeypatch.setattr(telemetry.time, "time", lambda: 100.0)
|
||||
telemetry._schedule_hub_sync()
|
||||
thread.start.assert_called_once()
|
||||
telemetry._schedule_hub_sync()
|
||||
thread.start.assert_called_once()
|
||||
|
||||
|
||||
def test_hf_api_fetch_handles_listing_errors_and_file_errors(monkeypatch, tmp_path):
|
||||
class ListingApi(_HubApi):
|
||||
def __init__(self, **kwargs):
|
||||
super().__init__(**kwargs)
|
||||
self.list_repo_files.return_value = ["README.md"]
|
||||
|
||||
monkeypatch.setitem(sys.modules, "huggingface_hub", _hub_module(ListingApi, hf_hub_download=Mock()))
|
||||
assert telemetry._fetch_via_hf_api("org/data", 2) == []
|
||||
|
||||
class BrokenListingApi(_HubApi):
|
||||
def __init__(self, **kwargs):
|
||||
super().__init__(**kwargs)
|
||||
self.list_repo_files.side_effect = RuntimeError("offline")
|
||||
|
||||
monkeypatch.setitem(sys.modules, "huggingface_hub", _hub_module(BrokenListingApi, hf_hub_download=Mock()))
|
||||
assert telemetry._fetch_via_hf_api("org/data", 2) == []
|
||||
|
||||
|
||||
def test_git_clone_fetch_parses_bounded_records_and_cleans_up(monkeypatch):
|
||||
def fake_run(command, **_kwargs):
|
||||
clone_dir = Path(command[-1])
|
||||
data = clone_dir / "data"
|
||||
data.mkdir()
|
||||
(data / "a.jsonl").write_text('\n{"id": 1}\ninvalid\n{"id": 2}\n')
|
||||
return SimpleNamespace(returncode=0, stderr="")
|
||||
|
||||
monkeypatch.setattr("subprocess.run", fake_run)
|
||||
assert telemetry._fetch_via_git_clone("org/data", 1) == [{"id": 1}]
|
||||
|
||||
|
||||
def test_git_clone_fetch_handles_failure_and_missing_data(monkeypatch):
|
||||
monkeypatch.setattr(
|
||||
"subprocess.run",
|
||||
lambda *_args, **_kwargs: SimpleNamespace(returncode=1, stderr="denied"),
|
||||
)
|
||||
assert telemetry._fetch_via_git_clone("org/data", 2) == []
|
||||
|
||||
monkeypatch.setattr(
|
||||
"subprocess.run",
|
||||
lambda *_args, **_kwargs: SimpleNamespace(returncode=0, stderr=""),
|
||||
)
|
||||
assert telemetry._fetch_via_git_clone("org/data", 2) == []
|
||||
|
||||
|
||||
def test_gpu_detection_and_peak_vram(monkeypatch):
|
||||
monkeypatch.setattr(torch.cuda, "is_available", lambda: True)
|
||||
monkeypatch.setattr(torch.cuda, "get_device_name", lambda _index: "GPU")
|
||||
monkeypatch.setattr(
|
||||
torch.cuda,
|
||||
"get_device_properties",
|
||||
lambda _index: SimpleNamespace(total_memory=8 * 1024**3),
|
||||
)
|
||||
monkeypatch.setattr(torch.cuda, "max_memory_allocated", lambda: 3 * 1024**3)
|
||||
monkeypatch.setattr(torch.cuda, "max_memory_reserved", lambda: 4 * 1024**3)
|
||||
assert telemetry._detect_gpu() == ("GPU", 8.0)
|
||||
assert telemetry._get_peak_vram() == {
|
||||
"peak_allocated_gb": 3.0,
|
||||
"peak_reserved_gb": 4.0,
|
||||
}
|
||||
assert telemetry._detect_model_family("org/Qwen-model") == "qwen"
|
||||
assert telemetry._detect_model_family("org/other") == "unknown"
|
||||
|
||||
|
||||
def test_direction_stats_and_excise_details_cover_optional_techniques(monkeypatch):
|
||||
pipeline = SimpleNamespace(
|
||||
refusal_directions={0: torch.tensor([1.0, 0.0]), 1: torch.tensor([0.0, 1.0])},
|
||||
refusal_subspaces={0: torch.eye(2)},
|
||||
_excise_modified_count=2,
|
||||
_refusal_heads={0: [1, 2]},
|
||||
_sae_directions={0: torch.ones(2)},
|
||||
_expert_safety_scores={0: 1.0},
|
||||
_layer_excise_weights={0: 0.2, 1: 0.8},
|
||||
_expert_directions={0: torch.ones(2)},
|
||||
_steering_hooks=[object()],
|
||||
invert_refusal=True,
|
||||
project_embeddings=True,
|
||||
activation_steering=True,
|
||||
expert_transplant=True,
|
||||
)
|
||||
stats = telemetry._direction_stats(pipeline)
|
||||
assert stats["direction_norms"] == {"0": 1.0, "1": 1.0}
|
||||
assert stats["mean_direction_persistence"] == 0.0
|
||||
assert stats["effective_ranks"] == {"0": 2.0}
|
||||
|
||||
details = telemetry._extract_excise_details(pipeline)
|
||||
assert details["modified_count"] == 2
|
||||
assert details["total_heads_projected"] == 2
|
||||
assert details["adaptive_weight_min"] == 0.2
|
||||
assert details["adaptive_weight_max"] == 0.8
|
||||
assert set(details["used_techniques"]) == {
|
||||
"head_surgery", "sae_features", "expert_gating", "layer_adaptive",
|
||||
"per_expert", "activation_steering", "inversion", "embedding_projection",
|
||||
"expert_transplant",
|
||||
}
|
||||
|
||||
monkeypatch.setattr(torch.linalg, "svdvals", Mock(side_effect=RuntimeError("svd")))
|
||||
assert "effective_ranks" not in telemetry._direction_stats(pipeline)
|
||||
|
||||
|
||||
def test_send_and_pipeline_failures_are_best_effort(monkeypatch, tmp_path):
|
||||
monkeypatch.setattr(telemetry, "is_enabled", lambda: True)
|
||||
monkeypatch.setattr(telemetry, "TELEMETRY_FILE", tmp_path / "missing" / "file.jsonl")
|
||||
telemetry._send_sync({"schema_version": 2})
|
||||
|
||||
logger = Mock()
|
||||
monkeypatch.setattr(telemetry, "logger", logger)
|
||||
pipeline = SimpleNamespace(handle=SimpleNamespace(summary=Mock(side_effect=RuntimeError("summary"))))
|
||||
telemetry.maybe_send_pipeline_report(pipeline)
|
||||
telemetry.maybe_send_informed_report(pipeline, SimpleNamespace())
|
||||
assert logger.debug.call_count == 2
|
||||
|
||||
|
||||
def test_push_to_hub_failure_paths(monkeypatch, tmp_path):
|
||||
assert telemetry.push_to_hub() is False
|
||||
|
||||
telemetry.TELEMETRY_FILE.write_text('{}\n')
|
||||
monkeypatch.setattr(telemetry, "read_telemetry", lambda: [{}])
|
||||
monkeypatch.setattr(telemetry, "_ensure_hub_repo", lambda _repo: False)
|
||||
assert telemetry.push_to_hub("org/data") is False
|
||||
|
||||
monkeypatch.setitem(sys.modules, "huggingface_hub", None)
|
||||
monkeypatch.setattr(telemetry, "_ensure_hub_repo", lambda _repo: True)
|
||||
assert telemetry.push_to_hub("org/data") is False
|
||||
|
||||
|
||||
def test_restore_and_background_restore_absorb_boundary_failures(monkeypatch):
|
||||
telemetry._TELEMETRY_REPO = "org/data"
|
||||
monkeypatch.setattr(telemetry, "fetch_hub_records", Mock(side_effect=RuntimeError("offline")))
|
||||
assert telemetry.restore_from_hub() == 0
|
||||
|
||||
monkeypatch.setattr(telemetry, "restore_from_hub", Mock(side_effect=RuntimeError("offline")))
|
||||
telemetry._restore_from_hub_bg()
|
||||
Reference in New Issue
Block a user