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.
Files changed (256) hide show
  1. open_interpretability-0.0.20.dist-info/METADATA +157 -0
  2. open_interpretability-0.0.20.dist-info/RECORD +256 -0
  3. open_interpretability-0.0.20.dist-info/WHEEL +4 -0
  4. open_interpretability-0.0.20.dist-info/entry_points.txt +2 -0
  5. open_interpretability-0.0.20.dist-info/licenses/LICENSE +21 -0
  6. openinterp/__init__.py +6 -0
  7. openinterp/agents/__init__.py +25 -0
  8. openinterp/agents/base.py +229 -0
  9. openinterp/agents/judge.py +149 -0
  10. openinterp/agents/proposer.py +471 -0
  11. openinterp/agents/proposer_profiles.py +79 -0
  12. openinterp/agents/review_measurements.py +173 -0
  13. openinterp/agents/reviewer.py +498 -0
  14. openinterp/agreement.py +63 -0
  15. openinterp/audit_options.py +79 -0
  16. openinterp/baseline.py +254 -0
  17. openinterp/benchmark.py +450 -0
  18. openinterp/blends.py +239 -0
  19. openinterp/builder/__init__.py +6 -0
  20. openinterp/builder/agents.py +260 -0
  21. openinterp/builder/aligned.py +41 -0
  22. openinterp/builder/charts.py +411 -0
  23. openinterp/builder/circuit_pages.py +37 -0
  24. openinterp/builder/circuits.py +264 -0
  25. openinterp/builder/compiler.py +495 -0
  26. openinterp/builder/corpora.py +98 -0
  27. openinterp/builder/coverage.py +46 -0
  28. openinterp/builder/dashboard.py +255 -0
  29. openinterp/builder/disk_memo.py +65 -0
  30. openinterp/builder/experiment.py +105 -0
  31. openinterp/builder/heat.py +121 -0
  32. openinterp/builder/hrefs.py +71 -0
  33. openinterp/builder/learned_baseline.py +204 -0
  34. openinterp/builder/model_facts.py +138 -0
  35. openinterp/builder/neuronpedia_export.py +107 -0
  36. openinterp/builder/parallel.py +100 -0
  37. openinterp/builder/parquet_export.py +458 -0
  38. openinterp/builder/pieces.py +127 -0
  39. openinterp/builder/pii.py +211 -0
  40. openinterp/builder/registry_stats.py +144 -0
  41. openinterp/builder/research.py +109 -0
  42. openinterp/builder/sections.py +28 -0
  43. openinterp/builder/significance.py +103 -0
  44. openinterp/builder/sister_graph.py +180 -0
  45. openinterp/builder/standing.py +185 -0
  46. openinterp/builder/statuses.py +34 -0
  47. openinterp/builder/stopped.py +91 -0
  48. openinterp/builder/stops.py +30 -0
  49. openinterp/builder/superpositions.py +102 -0
  50. openinterp/builder/templates/_agent_role_cells.html +9 -0
  51. openinterp/builder/templates/_causal_chart.html +67 -0
  52. openinterp/builder/templates/_circuit.html +8 -0
  53. openinterp/builder/templates/_dashboard.html +133 -0
  54. openinterp/builder/templates/_experiment.html +500 -0
  55. openinterp/builder/templates/_macros.html +82 -0
  56. openinterp/builder/templates/_patchscope.html +33 -0
  57. openinterp/builder/templates/_prior_art.html +24 -0
  58. openinterp/builder/templates/_specificity.html +21 -0
  59. openinterp/builder/templates/_steering.html +34 -0
  60. openinterp/builder/templates/_superposition.html +62 -0
  61. openinterp/builder/templates/_vocab_chart.html +88 -0
  62. openinterp/builder/templates/_wiring.html +44 -0
  63. openinterp/builder/templates/about.html +95 -0
  64. openinterp/builder/templates/agent.html +69 -0
  65. openinterp/builder/templates/agents.html +80 -0
  66. openinterp/builder/templates/base.html +103 -0
  67. openinterp/builder/templates/circuit.html +64 -0
  68. openinterp/builder/templates/circuits.html +46 -0
  69. openinterp/builder/templates/experiment.html +21 -0
  70. openinterp/builder/templates/hub.html +16 -0
  71. openinterp/builder/templates/index.html +387 -0
  72. openinterp/builder/templates/layer.html +69 -0
  73. openinterp/builder/templates/learned-baseline.html +44 -0
  74. openinterp/builder/templates/model.html +151 -0
  75. openinterp/builder/templates/models.html +47 -0
  76. openinterp/builder/templates/neuron.html +234 -0
  77. openinterp/builder/templates/paper.html +39 -0
  78. openinterp/builder/templates/redirect.html +1 -0
  79. openinterp/builder/templates/research.html +31 -0
  80. openinterp/builder/templates/user.html +54 -0
  81. openinterp/builder/templates/users.html +39 -0
  82. openinterp/builder/templates/wiki/experiment.md +271 -0
  83. openinterp/builder/templates/wiki/index.md +32 -0
  84. openinterp/builder/templates/wiki/neuron.md +30 -0
  85. openinterp/builder/templates/wiki/stop.md +36 -0
  86. openinterp/builder/tree.py +470 -0
  87. openinterp/builder/users.py +142 -0
  88. openinterp/builder/wiki.py +427 -0
  89. openinterp/checks.py +429 -0
  90. openinterp/cli.py +494 -0
  91. openinterp/cli_baseline.py +332 -0
  92. openinterp/cli_benchmark.py +300 -0
  93. openinterp/cli_build.py +128 -0
  94. openinterp/cli_check.py +51 -0
  95. openinterp/cli_concepts.py +76 -0
  96. openinterp/cli_corpus.py +252 -0
  97. openinterp/cli_costs.py +257 -0
  98. openinterp/cli_coverage.py +76 -0
  99. openinterp/cli_crossref.py +86 -0
  100. openinterp/cli_errata.py +142 -0
  101. openinterp/cli_evidence.py +495 -0
  102. openinterp/cli_evidence_store.py +93 -0
  103. openinterp/cli_explain.py +101 -0
  104. openinterp/cli_loop.py +125 -0
  105. openinterp/cli_memory.py +28 -0
  106. openinterp/cli_neuronpedia.py +175 -0
  107. openinterp/cli_pause.py +45 -0
  108. openinterp/cli_pole_effects.py +133 -0
  109. openinterp/cli_probing.py +119 -0
  110. openinterp/cli_registry.py +348 -0
  111. openinterp/cli_reproduce.py +446 -0
  112. openinterp/cli_review.py +302 -0
  113. openinterp/cli_scan.py +223 -0
  114. openinterp/cli_score.py +446 -0
  115. openinterp/cli_sisters.py +86 -0
  116. openinterp/cli_submission.py +217 -0
  117. openinterp/cli_superpositions.py +118 -0
  118. openinterp/cli_wiring.py +54 -0
  119. openinterp/codeid.py +73 -0
  120. openinterp/concepts.py +400 -0
  121. openinterp/core/__init__.py +59 -0
  122. openinterp/core/agent_names.py +76 -0
  123. openinterp/core/attribution.py +101 -0
  124. openinterp/core/cache_dirs.py +15 -0
  125. openinterp/core/causal.py +82 -0
  126. openinterp/core/confounds.py +103 -0
  127. openinterp/core/contributors.py +26 -0
  128. openinterp/core/corpora.py +126 -0
  129. openinterp/core/costs.py +430 -0
  130. openinterp/core/draws.py +109 -0
  131. openinterp/core/errata.py +244 -0
  132. openinterp/core/evidence.py +160 -0
  133. openinterp/core/examples.py +93 -0
  134. openinterp/core/exceptions.py +29 -0
  135. openinterp/core/explanation.py +130 -0
  136. openinterp/core/fence.py +42 -0
  137. openinterp/core/heavy_lock.py +114 -0
  138. openinterp/core/identifiers.py +198 -0
  139. openinterp/core/isolation.py +149 -0
  140. openinterp/core/ledger.py +130 -0
  141. openinterp/core/licenses.py +28 -0
  142. openinterp/core/manifold.py +117 -0
  143. openinterp/core/partners.py +32 -0
  144. openinterp/core/patchscope.py +59 -0
  145. openinterp/core/paths.py +40 -0
  146. openinterp/core/pause.py +96 -0
  147. openinterp/core/pricing.py +178 -0
  148. openinterp/core/prior_art.py +44 -0
  149. openinterp/core/probing.py +146 -0
  150. openinterp/core/profiles.py +221 -0
  151. openinterp/core/project.py +28 -0
  152. openinterp/core/run_environment.py +38 -0
  153. openinterp/core/schemas.py +484 -0
  154. openinterp/core/specificity.py +49 -0
  155. openinterp/core/stats.py +172 -0
  156. openinterp/core/steering.py +49 -0
  157. openinterp/core/strict.py +19 -0
  158. openinterp/core/takedowns.py +136 -0
  159. openinterp/core/transcripts.py +130 -0
  160. openinterp/core/wiring.py +80 -0
  161. openinterp/corpus.py +476 -0
  162. openinterp/corpus_cache.py +145 -0
  163. openinterp/corpus_layouts.py +108 -0
  164. openinterp/corpus_presets.py +168 -0
  165. openinterp/corpus_rebuild.py +93 -0
  166. openinterp/coverage.py +155 -0
  167. openinterp/crossref.py +442 -0
  168. openinterp/data/__init__.py +9 -0
  169. openinterp/data/offensive_words.txt +58 -0
  170. openinterp/data/public_suffix_list.dat +15771 -0
  171. openinterp/data/sample_corpus.txt +40 -0
  172. openinterp/draw_steps.py +359 -0
  173. openinterp/errata_checks.py +129 -0
  174. openinterp/evidence.py +462 -0
  175. openinterp/evidence_branch.py +119 -0
  176. openinterp/evidence_lenses.py +75 -0
  177. openinterp/experiment_loop.py +357 -0
  178. openinterp/explain.py +252 -0
  179. openinterp/half_precision.py +79 -0
  180. openinterp/harness/__init__.py +37 -0
  181. openinterp/harness/ane/__init__.py +9 -0
  182. openinterp/harness/ane/backend.py +324 -0
  183. openinterp/harness/ane/cache.py +276 -0
  184. openinterp/harness/ane/chunks.py +163 -0
  185. openinterp/harness/ane/convert.py +116 -0
  186. openinterp/harness/ane/forward.py +258 -0
  187. openinterp/harness/ane/qualified.py +40 -0
  188. openinterp/harness/annotations.py +329 -0
  189. openinterp/harness/attribution.py +96 -0
  190. openinterp/harness/causal.py +264 -0
  191. openinterp/harness/confounds.py +184 -0
  192. openinterp/harness/covariance.py +301 -0
  193. openinterp/harness/crossref.py +195 -0
  194. openinterp/harness/cuda_devices.py +88 -0
  195. openinterp/harness/dead.py +124 -0
  196. openinterp/harness/devices.py +401 -0
  197. openinterp/harness/dictionaries.py +496 -0
  198. openinterp/harness/dreaming.py +116 -0
  199. openinterp/harness/environment.py +64 -0
  200. openinterp/harness/explanation_scores.py +371 -0
  201. openinterp/harness/features.py +276 -0
  202. openinterp/harness/first_position.py +137 -0
  203. openinterp/harness/heads.py +226 -0
  204. openinterp/harness/hooks.py +499 -0
  205. openinterp/harness/input_attribution.py +367 -0
  206. openinterp/harness/jlens.py +183 -0
  207. openinterp/harness/layer_cache.py +301 -0
  208. openinterp/harness/layer_major.py +274 -0
  209. openinterp/harness/lookahead.py +93 -0
  210. openinterp/harness/manifold.py +200 -0
  211. openinterp/harness/memory.py +471 -0
  212. openinterp/harness/moe.py +134 -0
  213. openinterp/harness/patcher.py +322 -0
  214. openinterp/harness/patchscope.py +177 -0
  215. openinterp/harness/probing.py +383 -0
  216. openinterp/harness/scan.py +462 -0
  217. openinterp/harness/scan_backend.py +201 -0
  218. openinterp/harness/sister_passes.py +463 -0
  219. openinterp/harness/sisters.py +363 -0
  220. openinterp/harness/sites.py +101 -0
  221. openinterp/harness/specificity.py +107 -0
  222. openinterp/harness/steering.py +159 -0
  223. openinterp/harness/superpositions.py +116 -0
  224. openinterp/harness/units.py +203 -0
  225. openinterp/harness/wiring.py +432 -0
  226. openinterp/harness/wiring_activations.py +285 -0
  227. openinterp/langid.py +73 -0
  228. openinterp/names.py +129 -0
  229. openinterp/neuronpedia_baseline.py +88 -0
  230. openinterp/neuronpedia_review.py +148 -0
  231. openinterp/planner.py +466 -0
  232. openinterp/planner_effects.py +89 -0
  233. openinterp/planner_heads.py +92 -0
  234. openinterp/planner_models.py +174 -0
  235. openinterp/planner_sisters.py +122 -0
  236. openinterp/planner_states.py +57 -0
  237. openinterp/planner_superpositions.py +114 -0
  238. openinterp/pole_effects.py +102 -0
  239. openinterp/prior_art.py +143 -0
  240. openinterp/reproduce.py +450 -0
  241. openinterp/revision.py +45 -0
  242. openinterp/runner_cache.py +35 -0
  243. openinterp/score_controls.py +169 -0
  244. openinterp/sensitive.py +243 -0
  245. openinterp/show.py +236 -0
  246. openinterp/signing.py +209 -0
  247. openinterp/sisters.py +476 -0
  248. openinterp/superposition_finders.py +225 -0
  249. openinterp/sync/__init__.py +5 -0
  250. openinterp/sync/r2.py +280 -0
  251. openinterp/syntax.py +399 -0
  252. openinterp/takedown_checks.py +76 -0
  253. openinterp/toxicity.py +158 -0
  254. openinterp/verdict_regression.py +79 -0
  255. openinterp/wiring_backfill.py +170 -0
  256. openinterp/wordnet.py +362 -0
@@ -0,0 +1,217 @@
1
+ """The steps of ``open-interpretability audit`` after the proposer answers.
2
+
3
+ The steps are counterfactuals, the Patchscope, steering, WordNet specificity and wiring
4
+ readouts, and writing the submission.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ from pathlib import Path
10
+
11
+ import click
12
+
13
+ from openinterp.cli_evidence import with_costs
14
+ from openinterp.core.paths import DEFAULT_SUBMISSIONS_DIR
15
+
16
+
17
+ def read_patchscope(runner, submission):
18
+ """Return the model's own reading of the unit's output direction.
19
+
20
+ It runs after the proposer answers, so it can't steer the hypothesis. ``None`` for a
21
+ head, which writes a subspace.
22
+ """
23
+ from openinterp.core import costs
24
+ from openinterp.harness.patchscope import patchscope, write_direction
25
+ from openinterp.harness.units import submission_unit
26
+
27
+ direction = write_direction(runner, submission_unit(submission))
28
+ if direction is None or runner.tokenizer is None:
29
+ return None
30
+ click.echo("Reading the output direction back through a Patchscope…", err=True)
31
+ with costs.stage("patchscope"):
32
+ return patchscope(runner, submission.identifier.layer, direction)
33
+
34
+
35
+ def read_prior_art(runner, submission):
36
+ """Return published explanations of the unit, or of latents aligned with it.
37
+
38
+ It runs after the proposer answers, so it can't steer the hypothesis. The reviewer
39
+ sees it (:mod:`openinterp.prior_art`).
40
+ """
41
+ from openinterp.prior_art import enabled, find_prior_art
42
+
43
+ if enabled():
44
+ click.echo("Looking up prior art on Neuronpedia…", err=True)
45
+ return find_prior_art(submission, runner)
46
+
47
+
48
+ def read_steering(runner, submission):
49
+ """Return neutral prompts continued at baseline, with the unit ablated and clamped high.
50
+
51
+ It runs after the proposer answers. ``None`` for a head, which has no "high" to clamp
52
+ to.
53
+ """
54
+ from openinterp.core import costs
55
+ from openinterp.harness.steering import steering_samples
56
+ from openinterp.harness.units import HeadUnit, submission_unit
57
+
58
+ unit = submission_unit(submission)
59
+ if isinstance(unit, HeadUnit) or not submission.max_activation or runner.tokenizer is None:
60
+ return None
61
+ click.echo("Continuing neutral prompts with the unit clamped and ablated…", err=True)
62
+ with costs.stage("steering"):
63
+ return steering_samples(runner, unit, submission.max_activation)
64
+
65
+
66
+ def read_specificity(runner, submission, draft):
67
+ """Return the unit on a WordNet category's held-out words against its siblings' words.
68
+
69
+ The category is the draft's ``wordnet_synset``, or else the largest category of the
70
+ peak words. It runs after the proposer answers. ``None`` without WordNet, without a
71
+ category, or when either side has too few words.
72
+ """
73
+ from openinterp import wordnet
74
+ from openinterp.core import costs
75
+ from openinterp.harness.specificity import specificity
76
+ from openinterp.harness.units import submission_unit
77
+
78
+ lex = wordnet.lexicon()
79
+ if lex is None or not submission.max_activation or runner.tokenizer is None:
80
+ return None
81
+ peaks = wordnet.peak_words(submission.top_examples)
82
+ synset, source = draft.wordnet_synset, "proposer"
83
+ if synset is not None and not lex.has(synset):
84
+ click.echo(f"Skipping the WordNet check: no noun synset {synset!r}.", err=True)
85
+ return None
86
+ if synset is None:
87
+ groups = wordnet.categorise(peaks, lex).groups
88
+ if not groups:
89
+ return None
90
+ synset, source = groups[0].synset, "peak_words"
91
+ click.echo(f"Measuring {synset} words against its sibling categories…", err=True)
92
+ with costs.stage("specificity"):
93
+ unit = submission_unit(submission)
94
+ return specificity(runner, unit, lex, synset, source, submission.max_activation, peaks)
95
+
96
+
97
+ def read_wiring(runner, submission):
98
+ """Return the unit's top connections by virtual weight.
99
+
100
+ It runs after the proposer answers. ``None`` on an unsupported architecture.
101
+ """
102
+ from openinterp.core import costs
103
+ from openinterp.core.exceptions import ArchitectureNotSupportedError
104
+ from openinterp.harness.units import submission_unit
105
+ from openinterp.harness.wiring import weight_wiring
106
+
107
+ click.echo("Reading the unit's wiring from the weights…", err=True)
108
+ with costs.stage("wiring"):
109
+ try:
110
+ return weight_wiring(runner, submission_unit(submission))
111
+ except ArchitectureNotSupportedError as exc:
112
+ click.echo(f"Skipping wiring: {exc}", err=True)
113
+ return None
114
+
115
+
116
+ def readouts(runner, submission, draft) -> dict:
117
+ """The readouts taken after the proposer answers, by submission field."""
118
+ return {
119
+ "patchscope": read_patchscope(runner, submission),
120
+ "steering": read_steering(runner, submission),
121
+ "wordnet_specificity": read_specificity(runner, submission, draft),
122
+ "wiring": read_wiring(runner, submission),
123
+ "prior_art": read_prior_art(runner, submission),
124
+ }
125
+
126
+
127
+ def write_submission(
128
+ agent,
129
+ draft,
130
+ runner,
131
+ evidence,
132
+ superseded,
133
+ out: Path | None,
134
+ contributor: str | None = None,
135
+ sister_file: Path | None = None,
136
+ ) -> None:
137
+ """Build the submission (counterfactuals, Patchscope, steering, specificity, wiring), write it.
138
+
139
+ In a sister study (``sister_file``), the explanation is the sister's, word for word.
140
+ """
141
+ from openinterp.builder.wiki import experiment_id
142
+ from openinterp.core import costs
143
+ from openinterp.sisters import sister_ref
144
+
145
+ ident = evidence.identifier
146
+ if superseded is not None and superseded.submission.identifier.slug != ident.slug:
147
+ raise click.UsageError(
148
+ f"--supersedes names an experiment on {superseded.submission.identifier}, "
149
+ f"not on {ident}."
150
+ )
151
+ sister, draft = sister_draft(sister_file, ident, draft)
152
+ submission = build_submission(agent, draft, runner, evidence)
153
+ after = readouts(runner, submission, draft)
154
+ # An evidence file carries the scan's costs; a one-step audit measured them here.
155
+ provenance = with_costs(submission.provenance, costs.measured())
156
+ revises = superseded and experiment_id(superseded)
157
+ submission = submission.model_copy(
158
+ update={
159
+ "contributor": contributor,
160
+ "opposite_pole": evidence.opposite_pole,
161
+ "provenance": provenance,
162
+ "supersedes": revises,
163
+ "sister_of": sister and sister_ref(sister),
164
+ **after,
165
+ }
166
+ )
167
+ out = out or DEFAULT_SUBMISSIONS_DIR / f"{ident.slug}.json"
168
+ out.parent.mkdir(parents=True, exist_ok=True)
169
+ out.write_text(submission.model_dump_json(indent=2))
170
+ click.echo(f"Hypothesis: {submission.natural_language_explanation}")
171
+ if revises:
172
+ click.echo(f"Supersedes: {revises} ({superseded.verdict.status})")
173
+ if sister:
174
+ click.echo(f"Sister study of: {submission.sister_of}")
175
+ click.echo(f"Wrote {out}")
176
+
177
+
178
+ def sister_draft(sister_file, ident, draft):
179
+ """Return ``(sister record, draft)``.
180
+
181
+ In a sister study, the draft has the sister's explanation, word for word. Otherwise
182
+ the result is ``(None, draft)``.
183
+ """
184
+ from openinterp.cli_evidence import load_sister
185
+
186
+ if sister_file is None:
187
+ return None, draft
188
+ sister = load_sister(sister_file, ident)
189
+ text = sister.submission.natural_language_explanation
190
+ return sister, draft.model_copy(update={"explanation": text})
191
+
192
+
193
+ def build_submission(agent, draft, runner, evidence):
194
+ """Build the submission from the proposer's draft, timed as the counterfactuals stage."""
195
+ from openinterp.core import costs
196
+
197
+ with costs.stage("counterfactuals"):
198
+ try:
199
+ return agent.build_submission(
200
+ evidence.identifier,
201
+ draft,
202
+ runner=runner,
203
+ examples=evidence.top_examples,
204
+ histogram=evidence.activation_histogram,
205
+ provenance=evidence.provenance,
206
+ confounds=evidence.confounds,
207
+ correlated=evidence.correlated_neurons,
208
+ vocabulary_lens=evidence.vocabulary_lens,
209
+ input_attribution=evidence.input_attribution,
210
+ head_profile=evidence.head_profile,
211
+ dictionary_profile=evidence.dictionary_profile,
212
+ first_token_floor=evidence.first_token_floor,
213
+ superposition_profile=evidence.superposition_profile,
214
+ manifold=evidence.manifold,
215
+ )
216
+ except ValueError as exc: # e.g. a one-token counterfactual prompt
217
+ raise click.ClickException(f"Counterfactual: {exc}") from exc
@@ -0,0 +1,118 @@
1
+ """``open-interpretability superpositions``: write and show superposition definitions.
2
+
3
+ The finders write one definition per line (issue #3686). A line is what ``audit
4
+ --superposition`` reads. :mod:`openinterp.superposition_finders` holds the finders.
5
+ ``show`` writes one definition to its own file, for a planner draw (issue #3687).
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import json
11
+ from pathlib import Path
12
+
13
+ import click
14
+
15
+ from openinterp.cli import _load_runner, cli
16
+
17
+ out_option = click.option(
18
+ "--out",
19
+ "out",
20
+ required=True,
21
+ type=click.Path(dir_okay=False, path_type=Path),
22
+ help="The JSONL file to write: one definition per line.",
23
+ )
24
+
25
+
26
+ def _write(out: Path, definitions: list[dict]) -> None:
27
+ out.parent.mkdir(parents=True, exist_ok=True)
28
+ out.write_text("".join(json.dumps(d) + "\n" for d in definitions), encoding="utf-8")
29
+ click.echo(f"wrote {len(definitions)} definitions → {out}")
30
+
31
+
32
+ @cli.group()
33
+ def superpositions() -> None:
34
+ """Find superpositions: sparse directions over one layer's MLP neurons."""
35
+
36
+
37
+ @superpositions.command("from-circuit")
38
+ @click.option(
39
+ "--rows",
40
+ "rows_file",
41
+ required=True,
42
+ type=click.Path(exists=True, dir_okay=False, path_type=Path),
43
+ help="A circuit's rows, such as docs/analyses/sva-circuit.jsonl.",
44
+ )
45
+ @click.option("--model", "model", required=True, help="The model that the circuit is on.")
46
+ @click.option("--top", default=100, show_default=True, help="The circuit's top neurons by rank.")
47
+ @click.option("--min-members", default=2, show_default=True, help="The fewest neurons per layer.")
48
+ @out_option
49
+ def from_circuit_command(rows_file, model, top, min_members, out):
50
+ """One definition per layer with at least --min-members of the circuit's --top neurons."""
51
+ from openinterp.superposition_finders import definition, from_circuit
52
+
53
+ lines = rows_file.read_text(encoding="utf-8").splitlines()
54
+ rows = [json.loads(line) for line in lines if line.strip()]
55
+ source = f"{rows_file.as_posix()}, top {top} neurons by attribution (#3686)"
56
+ found = from_circuit(rows, top=top, min_members=min_members, source=source)
57
+ _write(out, [definition(model, layer, profile) for layer, profile in found])
58
+
59
+
60
+ @superpositions.command("grammatical-number")
61
+ @click.option("--model", "model", required=True, help="The model to probe.")
62
+ @click.option("--layers", required=True, help="Comma-separated layers, e.g. 0,4,19.")
63
+ @click.option("--k", default=16, show_default=True, help="Neurons per probe.")
64
+ @click.option("--n", default=400, show_default=True, help="Agreement pairs: 2 examples each.")
65
+ @click.option("--seed", default=0, show_default=True)
66
+ @click.option("--device", default="auto", show_default=True, help="cpu, cuda, mps or auto.")
67
+ @out_option
68
+ def grammatical_number_command(model, layers, k, n, seed, device, out):
69
+ """One definition per layer: a sparse probe for the number of the agreeing noun."""
70
+ from openinterp.superposition_finders import definition, grammatical_number
71
+
72
+ try:
73
+ layer_list = [int(x) for x in layers.split(",") if x.strip()]
74
+ except ValueError as exc:
75
+ raise click.BadParameter(
76
+ "give layers as integers, e.g. 0,4,19", param_hint="--layers"
77
+ ) from exc
78
+ runner = _load_runner(model, device, "float32", None)
79
+ found = grammatical_number(runner, layer_list, k=k, n=n, seed=seed)
80
+ for layer, _, accuracy in found:
81
+ click.echo(f"layer {layer}: held-out accuracy {accuracy:.3f}")
82
+ _write(out, [definition(model, layer, profile) for layer, profile, _ in found])
83
+
84
+
85
+ @superpositions.command("show")
86
+ @click.argument("superposition_id")
87
+ @click.option("--model", "model", required=True, help="The model that the superposition is on.")
88
+ @click.option(
89
+ "--definitions",
90
+ "definitions_file",
91
+ type=click.Path(exists=True, dir_okay=False, path_type=Path),
92
+ help="The JSONL file to search. Default: data/superpositions/<model slug>.jsonl.",
93
+ )
94
+ @click.option(
95
+ "--out",
96
+ "out",
97
+ required=True,
98
+ type=click.Path(dir_okay=False, path_type=Path),
99
+ help="The JSON file to write: the one definition that `audit --superposition` reads.",
100
+ )
101
+ def show_command(superposition_id, model, definitions_file, out):
102
+ """Write the definition whose id is SUPERPOSITION_ID, as one JSON object."""
103
+ from openinterp.planner_superpositions import (
104
+ default_definitions,
105
+ definition_identifier,
106
+ load_definitions,
107
+ )
108
+
109
+ path = definitions_file or default_definitions(model)
110
+ if not path.is_file():
111
+ raise click.ClickException(f"no definitions file at {path}")
112
+ for definition in load_definitions(path):
113
+ if definition_identifier(definition).superposition_id == superposition_id:
114
+ out.parent.mkdir(parents=True, exist_ok=True)
115
+ out.write_text(json.dumps(definition) + "\n", encoding="utf-8")
116
+ click.echo(f"wrote {model} superposition {superposition_id} → {out}")
117
+ return
118
+ raise click.ClickException(f"no definition in {path} has the id {superposition_id}")
@@ -0,0 +1,54 @@
1
+ """``open-interpretability wiring-backfill``: wiring for archived records that predate it.
2
+
3
+ See :mod:`openinterp.wiring_backfill`.
4
+ """
5
+
6
+ from __future__ import annotations
7
+
8
+ from pathlib import Path
9
+
10
+ import click
11
+
12
+ from openinterp.cli import _load_runner, cli, wiki_dir_option
13
+ from openinterp.wiring_backfill import DEFAULT_PATH
14
+
15
+
16
+ @cli.command("wiring-backfill")
17
+ @wiki_dir_option()
18
+ @click.option(
19
+ "--out",
20
+ default=DEFAULT_PATH,
21
+ show_default=True,
22
+ type=click.Path(dir_okay=False, path_type=Path),
23
+ help="JSONL of wiring rows; rows already in it are skipped, so a run can resume.",
24
+ )
25
+ @click.option("--limit", type=int, help="Wire at most this many records.")
26
+ @click.option(
27
+ "--device",
28
+ default="auto",
29
+ show_default=True,
30
+ help="cpu, cuda, mps or auto. Each record's own model revision and dtype are used.",
31
+ )
32
+ def wiring_backfill(wiki_dir, out, limit, device):
33
+ """Record the wiring of every archived experiment that has none, in --out.
34
+
35
+ Records are write-once and often signed, so they are left alone.
36
+ `open-interpretability build` shows a row's wiring for its record, marked as backfilled.
37
+ """
38
+ from openinterp.builder.wiki import load_archive
39
+ from openinterp.harness.units import attn_implementation
40
+ from openinterp.wiring_backfill import backfill, sort_rows
41
+
42
+ def load(record):
43
+ sub = record.submission
44
+ prov = sub.provenance
45
+ dtype = (prov.dtype if prov else None) or "float32"
46
+ revision = prov.model_revision if prov else None
47
+ return _load_runner(
48
+ sub.identifier.model_name, device, dtype, revision, attn_implementation(sub.identifier)
49
+ )
50
+
51
+ wrote = backfill(load_archive(wiki_dir), load, out, limit=limit, echo=click.echo)
52
+ if out.exists():
53
+ sort_rows(out)
54
+ click.echo(f"Wired {wrote} experiments → {out}")
openinterp/codeid.py ADDED
@@ -0,0 +1,73 @@
1
+ """A document's programming language, for corpora whose dataset doesn't say it.
2
+
3
+ Used by ``open-interpretability corpus --detect-code``. The detector is Google's Magika, a file-type
4
+ model that reads content only (a corpus has no file names). We compared it with go-enry
5
+ (GitHub Linguist), a CodeBERTa language classifier and Pygments
6
+ (``docs/analyses/code-detection.md``). The tests were labelled GitHub files, prose, prose
7
+ followed by code, and the registry's pile-10k corpus. Magika was the most accurate, and
8
+ it alone never labelled prose as code.
9
+
10
+ A document gets a label only when Magika's confidence reaches ``MIN_CONFIDENCE`` and its
11
+ label is a language of the github-code preset (``corpus_presets.GITHUB_CODE_LANGUAGES``).
12
+ So prose, data files (JSON, CSV, XML) and file types outside that list stay unlabelled. The label
13
+ is that preset's name for the language, so exact and detected labels agree. Detection is
14
+ per document, never per window: at 200 characters Magika is right on 70% of files, and
15
+ on whole files on 91.5%.
16
+
17
+ Magika's model ships inside its package, so :func:`detector` records the package version
18
+ and the model name.
19
+ """
20
+
21
+ from __future__ import annotations
22
+
23
+ from collections.abc import Sequence
24
+ from functools import cache
25
+ from importlib.metadata import version
26
+
27
+ # Below this the document is left unlabelled. On 1,000 labelled github-code files it keeps
28
+ # 89% at 95.5% accuracy (0.5 keeps 98% at 92.7%). No line labelled any of 300 prose documents.
29
+ MIN_CONFIDENCE = 0.8
30
+
31
+ # Magika's labels of the github-code languages, and those languages' names there
32
+ # (tests/test_codeid.py checks that every name in GITHUB_CODE_LANGUAGES has one label).
33
+ MAGIKA_LABELS = {
34
+ "asm": "Assembly", "batch": "Batchfile", "c": "C", "cs": "C#", "cpp": "C++",
35
+ "cmake": "CMake", "css": "CSS", "dockerfile": "Dockerfile", "fortran": "FORTRAN",
36
+ "go": "GO", "html": "HTML", "haskell": "Haskell", "java": "Java",
37
+ "javascript": "JavaScript", "julia": "Julia", "lua": "Lua", "makefile": "Makefile",
38
+ "markdown": "Markdown", "php": "PHP", "perl": "Perl", "powershell": "PowerShell",
39
+ "python": "Python", "ruby": "Ruby", "rust": "Rust", "sql": "SQL", "scala": "Scala",
40
+ "shell": "Shell", "latex": "TeX", "typescript": "TypeScript", "vba": "Visual Basic",
41
+ } # fmt: skip
42
+
43
+
44
+ @cache
45
+ def _magika():
46
+ from magika import Magika
47
+
48
+ return Magika()
49
+
50
+
51
+ @cache
52
+ def detector() -> str:
53
+ """The detector and its settings, as a corpus's source sidecar records them."""
54
+ model = _magika().get_model_name()
55
+ return f"magika {version('magika')} (model {model}), confidence >= {MIN_CONFIDENCE}"
56
+
57
+
58
+ def predict(text: str) -> tuple[str | None, float]:
59
+ """Return the github-code language that Magika gives ``text``, and its confidence.
60
+
61
+ The language is ``None`` when Magika's label is not one of those languages.
62
+ """
63
+ prediction = _magika().identify_bytes(text.encode("utf-8", "surrogatepass")).prediction
64
+ return MAGIKA_LABELS.get(prediction.dl.label), float(prediction.score)
65
+
66
+
67
+ def detect(texts: Sequence[str]) -> list[str | None]:
68
+ """Each document's programming language, or ``None`` (see the module docstring)."""
69
+ out: list[str | None] = []
70
+ for text in texts:
71
+ language, p = predict(text)
72
+ out.append(language if p >= MIN_CONFIDENCE else None)
73
+ return out