logogram 0.1.0__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 (54) hide show
  1. logogram/__init__.py +6 -0
  2. logogram/__main__.py +5 -0
  3. logogram/analysis.py +419 -0
  4. logogram/atp.py +120 -0
  5. logogram/backends/__init__.py +5 -0
  6. logogram/backends/base.py +202 -0
  7. logogram/backends/hub.py +375 -0
  8. logogram/backends/saes.py +277 -0
  9. logogram/backends/transformer_lens.py +872 -0
  10. logogram/cli.py +496 -0
  11. logogram/compare.py +177 -0
  12. logogram/datasets.py +159 -0
  13. logogram/direct.py +193 -0
  14. logogram/engine.py +550 -0
  15. logogram/examples/ioi-gpt2/.gitignore +3 -0
  16. logogram/examples/ioi-gpt2/datasets/ioi.jsonl +32 -0
  17. logogram/examples/ioi-gpt2/experiments/ioi-head-patching/spec.json +42 -0
  18. logogram/examples/ioi-gpt2/project.json +6 -0
  19. logogram/exports.py +33 -0
  20. logogram/features.py +368 -0
  21. logogram/fileio.py +63 -0
  22. logogram/ioi.py +220 -0
  23. logogram/paths.py +204 -0
  24. logogram/project.py +444 -0
  25. logogram/prompts.py +204 -0
  26. logogram/research.py +84 -0
  27. logogram/results.py +240 -0
  28. logogram/runner.py +396 -0
  29. logogram/runs.py +98 -0
  30. logogram/sae.py +161 -0
  31. logogram/schema.py +302 -0
  32. logogram/server/__init__.py +1 -0
  33. logogram/server/app.py +1083 -0
  34. logogram/server/models.py +426 -0
  35. logogram/server/security.py +212 -0
  36. logogram/server/state.py +585 -0
  37. logogram/sites.py +249 -0
  38. logogram/spec.py +518 -0
  39. logogram/stats.py +171 -0
  40. logogram/steering.py +258 -0
  41. logogram/system.py +379 -0
  42. logogram/updates.py +194 -0
  43. logogram/verify.py +39 -0
  44. logogram/web_dist/assets/index-BvCU-2uy.js +54 -0
  45. logogram/web_dist/assets/index-DTr8_ucV.css +1 -0
  46. logogram/web_dist/assets/instrument-sans-latin-ext-standard-normal-C5E2Gvlv.woff2 +0 -0
  47. logogram/web_dist/assets/instrument-sans-latin-standard-normal-BVScPF0l.woff2 +0 -0
  48. logogram/web_dist/favicon.svg +1 -0
  49. logogram/web_dist/index.html +15 -0
  50. logogram-0.1.0.dist-info/METADATA +550 -0
  51. logogram-0.1.0.dist-info/RECORD +54 -0
  52. logogram-0.1.0.dist-info/WHEEL +4 -0
  53. logogram-0.1.0.dist-info/entry_points.txt +2 -0
  54. logogram-0.1.0.dist-info/licenses/LICENSE +21 -0
logogram/schema.py ADDED
@@ -0,0 +1,302 @@
1
+ """Schemas for what Logogram writes and sends: run files, listings and live events.
2
+
3
+ The experiment spec lives in :mod:`logogram.spec`; request bodies are defined next to their
4
+ endpoints. Everything here is validated when it is produced, so files on disk and messages to
5
+ the web app always have these shapes.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ from typing import Any, Literal
11
+
12
+ from pydantic import BaseModel, ConfigDict
13
+
14
+
15
+ class _Model(BaseModel):
16
+ model_config = ConfigDict(extra="forbid")
17
+
18
+
19
+ # -- per-site statistics -----------------------------------------------------------------------
20
+
21
+
22
+ class Stat(_Model):
23
+ mean: float | None
24
+ sd: float | None
25
+ lo: float | None
26
+ hi: float | None
27
+
28
+
29
+ class SiteRef(_Model):
30
+ index: int
31
+ kind: Literal[
32
+ "resid_pre", "resid_mid", "resid_post", "attn_out", "mlp_out", "head", "sae_feature"
33
+ ]
34
+ layer: int
35
+ head: int | None
36
+ feature: int | None = None
37
+ position: dict[str, Any]
38
+ position_key: str
39
+ row: int
40
+ col: int
41
+ label: str
42
+ # A variant of the site, such as a steering strength; None for one measurement per site.
43
+ variant: dict[str, Any] | None = None
44
+ variant_key: str | None = None
45
+
46
+
47
+ class SiteSummary(SiteRef):
48
+ n: int
49
+ effect: Stat
50
+ delta: Stat
51
+ patched_logit_diff: float | None
52
+ answer_prob: float | None
53
+ answer_prob_delta: float | None
54
+ sign_flips: int
55
+ opposite_sign: int
56
+
57
+
58
+ class LayoutAxis(BaseModel):
59
+ model_config = ConfigDict(extra="allow")
60
+
61
+ key: str
62
+ label: str
63
+
64
+
65
+ class Layout(_Model):
66
+ kind: Literal["heads", "layer_position", "layer_components", "sites", "steering"]
67
+ site: str | None = None
68
+ row_title: str
69
+ col_title: str
70
+ rows: list[LayoutAxis]
71
+ cols: list[LayoutAxis]
72
+
73
+
74
+ # -- summary.json ------------------------------------------------------------------------------
75
+
76
+
77
+ class GroupStats(_Model):
78
+ mean: float | None
79
+ sd: float | None
80
+
81
+
82
+ class PromptSetStats(_Model):
83
+ logit_diff: GroupStats
84
+ answer_prob: GroupStats
85
+ prefers_answer: int
86
+
87
+
88
+ class BaselineSummary(_Model):
89
+ clean: PromptSetStats
90
+ corrupt: PromptSetStats
91
+ gap: GroupStats
92
+
93
+
94
+ class MetricInfo(_Model):
95
+ kind: str
96
+ normalization: Literal["dataset_gap", "prompt_gap"]
97
+ denominator: float | None
98
+ description: str
99
+ normalized_effect: str
100
+
101
+
102
+ class StatisticsInfo(_Model):
103
+ bootstrap: int
104
+ ci: float
105
+ seed: int
106
+ method: str
107
+
108
+
109
+ class ModelShape(_Model):
110
+ id: str
111
+ n_layers: int
112
+ n_heads: int
113
+ d_model: int | None = None
114
+ d_head: int | None = None
115
+ d_mlp: int | None = None
116
+ site_kinds: list[str] | None = None
117
+ block_structure: str | None = None
118
+ normalization: str | None = None
119
+ activation: str | None = None
120
+
121
+
122
+ class DirectSplit(_Model):
123
+ """Direct logit attribution: the mean logit difference and the parts it splits into."""
124
+
125
+ prompts: Literal["clean", "corrupt"]
126
+ logit_diff: float | None
127
+ embeddings: float | None
128
+ attention: float | None
129
+ mlp: float | None
130
+ biases: float | None
131
+
132
+
133
+ class SteeringInfo(_Model):
134
+ """Which prompts trained the steering directions and which measured them, and how long each
135
+ site's direction is (one per row of the layout)."""
136
+
137
+ train: list[int]
138
+ test: list[int]
139
+ norms: list[float]
140
+
141
+
142
+ class FeatureRun(_Model):
143
+ """Runs on SAE features: the SAE, how well it fits these prompts, and for attribution
144
+ patching how much of the site's estimated effect its features account for."""
145
+
146
+ sae: dict[str, Any]
147
+ fit: dict[str, Any]
148
+ site_estimate: float | None = None
149
+ features_estimate: float | None = None
150
+ evaluated: int | None = None
151
+
152
+
153
+ class Summary(_Model):
154
+ logogram_summary: Literal[1] = 1
155
+ run_id: str
156
+ name: str
157
+ description: str
158
+ model: ModelShape | None
159
+ n_prompts: int
160
+ n_sites: int
161
+ layout: Layout
162
+ receiver: Literal["clean", "corrupt"]
163
+ reference: Literal["clean", "corrupt"]
164
+ # What each per-prompt value is: a patched run, an estimate of one, or a term of a split.
165
+ measure: Literal["intervention", "estimate", "attribution"] = "intervention"
166
+ metric: MetricInfo
167
+ statistics: StatisticsInfo
168
+ baseline: BaselineSummary
169
+ sites: list[SiteSummary]
170
+ per_prompt: dict[str, list[float | None]]
171
+ donors: list[list[int]] | None
172
+ warnings: list[str]
173
+ direct: DirectSplit | None = None
174
+ steering: SteeringInfo | None = None
175
+ features: FeatureRun | None = None
176
+
177
+
178
+ # -- manifest.json -----------------------------------------------------------------------------
179
+
180
+
181
+ class ModelProvenance(_Model):
182
+ id: str
183
+ revision: str | None
184
+ architecture: str
185
+ process_weights: bool
186
+ backend: str
187
+ backend_version: str
188
+
189
+
190
+ class DeviceInfo(_Model):
191
+ type: str
192
+ name: str
193
+ cuda: str | None
194
+ threads: int
195
+ deterministic_algorithms: bool
196
+
197
+
198
+ class DatasetProvenance(_Model):
199
+ path: str
200
+ sha256: str | None
201
+ n: int
202
+ source_path: str | None = None
203
+
204
+
205
+ class DerivedFrom(_Model):
206
+ run: str
207
+ # robustness: one methodological choice changed; rerun: the same spec again; verification:
208
+ # the strongest estimated sites of an attribution patching run, patched for real.
209
+ kind: Literal["robustness", "rerun", "verification"]
210
+ change: str | None = None
211
+
212
+
213
+ class Manifest(_Model):
214
+ logogram_manifest: Literal[1] = 1
215
+ run_id: str
216
+ status: Literal["running", "finished", "failed", "cancelled"]
217
+ started_at: str
218
+ finished_at: str | None = None
219
+ wall_time_s: float | None = None
220
+ error: str | None = None
221
+ versions: dict[str, str | None]
222
+ derived_from: DerivedFrom | None = None
223
+ model: ModelProvenance | None = None
224
+ device: DeviceInfo | None = None
225
+ dtype: str | None = None
226
+ dataset: DatasetProvenance | None = None
227
+ sae: dict[str, Any] | None = None
228
+
229
+
230
+ # -- live events (WebSocket) -------------------------------------------------------------------
231
+
232
+
233
+ class RunStarted(_Model):
234
+ run_id: str
235
+ n_prompts: int
236
+ model: ModelShape
237
+ layout: Layout
238
+ sites: list[SiteRef]
239
+
240
+
241
+ class RunLayer(_Model):
242
+ run_id: str
243
+ layer: int
244
+ sites: list[SiteSummary]
245
+
246
+
247
+ class RunProgress(_Model):
248
+ run_id: str
249
+ done: int
250
+ total: int
251
+ layer: int
252
+ memory: int | None
253
+ elapsed_s: float
254
+
255
+
256
+ class RunEnded(_Model):
257
+ run_id: str
258
+ error: str | None = None
259
+
260
+
261
+ EVENT_SCHEMAS: dict[str, type[BaseModel]] = {
262
+ "run.started": RunStarted,
263
+ "run.layer": RunLayer,
264
+ "run.progress": RunProgress,
265
+ "run.finished": RunEnded,
266
+ "run.failed": RunEnded,
267
+ "run.cancelled": RunEnded,
268
+ }
269
+
270
+
271
+ def validate_event(kind: str, data: dict[str, Any]) -> dict[str, Any]:
272
+ """Check a run event against its schema before it is sent."""
273
+ schema = EVENT_SCHEMAS.get(kind)
274
+ if schema is None:
275
+ return data
276
+ return schema.model_validate(data).model_dump(mode="json")
277
+
278
+
279
+ # -- history -----------------------------------------------------------------------------------
280
+
281
+
282
+ class RunListing(_Model):
283
+ id: str
284
+ name: str
285
+ description: str
286
+ status: Literal["finished", "failed", "cancelled", "draft", "running"]
287
+ created: str | None
288
+ finished: str | None
289
+ wall_time_s: float | None
290
+ n_prompts: int | None
291
+ layout_kind: str | None
292
+ model_id: str
293
+ dataset: str
294
+ experiment: dict[str, Any]
295
+ scope: dict[str, Any]
296
+ derived_from: DerivedFrom | None
297
+ error: str | None
298
+ # Per layer, the largest normalized effect (signed): what writes the run's logogram in the app.
299
+ profile: list[float] | None = None
300
+
301
+ def to_dict(self) -> dict[str, Any]:
302
+ return self.model_dump(mode="json")
@@ -0,0 +1 @@
1
+ """The local web server."""