mirror of
https://github.com/elder-plinius/OBLITERATUS.git
synced 2026-08-17 16:37:30 +02:00
test: enforce research integrity contracts
This commit is contained in:
@@ -18,6 +18,7 @@ from obliteratus.evaluation.advanced_metrics import (
|
||||
linear_cka,
|
||||
refusal_projection_magnitude,
|
||||
refusal_rate,
|
||||
refusal_rate_with_ci,
|
||||
token_kl_divergence,
|
||||
)
|
||||
|
||||
@@ -122,6 +123,41 @@ class TestRefusalRate:
|
||||
def test_refusal_substrings_nonempty(self):
|
||||
assert len(REFUSAL_SUBSTRINGS) > 10
|
||||
|
||||
@pytest.mark.parametrize("entrypoint", [refusal_rate, refusal_rate_with_ci])
|
||||
def test_rejects_unknown_mode(self, entrypoint):
|
||||
with pytest.raises(ValueError, match="Unknown refusal detection mode"):
|
||||
entrypoint(["I cannot help"], mode="typo")
|
||||
|
||||
def test_private_classifier_rejects_unknown_mode(self):
|
||||
with pytest.raises(ValueError, match="Unknown refusal detection mode"):
|
||||
_is_refusal("I cannot help", mode="typo")
|
||||
|
||||
@pytest.mark.parametrize("confidence", [0.0, 0.9 + 1e-10, 1.0])
|
||||
def test_rejects_unsupported_confidence(self, confidence):
|
||||
with pytest.raises(ValueError, match="confidence must"):
|
||||
refusal_rate_with_ci(["Sure"], confidence=confidence)
|
||||
|
||||
def test_measured_zero_is_not_unavailable(self):
|
||||
result = refusal_rate_with_ci(["Sure, here is the answer."])
|
||||
assert result["available"] is True
|
||||
assert result["rate"] == 0.0
|
||||
assert result["refusal_count"] == 0
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"response",
|
||||
[
|
||||
" I'M SORRY!!! I cannot help with that.",
|
||||
"抱歉,我无法帮助这个请求。",
|
||||
"Извините, я не могу помочь с этим запросом.",
|
||||
],
|
||||
)
|
||||
def test_case_punctuation_and_multilingual_refusals(self, response):
|
||||
assert _is_refusal(response, mode="combined")
|
||||
|
||||
def test_hard_negative_is_not_a_refusal(self):
|
||||
response = "The parser accepts cannot_help as an ordinary variable name."
|
||||
assert not _is_refusal(response, mode="combined")
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# KL Divergence
|
||||
@@ -174,6 +210,29 @@ class TestKLDivergence:
|
||||
kl_t5 = token_kl_divergence(logits_a, logits_b, temperature=5.0)
|
||||
assert kl_t5 < kl_t1
|
||||
|
||||
def test_common_logit_offset_is_invariant(self):
|
||||
torch.manual_seed(42)
|
||||
logits_a = torch.randn(2, 3, 8)
|
||||
logits_b = torch.randn(2, 3, 8)
|
||||
expected = token_kl_divergence(logits_a, logits_b)
|
||||
assert token_kl_divergence(logits_a + 11, logits_b - 7) == pytest.approx(
|
||||
expected, abs=1e-6,
|
||||
)
|
||||
|
||||
@pytest.mark.parametrize("temperature", [0, -1, float("inf"), float("nan"), "hot"])
|
||||
def test_rejects_invalid_temperature(self, temperature):
|
||||
logits = torch.zeros(1, 2, 3)
|
||||
with pytest.raises(ValueError, match="temperature"):
|
||||
token_kl_divergence(logits, logits, temperature=temperature)
|
||||
|
||||
def test_rejects_shape_and_nonfinite_input(self):
|
||||
with pytest.raises(ValueError, match="identical shapes"):
|
||||
token_kl_divergence(torch.zeros(1, 2, 3), torch.zeros(1, 3, 3))
|
||||
logits = torch.zeros(1, 2, 3)
|
||||
logits[0, 0, 0] = float("inf")
|
||||
with pytest.raises(ValueError, match="finite"):
|
||||
first_token_kl_divergence(logits, logits)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Effective Rank
|
||||
@@ -260,6 +319,14 @@ class TestActivationCosineSimilarity:
|
||||
sim = activation_cosine_similarity(a, b)
|
||||
assert -1.0 <= sim <= 1.0
|
||||
|
||||
def test_rejects_mismatched_or_nonfinite_activations(self):
|
||||
with pytest.raises(ValueError, match="identical shapes"):
|
||||
activation_cosine_similarity(torch.zeros(2, 3), torch.zeros(3, 3))
|
||||
bad = torch.zeros(2, 3)
|
||||
bad[0, 0] = float("nan")
|
||||
with pytest.raises(ValueError, match="finite"):
|
||||
activation_cosine_similarity(bad, bad)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Linear CKA
|
||||
@@ -310,6 +377,23 @@ class TestLinearCKA:
|
||||
cka = linear_cka(X, Y)
|
||||
assert -0.01 <= cka <= 1.01
|
||||
|
||||
def test_joint_row_permutation_is_invariant(self):
|
||||
torch.manual_seed(42)
|
||||
x = torch.randn(20, 8)
|
||||
y = torch.randn(20, 12)
|
||||
permutation = torch.randperm(20)
|
||||
assert linear_cka(x[permutation], y[permutation]) == pytest.approx(
|
||||
linear_cka(x, y), abs=1e-6,
|
||||
)
|
||||
|
||||
def test_rejects_different_sample_counts(self):
|
||||
with pytest.raises(ValueError, match="same sample count"):
|
||||
linear_cka(torch.zeros(2, 3), torch.zeros(3, 4))
|
||||
|
||||
def test_rejects_single_sample_degeneracy(self):
|
||||
with pytest.raises(ValueError, match="at least two samples"):
|
||||
linear_cka(torch.zeros(1, 3), torch.zeros(1, 4))
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Refusal Direction Projection Magnitude
|
||||
@@ -346,6 +430,17 @@ class TestRefusalProjection:
|
||||
result = refusal_projection_magnitude(acts, d)
|
||||
assert set(result.keys()) == {"mean", "std", "max", "min", "abs_mean"}
|
||||
|
||||
def test_single_sample_has_defined_population_std(self):
|
||||
result = refusal_projection_magnitude(
|
||||
torch.tensor([[2.0, 0.0]]), torch.tensor([1.0, 0.0]),
|
||||
)
|
||||
assert result["std"] == 0.0
|
||||
|
||||
@pytest.mark.parametrize("direction", [torch.zeros(2), torch.ones(3)])
|
||||
def test_rejects_invalid_direction(self, direction):
|
||||
with pytest.raises(ValueError):
|
||||
refusal_projection_magnitude(torch.ones(2, 2), direction)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Eval Report Formatting
|
||||
@@ -380,6 +475,20 @@ class TestEvalReport:
|
||||
report = format_eval_report(result)
|
||||
assert "significant damage" in report
|
||||
|
||||
def test_unavailable_metrics_are_not_rendered_as_zero(self):
|
||||
result = AbliterationEvalResult(
|
||||
refusal_rate_harmful=None,
|
||||
refusal_rate_harmless=0.0,
|
||||
kl_divergence=None,
|
||||
perplexity=None,
|
||||
coherence_score=None,
|
||||
mean_activation_cosine=None,
|
||||
mean_cka=None,
|
||||
)
|
||||
report = format_eval_report(result)
|
||||
assert report.count("unavailable") >= 4
|
||||
assert "Harmless prompt over-refusal: 0.0%" in report
|
||||
|
||||
def test_format_report_no_kl(self):
|
||||
result = AbliterationEvalResult(
|
||||
refusal_rate_harmful=0.5,
|
||||
@@ -392,4 +501,4 @@ class TestEvalReport:
|
||||
)
|
||||
report = format_eval_report(result)
|
||||
assert "50.0%" in report
|
||||
assert "KL" not in report
|
||||
assert "KL divergence: unavailable" in report
|
||||
|
||||
+53
-3
@@ -162,6 +162,13 @@ class TestSaveContribution:
|
||||
name = path.stem
|
||||
assert name.startswith("llama-2-7b-chat-hf_advanced_")
|
||||
|
||||
def test_method_cannot_escape_output_directory(self, tmp_path):
|
||||
pipeline = _make_mock_pipeline()
|
||||
pipeline.method = "../../unsafe method"
|
||||
path = save_contribution(pipeline, model_name="test/model", output_dir=tmp_path)
|
||||
assert path.parent == tmp_path
|
||||
assert ".." not in path.name
|
||||
|
||||
def test_includes_telemetry_report(self, tmp_path):
|
||||
pipeline = _make_mock_pipeline()
|
||||
path = save_contribution(
|
||||
@@ -289,6 +296,14 @@ class TestLoadContributions:
|
||||
records = load_contributions(tmp_path)
|
||||
assert "_source_file" in records[0]
|
||||
assert "contrib_0.json" in records[0]["_source_file"]
|
||||
assert records[0]["_source_file"] == "contrib_0.json"
|
||||
|
||||
def test_skips_unsupported_schema_version(self, tmp_path):
|
||||
path = self._write_contrib(tmp_path, "test/model", "advanced", 0.05, 0)
|
||||
data = json.loads(path.read_text())
|
||||
data["contribution_schema_version"] = 999
|
||||
path.write_text(json.dumps(data))
|
||||
assert load_contributions(tmp_path) == []
|
||||
|
||||
def test_ignores_non_json_files(self, tmp_path):
|
||||
(tmp_path / "readme.txt").write_text("some text")
|
||||
@@ -372,11 +387,32 @@ class TestAggregateResults:
|
||||
assert "coherence" in stats
|
||||
assert stats["perplexity"]["mean"] == 5.2
|
||||
|
||||
def test_missing_metric_skipped(self):
|
||||
def test_missing_metric_is_explicitly_unavailable(self):
|
||||
records = [self._make_record("model-a", "advanced", 0.05)]
|
||||
result = aggregate_results(records)
|
||||
# coherence not provided, should not appear
|
||||
assert "coherence" not in result["model-a"]["advanced"]
|
||||
stats = result["model-a"]["advanced"]["coherence"]
|
||||
assert stats["status"] == "unavailable"
|
||||
assert stats["mean"] is None
|
||||
assert stats["n"] == 0
|
||||
assert stats["unavailable"] == 1
|
||||
|
||||
def test_invalid_and_nonfinite_metrics_are_unavailable(self):
|
||||
records = [
|
||||
self._make_record("model-a", "advanced", float("nan"), perplexity=-1),
|
||||
self._make_record("model-a", "advanced", True, coherence=2.0),
|
||||
]
|
||||
result = aggregate_results(records)["model-a"]["advanced"]
|
||||
assert result["refusal_rate"]["status"] == "unavailable"
|
||||
assert result["perplexity"]["status"] == "unavailable"
|
||||
assert result["coherence"]["status"] == "unavailable"
|
||||
|
||||
def test_zero_is_a_measured_value(self):
|
||||
result = aggregate_results([
|
||||
self._make_record("model-a", "advanced", 0.0),
|
||||
])["model-a"]["advanced"]["refusal_rate"]
|
||||
assert result["status"] == "measured"
|
||||
assert result["mean"] == 0.0
|
||||
assert result["n"] == 1
|
||||
|
||||
def test_unknown_model_and_method(self):
|
||||
records = [{
|
||||
@@ -419,6 +455,20 @@ class TestGenerateLatexTable:
|
||||
assert "\\toprule" in latex
|
||||
assert "\\bottomrule" in latex
|
||||
|
||||
def test_escapes_untrusted_labels(self):
|
||||
latex = generate_latex_table({
|
||||
"org/model_name&x": {
|
||||
"advanced_mode": {
|
||||
"n_runs": 1,
|
||||
"refusal_rate": {
|
||||
"status": "measured", "mean": 0.0, "std": 0.0, "n": 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
assert "model\\_name\\&x" in latex
|
||||
assert "advanced\\_mode" in latex
|
||||
|
||||
def test_includes_model_names(self):
|
||||
agg = self._sample_aggregated()
|
||||
latex = generate_latex_table(agg)
|
||||
|
||||
+44
-1
@@ -2,7 +2,7 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
|
||||
import pytest
|
||||
import torch
|
||||
|
||||
from obliteratus.evaluation.metrics import accuracy, f1_score_metric, perplexity
|
||||
@@ -36,6 +36,38 @@ class TestPerplexity:
|
||||
ppl = perplexity(logits, labels)
|
||||
assert ppl > 10, f"Random logits should yield high perplexity, got {ppl}"
|
||||
|
||||
def test_uniform_logits_equal_vocabulary_size(self):
|
||||
logits = torch.zeros(2, 4, 7)
|
||||
labels = torch.tensor([[0, 1, 2, 3], [3, 4, 5, 6]])
|
||||
assert perplexity(logits, labels) == pytest.approx(7.0)
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("logits", "labels", "message"),
|
||||
[
|
||||
(torch.zeros(2, 3), torch.zeros(2, 3, dtype=torch.long), "3D"),
|
||||
(torch.zeros(2, 3, 4), torch.zeros(2, 3, 1, dtype=torch.long), "2D"),
|
||||
(torch.zeros(2, 3, 4), torch.zeros(2, 2, dtype=torch.long), "does not match"),
|
||||
(torch.zeros(2, 1, 4), torch.zeros(2, 1, dtype=torch.long), "two sequence"),
|
||||
],
|
||||
)
|
||||
def test_rejects_invalid_shapes(self, logits, labels, message):
|
||||
with pytest.raises(ValueError, match=message):
|
||||
perplexity(logits, labels)
|
||||
|
||||
def test_rejects_unavailable_and_invalid_targets(self):
|
||||
logits = torch.zeros(1, 3, 4)
|
||||
with pytest.raises(ValueError, match="every target"):
|
||||
perplexity(logits, torch.full((1, 3), -100))
|
||||
with pytest.raises(ValueError, match="outside"):
|
||||
perplexity(logits, torch.tensor([[0, 1, 9]]))
|
||||
logits[0, 0, 0] = float("nan")
|
||||
with pytest.raises(ValueError, match="finite"):
|
||||
perplexity(logits, torch.tensor([[0, 1, 2]]))
|
||||
|
||||
def test_rejects_non_integer_labels(self):
|
||||
with pytest.raises(ValueError, match="integer token IDs"):
|
||||
perplexity(torch.zeros(1, 3, 4), torch.zeros(1, 3))
|
||||
|
||||
|
||||
class TestAccuracy:
|
||||
def test_perfect(self):
|
||||
@@ -50,6 +82,10 @@ class TestAccuracy:
|
||||
def test_empty(self):
|
||||
assert accuracy([], []) == 0.0
|
||||
|
||||
def test_rejects_length_mismatch_instead_of_truncating(self):
|
||||
with pytest.raises(ValueError, match="equal length"):
|
||||
accuracy([1, 2], [1])
|
||||
|
||||
|
||||
class TestF1:
|
||||
def test_perfect(self):
|
||||
@@ -58,3 +94,10 @@ class TestF1:
|
||||
def test_zero(self):
|
||||
score = f1_score_metric([0, 0, 0, 0], [1, 1, 1, 1])
|
||||
assert score == 0.0
|
||||
|
||||
def test_empty(self):
|
||||
assert f1_score_metric([], []) == 0.0
|
||||
|
||||
def test_rejects_length_mismatch(self):
|
||||
with pytest.raises(ValueError, match="equal length"):
|
||||
f1_score_metric([1, 2], [1])
|
||||
|
||||
@@ -132,8 +132,12 @@ class TestRefusalRateWithCI:
|
||||
|
||||
def test_empty_responses(self):
|
||||
ci = refusal_rate_with_ci([], mode="combined")
|
||||
assert ci["rate"] == 0.0
|
||||
assert ci["available"] is False
|
||||
assert ci["rate"] is None
|
||||
assert ci["ci_lower"] is None
|
||||
assert ci["ci_upper"] is None
|
||||
assert ci["n_samples"] == 0
|
||||
assert ci["refusal_count"] == 0
|
||||
|
||||
def test_ci_narrower_with_more_samples(self):
|
||||
"""More samples should produce tighter confidence intervals."""
|
||||
|
||||
+52
-1
@@ -4,7 +4,11 @@ from __future__ import annotations
|
||||
|
||||
import json
|
||||
|
||||
from obliteratus.reporting.report import AblationReport, AblationResult
|
||||
from obliteratus.reporting.report import (
|
||||
REPORT_SCHEMA_VERSION,
|
||||
AblationReport,
|
||||
AblationResult,
|
||||
)
|
||||
|
||||
|
||||
def _make_report() -> AblationReport:
|
||||
@@ -46,6 +50,7 @@ class TestAblationReport:
|
||||
assert data["model_name"] == "test-model"
|
||||
assert len(data["results"]) == 2
|
||||
assert data["baseline_metrics"]["perplexity"] == 25.0
|
||||
assert data["schema_version"] == REPORT_SCHEMA_VERSION
|
||||
|
||||
def test_save_csv(self, tmp_path):
|
||||
report = _make_report()
|
||||
@@ -68,3 +73,49 @@ class TestAblationReport:
|
||||
report.plot_impact(metric="perplexity", output_path=out)
|
||||
assert out.exists()
|
||||
assert out.stat().st_size > 0
|
||||
|
||||
def test_measured_zero_and_unavailable_are_distinct(self):
|
||||
report = AblationReport(model_name="test")
|
||||
report.add_baseline({"refusal_rate": 0.0, "perplexity": None})
|
||||
report.add_result(AblationResult(
|
||||
strategy="advanced",
|
||||
component="all",
|
||||
description="partial evaluation",
|
||||
metrics={"refusal_rate": 0.0, "perplexity": None},
|
||||
))
|
||||
data = report.to_dict()
|
||||
assert data["baseline_metric_status"] == {
|
||||
"perplexity": "unavailable", "refusal_rate": "measured",
|
||||
}
|
||||
assert data["results"][0]["metric_status"] == {
|
||||
"perplexity": "unavailable", "refusal_rate": "measured",
|
||||
}
|
||||
assert data["results"][0]["metrics"]["refusal_rate"] == 0.0
|
||||
|
||||
def test_json_is_deterministic_finite_and_redacted(self, tmp_path):
|
||||
report = AblationReport(model_name="/private/models/secret-model")
|
||||
report.add_baseline({"score": float("nan")})
|
||||
report.add_result(AblationResult(
|
||||
strategy="/private/run/advanced",
|
||||
component="layer_0",
|
||||
description="artifact /private/run/output hf_abcdefghijkl",
|
||||
metrics={"score": float("inf")},
|
||||
metadata={
|
||||
"token": "hf_abcdefghijkl",
|
||||
"artifact": "/private/run/checkpoint.bin",
|
||||
},
|
||||
))
|
||||
first = tmp_path / "first.json"
|
||||
second = tmp_path / "second.json"
|
||||
report.save_json(first)
|
||||
report.save_json(second)
|
||||
assert first.read_bytes() == second.read_bytes()
|
||||
text = first.read_text()
|
||||
assert "/private/" not in text
|
||||
assert "hf_abcdefghijkl" not in text
|
||||
assert '"token"' not in text
|
||||
assert "NaN" not in text and "Infinity" not in text
|
||||
|
||||
def test_public_model_identifier_is_preserved(self):
|
||||
report = AblationReport(model_name="org/model-name")
|
||||
assert report.to_dict()["model_name"] == "org/model-name"
|
||||
|
||||
+245
-1
@@ -7,22 +7,33 @@ from dataclasses import dataclass, field
|
||||
from pathlib import Path
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
import pytest
|
||||
import torch
|
||||
|
||||
from obliteratus.telemetry import (
|
||||
_ALLOWED_METHOD_CONFIG_KEYS,
|
||||
BENCHMARK_SCHEMA_VERSION,
|
||||
TELEMETRY_SCHEMA_VERSION,
|
||||
BenchmarkRecord,
|
||||
_direction_stats,
|
||||
_extract_excise_details,
|
||||
_extract_prompt_counts,
|
||||
_extract_analysis_insights,
|
||||
_fetch_via_hf_api,
|
||||
_is_mount_point,
|
||||
_test_writable,
|
||||
build_report,
|
||||
disable_telemetry,
|
||||
enable_telemetry,
|
||||
fetch_hub_records,
|
||||
get_leaderboard_data,
|
||||
is_enabled,
|
||||
log_benchmark,
|
||||
log_benchmark_from_dict,
|
||||
maybe_send_informed_report,
|
||||
maybe_send_pipeline_report,
|
||||
read_telemetry,
|
||||
push_to_hub,
|
||||
restore_from_hub,
|
||||
send_report,
|
||||
storage_diagnostic,
|
||||
@@ -110,7 +121,7 @@ class TestBuildReport:
|
||||
|
||||
def test_schema_version_2(self):
|
||||
report = build_report(**self._base_kwargs())
|
||||
assert report["schema_version"] == 2
|
||||
assert report["schema_version"] == TELEMETRY_SCHEMA_VERSION
|
||||
|
||||
def test_basic_fields(self):
|
||||
report = build_report(**self._base_kwargs())
|
||||
@@ -202,6 +213,45 @@ class TestBuildReport:
|
||||
assert "analysis_insights" not in report
|
||||
assert "informed" not in report
|
||||
|
||||
def test_quality_metrics_have_availability_and_range_contracts(self):
|
||||
report = build_report(**self._base_kwargs(quality_metrics={
|
||||
"refusal_rate": 0.0,
|
||||
"perplexity": None,
|
||||
"coherence": 2.0,
|
||||
"unknown_metric": 4.2,
|
||||
}))
|
||||
assert report["quality_metrics"] == {
|
||||
"coherence": None,
|
||||
"perplexity": None,
|
||||
"refusal_rate": 0.0,
|
||||
}
|
||||
assert report["quality_metric_status"] == {
|
||||
"coherence": "unavailable",
|
||||
"perplexity": "unavailable",
|
||||
"refusal_rate": "measured",
|
||||
}
|
||||
|
||||
def test_public_payload_redacts_paths_tokens_and_secret_keys(self):
|
||||
report = build_report(**self._base_kwargs(
|
||||
architecture="/private/models/LlamaForCausalLM",
|
||||
method_config={
|
||||
"n_directions": 4,
|
||||
"token": "hf_abcdefghijkl",
|
||||
"regularization": "/private/run/value",
|
||||
},
|
||||
quality_metrics={"perplexity": float("nan")},
|
||||
informed_extras={
|
||||
"error": "failed at /private/run/file.bin with sk-abcdefghijklmnop",
|
||||
"api_key": "secret",
|
||||
},
|
||||
))
|
||||
encoded = json.dumps(report, allow_nan=False)
|
||||
assert "/private/" not in encoded
|
||||
assert "hf_abcdefghijkl" not in encoded
|
||||
assert "sk-abcdefghijklmnop" not in encoded
|
||||
assert "api_key" not in encoded
|
||||
assert report["quality_metrics"]["perplexity"] is None
|
||||
|
||||
|
||||
# ── Direction stats extraction ──────────────────────────────────────────
|
||||
|
||||
@@ -694,3 +744,197 @@ class TestHubRestore:
|
||||
restore_from_hub()
|
||||
# Second call should return 0 immediately
|
||||
assert restore_from_hub() == 0
|
||||
|
||||
|
||||
class TestTelemetryRecords:
|
||||
def setup_method(self):
|
||||
enable_telemetry()
|
||||
|
||||
def teardown_method(self):
|
||||
_reset_telemetry()
|
||||
|
||||
def test_benchmark_schema_and_safe_deterministic_jsonl(self, tmp_path):
|
||||
import obliteratus.telemetry as telemetry
|
||||
|
||||
output = tmp_path / "telemetry.jsonl"
|
||||
record = BenchmarkRecord(
|
||||
model_id="/private/models/test-model",
|
||||
method="advanced",
|
||||
refusal_rate=0.0,
|
||||
perplexity=None,
|
||||
error="failed at /private/run/file.bin using hf_abcdefghijkl",
|
||||
extra={"api_token": "sk-abcdefghijklmnop", "safe": 1},
|
||||
)
|
||||
with (
|
||||
patch.object(telemetry, "TELEMETRY_FILE", output),
|
||||
patch("obliteratus.telemetry._schedule_hub_sync"),
|
||||
patch("obliteratus.telemetry._detect_gpu", return_value=("", 0.0)),
|
||||
):
|
||||
assert log_benchmark(record)
|
||||
|
||||
text = output.read_text()
|
||||
data = json.loads(text)
|
||||
assert data["schema_version"] == BENCHMARK_SCHEMA_VERSION
|
||||
assert data["refusal_rate"] == 0.0
|
||||
assert data["perplexity"] is None
|
||||
assert data["model_id"] == "test-model"
|
||||
assert data["extra"] == {"safe": 1}
|
||||
assert "/private/" not in text
|
||||
assert "hf_abcdefghijkl" not in text
|
||||
assert "sk-abcdefghijklmnop" not in text
|
||||
assert text.endswith("\n")
|
||||
|
||||
def test_read_validates_limit_and_skips_malformed_lines(self, tmp_path):
|
||||
import obliteratus.telemetry as telemetry
|
||||
|
||||
output = tmp_path / "telemetry.jsonl"
|
||||
output.write_text('{"timestamp":"1"}\nnot json\n{"timestamp":"2"}\n')
|
||||
with patch.object(telemetry, "TELEMETRY_FILE", output):
|
||||
assert [record["timestamp"] for record in read_telemetry()] == ["2", "1"]
|
||||
with pytest.raises(ValueError, match="greater than zero"):
|
||||
read_telemetry(0)
|
||||
|
||||
|
||||
class TestLeaderboardIntegrity:
|
||||
def test_mixed_schemas_preserve_partial_failures_and_measured_zero(self):
|
||||
v1_zero = {
|
||||
"schema_version": 1,
|
||||
"session_id": "v1-zero",
|
||||
"timestamp": "2026-01-01T00:00:00Z",
|
||||
"model_id": "org/zero-model",
|
||||
"method": "advanced",
|
||||
"refusal_rate": 0.0,
|
||||
"perplexity": 4.0,
|
||||
"coherence": 0.8,
|
||||
"time_seconds": 5.0,
|
||||
}
|
||||
v2_partial = {
|
||||
"schema_version": 2,
|
||||
"session_id": "v2-partial",
|
||||
"timestamp": "2026-01-02T00:00:00Z",
|
||||
"model": {"architecture": "PartialArchitecture"},
|
||||
"method": "advanced",
|
||||
"quality_metrics": {"refusal_rate": None, "perplexity": 3.0},
|
||||
"error": "coherence failed",
|
||||
}
|
||||
v1_missing = {
|
||||
"schema_version": 1,
|
||||
"session_id": "v1-missing",
|
||||
"timestamp": "2026-01-03T00:00:00Z",
|
||||
"model_id": "org/missing-model",
|
||||
"method": "advanced",
|
||||
"refusal_rate": None,
|
||||
"perplexity": 2.0,
|
||||
}
|
||||
with (
|
||||
patch("obliteratus.telemetry.read_telemetry", return_value=[v1_missing, v1_zero]),
|
||||
patch("obliteratus.telemetry.fetch_hub_records", return_value=[v2_partial]),
|
||||
):
|
||||
leaderboard = get_leaderboard_data()
|
||||
|
||||
assert leaderboard[0]["model_id"] == "org/zero-model"
|
||||
assert leaderboard[0]["best_refusal"] == 0.0
|
||||
partial = next(row for row in leaderboard if row["model_id"] == "PartialArchitecture")
|
||||
assert partial["runs"] == 1
|
||||
assert partial["successful_runs"] == 0
|
||||
assert partial["failed_runs"] == 1
|
||||
assert partial["perplexity_measurements"] == 1
|
||||
assert partial["best_perplexity"] == 3.0
|
||||
assert partial["best_refusal"] is None
|
||||
|
||||
def test_invalid_ranges_do_not_enter_aggregates(self):
|
||||
record = {
|
||||
"session_id": "bad", "timestamp": "1", "model_id": "bad/model",
|
||||
"method": "advanced", "refusal_rate": -0.1,
|
||||
"perplexity": float("nan"), "coherence": True,
|
||||
}
|
||||
with (
|
||||
patch("obliteratus.telemetry.read_telemetry", return_value=[record]),
|
||||
patch("obliteratus.telemetry.fetch_hub_records", return_value=[]),
|
||||
):
|
||||
row = get_leaderboard_data()[0]
|
||||
assert row["refusal_measurements"] == 0
|
||||
assert row["perplexity_measurements"] == 0
|
||||
assert row["coherence_measurements"] == 0
|
||||
assert row["best_refusal"] is None
|
||||
|
||||
|
||||
class TestTelemetryHubBoundaries:
|
||||
def test_fetch_prefers_api_and_falls_back_to_git(self):
|
||||
api_records = [{"session_id": "api"}]
|
||||
with (
|
||||
patch("obliteratus.telemetry._fetch_via_hf_api", return_value=api_records),
|
||||
patch("obliteratus.telemetry._fetch_via_git_clone") as git_fetch,
|
||||
):
|
||||
assert fetch_hub_records(3) == api_records
|
||||
git_fetch.assert_not_called()
|
||||
|
||||
git_records = [{"session_id": "git"}]
|
||||
with (
|
||||
patch("obliteratus.telemetry._fetch_via_hf_api", return_value=[]),
|
||||
patch("obliteratus.telemetry._fetch_via_git_clone", return_value=git_records),
|
||||
):
|
||||
assert fetch_hub_records(3) == git_records
|
||||
|
||||
def test_fetch_returns_empty_when_both_boundaries_fail(self):
|
||||
with (
|
||||
patch("obliteratus.telemetry._fetch_via_hf_api", side_effect=RuntimeError("api")),
|
||||
patch("obliteratus.telemetry._fetch_via_git_clone", side_effect=RuntimeError("git")),
|
||||
):
|
||||
assert fetch_hub_records() == []
|
||||
|
||||
def test_hf_api_parser_filters_files_malformed_lines_and_limit(self, tmp_path):
|
||||
first = tmp_path / "first.jsonl"
|
||||
second = tmp_path / "second.jsonl"
|
||||
first.write_text('\n{"session_id":"one"}\nnot-json\n{"session_id":"two"}\n')
|
||||
second.write_text('{"session_id":"three"}\n')
|
||||
api = MagicMock()
|
||||
api.list_repo_files.return_value = [
|
||||
"README.md", "other/ignored.jsonl", "data/first.jsonl", "data/second.jsonl",
|
||||
]
|
||||
with (
|
||||
patch("huggingface_hub.HfApi", return_value=api),
|
||||
patch("huggingface_hub.hf_hub_download", side_effect=[str(first), str(second)]) as download,
|
||||
):
|
||||
records = _fetch_via_hf_api("org/repo", 2)
|
||||
assert [record["session_id"] for record in records] == ["one", "two"]
|
||||
assert download.call_count == 1
|
||||
|
||||
def test_log_from_dict_maps_partial_result_without_erasing_error(self):
|
||||
with patch("obliteratus.telemetry.log_benchmark", return_value=True) as write:
|
||||
assert log_benchmark_from_dict(
|
||||
"org/model",
|
||||
"advanced",
|
||||
{"refusal_rate": 0.0, "perplexity": None, "error": "partial"},
|
||||
dataset="fixture",
|
||||
n_prompts=4,
|
||||
pipeline_config={"n_directions": 3, "bayesian_trials": 2},
|
||||
)
|
||||
record = write.call_args.args[0]
|
||||
assert record.refusal_rate == 0.0
|
||||
assert record.perplexity is None
|
||||
assert record.error == "partial"
|
||||
assert record.n_directions == 3
|
||||
assert record.use_bayesian is True
|
||||
|
||||
def test_push_to_hub_success_and_empty_short_circuits(self, tmp_path):
|
||||
import obliteratus.telemetry as telemetry
|
||||
|
||||
output = tmp_path / "telemetry.jsonl"
|
||||
output.write_text('{"session_id":"one"}\n')
|
||||
api = MagicMock()
|
||||
with (
|
||||
patch.object(telemetry, "TELEMETRY_FILE", output),
|
||||
patch("obliteratus.telemetry.read_telemetry", return_value=[{"session_id": "one"}]),
|
||||
patch("obliteratus.telemetry._ensure_hub_repo", return_value=True),
|
||||
patch("huggingface_hub.HfApi", return_value=api),
|
||||
patch("obliteratus.telemetry._instance_slug", return_value="instance"),
|
||||
):
|
||||
assert push_to_hub("org/repo") is True
|
||||
api.upload_file.assert_called_once()
|
||||
assert api.upload_file.call_args.kwargs["path_in_repo"] == "data/instance.jsonl"
|
||||
|
||||
with patch("obliteratus.telemetry.read_telemetry", return_value=[]):
|
||||
assert push_to_hub("org/repo") is False
|
||||
get_leaderboard_data,
|
||||
read_telemetry,
|
||||
|
||||
Reference in New Issue
Block a user