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,128 @@
1
+ """``open-interpretability build``: compile the archived experiments into the static site."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from pathlib import Path
6
+
7
+ import click
8
+
9
+ from openinterp.cli import cli, wiki_dir_option
10
+ from openinterp.core.schemas import COMMITTED_SOURCES
11
+ from openinterp.sisters import DEFAULT_PATH as SISTERS_PATH
12
+
13
+
14
+ @cli.command()
15
+ @wiki_dir_option("Experiment archive (wiki/experiments/**/<id>.json).", exists=True)
16
+ @click.option(
17
+ "--output-dir", default=Path("dist"), show_default=True, type=click.Path(path_type=Path)
18
+ )
19
+ @click.option(
20
+ "--wiring",
21
+ "wiring_path",
22
+ default=Path("docs/analyses/wiring.jsonl"),
23
+ show_default=True,
24
+ type=click.Path(dir_okay=False, path_type=Path),
25
+ help="Backfilled wiring (`open-interpretability wiring-backfill`) for records without "
26
+ "their own; skipped if missing.",
27
+ )
28
+ @click.option(
29
+ "--corpus-sources",
30
+ "source_paths",
31
+ multiple=True,
32
+ default=[Path(name) for name in COMMITTED_SOURCES],
33
+ show_default=True,
34
+ type=click.Path(dir_okay=False, path_type=Path),
35
+ help="Corpus sidecars that give the source of records which scanned their corpus "
36
+ "(matched by SHA-256) without recording one, shown as inferred; skipped if missing.",
37
+ )
38
+ @click.option(
39
+ "--alignment",
40
+ "alignment_path",
41
+ default=Path("docs/analyses/dictionary-alignment.jsonl"),
42
+ show_default=True,
43
+ type=click.Path(dir_okay=False, path_type=Path),
44
+ help="Dictionary latents aligned with each neuron pole (`open-interpretability crossref`); "
45
+ "skipped if missing.",
46
+ )
47
+ @click.option(
48
+ "--sisters",
49
+ "sisters_path",
50
+ default=SISTERS_PATH,
51
+ show_default=True,
52
+ type=click.Path(dir_okay=False, path_type=Path),
53
+ help="Each VERIFIED pole's matches in other models (`open-interpretability sisters`); "
54
+ "skipped if missing.",
55
+ )
56
+ @click.option(
57
+ "--coverage",
58
+ "coverage_path",
59
+ default=Path("docs/analyses/registry-coverage.jsonl"),
60
+ show_default=True,
61
+ type=click.Path(dir_okay=False, path_type=Path),
62
+ help="Registry coverage rows (`open-interpretability coverage`) for the home page; "
63
+ "skipped if missing.",
64
+ )
65
+ @click.option(
66
+ "--papers",
67
+ "papers_dir",
68
+ default=Path("papers"),
69
+ show_default=True,
70
+ type=click.Path(file_okay=False, path_type=Path),
71
+ help="The papers of the Research section; the section is empty if missing.",
72
+ )
73
+ @click.option(
74
+ "--jobs",
75
+ type=click.IntRange(min=1),
76
+ default=None,
77
+ help="Worker processes that render the unit pages. Default: one per CPU. 1 renders in "
78
+ "this process.",
79
+ )
80
+ def build( # noqa: PLR0913 (one option per input file)
81
+ wiki_dir,
82
+ output_dir,
83
+ wiring_path,
84
+ source_paths,
85
+ alignment_path,
86
+ sisters_path,
87
+ coverage_path,
88
+ papers_dir,
89
+ jobs,
90
+ ):
91
+ """Compile every archived experiment into static pages and data/registry.parquet."""
92
+ from openinterp.builder.compiler import compile_site
93
+ from openinterp.builder.coverage import load_rows as load_coverage
94
+ from openinterp.builder.model_facts import load_model_facts
95
+ from openinterp.builder.parallel import default_jobs
96
+ from openinterp.builder.users import load_users
97
+ from openinterp.builder.wiki import load_archive
98
+ from openinterp.core.draws import load_draws, load_stops
99
+ from openinterp.core.errata import load_errata
100
+ from openinterp.core.project import project_links
101
+ from openinterp.core.takedowns import activate, load_takedowns
102
+ from openinterp.corpus import load_sources
103
+ from openinterp.crossref import load_rows as load_alignment
104
+ from openinterp.planner import configured_models
105
+ from openinterp.sisters import load_rows as load_sisters
106
+ from openinterp.wiring_backfill import load_rows
107
+
108
+ activate(load_takedowns(wiki_dir)) # before any text is scrubbed; workers fork after this
109
+ report = compile_site(
110
+ load_archive(wiki_dir),
111
+ output_dir,
112
+ wiring_rows=load_rows(wiring_path),
113
+ draws=load_draws(wiki_dir),
114
+ corpus_sources=load_sources(source_paths),
115
+ alignment_rows=load_alignment(alignment_path),
116
+ sister_rows=load_sisters(sisters_path),
117
+ papers_dir=papers_dir,
118
+ stops=load_stops(wiki_dir),
119
+ model_facts=load_model_facts(configured_models()), # config.json only, no weights
120
+ jobs=jobs or default_jobs(),
121
+ errata=load_errata(wiki_dir),
122
+ users=load_users(project_links()["repo_url"], wiki_dir),
123
+ coverage_rows=load_coverage(coverage_path),
124
+ )
125
+ click.echo(
126
+ f"Published {report.experiments_published} experiments: {len(report.pages)} pages, "
127
+ f"{report.parquet} → {output_dir}"
128
+ )
@@ -0,0 +1,51 @@
1
+ """The ``check`` command: registry consistency, for CI and before a push."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import click
6
+
7
+ from openinterp.cli import cli, wiki_dir_option
8
+
9
+
10
+ @cli.command()
11
+ @wiki_dir_option()
12
+ @click.option(
13
+ "--base-ref",
14
+ help="Git ref to diff against (e.g. origin/main). Enforces write-once pages, and checks "
15
+ "only the branch's changes unless it also changes files outside the wiki.",
16
+ )
17
+ def check(wiki_dir, base_ref):
18
+ """Check that every posted experiment has a valid record and wiki page (CI gate)."""
19
+ import os
20
+ import subprocess
21
+
22
+ from openinterp.checks import check_registry
23
+
24
+ try:
25
+ report = check_registry(wiki_dir, base_ref=base_ref)
26
+ except subprocess.CalledProcessError as error:
27
+ raise click.ClickException(
28
+ f"can't diff against {base_ref} ({error.stderr.strip()}). "
29
+ "Fetch the ref, or run `open-interpretability check` to check every record."
30
+ ) from error
31
+ in_actions = os.environ.get("GITHUB_ACTIONS") == "true"
32
+ for problem in report.problems:
33
+ if in_actions:
34
+ click.echo(f"::error file={problem.path}::{problem.message}")
35
+ else:
36
+ click.echo(f"error: {problem}", err=True)
37
+
38
+ summary = (
39
+ f"Checked {report.records} experiment records and {report.pages} experiment pages"
40
+ + "".join(f" and {n} {kind} records" for kind, n in report.extra_counts.items() if n)
41
+ + (f" changed since {base_ref}" if report.changed_only else "")
42
+ + ": "
43
+ + ("all consistent." if report.ok else f"{len(report.problems)} problem(s).")
44
+ )
45
+ click.echo(summary)
46
+ if in_actions and os.environ.get("GITHUB_STEP_SUMMARY"):
47
+ with open(os.environ["GITHUB_STEP_SUMMARY"], "a", encoding="utf-8") as fh:
48
+ fh.write(f"### Experiment check\n\n{summary}\n\n")
49
+ fh.writelines(f"- `{p.path}`: {p.message}\n" for p in report.problems)
50
+ if not report.ok:
51
+ raise SystemExit(1)
@@ -0,0 +1,76 @@
1
+ """``open-interpretability concepts``: a labelled concept set from an annotated dataset.
2
+
3
+ See :mod:`openinterp.concepts` for the presets and how labels become spans.
4
+ """
5
+
6
+ from __future__ import annotations
7
+
8
+ from pathlib import Path
9
+
10
+ import click
11
+
12
+ from openinterp.cli import cli
13
+ from openinterp.concepts import CONCEPT_PRESETS
14
+
15
+
16
+ @cli.command()
17
+ @click.option("--dataset", "preset", required=True, type=click.Choice(list(CONCEPT_PRESETS)))
18
+ @click.option(
19
+ "--label",
20
+ help="The label to mark (PER, NOUN, nsubj, fr, Sports). Omit it to list the split's "
21
+ "labels and how many words or documents carry each.",
22
+ )
23
+ @click.option("--split", help="Dataset split. Default: the preset's held-out one.")
24
+ @click.option("--subset", help="Universal Dependencies treebank (default en_ewt).")
25
+ @click.option("--column", help="Read labels from this column instead (e.g. UD's xpos).")
26
+ @click.option("--docs", "documents", default=1000, show_default=True, type=click.IntRange(1))
27
+ @click.option("--seed", default=0, show_default=True, type=int)
28
+ @click.option(
29
+ "--max-chars",
30
+ default=3000,
31
+ show_default=True,
32
+ type=click.IntRange(1),
33
+ help="Join sentences into documents of at most this many characters.",
34
+ )
35
+ @click.option("--out", type=click.Path(path_type=Path), help="Concept set JSONL.")
36
+ def concepts(preset, label, split, subset, column, documents, seed, max_chars, out):
37
+ """Write a concept set from human-annotated labels.
38
+
39
+ The set is JSON lines of {"text": ..., "spans": [[start, end], ...]}. Use it for
40
+ `open-interpretability probe-compare --train/--test`, sparse-probing searches and reviewers'
41
+ positive holdouts.
42
+
43
+ Word tags (conll2003-*, ud-*) mark each tagged word; document classes
44
+ (language-identification, ag_news) mark whole documents, half of them of the class.
45
+ Also writes <name>.source.json, which pins the dataset commit, split and licence.
46
+ """
47
+ from openinterp.concepts import build_concept, load_split
48
+
49
+ if label is None:
50
+ loaded = load_split(preset, split=split, subset=subset, column=column, max_chars=max_chars)
51
+ click.echo(
52
+ f"{loaded.dataset}@{loaded.revision[:8]} {loaded.files}: {len(loaded.docs)} docs"
53
+ )
54
+ for tag, n in loaded.counts().most_common():
55
+ click.echo(f"{n:>8,} {tag}")
56
+ return
57
+ if out is None:
58
+ raise click.UsageError("--out is required with --label.")
59
+ try:
60
+ source = build_concept(
61
+ preset,
62
+ label,
63
+ out,
64
+ split=split,
65
+ subset=subset,
66
+ column=column,
67
+ documents=documents,
68
+ seed=seed,
69
+ max_chars=max_chars,
70
+ )
71
+ except ValueError as exc:
72
+ raise click.ClickException(str(exc)) from exc
73
+ click.echo(f"Wrote {out}: {source}")
74
+ if source.skipped_sentences:
75
+ click.echo(f"warning: {source.skipped_sentences} sentences' words not found", err=True)
76
+ click.echo(f"Licence: {source.license or 'not stated on the dataset card'}")
@@ -0,0 +1,252 @@
1
+ """``open-interpretability corpus``: build a scan corpus from a Hugging Face dataset."""
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.blends import BLEND_ALIASES, BLENDS, DEFAULT_DATASET
11
+ from openinterp.cli import cli
12
+ from openinterp.corpus import LAYOUTS, PRESETS
13
+
14
+
15
+ @cli.command()
16
+ @click.option(
17
+ "--dataset",
18
+ default=DEFAULT_DATASET,
19
+ show_default=True,
20
+ help=f"A blend of presets ({', '.join([*BLEND_ALIASES, *BLENDS])}; '{DEFAULT_DATASET}' is "
21
+ f"the latest), a preset ({', '.join(PRESETS)}) or a Hugging Face dataset ID (then pass "
22
+ "--files).",
23
+ )
24
+ @click.option(
25
+ "--files",
26
+ help="Glob of Parquet files within the dataset; comma-separate several to take an equal "
27
+ "share of documents from each. Default: the preset's.",
28
+ )
29
+ @click.option(
30
+ "--text-column",
31
+ help="Column holding the text; comma-separate several to join them into one document. "
32
+ "Default: the preset's, or 'text'.",
33
+ )
34
+ @click.option(
35
+ "--revision",
36
+ help="Dataset branch, tag or commit. Default: the preset's, or main; pinned on use.",
37
+ )
38
+ @click.option(
39
+ "--layout",
40
+ type=click.Choice(list(LAYOUTS)),
41
+ help="Join rows into documents this way, e.g. one WikiText row per paragraph. Default: "
42
+ "the preset's, or one document per row.",
43
+ )
44
+ @click.option("--docs", "documents", default=500, show_default=True, type=click.IntRange(1))
45
+ @click.option("--seed", default=0, show_default=True, type=int)
46
+ @click.option(
47
+ "--max-chars",
48
+ default=3000,
49
+ show_default=True,
50
+ type=click.IntRange(1),
51
+ help="Cut each document to this many characters.",
52
+ )
53
+ @click.option(
54
+ "--docs-per-row-group",
55
+ type=click.IntRange(1),
56
+ help="Documents taken from each Parquet row group read; lower spreads the sample wider. "
57
+ "Default: the preset's, or 20.",
58
+ )
59
+ @click.option(
60
+ "--first",
61
+ is_flag=True,
62
+ help="Take the dataset's first --docs documents in file order, cut but not stripped, "
63
+ "instead of a seeded sample.",
64
+ )
65
+ @click.option(
66
+ "--ascii",
67
+ "ascii_",
68
+ is_flag=True,
69
+ help="Escape non-ASCII characters in the JSONL. With --first, rebuilds the registry's "
70
+ "corpus from pile-10k (README).",
71
+ )
72
+ @click.option(
73
+ "--lang-pattern",
74
+ help="Regular expression whose group, matched in each --files glob, is the language its "
75
+ "documents record as 'lang' (e.g. '^\\d+\\.(\\w+)/' for Wikipedia). Default: the preset's.",
76
+ )
77
+ @click.option(
78
+ "--detect-lang",
79
+ is_flag=True,
80
+ help="Detect each document's language (fastText lid.176, downloaded once) and record "
81
+ "it as 'lang' where the detector is sure, for datasets that don't say. The source "
82
+ "sidecar records the detector. Replaces a preset's exact languages; not with --lang-pattern.",
83
+ )
84
+ @click.option(
85
+ "--detect-code",
86
+ is_flag=True,
87
+ help="Detect each document's programming language (Magika) and record it as "
88
+ "'code_language' where the detector is sure, for datasets that don't say (e.g. the "
89
+ "Pile's GitHub files). Prose stays unlabelled. The source sidecar records the "
90
+ "detector. Not with a preset that gives exact programming languages.",
91
+ )
92
+ @click.option(
93
+ "--chat-template",
94
+ metavar="MODEL",
95
+ help="Write each conversation in --text-column (a list of role/content messages) with "
96
+ "this model's chat template, pinned to its commit. Default: the preset's ("
97
+ + ", ".join(f"{name}: {p.chat_template}" for name, p in PRESETS.items() if p.chat_template)
98
+ + "), or none.",
99
+ )
100
+ @click.option(
101
+ "--same-as",
102
+ type=click.Path(exists=True, dir_okay=False, path_type=Path),
103
+ help="Rebuild the corpus this record scanned, at its pinned dataset commits, and check "
104
+ "its SHA-256. It sets the source, so only --out goes with it.",
105
+ )
106
+ @click.option(
107
+ "--out", default=Path("corpus.jsonl"), show_default=True, type=click.Path(path_type=Path)
108
+ )
109
+ @click.pass_context
110
+ def corpus(
111
+ ctx,
112
+ dataset,
113
+ files,
114
+ text_column,
115
+ revision,
116
+ layout,
117
+ documents,
118
+ seed,
119
+ max_chars,
120
+ docs_per_row_group,
121
+ first,
122
+ ascii_,
123
+ lang_pattern,
124
+ detect_lang,
125
+ detect_code,
126
+ chat_template,
127
+ same_as,
128
+ out,
129
+ ):
130
+ """Sample a scan corpus from a Hugging Face dataset, without downloading all of it.
131
+
132
+ Writes <out> (one {"text": ...} per line) and <name>.source.json next to it, which
133
+ pins the dataset commit, files, seed and licence. `open-interpretability audit --corpus <out>`
134
+ records that source in the submission's provenance.
135
+ """
136
+ from openinterp.corpus import build_from_hub
137
+
138
+ if same_as:
139
+ return _same_as(ctx, same_as, out)
140
+ if dataset in BLENDS or dataset in BLEND_ALIASES:
141
+ per_dataset = {
142
+ "--files": files, "--text-column": text_column, "--revision": revision,
143
+ "--layout": layout, "--docs-per-row-group": docs_per_row_group, "--first": first,
144
+ "--lang-pattern": lang_pattern, "--detect-lang": detect_lang,
145
+ "--detect-code": detect_code, "--chat-template": chat_template,
146
+ } # fmt: skip
147
+ return _blend(dataset, per_dataset, out, documents, seed, max_chars, ascii_)
148
+ preset = PRESETS.get(dataset)
149
+ _check_dataset(dataset, preset, files, lang_pattern, detect_lang, detect_code)
150
+ try:
151
+ source = build_from_hub(
152
+ preset.dataset if preset else dataset,
153
+ out,
154
+ files=files or preset.files,
155
+ text_column=text_column or (preset.text_column if preset else "text"),
156
+ revision=revision or (preset.revision if preset else None),
157
+ layout=layout or (preset.layout if preset else None),
158
+ documents=documents,
159
+ seed=seed,
160
+ max_chars=max_chars,
161
+ docs_per_row_group=docs_per_row_group or (preset.docs_per_row_group if preset else 20),
162
+ first=first,
163
+ ascii=ascii_,
164
+ lang_pattern=None if detect_lang else lang_pattern or (preset and preset.lang_pattern),
165
+ detect_lang=detect_lang,
166
+ code_language_column=preset.code_language_column if preset else None,
167
+ detect_code=detect_code,
168
+ chat_template=chat_template or (preset.chat_template if preset else None),
169
+ )
170
+ except ValueError as error: # e.g. a model without a chat template
171
+ raise click.ClickException(str(error)) from error
172
+ _report(source, out, documents)
173
+
174
+
175
+ def _check_dataset(dataset, preset, files, lang_pattern, detect_lang, detect_code) -> None:
176
+ """Raise ``click.UsageError`` if the options can't sample ``dataset``."""
177
+ if detect_lang and lang_pattern:
178
+ raise click.UsageError("--detect-lang and --lang-pattern both set the languages.")
179
+ if detect_code and preset and preset.code_language_column:
180
+ raise click.UsageError(f"{dataset!r} has exact programming languages: no --detect-code.")
181
+ if preset is None and not files:
182
+ raise click.UsageError(f"--files is required for {dataset!r}, which is not a preset.")
183
+
184
+
185
+ @cli.command("corpus-cache")
186
+ @click.option("--clear", is_flag=True, help="Delete everything cached.")
187
+ def corpus_cache(clear):
188
+ """Show the size of (or clear) the cache of Parquet data that `corpus` reads.
189
+
190
+ \b
191
+ Location: $OPENINTERP_CORPUS_CACHE (\"off\" disables it), else
192
+ $XDG_CACHE_HOME/openinterp/corpora or ~/.cache/openinterp/corpora.
193
+ Size cap: $OPENINTERP_CORPUS_CACHE_GB (default 5); least recently used go first.
194
+ """
195
+ from openinterp.corpus_cache import ParquetCache
196
+
197
+ cache = ParquetCache.default()
198
+ if cache is None:
199
+ click.echo("The corpus cache is off ($OPENINTERP_CORPUS_CACHE).")
200
+ return
201
+ if clear:
202
+ click.echo(f"Deleted {cache.clear()} entries from {cache.root}")
203
+ return
204
+ click.echo(
205
+ f"{cache.root}: {len(cache.entries())} entries, "
206
+ f"{cache.size() / 1e9:.2f} of {cache.max_bytes / 1e9:.0f} GB"
207
+ )
208
+
209
+
210
+ def _same_as(ctx, record: Path, out: Path) -> None:
211
+ from openinterp.cli_review import load_submission
212
+ from openinterp.corpus_rebuild import rebuild, scanned_source
213
+
214
+ given = [
215
+ name
216
+ for name in ctx.params
217
+ if name not in ("same_as", "out")
218
+ and ctx.get_parameter_source(name) != ParameterSource.DEFAULT
219
+ ]
220
+ if given:
221
+ raise click.UsageError(f"--same-as sets the source, so not with: {', '.join(given)}.")
222
+ try:
223
+ source = rebuild(scanned_source(load_submission(record)), out)
224
+ except ValueError as error:
225
+ raise click.ClickException(f"{record}: {error}") from error
226
+ _report(source, out, source.documents)
227
+
228
+
229
+ def _blend(dataset, per_dataset: dict, out: Path, documents, seed, max_chars, ascii_) -> None:
230
+ from openinterp.blends import build_blend
231
+
232
+ if given := [name for name, value in per_dataset.items() if value]:
233
+ raise click.UsageError(
234
+ f"{', '.join(given)}: set per preset, so not with the blend {dataset!r}."
235
+ )
236
+ source = build_blend(
237
+ dataset, out, documents=documents, seed=seed, max_chars=max_chars, ascii=ascii_
238
+ )
239
+ _report(source, out, documents)
240
+ for part in source.components:
241
+ click.echo(f" {part.documents} from {part}")
242
+
243
+
244
+ def _report(source, out: Path, documents: int) -> None:
245
+ click.echo(f"Wrote {source.documents} documents to {out}: {source}")
246
+ if source.lang_detector:
247
+ click.echo(f"Languages detected by {source.lang_detector}", err=True)
248
+ if source.code_detector:
249
+ click.echo(f"Programming languages detected by {source.code_detector}", err=True)
250
+ if source.documents < documents:
251
+ click.echo(f"warning: only {source.documents} of {documents} documents found", err=True)
252
+ click.echo(f"Licence: {source.license or 'not stated on the dataset card'}")