open-interpretability 0.0.20__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.
- open_interpretability-0.0.20.dist-info/METADATA +157 -0
- open_interpretability-0.0.20.dist-info/RECORD +256 -0
- open_interpretability-0.0.20.dist-info/WHEEL +4 -0
- open_interpretability-0.0.20.dist-info/entry_points.txt +2 -0
- open_interpretability-0.0.20.dist-info/licenses/LICENSE +21 -0
- openinterp/__init__.py +6 -0
- openinterp/agents/__init__.py +25 -0
- openinterp/agents/base.py +229 -0
- openinterp/agents/judge.py +149 -0
- openinterp/agents/proposer.py +471 -0
- openinterp/agents/proposer_profiles.py +79 -0
- openinterp/agents/review_measurements.py +173 -0
- openinterp/agents/reviewer.py +498 -0
- openinterp/agreement.py +63 -0
- openinterp/audit_options.py +79 -0
- openinterp/baseline.py +254 -0
- openinterp/benchmark.py +450 -0
- openinterp/blends.py +239 -0
- openinterp/builder/__init__.py +6 -0
- openinterp/builder/agents.py +260 -0
- openinterp/builder/aligned.py +41 -0
- openinterp/builder/charts.py +411 -0
- openinterp/builder/circuit_pages.py +37 -0
- openinterp/builder/circuits.py +264 -0
- openinterp/builder/compiler.py +495 -0
- openinterp/builder/corpora.py +98 -0
- openinterp/builder/coverage.py +46 -0
- openinterp/builder/dashboard.py +255 -0
- openinterp/builder/disk_memo.py +65 -0
- openinterp/builder/experiment.py +105 -0
- openinterp/builder/heat.py +121 -0
- openinterp/builder/hrefs.py +71 -0
- openinterp/builder/learned_baseline.py +204 -0
- openinterp/builder/model_facts.py +138 -0
- openinterp/builder/neuronpedia_export.py +107 -0
- openinterp/builder/parallel.py +100 -0
- openinterp/builder/parquet_export.py +458 -0
- openinterp/builder/pieces.py +127 -0
- openinterp/builder/pii.py +211 -0
- openinterp/builder/registry_stats.py +144 -0
- openinterp/builder/research.py +109 -0
- openinterp/builder/sections.py +28 -0
- openinterp/builder/significance.py +103 -0
- openinterp/builder/sister_graph.py +180 -0
- openinterp/builder/standing.py +185 -0
- openinterp/builder/statuses.py +34 -0
- openinterp/builder/stopped.py +91 -0
- openinterp/builder/stops.py +30 -0
- openinterp/builder/superpositions.py +102 -0
- openinterp/builder/templates/_agent_role_cells.html +9 -0
- openinterp/builder/templates/_causal_chart.html +67 -0
- openinterp/builder/templates/_circuit.html +8 -0
- openinterp/builder/templates/_dashboard.html +133 -0
- openinterp/builder/templates/_experiment.html +500 -0
- openinterp/builder/templates/_macros.html +82 -0
- openinterp/builder/templates/_patchscope.html +33 -0
- openinterp/builder/templates/_prior_art.html +24 -0
- openinterp/builder/templates/_specificity.html +21 -0
- openinterp/builder/templates/_steering.html +34 -0
- openinterp/builder/templates/_superposition.html +62 -0
- openinterp/builder/templates/_vocab_chart.html +88 -0
- openinterp/builder/templates/_wiring.html +44 -0
- openinterp/builder/templates/about.html +95 -0
- openinterp/builder/templates/agent.html +69 -0
- openinterp/builder/templates/agents.html +80 -0
- openinterp/builder/templates/base.html +103 -0
- openinterp/builder/templates/circuit.html +64 -0
- openinterp/builder/templates/circuits.html +46 -0
- openinterp/builder/templates/experiment.html +21 -0
- openinterp/builder/templates/hub.html +16 -0
- openinterp/builder/templates/index.html +387 -0
- openinterp/builder/templates/layer.html +69 -0
- openinterp/builder/templates/learned-baseline.html +44 -0
- openinterp/builder/templates/model.html +151 -0
- openinterp/builder/templates/models.html +47 -0
- openinterp/builder/templates/neuron.html +234 -0
- openinterp/builder/templates/paper.html +39 -0
- openinterp/builder/templates/redirect.html +1 -0
- openinterp/builder/templates/research.html +31 -0
- openinterp/builder/templates/user.html +54 -0
- openinterp/builder/templates/users.html +39 -0
- openinterp/builder/templates/wiki/experiment.md +271 -0
- openinterp/builder/templates/wiki/index.md +32 -0
- openinterp/builder/templates/wiki/neuron.md +30 -0
- openinterp/builder/templates/wiki/stop.md +36 -0
- openinterp/builder/tree.py +470 -0
- openinterp/builder/users.py +142 -0
- openinterp/builder/wiki.py +427 -0
- openinterp/checks.py +429 -0
- openinterp/cli.py +494 -0
- openinterp/cli_baseline.py +332 -0
- openinterp/cli_benchmark.py +300 -0
- openinterp/cli_build.py +128 -0
- openinterp/cli_check.py +51 -0
- openinterp/cli_concepts.py +76 -0
- openinterp/cli_corpus.py +252 -0
- openinterp/cli_costs.py +257 -0
- openinterp/cli_coverage.py +76 -0
- openinterp/cli_crossref.py +86 -0
- openinterp/cli_errata.py +142 -0
- openinterp/cli_evidence.py +495 -0
- openinterp/cli_evidence_store.py +93 -0
- openinterp/cli_explain.py +101 -0
- openinterp/cli_loop.py +125 -0
- openinterp/cli_memory.py +28 -0
- openinterp/cli_neuronpedia.py +175 -0
- openinterp/cli_pause.py +45 -0
- openinterp/cli_pole_effects.py +133 -0
- openinterp/cli_probing.py +119 -0
- openinterp/cli_registry.py +348 -0
- openinterp/cli_reproduce.py +446 -0
- openinterp/cli_review.py +302 -0
- openinterp/cli_scan.py +223 -0
- openinterp/cli_score.py +446 -0
- openinterp/cli_sisters.py +86 -0
- openinterp/cli_submission.py +217 -0
- openinterp/cli_superpositions.py +118 -0
- openinterp/cli_wiring.py +54 -0
- openinterp/codeid.py +73 -0
- openinterp/concepts.py +400 -0
- openinterp/core/__init__.py +59 -0
- openinterp/core/agent_names.py +76 -0
- openinterp/core/attribution.py +101 -0
- openinterp/core/cache_dirs.py +15 -0
- openinterp/core/causal.py +82 -0
- openinterp/core/confounds.py +103 -0
- openinterp/core/contributors.py +26 -0
- openinterp/core/corpora.py +126 -0
- openinterp/core/costs.py +430 -0
- openinterp/core/draws.py +109 -0
- openinterp/core/errata.py +244 -0
- openinterp/core/evidence.py +160 -0
- openinterp/core/examples.py +93 -0
- openinterp/core/exceptions.py +29 -0
- openinterp/core/explanation.py +130 -0
- openinterp/core/fence.py +42 -0
- openinterp/core/heavy_lock.py +114 -0
- openinterp/core/identifiers.py +198 -0
- openinterp/core/isolation.py +149 -0
- openinterp/core/ledger.py +130 -0
- openinterp/core/licenses.py +28 -0
- openinterp/core/manifold.py +117 -0
- openinterp/core/partners.py +32 -0
- openinterp/core/patchscope.py +59 -0
- openinterp/core/paths.py +40 -0
- openinterp/core/pause.py +96 -0
- openinterp/core/pricing.py +178 -0
- openinterp/core/prior_art.py +44 -0
- openinterp/core/probing.py +146 -0
- openinterp/core/profiles.py +221 -0
- openinterp/core/project.py +28 -0
- openinterp/core/run_environment.py +38 -0
- openinterp/core/schemas.py +484 -0
- openinterp/core/specificity.py +49 -0
- openinterp/core/stats.py +172 -0
- openinterp/core/steering.py +49 -0
- openinterp/core/strict.py +19 -0
- openinterp/core/takedowns.py +136 -0
- openinterp/core/transcripts.py +130 -0
- openinterp/core/wiring.py +80 -0
- openinterp/corpus.py +476 -0
- openinterp/corpus_cache.py +145 -0
- openinterp/corpus_layouts.py +108 -0
- openinterp/corpus_presets.py +168 -0
- openinterp/corpus_rebuild.py +93 -0
- openinterp/coverage.py +155 -0
- openinterp/crossref.py +442 -0
- openinterp/data/__init__.py +9 -0
- openinterp/data/offensive_words.txt +58 -0
- openinterp/data/public_suffix_list.dat +15771 -0
- openinterp/data/sample_corpus.txt +40 -0
- openinterp/draw_steps.py +359 -0
- openinterp/errata_checks.py +129 -0
- openinterp/evidence.py +462 -0
- openinterp/evidence_branch.py +119 -0
- openinterp/evidence_lenses.py +75 -0
- openinterp/experiment_loop.py +357 -0
- openinterp/explain.py +252 -0
- openinterp/half_precision.py +79 -0
- openinterp/harness/__init__.py +37 -0
- openinterp/harness/ane/__init__.py +9 -0
- openinterp/harness/ane/backend.py +324 -0
- openinterp/harness/ane/cache.py +276 -0
- openinterp/harness/ane/chunks.py +163 -0
- openinterp/harness/ane/convert.py +116 -0
- openinterp/harness/ane/forward.py +258 -0
- openinterp/harness/ane/qualified.py +40 -0
- openinterp/harness/annotations.py +329 -0
- openinterp/harness/attribution.py +96 -0
- openinterp/harness/causal.py +264 -0
- openinterp/harness/confounds.py +184 -0
- openinterp/harness/covariance.py +301 -0
- openinterp/harness/crossref.py +195 -0
- openinterp/harness/cuda_devices.py +88 -0
- openinterp/harness/dead.py +124 -0
- openinterp/harness/devices.py +401 -0
- openinterp/harness/dictionaries.py +496 -0
- openinterp/harness/dreaming.py +116 -0
- openinterp/harness/environment.py +64 -0
- openinterp/harness/explanation_scores.py +371 -0
- openinterp/harness/features.py +276 -0
- openinterp/harness/first_position.py +137 -0
- openinterp/harness/heads.py +226 -0
- openinterp/harness/hooks.py +499 -0
- openinterp/harness/input_attribution.py +367 -0
- openinterp/harness/jlens.py +183 -0
- openinterp/harness/layer_cache.py +301 -0
- openinterp/harness/layer_major.py +274 -0
- openinterp/harness/lookahead.py +93 -0
- openinterp/harness/manifold.py +200 -0
- openinterp/harness/memory.py +471 -0
- openinterp/harness/moe.py +134 -0
- openinterp/harness/patcher.py +322 -0
- openinterp/harness/patchscope.py +177 -0
- openinterp/harness/probing.py +383 -0
- openinterp/harness/scan.py +462 -0
- openinterp/harness/scan_backend.py +201 -0
- openinterp/harness/sister_passes.py +463 -0
- openinterp/harness/sisters.py +363 -0
- openinterp/harness/sites.py +101 -0
- openinterp/harness/specificity.py +107 -0
- openinterp/harness/steering.py +159 -0
- openinterp/harness/superpositions.py +116 -0
- openinterp/harness/units.py +203 -0
- openinterp/harness/wiring.py +432 -0
- openinterp/harness/wiring_activations.py +285 -0
- openinterp/langid.py +73 -0
- openinterp/names.py +129 -0
- openinterp/neuronpedia_baseline.py +88 -0
- openinterp/neuronpedia_review.py +148 -0
- openinterp/planner.py +466 -0
- openinterp/planner_effects.py +89 -0
- openinterp/planner_heads.py +92 -0
- openinterp/planner_models.py +174 -0
- openinterp/planner_sisters.py +122 -0
- openinterp/planner_states.py +57 -0
- openinterp/planner_superpositions.py +114 -0
- openinterp/pole_effects.py +102 -0
- openinterp/prior_art.py +143 -0
- openinterp/reproduce.py +450 -0
- openinterp/revision.py +45 -0
- openinterp/runner_cache.py +35 -0
- openinterp/score_controls.py +169 -0
- openinterp/sensitive.py +243 -0
- openinterp/show.py +236 -0
- openinterp/signing.py +209 -0
- openinterp/sisters.py +476 -0
- openinterp/superposition_finders.py +225 -0
- openinterp/sync/__init__.py +5 -0
- openinterp/sync/r2.py +280 -0
- openinterp/syntax.py +399 -0
- openinterp/takedown_checks.py +76 -0
- openinterp/toxicity.py +158 -0
- openinterp/verdict_regression.py +79 -0
- openinterp/wiring_backfill.py +170 -0
- openinterp/wordnet.py +362 -0
|
@@ -0,0 +1,484 @@
|
|
|
1
|
+
"""Strict Pydantic v2 schemas for audits, hypotheses and evidence.
|
|
2
|
+
|
|
3
|
+
Every model is strict (no implicit type coercion) and forbids unknown fields. So a
|
|
4
|
+
record that validates here is safe to compile into the public registry. Records on disk
|
|
5
|
+
are JSON. Load them with ``Model.model_validate_json``: strict mode then accepts
|
|
6
|
+
ISO-8601 strings for datetimes.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
from typing import Annotated, Literal
|
|
12
|
+
|
|
13
|
+
from pydantic import AwareDatetime, Field, model_validator
|
|
14
|
+
|
|
15
|
+
from openinterp.core import evidence
|
|
16
|
+
from openinterp.core.agent_names import same_agent
|
|
17
|
+
from openinterp.core.attribution import InputAttribution
|
|
18
|
+
from openinterp.core.causal import ( # noqa: F401 (re-exported)
|
|
19
|
+
CausalMetrics,
|
|
20
|
+
CausalPairEffect,
|
|
21
|
+
CausalTarget,
|
|
22
|
+
PatchEffect,
|
|
23
|
+
)
|
|
24
|
+
from openinterp.core.confounds import ( # noqa: F401 (re-exported)
|
|
25
|
+
ConfoundProfile,
|
|
26
|
+
LabelConfound,
|
|
27
|
+
LabelStat,
|
|
28
|
+
TokenStat,
|
|
29
|
+
)
|
|
30
|
+
from openinterp.core.contributors import CONTRIBUTOR_PATTERN
|
|
31
|
+
from openinterp.core.corpora import CorpusSample, CorpusSource # noqa: F401 (re-exported)
|
|
32
|
+
from openinterp.core.costs import ExperimentCosts
|
|
33
|
+
from openinterp.core.examples import ( # noqa: F401 (re-exported)
|
|
34
|
+
ActivatingExample,
|
|
35
|
+
Histogram,
|
|
36
|
+
OppositePole,
|
|
37
|
+
check_opposite_pole,
|
|
38
|
+
)
|
|
39
|
+
from openinterp.core.explanation import ExplanationScores
|
|
40
|
+
from openinterp.core.identifiers import ( # noqa: F401 (re-exported)
|
|
41
|
+
ComponentIdentifier,
|
|
42
|
+
FeatureIdentifier,
|
|
43
|
+
HeadIdentifier,
|
|
44
|
+
NeuronIdentifier,
|
|
45
|
+
Pole,
|
|
46
|
+
SuperpositionIdentifier,
|
|
47
|
+
)
|
|
48
|
+
from openinterp.core.manifold import ManifoldCheck
|
|
49
|
+
from openinterp.core.partners import CorrelatedNeuron # noqa: F401 (re-exported)
|
|
50
|
+
from openinterp.core.patchscope import PatchscopeReadout
|
|
51
|
+
from openinterp.core.prior_art import PriorArtField
|
|
52
|
+
from openinterp.core.profiles import ( # noqa: F401 (re-exported)
|
|
53
|
+
DictionaryProfile,
|
|
54
|
+
DictionarySite,
|
|
55
|
+
HeadProfile,
|
|
56
|
+
SuperpositionProfile,
|
|
57
|
+
require_unit_profile,
|
|
58
|
+
)
|
|
59
|
+
from openinterp.core.run_environment import RunEnvironment # noqa: F401 (re-exported)
|
|
60
|
+
from openinterp.core.specificity import WordnetSpecificity, WordStrength # noqa: F401
|
|
61
|
+
from openinterp.core.steering import SteeringSamples
|
|
62
|
+
from openinterp.core.strict import StrictModel, UnitFloat
|
|
63
|
+
from openinterp.core.wiring import Wiring, WiringEdge # noqa: F401 (re-exported)
|
|
64
|
+
|
|
65
|
+
VerdictStatus = Literal["VERIFIED", "REJECTED", "NEEDS_REVISION"]
|
|
66
|
+
ProbeKind = Literal["hard_negative", "positive_holdout", "counterfactual", "lookahead_consistency"]
|
|
67
|
+
SelectionMethod = Literal["random", "attribution", "manual"]
|
|
68
|
+
|
|
69
|
+
PROTOCOL_VERSION = "0.9"
|
|
70
|
+
# Probes a review asks for (PROTOCOL.md §2, step 5): 20 hard negatives, 10 holdouts.
|
|
71
|
+
MAX_PROBES = 30
|
|
72
|
+
# Share of max activation counted as "active" (PROTOCOL.md §1); ReviewPolicy's default.
|
|
73
|
+
ACTIVATION_THRESHOLD = 0.25
|
|
74
|
+
# An experiment is named by its verdict timestamp: ``%Y%m%dT%H%M%SZ``.
|
|
75
|
+
EXPERIMENT_ID_PATTERN = r"^\d{8}T\d{6}Z$"
|
|
76
|
+
# An experiment on a neuron pole anywhere in the registry: its pole's slug, then its id.
|
|
77
|
+
SISTER_OF_PATTERN = r"^[A-Za-z0-9._-]+/L\d+/N\d+-(positive|negative)/\d{8}T\d{6}Z$"
|
|
78
|
+
# ``provenance.corpus`` of a scan without ``--corpus``: the 552-token built-in sample.
|
|
79
|
+
BUILTIN_CORPUS = "builtin:sample_corpus"
|
|
80
|
+
# Sources of corpora that records scanned without recording one. They match by SHA-256.
|
|
81
|
+
# The first is the default scan corpus's sidecar (README, "A scan corpus"). The second is
|
|
82
|
+
# a table of the others.
|
|
83
|
+
COMMITTED_SOURCES = ("corpus.source.json", "corpus-sources.json")
|
|
84
|
+
Contributor = Annotated[
|
|
85
|
+
str,
|
|
86
|
+
Field(
|
|
87
|
+
pattern=CONTRIBUTOR_PATTERN,
|
|
88
|
+
description="The human behind this step: a public handle (e.g. a GitHub username), "
|
|
89
|
+
"never an email. None: not recorded.",
|
|
90
|
+
),
|
|
91
|
+
]
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
class CounterfactualPair(StrictModel):
|
|
95
|
+
"""A minimal pair predicted to change the neuron's pole activation.
|
|
96
|
+
|
|
97
|
+
A delta is ``strength(base) - strength(counterfactual)``, divided by the neuron's
|
|
98
|
+
maximum observed activation. So ``1.0`` means that the base prompt fully activates
|
|
99
|
+
the pole, and the counterfactual does not activate it at all.
|
|
100
|
+
"""
|
|
101
|
+
|
|
102
|
+
base_prompt: str = Field(min_length=1)
|
|
103
|
+
counterfactual_prompt: str = Field(min_length=1)
|
|
104
|
+
expected_delta: float
|
|
105
|
+
observed_delta: float | None = None
|
|
106
|
+
|
|
107
|
+
@model_validator(mode="after")
|
|
108
|
+
def _prompts_differ(self) -> CounterfactualPair:
|
|
109
|
+
if self.base_prompt == self.counterfactual_prompt:
|
|
110
|
+
raise ValueError("base_prompt and counterfactual_prompt must differ")
|
|
111
|
+
return self
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
class LookaheadProfile(StrictModel):
|
|
115
|
+
"""Whether the neuron prepares specific upcoming tokens rather than reacting to past ones."""
|
|
116
|
+
|
|
117
|
+
is_lookahead: bool
|
|
118
|
+
target_upcoming_tokens: list[str] = Field(default_factory=list)
|
|
119
|
+
kl_divergence: float = Field(ge=0.0)
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
class ExperimentProvenance(StrictModel):
|
|
123
|
+
"""Everything needed to re-run the scan that produced a submission."""
|
|
124
|
+
|
|
125
|
+
model_revision: str | None = Field(default=None, description="Hugging Face commit hash.")
|
|
126
|
+
dtype: str
|
|
127
|
+
corpus: str = Field(min_length=1, description="Human-readable corpus description.")
|
|
128
|
+
corpus_sha256: str | None = None
|
|
129
|
+
n_snippets: int = Field(ge=0)
|
|
130
|
+
tokens_scanned: int = Field(ge=0)
|
|
131
|
+
snippet_tokens: int | None = Field(
|
|
132
|
+
default=None,
|
|
133
|
+
gt=0,
|
|
134
|
+
description="Window length when documents were scanned as overlapping windows; "
|
|
135
|
+
"None when each was scanned whole.",
|
|
136
|
+
)
|
|
137
|
+
snippet_stride: int | None = Field(
|
|
138
|
+
default=None, gt=0, description="Tokens between the starts of consecutive windows."
|
|
139
|
+
)
|
|
140
|
+
selection: SelectionMethod = Field(
|
|
141
|
+
description="How the neuron was chosen; only 'random' gives unbiased registry statistics."
|
|
142
|
+
)
|
|
143
|
+
seed: int | None = None
|
|
144
|
+
pole_forced: bool | None = Field(
|
|
145
|
+
default=None,
|
|
146
|
+
description="For a random draw: True when --pole fixed the pole, False when the seed "
|
|
147
|
+
"drew it. None for a chosen unit, and for records before issue #3205.",
|
|
148
|
+
)
|
|
149
|
+
harness_version: str
|
|
150
|
+
environment: RunEnvironment | None = None
|
|
151
|
+
corpus_source: CorpusSource | None = None
|
|
152
|
+
label_detector: str | None = Field(
|
|
153
|
+
default=None,
|
|
154
|
+
description="What fixes the scan's match labels (openinterp.sensitive.detector, "
|
|
155
|
+
"openinterp.names.DETECTOR, openinterp.toxicity.DETECTOR). None: the scan "
|
|
156
|
+
"predates those labels.",
|
|
157
|
+
)
|
|
158
|
+
corpus_sample: CorpusSample | None = Field(
|
|
159
|
+
default=None,
|
|
160
|
+
description="The sample of the corpus scanned; None when the whole corpus was "
|
|
161
|
+
"(every record before protocol 0.7).",
|
|
162
|
+
)
|
|
163
|
+
costs: ExperimentCosts | None = Field(
|
|
164
|
+
default=None,
|
|
165
|
+
description="Wall time per stage and LLM usage of the run that produced this record "
|
|
166
|
+
"(reported, never gated).",
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
class VocabularyLens(StrictModel):
|
|
171
|
+
"""The pole's output direction read through the logit lens and the Jacobian lens.
|
|
172
|
+
|
|
173
|
+
The Jacobian lens (Gurnee et al., 2026) first moves the direction to the final layer
|
|
174
|
+
with the context-averaged Jacobian. So it stays reliable in middle layers, where the
|
|
175
|
+
logit lens (J = I) does not. ``overlap`` is the shared fraction of the two top-k
|
|
176
|
+
lists. ``direction_cosine`` is cos(w, J·w). A low value means that later layers
|
|
177
|
+
rotate what this neuron writes by a large angle. Kurtosis of the vocabulary
|
|
178
|
+
z-scores is high when the pole promotes a small, specific token set.
|
|
179
|
+
|
|
180
|
+
The ``*_z`` lists hold each listed token's z-score over the whole vocabulary,
|
|
181
|
+
in the same order. They are scale-invariant, so the two lenses share an axis.
|
|
182
|
+
Records before they were added have ``None``.
|
|
183
|
+
"""
|
|
184
|
+
|
|
185
|
+
logit_promoted: list[str]
|
|
186
|
+
logit_suppressed: list[str]
|
|
187
|
+
jacobian_promoted: list[str]
|
|
188
|
+
jacobian_suppressed: list[str]
|
|
189
|
+
logit_promoted_z: list[float] | None = None
|
|
190
|
+
logit_suppressed_z: list[float] | None = None
|
|
191
|
+
jacobian_promoted_z: list[float] | None = None
|
|
192
|
+
jacobian_suppressed_z: list[float] | None = None
|
|
193
|
+
overlap: UnitFloat
|
|
194
|
+
direction_cosine: float = Field(ge=-1.0, le=1.0)
|
|
195
|
+
logit_kurtosis: float = Field(ge=0.0)
|
|
196
|
+
jacobian_kurtosis: float = Field(ge=0.0)
|
|
197
|
+
prompts: int = Field(ge=0)
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
class JointAblation(StrictModel):
|
|
201
|
+
"""Next-token KL when ablating the pole, its correlated partners, or both.
|
|
202
|
+
|
|
203
|
+
``redundancy_ratio`` = (kl_joint − kl_partners) / kl_single: how much more the pole
|
|
204
|
+
matters once its correlated partners are gone (> 1 suggests redundancy).
|
|
205
|
+
"""
|
|
206
|
+
|
|
207
|
+
partners: list[int]
|
|
208
|
+
kl_single: float = Field(ge=0.0)
|
|
209
|
+
kl_partners: float = Field(ge=0.0)
|
|
210
|
+
kl_joint: float = Field(ge=0.0)
|
|
211
|
+
redundancy_ratio: float | None = None
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
class ScanEvidence(StrictModel):
|
|
215
|
+
"""What ``open-interpretability audit --evidence-out`` measured, for a proposer working offline.
|
|
216
|
+
|
|
217
|
+
It holds everything a submission needs except the hypothesis, so ``open-interpretability audit
|
|
218
|
+
--evidence <file> --hypothesis <file>`` can build the submission without rescanning.
|
|
219
|
+
"""
|
|
220
|
+
|
|
221
|
+
identifier: ComponentIdentifier
|
|
222
|
+
top_examples: list[ActivatingExample]
|
|
223
|
+
activation_histogram: Histogram | None = None
|
|
224
|
+
first_token_floor: bool | None = None # v0.8, see NeuronAuditSubmission
|
|
225
|
+
provenance: ExperimentProvenance | None = None
|
|
226
|
+
confounds: ConfoundProfile | None = None
|
|
227
|
+
correlated_neurons: list[CorrelatedNeuron] = Field(default_factory=list)
|
|
228
|
+
vocabulary_lens: VocabularyLens | None = None
|
|
229
|
+
input_attribution: InputAttribution | None = None
|
|
230
|
+
head_profile: HeadProfile | None = None
|
|
231
|
+
model_n_heads: int | None = Field(default=None, gt=0)
|
|
232
|
+
dictionary_profile: DictionaryProfile | None = None
|
|
233
|
+
superposition_profile: SuperpositionProfile | None = None
|
|
234
|
+
opposite_pole: OppositePole | None = None
|
|
235
|
+
manifold: ManifoldCheck | None = None # see NeuronAuditSubmission
|
|
236
|
+
|
|
237
|
+
@model_validator(mode="after")
|
|
238
|
+
def _head_profile(self) -> ScanEvidence:
|
|
239
|
+
check_opposite_pole(self)
|
|
240
|
+
return require_unit_profile(self)
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
class NeuronAuditSubmission(StrictModel):
|
|
244
|
+
"""A hypothesis about one unit, frozen once submitted.
|
|
245
|
+
|
|
246
|
+
The unit is a neuron pole, an attention head (since protocol 0.6) or a dictionary
|
|
247
|
+
feature. The name predates heads and is kept for compatibility.
|
|
248
|
+
"""
|
|
249
|
+
|
|
250
|
+
identifier: ComponentIdentifier
|
|
251
|
+
natural_language_explanation: str = Field(min_length=1)
|
|
252
|
+
activating_tokens: list[str]
|
|
253
|
+
counterfactuals: list[CounterfactualPair] = Field(min_length=1)
|
|
254
|
+
lookahead: LookaheadProfile
|
|
255
|
+
submitting_agent_model: str = Field(min_length=1)
|
|
256
|
+
contributor: Contributor | None = None
|
|
257
|
+
# Evidence gathered by the harness. Optional so hand-written submissions validate.
|
|
258
|
+
max_activation: float | None = Field(default=None, gt=0.0)
|
|
259
|
+
top_examples: list[ActivatingExample] = Field(default_factory=list)
|
|
260
|
+
activation_histogram: Histogram | None = None
|
|
261
|
+
first_token_floor: bool | None = Field(
|
|
262
|
+
default=None,
|
|
263
|
+
description="Protocol 0.8: the unit is active at the first position on at least half "
|
|
264
|
+
"the vocabulary, so it is not measured at a text's first token. None: not judged (a "
|
|
265
|
+
"record from before 0.8), and a review judges it.",
|
|
266
|
+
)
|
|
267
|
+
model_num_layers: int | None = Field(default=None, gt=0)
|
|
268
|
+
model_mlp_width: int | None = Field(default=None, gt=0, description="Neurons per MLP layer.")
|
|
269
|
+
provenance: ExperimentProvenance | None = None
|
|
270
|
+
correlated_neurons: list[CorrelatedNeuron] = Field(default_factory=list)
|
|
271
|
+
confounds: ConfoundProfile | None = None
|
|
272
|
+
vocabulary_lens: VocabularyLens | None = None
|
|
273
|
+
input_attribution: InputAttribution | None = None
|
|
274
|
+
head_profile: HeadProfile | None = None
|
|
275
|
+
model_n_heads: int | None = Field(default=None, gt=0, description="Heads per layer.")
|
|
276
|
+
dictionary_profile: DictionaryProfile | None = None
|
|
277
|
+
superposition_profile: SuperpositionProfile | None = None
|
|
278
|
+
patchscope: PatchscopeReadout | None = Field(
|
|
279
|
+
default=None,
|
|
280
|
+
description="The model's own description of the unit's output direction, read after "
|
|
281
|
+
"the proposer answered and shown to neither agent (reported, not gated).",
|
|
282
|
+
)
|
|
283
|
+
prior_art: PriorArtField = None
|
|
284
|
+
steering: SteeringSamples | None = Field(
|
|
285
|
+
default=None,
|
|
286
|
+
description="Neutral prompts continued with the unit clamped high, at baseline and "
|
|
287
|
+
"ablated, taken after the proposer answered and shown to neither agent (reported, "
|
|
288
|
+
"not gated).",
|
|
289
|
+
)
|
|
290
|
+
wordnet_specificity: WordnetSpecificity | None = Field(
|
|
291
|
+
default=None,
|
|
292
|
+
description="The unit on held-out words of a WordNet category against words of its "
|
|
293
|
+
"sibling categories, measured after the proposer answered and shown to neither "
|
|
294
|
+
"agent (reported, not gated).",
|
|
295
|
+
)
|
|
296
|
+
supersedes: str | None = Field(
|
|
297
|
+
default=None,
|
|
298
|
+
pattern=EXPERIMENT_ID_PATTERN,
|
|
299
|
+
description="Experiment id of the earlier experiment on the same unit that this "
|
|
300
|
+
"submission revises (reported, not gated).",
|
|
301
|
+
)
|
|
302
|
+
sister_of: str | None = Field(
|
|
303
|
+
default=None,
|
|
304
|
+
pattern=SISTER_OF_PATTERN,
|
|
305
|
+
description="A sister-neuron study: the experiment (``<pole slug>/<experiment id>``) "
|
|
306
|
+
"on a VERIFIED pole of another model whose explanation this submission tests on its "
|
|
307
|
+
"sister, unchanged (reported, not gated).",
|
|
308
|
+
)
|
|
309
|
+
opposite_pole: OppositePole | None = Field(
|
|
310
|
+
default=None,
|
|
311
|
+
description="A neuron's other pole, from the same scan: its top examples and "
|
|
312
|
+
"histogram, shown beside this pole on the neuron page (reported, not gated).",
|
|
313
|
+
)
|
|
314
|
+
wiring: Wiring | None = Field(
|
|
315
|
+
default=None,
|
|
316
|
+
description="A neuron pole's top connections by virtual weight, read after the "
|
|
317
|
+
"proposer answered and shown to neither agent (reported, not gated).",
|
|
318
|
+
)
|
|
319
|
+
manifold: ManifoldCheck | None = Field(
|
|
320
|
+
default=None,
|
|
321
|
+
description="A neuron pole and its partner poles on the activating set and on "
|
|
322
|
+
"ordered word families, and whether they trace a circle or a line (reported, not "
|
|
323
|
+
"gated).",
|
|
324
|
+
)
|
|
325
|
+
|
|
326
|
+
@model_validator(mode="after")
|
|
327
|
+
def _head_profile(self) -> NeuronAuditSubmission:
|
|
328
|
+
check_opposite_pole(self)
|
|
329
|
+
return require_unit_profile(self)
|
|
330
|
+
|
|
331
|
+
|
|
332
|
+
class AdversarialTestResult(StrictModel):
|
|
333
|
+
"""The outcome of one reviewer probe, measured on the model; the reviewer never sees it."""
|
|
334
|
+
|
|
335
|
+
kind: ProbeKind
|
|
336
|
+
prompt: str
|
|
337
|
+
passed: bool
|
|
338
|
+
expected_active: bool | None = None
|
|
339
|
+
activation: float | None = None
|
|
340
|
+
expected_delta: float | None = None
|
|
341
|
+
observed_delta: float | None = None
|
|
342
|
+
detail: str = ""
|
|
343
|
+
# v0.8: False for a one-token prompt to a unit with a first-token floor, which is not
|
|
344
|
+
# measured at the first token. It is recorded but counts in no metric.
|
|
345
|
+
measured: bool = True
|
|
346
|
+
|
|
347
|
+
|
|
348
|
+
class ReviewPolicy(StrictModel):
|
|
349
|
+
"""The decision rule a verdict was computed under; docs/PROTOCOL.md quotes its defaults."""
|
|
350
|
+
|
|
351
|
+
protocol_version: str = PROTOCOL_VERSION
|
|
352
|
+
activation_threshold: UnitFloat = ACTIVATION_THRESHOLD
|
|
353
|
+
delta_abs_tolerance: float = Field(default=0.10, ge=0.0)
|
|
354
|
+
delta_rel_tolerance: float = Field(default=0.50, ge=0.0)
|
|
355
|
+
min_hard_negatives: int = Field(default=6, ge=0)
|
|
356
|
+
min_holdouts: int = Field(default=3, ge=0)
|
|
357
|
+
min_specificity: UnitFloat = 0.80
|
|
358
|
+
min_sensitivity: UnitFloat = 0.60
|
|
359
|
+
min_counterfactual_reproduction: UnitFloat = 0.75
|
|
360
|
+
reject_below_balanced_accuracy: UnitFloat = 0.60
|
|
361
|
+
# v0.4: proposer/reviewer independence is recorded but no longer gated by default.
|
|
362
|
+
require_independent_review: bool = False
|
|
363
|
+
freeze_downstream: bool = True
|
|
364
|
+
# v0.5: causal effects are ranked against this many same-layer neurons whose strength
|
|
365
|
+
# changes as much between each minimal pair.
|
|
366
|
+
causal_null_neurons: int = Field(default=32, ge=1)
|
|
367
|
+
# v0.3: near-duplicate probes (word-set Jaccard >= this) count as one observation.
|
|
368
|
+
probe_cluster_threshold: UnitFloat = 0.5
|
|
369
|
+
# v0.3: the hypothesis must match or beat trivial token/position baselines.
|
|
370
|
+
require_confound_control: bool = True
|
|
371
|
+
joint_ablation_partners: int = Field(default=3, ge=0)
|
|
372
|
+
# Partners for joint ablation: lower-bound co-activation lift >= min_partner_lift on
|
|
373
|
+
# at least min_partner_coactive tokens, or |Spearman| >= min_partner_correlation.
|
|
374
|
+
min_partner_correlation: UnitFloat = 0.2
|
|
375
|
+
min_partner_lift: float = Field(default=2.0, ge=1.0)
|
|
376
|
+
min_partner_coactive: int = Field(default=5, ge=1)
|
|
377
|
+
# v0.8: a unit active on at least this share of its scan's tokens is default-on. It
|
|
378
|
+
# is not audited as a detector, because no hard negative of a few words can stay
|
|
379
|
+
# inactive. A unit active at the first position on this share of the vocabulary has
|
|
380
|
+
# a first-token floor.
|
|
381
|
+
max_active_share: UnitFloat = 0.5
|
|
382
|
+
|
|
383
|
+
|
|
384
|
+
def scan_active_share(submission: NeuronAuditSubmission, policy: ReviewPolicy) -> float | None:
|
|
385
|
+
"""Return the share of the scan's tokens that the unit is active on.
|
|
386
|
+
|
|
387
|
+
It comes from the submission's histogram. ``None`` without one.
|
|
388
|
+
"""
|
|
389
|
+
histogram, scale = submission.activation_histogram, submission.max_activation
|
|
390
|
+
if histogram is None or not scale:
|
|
391
|
+
return None
|
|
392
|
+
return histogram.share_at_least(policy.activation_threshold * scale)
|
|
393
|
+
|
|
394
|
+
|
|
395
|
+
class ReviewMetrics(StrictModel):
|
|
396
|
+
"""The rates and gates the decision rule computes from the probes (docs/PROTOCOL.md §4)."""
|
|
397
|
+
|
|
398
|
+
specificity: UnitFloat | None # hard negatives correctly inactive
|
|
399
|
+
sensitivity: UnitFloat | None # positive holdouts correctly active
|
|
400
|
+
balanced_accuracy: UnitFloat
|
|
401
|
+
counterfactual_reproduction: UnitFloat | None
|
|
402
|
+
lookahead_consistent: bool
|
|
403
|
+
independent_review: bool
|
|
404
|
+
gates: dict[str, bool]
|
|
405
|
+
effective_hard_negatives: int | None = None # clusters of near-duplicate probes
|
|
406
|
+
effective_holdouts: int | None = None
|
|
407
|
+
confound_baseline_accuracy: UnitFloat | None = None # best trivial baseline, same probes
|
|
408
|
+
confound_baseline: str | None = None
|
|
409
|
+
# v0.8: the share of the scan's tokens at or above the "active" line (from its
|
|
410
|
+
# histogram). Also, whether the measurement left out each text's first token.
|
|
411
|
+
active_share: UnitFloat | None = None
|
|
412
|
+
first_token_floor: bool | None = None
|
|
413
|
+
|
|
414
|
+
|
|
415
|
+
class VerificationVerdict(StrictModel):
|
|
416
|
+
"""The reviewer's verdict on one submission, with the evidence and the policy it used."""
|
|
417
|
+
|
|
418
|
+
status: VerdictStatus
|
|
419
|
+
confidence_score: UnitFloat
|
|
420
|
+
adversarial_test_results: list[AdversarialTestResult]
|
|
421
|
+
reviewer_agent_model: str = Field(min_length=1)
|
|
422
|
+
contributor: Contributor | None = None
|
|
423
|
+
timestamp: AwareDatetime
|
|
424
|
+
causal_metrics: CausalMetrics | None = None
|
|
425
|
+
metrics: ReviewMetrics | None = None
|
|
426
|
+
policy: ReviewPolicy | None = None
|
|
427
|
+
joint_ablation: JointAblation | None = None
|
|
428
|
+
environment: RunEnvironment | None = None
|
|
429
|
+
explanation_scores: ExplanationScores | None = None
|
|
430
|
+
wiring: Wiring | None = Field(
|
|
431
|
+
default=None,
|
|
432
|
+
description="A neuron pole's top connections by attribution on the review's prompts "
|
|
433
|
+
"where it fired (reported, not gated).",
|
|
434
|
+
)
|
|
435
|
+
notes: str = ""
|
|
436
|
+
|
|
437
|
+
|
|
438
|
+
class AuditRecord(StrictModel):
|
|
439
|
+
"""The unit stored in the registry: a submission plus its review verdict.
|
|
440
|
+
|
|
441
|
+
Loading resolves a slim record's evidence references (:mod:`.evidence`, issue #3254).
|
|
442
|
+
"""
|
|
443
|
+
|
|
444
|
+
submission: NeuronAuditSubmission
|
|
445
|
+
verdict: VerificationVerdict | None = None
|
|
446
|
+
|
|
447
|
+
@classmethod
|
|
448
|
+
def model_validate_json(cls, json_data, **kwargs) -> AuditRecord: # noqa: D102
|
|
449
|
+
return super().model_validate_json(evidence.resolved_text(json_data), **kwargs)
|
|
450
|
+
|
|
451
|
+
@classmethod
|
|
452
|
+
def model_validate(cls, obj, **kwargs) -> AuditRecord: # noqa: D102
|
|
453
|
+
text = evidence.resolved_dict_text(obj)
|
|
454
|
+
if text is None:
|
|
455
|
+
return super().model_validate(obj, **kwargs)
|
|
456
|
+
return super().model_validate_json(text, **kwargs)
|
|
457
|
+
|
|
458
|
+
@property
|
|
459
|
+
def is_verified(self) -> bool:
|
|
460
|
+
"""True when a verdict exists and its status is VERIFIED."""
|
|
461
|
+
return self.verdict is not None and self.verdict.status == "VERIFIED"
|
|
462
|
+
|
|
463
|
+
@property
|
|
464
|
+
def scanned_on_builtin_sample(self) -> bool:
|
|
465
|
+
"""Return whether the scan read only the 552-token built-in sample.
|
|
466
|
+
|
|
467
|
+
That sample is too small for a meaningful max activation, so follow-ups rescan
|
|
468
|
+
the unit instead of reusing the scan.
|
|
469
|
+
"""
|
|
470
|
+
provenance = self.submission.provenance
|
|
471
|
+
return provenance is not None and provenance.corpus == BUILTIN_CORPUS
|
|
472
|
+
|
|
473
|
+
@property
|
|
474
|
+
def self_reviewed(self) -> bool:
|
|
475
|
+
"""Return whether the proposer's model also reviewed the submission.
|
|
476
|
+
|
|
477
|
+
Self-review is recorded and badged, not gated (§2). It is also true when the
|
|
478
|
+
record calls the review independent but spells one model two ways (``[1m]``).
|
|
479
|
+
"""
|
|
480
|
+
if self.verdict is None:
|
|
481
|
+
return False
|
|
482
|
+
if self.verdict.metrics is not None and not self.verdict.metrics.independent_review:
|
|
483
|
+
return True
|
|
484
|
+
return same_agent(self.verdict.reviewer_agent_model, self.submission.submitting_agent_model)
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"""Schemas for the WordNet specificity check: a category's words against its siblings'.
|
|
2
|
+
|
|
3
|
+
A "cities" unit might really be a "places" unit. To tell the two apart, the harness
|
|
4
|
+
measures the unit on two lists of words. The first is words that WordNet puts under the
|
|
5
|
+
claimed category (its hyponyms). The second is words from the category's siblings
|
|
6
|
+
(other hyponyms of its hypernyms). Each word sits in a few fixed carrier sentences.
|
|
7
|
+
Peak words of the scan are left out of both lists, so every word is held out. Reported,
|
|
8
|
+
not gated (docs/PROTOCOL.md §4). Neither agent sees it.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
from typing import Literal
|
|
14
|
+
|
|
15
|
+
from pydantic import Field
|
|
16
|
+
|
|
17
|
+
from openinterp.core.strict import StrictModel, UnitFloat
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class WordStrength(StrictModel):
|
|
21
|
+
"""One word's strength: the mean over the carriers of its peak over the word's tokens."""
|
|
22
|
+
|
|
23
|
+
word: str = Field(min_length=1)
|
|
24
|
+
strength: float = Field(ge=0.0)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class WordnetSpecificity(StrictModel):
|
|
28
|
+
"""The unit on a WordNet category's words against the words of its sibling categories.
|
|
29
|
+
|
|
30
|
+
A word is active at or above ``ACTIVATION_THRESHOLD × scale``. ``auroc`` is the
|
|
31
|
+
chance that a category word is stronger than a sibling word, ties counting half. It
|
|
32
|
+
is 1 when the unit picks out the category. It is 0.5 when the unit can't tell the
|
|
33
|
+
category from its siblings.
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
synset: str = Field(description="The category, e.g. 'city.n.01'.")
|
|
37
|
+
lemma: str
|
|
38
|
+
source: Literal["proposer", "peak_words"] = Field(
|
|
39
|
+
description="'proposer': the draft's wordnet_synset. 'peak_words': the proposer named "
|
|
40
|
+
"none, so the largest category of the scan's peak words."
|
|
41
|
+
)
|
|
42
|
+
sibling_synsets: list[str]
|
|
43
|
+
carriers: list[str] = Field(min_length=1, description="Templates; {} is the word.")
|
|
44
|
+
scale: float = Field(gt=0.0, description="The unit's top activation in the scan.")
|
|
45
|
+
category: list[WordStrength]
|
|
46
|
+
siblings: list[WordStrength]
|
|
47
|
+
category_active: UnitFloat
|
|
48
|
+
siblings_active: UnitFloat
|
|
49
|
+
auroc: UnitFloat
|