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,348 @@
|
|
|
1
|
+
"""Registry-maintenance commands: ``wiki``, ``stats`` and ``next``."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
import click
|
|
8
|
+
from click.core import ParameterSource
|
|
9
|
+
|
|
10
|
+
from openinterp.cli import cli, wiki_dir_option
|
|
11
|
+
from openinterp.core.paths import experiments_dir
|
|
12
|
+
from openinterp.planner import DEFAULT_EPSILON
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@cli.command()
|
|
16
|
+
@click.option(
|
|
17
|
+
"--import-dir",
|
|
18
|
+
type=click.Path(exists=True, file_okay=False, path_type=Path),
|
|
19
|
+
help="Import loose audit records (*.json) found here as experiments.",
|
|
20
|
+
)
|
|
21
|
+
@wiki_dir_option()
|
|
22
|
+
def wiki(import_dir, wiki_dir):
|
|
23
|
+
"""Rebuild the wiki indexes, importing loose records as experiments if asked."""
|
|
24
|
+
from openinterp.builder.compiler import load_records
|
|
25
|
+
from openinterp.builder.wiki import backfill
|
|
26
|
+
|
|
27
|
+
written = backfill(load_records(import_dir) if import_dir else [], wiki_dir)
|
|
28
|
+
for page in written:
|
|
29
|
+
click.echo(f"Wiki: {page}")
|
|
30
|
+
click.echo(f"{len(written)} new experiments; indexes rebuilt in {wiki_dir}")
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def registry_stats(records, set_aside: int = 0) -> str:
|
|
34
|
+
"""Return the state of the registry.
|
|
35
|
+
|
|
36
|
+
That is the experiments, units and models, the VERIFIED and self-reviewed shares, and
|
|
37
|
+
the protocol versions that computed the verdicts. ``set_aside`` is how many records the
|
|
38
|
+
errata left out of ``records``.
|
|
39
|
+
"""
|
|
40
|
+
from collections import Counter
|
|
41
|
+
|
|
42
|
+
def share(n: int, of: int) -> str:
|
|
43
|
+
return f"{n}/{of} ({100 * n / of:.0f}%)" if of else "0/0"
|
|
44
|
+
|
|
45
|
+
def summary(rs) -> str:
|
|
46
|
+
units = {r.submission.identifier.slug for r in rs}
|
|
47
|
+
reviewed = [r for r in rs if r.verdict is not None]
|
|
48
|
+
verified = sum(r.is_verified for r in rs)
|
|
49
|
+
self_reviewed = sum(r.self_reviewed for r in reviewed)
|
|
50
|
+
return (
|
|
51
|
+
f"{len(rs)} experiments on {len(units)} units; VERIFIED {share(verified, len(rs))}, "
|
|
52
|
+
f"self-reviewed {share(self_reviewed, len(reviewed))}"
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
by_model: dict[str, list] = {}
|
|
56
|
+
for r in records:
|
|
57
|
+
by_model.setdefault(r.submission.identifier.model_name, []).append(r)
|
|
58
|
+
versions = Counter(
|
|
59
|
+
(r.verdict.policy.protocol_version if r.verdict.policy else "< 0.2")
|
|
60
|
+
for r in records
|
|
61
|
+
if r.verdict is not None
|
|
62
|
+
)
|
|
63
|
+
lines = [f"Registry: {summary(records)}, {len(by_model)} models"]
|
|
64
|
+
lines += [f" {model}: {summary(rs)}" for model, rs in sorted(by_model.items())]
|
|
65
|
+
lines.append(
|
|
66
|
+
"Protocol versions: "
|
|
67
|
+
+ (", ".join(f"{v} ×{n}" for v, n in sorted(versions.items(), key=_newest)) or "none")
|
|
68
|
+
)
|
|
69
|
+
if set_aside:
|
|
70
|
+
lines.append(f"Set aside by errata, and not counted: {set_aside} experiments")
|
|
71
|
+
return "\n".join(lines)
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def _newest(item) -> tuple:
|
|
75
|
+
version = item[0]
|
|
76
|
+
return (1,) if version == "< 0.2" else (0, *(-int(p) for p in version.split(".")))
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
@cli.command()
|
|
80
|
+
@wiki_dir_option()
|
|
81
|
+
def stats(wiki_dir):
|
|
82
|
+
"""Print the state of the registry.
|
|
83
|
+
|
|
84
|
+
It prints experiments, units, models, VERIFIED share, self-reviewed share and the
|
|
85
|
+
protocol versions in use. It counts the records as the site does, with the errata's
|
|
86
|
+
edits applied and the records that they set aside left out.
|
|
87
|
+
"""
|
|
88
|
+
from openinterp.builder.compiler import load_records
|
|
89
|
+
from openinterp.core.errata import load_errata, standing_records
|
|
90
|
+
|
|
91
|
+
records = load_records(experiments_dir(wiki_dir))
|
|
92
|
+
counted = standing_records(records, load_errata(wiki_dir))
|
|
93
|
+
click.echo(registry_stats(counted, set_aside=len(records) - len(counted)))
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
@cli.command()
|
|
97
|
+
@click.argument("path", type=click.Path(exists=True, path_type=Path))
|
|
98
|
+
@click.option("--full", is_flag=True, help="Don't cut probes, examples or the Discussion.")
|
|
99
|
+
def show(path, full):
|
|
100
|
+
"""Summarise a record, submission, scan evidence (ev.json) or a unit's directory.
|
|
101
|
+
|
|
102
|
+
Read this instead of opening the JSON. For a record, it shows the verdict, failed
|
|
103
|
+
gates and probes, scores and each page's Discussion. For a scan, it shows the corpus,
|
|
104
|
+
peak and top examples.
|
|
105
|
+
"""
|
|
106
|
+
from openinterp.show import show as summarise
|
|
107
|
+
|
|
108
|
+
try:
|
|
109
|
+
click.echo(summarise(path, full))
|
|
110
|
+
except ValueError as exc:
|
|
111
|
+
raise click.BadParameter(str(exc), param_hint="PATH") from exc
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def _refuse_while_paused() -> None:
|
|
115
|
+
"""Exit 75 with the reason if `open-interpretability pause` is in force."""
|
|
116
|
+
from openinterp.core import pause
|
|
117
|
+
|
|
118
|
+
if paused := pause.read():
|
|
119
|
+
click.echo(paused.describe(), err=True)
|
|
120
|
+
raise SystemExit(pause.EXIT_PAUSED)
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
@cli.command(name="next")
|
|
124
|
+
@wiki_dir_option()
|
|
125
|
+
@click.option("--seed", type=int, help="Reproduce a previous choice. Default: fresh randomness.")
|
|
126
|
+
@click.option(
|
|
127
|
+
"--epsilon",
|
|
128
|
+
default=DEFAULT_EPSILON,
|
|
129
|
+
show_default=True,
|
|
130
|
+
type=click.FloatRange(0, 1),
|
|
131
|
+
help="Probability of exploring a uniformly random new neuron.",
|
|
132
|
+
)
|
|
133
|
+
@click.option(
|
|
134
|
+
"--temperature",
|
|
135
|
+
default=1.0,
|
|
136
|
+
show_default=True,
|
|
137
|
+
type=click.FloatRange(min=0.05),
|
|
138
|
+
help="Higher flattens the weights (more exploration among follow-ups).",
|
|
139
|
+
)
|
|
140
|
+
@click.option(
|
|
141
|
+
"--model",
|
|
142
|
+
"models",
|
|
143
|
+
multiple=True,
|
|
144
|
+
help="Model(s) to explore. Default: [tool.openinterp] models in pyproject.toml.",
|
|
145
|
+
)
|
|
146
|
+
@click.option(
|
|
147
|
+
"--claims/--no-claims",
|
|
148
|
+
default=True,
|
|
149
|
+
show_default=True,
|
|
150
|
+
help="Down-weight neurons claimed by open PRs (needs `gh`).",
|
|
151
|
+
)
|
|
152
|
+
@click.option("--top", default=0, show_default=True, help="Also list the N heaviest candidates.")
|
|
153
|
+
@click.option("--json", "as_json", is_flag=True, help="Print the full plan as JSON.")
|
|
154
|
+
@click.option(
|
|
155
|
+
"--steps",
|
|
156
|
+
is_flag=True,
|
|
157
|
+
help="Also print every step of the draw as exact commands: claim, history, run, post.",
|
|
158
|
+
)
|
|
159
|
+
@click.option(
|
|
160
|
+
"--corpus",
|
|
161
|
+
default="corpus.jsonl",
|
|
162
|
+
show_default=True,
|
|
163
|
+
help="For --steps: the corpus a new scan reads.",
|
|
164
|
+
)
|
|
165
|
+
@click.option(
|
|
166
|
+
"--fresh-corpus",
|
|
167
|
+
is_flag=True,
|
|
168
|
+
help="For --steps: a new scan first builds its own corpus (the default blend, with the "
|
|
169
|
+
"draw's seed). Not with --corpus.",
|
|
170
|
+
)
|
|
171
|
+
@click.option(
|
|
172
|
+
"--proposer",
|
|
173
|
+
help="For --steps: a LiteLLM model for `audit`. Default: the neuron-proposer subagent.",
|
|
174
|
+
)
|
|
175
|
+
@click.option(
|
|
176
|
+
"--reviewer",
|
|
177
|
+
help="For --steps: a LiteLLM model for `verify`. Default: the neuron-reviewer and "
|
|
178
|
+
"explanation-judge subagents.",
|
|
179
|
+
)
|
|
180
|
+
def next_experiment(
|
|
181
|
+
wiki_dir,
|
|
182
|
+
seed,
|
|
183
|
+
epsilon,
|
|
184
|
+
temperature,
|
|
185
|
+
models,
|
|
186
|
+
claims,
|
|
187
|
+
top,
|
|
188
|
+
as_json,
|
|
189
|
+
steps,
|
|
190
|
+
corpus,
|
|
191
|
+
fresh_corpus,
|
|
192
|
+
proposer,
|
|
193
|
+
reviewer,
|
|
194
|
+
):
|
|
195
|
+
"""Pick the next experiment (randomised; balances exploration and follow-ups).
|
|
196
|
+
|
|
197
|
+
While `open-interpretability pause` is in force it draws nothing and exits 75.
|
|
198
|
+
"""
|
|
199
|
+
from openinterp.planner import open_pr_claims, plan_next
|
|
200
|
+
|
|
201
|
+
_refuse_while_paused()
|
|
202
|
+
|
|
203
|
+
source = click.get_current_context().get_parameter_source("corpus")
|
|
204
|
+
if fresh_corpus and source != ParameterSource.DEFAULT:
|
|
205
|
+
raise click.UsageError("--fresh-corpus and --corpus both set the scan's corpus.")
|
|
206
|
+
plan = plan_next(
|
|
207
|
+
wiki_dir,
|
|
208
|
+
seed=seed,
|
|
209
|
+
epsilon=epsilon,
|
|
210
|
+
temperature=temperature,
|
|
211
|
+
models=models,
|
|
212
|
+
claims=open_pr_claims() if claims else (),
|
|
213
|
+
)
|
|
214
|
+
if as_json:
|
|
215
|
+
click.echo(plan.to_json())
|
|
216
|
+
return
|
|
217
|
+
c = plan.choice
|
|
218
|
+
click.echo(f"Next experiment: {c.kind}" + (f" on {c.slug}" if c.slug else f" ({c.model})"))
|
|
219
|
+
click.echo(f"Why: {c.rationale}")
|
|
220
|
+
for command in c.commands:
|
|
221
|
+
click.echo(f" $ {command}")
|
|
222
|
+
if any(
|
|
223
|
+
cmd.startswith("open-interpretability audit") and "--neuron" in cmd for cmd in c.commands
|
|
224
|
+
):
|
|
225
|
+
_scan_cache_note(c)
|
|
226
|
+
if c.skills:
|
|
227
|
+
click.echo(f"Skills: {', '.join(c.skills)}")
|
|
228
|
+
click.echo(
|
|
229
|
+
f"(seed {plan.seed}; {len(plan.candidates)} follow-up candidates; "
|
|
230
|
+
f"{'explored' if plan.explored else 'exploited'})"
|
|
231
|
+
)
|
|
232
|
+
_list_candidates(plan, top)
|
|
233
|
+
if steps:
|
|
234
|
+
_print_steps(plan, wiki_dir, corpus, proposer, reviewer, fresh_corpus)
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
def _print_steps(plan, wiki_dir, corpus, proposer, reviewer, fresh_corpus) -> None:
|
|
238
|
+
"""Record the draw in the ledger, then print its steps, whose claim commits the entry."""
|
|
239
|
+
from openinterp.draw_steps import render
|
|
240
|
+
from openinterp.draw_steps import steps as draw_steps
|
|
241
|
+
|
|
242
|
+
ledger = record_draw_entry(plan, wiki_dir)
|
|
243
|
+
click.echo(f"\nDraw recorded in the ledger: {ledger}")
|
|
244
|
+
click.echo("\nSteps:")
|
|
245
|
+
click.echo(render(draw_steps(plan, corpus, proposer, reviewer, fresh_corpus, ledger)))
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
@cli.command(name="ledger")
|
|
249
|
+
@wiki_dir_option()
|
|
250
|
+
@click.option("--json", "as_json", is_flag=True, help="Print the summary as JSON.")
|
|
251
|
+
def ledger_report(wiki_dir, as_json):
|
|
252
|
+
"""Report every recorded draw's outcome, and VERIFIED rates against all draws.
|
|
253
|
+
|
|
254
|
+
The ledger (wiki/ledger/) holds every draw that `open-interpretability next --steps` made,
|
|
255
|
+
before its run. A draw with no record and no stop was dropped, or is still running.
|
|
256
|
+
"""
|
|
257
|
+
import json
|
|
258
|
+
|
|
259
|
+
from openinterp.core.ledger import load_ledger, resolve, summary
|
|
260
|
+
|
|
261
|
+
entries = load_ledger(Path(wiki_dir))
|
|
262
|
+
records, stopped = _ledger_rows(Path(wiki_dir))
|
|
263
|
+
resolved = [(e, *resolve(e, records, stopped)) for e in entries]
|
|
264
|
+
result = summary(resolved)
|
|
265
|
+
if as_json:
|
|
266
|
+
click.echo(json.dumps(result, indent=1))
|
|
267
|
+
return
|
|
268
|
+
for e, outcome, status in resolved:
|
|
269
|
+
what = e.slug or f"{e.model} (seed {e.audit_seed})"
|
|
270
|
+
click.echo(f"{e.timestamp:%Y-%m-%d %H:%M} {e.kind:<18} {what}: {status or outcome}")
|
|
271
|
+
n = result["explore_entries"]
|
|
272
|
+
click.echo(
|
|
273
|
+
f"{result['entries']} draws: {result['outcomes']}. Explore draws: {n}, "
|
|
274
|
+
f"{result['explore_verified']} VERIFIED."
|
|
275
|
+
)
|
|
276
|
+
if n:
|
|
277
|
+
share = result["verified_share_of_finished_explore"]
|
|
278
|
+
click.echo(
|
|
279
|
+
f"VERIFIED share of explore draws: {result['verified_share_of_all_explore']:.1%} "
|
|
280
|
+
f"of all, {share:.1%} of finished."
|
|
281
|
+
if share is not None
|
|
282
|
+
else ""
|
|
283
|
+
)
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
def _ledger_rows(wiki_dir: Path):
|
|
287
|
+
"""(slug, random seed, time, status) of every record, and of every draw or stop."""
|
|
288
|
+
from openinterp.core.draws import load_draws, load_stops
|
|
289
|
+
from openinterp.core.schemas import AuditRecord
|
|
290
|
+
|
|
291
|
+
records = []
|
|
292
|
+
for path in sorted(experiments_dir(wiki_dir).rglob("*.json")):
|
|
293
|
+
r = AuditRecord.model_validate_json(path.read_text())
|
|
294
|
+
if r.verdict is None:
|
|
295
|
+
continue
|
|
296
|
+
p = r.submission.provenance
|
|
297
|
+
seed = p.seed if p and p.selection == "random" else None
|
|
298
|
+
records.append((r.submission.identifier.slug, seed, r.verdict.timestamp, r.verdict.status))
|
|
299
|
+
stopped = [
|
|
300
|
+
(d.identifier.slug, d.provenance.seed, d.timestamp, None)
|
|
301
|
+
for d in [*load_draws(wiki_dir), *load_stops(wiki_dir)]
|
|
302
|
+
]
|
|
303
|
+
return records, stopped
|
|
304
|
+
|
|
305
|
+
|
|
306
|
+
def record_draw_entry(plan, wiki_dir) -> str:
|
|
307
|
+
"""Write the draw's ledger entry before any run (issue #3205), and return its path."""
|
|
308
|
+
from datetime import UTC, datetime
|
|
309
|
+
|
|
310
|
+
from openinterp.core.ledger import DrawEntry, write_entry
|
|
311
|
+
from openinterp.draw_steps import draw_slug
|
|
312
|
+
|
|
313
|
+
c = plan.choice
|
|
314
|
+
options = (c.audit or "").split()
|
|
315
|
+
entry = DrawEntry(
|
|
316
|
+
timestamp=datetime.now(UTC).replace(microsecond=0),
|
|
317
|
+
seed=plan.seed,
|
|
318
|
+
kind=c.kind,
|
|
319
|
+
model=c.model,
|
|
320
|
+
slug=draw_slug(c),
|
|
321
|
+
record=c.record,
|
|
322
|
+
audit=c.audit,
|
|
323
|
+
pole_forced=("--pole" in options) if "--random" in options else None,
|
|
324
|
+
)
|
|
325
|
+
return str(write_entry(entry, Path(wiki_dir)))
|
|
326
|
+
|
|
327
|
+
|
|
328
|
+
def _scan_cache_note(c) -> None:
|
|
329
|
+
from openinterp.harness.layer_cache import LayerCache
|
|
330
|
+
|
|
331
|
+
cache = LayerCache.default()
|
|
332
|
+
if cache is not None and c.layer in cache.cached_layers(c.model):
|
|
333
|
+
click.echo(
|
|
334
|
+
f"Scan cache: layer {c.layer} is cached, so the audit replays its scan "
|
|
335
|
+
"(when the corpus options match) instead of running the model."
|
|
336
|
+
)
|
|
337
|
+
|
|
338
|
+
|
|
339
|
+
def _list_candidates(plan, top: int) -> None:
|
|
340
|
+
"""The ``top`` heaviest follow-ups by share of the weight, and the explore draws."""
|
|
341
|
+
total = sum(x.weight for x in plan.candidates) or 1.0
|
|
342
|
+
for x in plan.candidates[:top]:
|
|
343
|
+
click.echo(f" {x.weight / total:6.1%} {x.kind:18s} {x.slug} — {x.rationale}")
|
|
344
|
+
if top and len(plan.explore_models) > 1:
|
|
345
|
+
click.echo("Explore draws by model (neurons covered):")
|
|
346
|
+
for model, p in sorted(plan.explore_models.items(), key=lambda kv: -kv[1]):
|
|
347
|
+
focus = " ← smallest model below the verified target" if model == plan.focus else ""
|
|
348
|
+
click.echo(f" {p:6.1%} {model} {plan.coverage[model]}{focus}")
|