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,6 @@
1
+ """The static site and the Parquet registry, compiled from the archived records."""
2
+
3
+ from openinterp.builder.compiler import BuildReport, compile_site, load_records
4
+ from openinterp.builder.parquet_export import export_registry, records_to_table
5
+
6
+ __all__ = ["BuildReport", "compile_site", "export_registry", "load_records", "records_to_table"]
@@ -0,0 +1,260 @@
1
+ """The agents page: per agent model and role, its experiments, VERIFIED rate and costs.
2
+
3
+ A model takes part in an experiment in one of three roles. It is the proposer
4
+ (``submitting_agent_model``), the reviewer (``reviewer_agent_model``), or the judge that
5
+ scored the explanation. Self-reviewed experiments are counted apart from the rate. In
6
+ them, the proposer's model also wrote the probes. A model that passes its own probes
7
+ inflates the rate.
8
+
9
+ Records spell one model in several ways. So pages are per canonical name
10
+ (:func:`openinterp.core.agent_names.canonical_agent`). Each old per-spelling page
11
+ redirects to its model's page (:func:`legacy_redirects`).
12
+
13
+ The same page credits the people who ran the agents (:func:`contributors_view`). A
14
+ person's handle is the ``contributor`` that a submission or verdict records. When the
15
+ submission records none, it is the author of the PR that merged it
16
+ (:mod:`openinterp.builder.users`). Each person also gets a page under ``users/``.
17
+ """
18
+
19
+ from __future__ import annotations
20
+
21
+ import re
22
+ from collections.abc import Sequence
23
+ from dataclasses import dataclass, field
24
+
25
+ from openinterp.builder.hrefs import user_href
26
+ from openinterp.builder.significance import STRONG_EVIDENCE
27
+ from openinterp.builder.tree import LABEL_PREFIX, Experiment
28
+ from openinterp.core.agent_names import NOTES, canonical_agent
29
+ from openinterp.core.costs import ROLE_ORDER, by_agent
30
+ from openinterp.core.exceptions import RegistryError
31
+
32
+ RECENT_PER_AGENT = 20 # rows in an agent page's "Most recent experiments"
33
+ RECENT_PER_USER = 10 # rows in each list of a user's page
34
+
35
+
36
+ def _slug(name: str) -> str:
37
+ return re.sub(r"[^a-z0-9._]+", "-", name.lower()).strip("-") or "unknown"
38
+
39
+
40
+ def agent_slug(model: str) -> str:
41
+ """The agent page's name: ``external/deepseek-flash[1m]`` → ``deepseek-flash``."""
42
+ return _slug(canonical_agent(model))
43
+
44
+
45
+ def agent_href(model: str) -> str:
46
+ """The agent page of ``model``, by its canonical name."""
47
+ return f"agents/{agent_slug(model)}.html"
48
+
49
+
50
+ def legacy_href(model: str) -> str:
51
+ """Return the page that a recorded spelling had before spellings were merged.
52
+
53
+ For example, ``external/deepseek-flash[1m]`` → ``agents/external-deepseek-flash-1m.html``.
54
+ """
55
+ return f"agents/{_slug(model)}.html"
56
+
57
+
58
+ @dataclass
59
+ class RoleStats:
60
+ """One agent's experiments in one role, and how many were VERIFIED."""
61
+
62
+ role: str
63
+ experiments: int = 0
64
+ verified: int = 0
65
+ self_reviewed: int = 0 # proposer and reviewer only
66
+ self_verified: int = 0
67
+ costs: dict | None = None # a :func:`openinterp.core.costs.by_agent` row
68
+
69
+ @property
70
+ def independent(self) -> int:
71
+ """Experiments that another model reviewed."""
72
+ return self.experiments - self.self_reviewed
73
+
74
+ @property
75
+ def independent_verified(self) -> int:
76
+ """VERIFIED experiments that another model reviewed."""
77
+ return self.verified - self.self_verified
78
+
79
+ @property
80
+ def rate(self) -> float | None:
81
+ """VERIFIED / experiments, over independent reviews only.
82
+
83
+ A judge scores explanations after the verdict, so its experiments' verdicts aren't
84
+ its rate.
85
+ """
86
+ if self.role == "judge" or not self.independent:
87
+ return None
88
+ return self.independent_verified / self.independent
89
+
90
+
91
+ @dataclass
92
+ class AgentView:
93
+ """One agent model's page: its roles, spellings and recent experiments."""
94
+
95
+ model: str # canonical name
96
+ href: str
97
+ roles: dict[str, RoleStats] = field(default_factory=dict)
98
+ spellings: set[str] = field(default_factory=set) # as the records name it
99
+ experiments: int = 0 # experiments it took any part in, in any role
100
+ recent: list[dict] = field(default_factory=list) # newest first, any role
101
+
102
+ @property
103
+ def role_rows(self) -> list[RoleStats]:
104
+ """The agent's roles in ``ROLE_ORDER``."""
105
+ return [self.roles[r] for r in ROLE_ORDER if r in self.roles]
106
+
107
+ @property
108
+ def note(self) -> str | None:
109
+ """The page's note on this model (for example, a remapped name), if any."""
110
+ return NOTES.get(self.model)
111
+
112
+ @property
113
+ def other_spellings(self) -> list[str]:
114
+ """Recorded names other than the canonical one, for the page to list."""
115
+ return sorted(s for s in self.spellings if s.removeprefix("external/") != self.model)
116
+
117
+
118
+ def _roles_of(e: Experiment) -> dict[str, str]:
119
+ """``role → model`` for the agents behind one (reviewed) experiment, as recorded."""
120
+ sub, verdict = e.record.submission, e.record.verdict
121
+ roles = {"proposer": sub.submitting_agent_model, "reviewer": verdict.reviewer_agent_model}
122
+ if verdict.explanation_scores and verdict.explanation_scores.judge_model:
123
+ roles["judge"] = verdict.explanation_scores.judge_model
124
+ return roles
125
+
126
+
127
+ def agents_view(experiments: Sequence[Experiment]) -> list[AgentView]:
128
+ """One view per agent model, most experiments first."""
129
+ agents: dict[str, AgentView] = {}
130
+ involved: dict[str, list[tuple[Experiment, list[str]]]] = {}
131
+
132
+ def agent(model: str) -> AgentView:
133
+ name = canonical_agent(model)
134
+ view = agents.setdefault(name, AgentView(name, agent_href(name)))
135
+ view.spellings.add(model)
136
+ return view
137
+
138
+ for e in experiments:
139
+ verified = e.record.is_verified
140
+ own = e.record.self_reviewed
141
+ by_model: dict[str, list[str]] = {}
142
+ for role, recorded in _roles_of(e).items():
143
+ model = agent(recorded).model
144
+ stats = agents[model].roles.setdefault(role, RoleStats(role))
145
+ stats.experiments += 1
146
+ stats.verified += verified
147
+ if role != "judge" and own:
148
+ stats.self_reviewed += 1
149
+ stats.self_verified += verified
150
+ by_model.setdefault(model, []).append(role)
151
+ for model, roles in by_model.items():
152
+ involved.setdefault(model, []).append((e, roles))
153
+
154
+ for row in _costs(experiments):
155
+ if row["agent"] in ROLE_ORDER:
156
+ agent(row["model"]).roles.setdefault(row["agent"], RoleStats(row["agent"])).costs = row
157
+
158
+ for model, view in agents.items():
159
+ rows = involved.get(model, [])
160
+ view.experiments = len(rows)
161
+ newest = sorted(rows, key=lambda r: (r[0].record.verdict.timestamp, r[0].id), reverse=True)
162
+ view.recent = [_recent_row(e, roles) for e, roles in newest[:RECENT_PER_AGENT]]
163
+
164
+ _check_slugs_unique(agents)
165
+ return sorted(agents.values(), key=lambda a: (-a.experiments, a.model))
166
+
167
+
168
+ def _costs(experiments: Sequence[Experiment]) -> list[dict]:
169
+ """:func:`openinterp.core.costs.by_agent` rows, one per role and canonical name."""
170
+ return by_agent(
171
+ [
172
+ (e.record.verdict.status, e.record.submission.provenance.costs)
173
+ for e in experiments
174
+ if e.record.submission.provenance
175
+ ],
176
+ canonical_agent,
177
+ )
178
+
179
+
180
+ def _recent_row(e: Experiment, roles: list[str]) -> dict:
181
+ ident = e.record.submission.identifier
182
+ return {
183
+ "e": e,
184
+ "roles": roles,
185
+ "href": e.href,
186
+ "model_name": ident.model_name,
187
+ "unit": f"L{ident.layer}/{LABEL_PREFIX[ident.kind]}{_unit_index(ident)}",
188
+ "pole": ident.pole,
189
+ }
190
+
191
+
192
+ def _unit_index(ident) -> int | str:
193
+ """What follows the kind's prefix in a unit's label: a superposition's id, else its index."""
194
+ return getattr(ident, "superposition_id", ident.index)
195
+
196
+
197
+ @dataclass
198
+ class ContributorStats:
199
+ """One person's part in the registry, as maker of experiments and as reviewer."""
200
+
201
+ handle: str
202
+ experiments: int = 0 # made: proposed, or merged when no contributor is recorded
203
+ verified: int = 0
204
+ conclusive_nulls: int = 0 # well-probed rejections: strong evidence against
205
+ reviews: int = 0
206
+ made: list[dict] = field(default_factory=list) # their newest experiments, at most 10
207
+ reviewed: list[dict] = field(default_factory=list) # the newest they reviewed, at most 10
208
+
209
+ @property
210
+ def href(self) -> str:
211
+ """Their page on the site."""
212
+ return user_href(self.handle)
213
+
214
+
215
+ def contributors_view(experiments: Sequence[Experiment]) -> list[ContributorStats]:
216
+ """Return one row per person, most experiments and reviews first.
217
+
218
+ An experiment's maker is ``Experiment.user``. That is the proposer's recorded
219
+ contributor, else the author of the PR that merged it (:mod:`openinterp.builder.users`). Its
220
+ reviewer is the verdict's recorded contributor. Each list of rows is newest first, and
221
+ keeps at most :data:`RECENT_PER_USER` rows. The counts include every experiment.
222
+ """
223
+ people: dict[str, ContributorStats] = {}
224
+ newest = sorted(experiments, key=lambda e: (e.record.verdict.timestamp, e.id), reverse=True)
225
+ for e in newest:
226
+ verdict = e.record.verdict
227
+ if e.user:
228
+ row = people.setdefault(e.user, ContributorStats(e.user))
229
+ row.experiments += 1
230
+ if len(row.made) < RECENT_PER_USER:
231
+ row.made.append(_recent_row(e, []))
232
+ row.verified += e.record.is_verified
233
+ row.conclusive_nulls += (
234
+ e.sig.direction == "refutes" and e.sig.conclusiveness >= STRONG_EVIDENCE
235
+ )
236
+ if verdict.contributor:
237
+ row = people.setdefault(verdict.contributor, ContributorStats(verdict.contributor))
238
+ row.reviews += 1
239
+ if len(row.reviewed) < RECENT_PER_USER:
240
+ row.reviewed.append(_recent_row(e, []))
241
+ return sorted(people.values(), key=lambda c: (-c.experiments - c.reviews, c.handle.lower()))
242
+
243
+
244
+ def legacy_redirects(agents: Sequence[AgentView]) -> dict[str, str]:
245
+ """``old page → its model's page``, for every recorded spelling whose page moved."""
246
+ pages = {a.href for a in agents}
247
+ return {
248
+ old: a.href
249
+ for a in agents
250
+ for old in map(legacy_href, a.spellings)
251
+ if old != a.href and old not in pages
252
+ }
253
+
254
+
255
+ def _check_slugs_unique(agents: dict[str, AgentView]) -> None:
256
+ seen: dict[str, str] = {}
257
+ for model, view in agents.items():
258
+ other = seen.setdefault(view.href, model)
259
+ if other != model:
260
+ raise RegistryError(f"agent models {other!r} and {model!r} share the page {view.href}")
@@ -0,0 +1,41 @@
1
+ """The dictionary latents aligned with a neuron's poles, as its page shows them."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from openinterp.builder.tree import feature_href
6
+ from openinterp.crossref import neuronpedia_url
7
+
8
+
9
+ def aligned_latents(model, neuron, alignment: dict, studied: set) -> list[dict]:
10
+ """Return each pole's aligned dictionary latents for a neuron page.
11
+
12
+ Each latent links to its page here (if audited) and on Neuronpedia (if hosted there).
13
+ """
14
+ if not neuron.is_neuron:
15
+ return []
16
+ poles = []
17
+ for pole in neuron.sides:
18
+ rows = alignment.get(f"{model.slug}/L{neuron.layer}/N{neuron.idx}-{pole}", [])
19
+ views = []
20
+ for row in rows:
21
+ sides = []
22
+ for name in ("writes", "reads"):
23
+ side = getattr(row, name)
24
+ if side is None:
25
+ continue
26
+ matches = []
27
+ for m in side.matches:
28
+ href = feature_href(model.slug, neuron.layer, row.dictionary, m.feature_idx)
29
+ matches.append(
30
+ {
31
+ "m": m,
32
+ "href": href if href in studied else None,
33
+ "neuronpedia": neuronpedia_url(row, m.feature_idx),
34
+ "above": m.cosine > side.chance,
35
+ }
36
+ )
37
+ sides.append({"name": name, "side": side, "matches": matches})
38
+ views.append({"row": row, "sides": sides})
39
+ if views:
40
+ poles.append({"pole": pole, "rows": views})
41
+ return poles