transcript-viewer 0.8.1__tar.gz → 0.10.0__tar.gz
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.
- {transcript_viewer-0.8.1 → transcript_viewer-0.10.0}/PKG-INFO +9 -3
- {transcript_viewer-0.8.1 → transcript_viewer-0.10.0}/README.md +7 -1
- {transcript_viewer-0.8.1 → transcript_viewer-0.10.0}/pyproject.toml +2 -2
- {transcript_viewer-0.8.1 → transcript_viewer-0.10.0}/src/transcript_viewer/corpus.py +2 -0
- {transcript_viewer-0.8.1 → transcript_viewer-0.10.0}/src/transcript_viewer/fetch.py +14 -4
- {transcript_viewer-0.8.1 → transcript_viewer-0.10.0}/src/transcript_viewer/page.html +6 -0
- {transcript_viewer-0.8.1 → transcript_viewer-0.10.0}/tests/test_fetch.py +28 -6
- {transcript_viewer-0.8.1 → transcript_viewer-0.10.0}/uv.lock +5 -5
- {transcript_viewer-0.8.1 → transcript_viewer-0.10.0}/.coverage +0 -0
- {transcript_viewer-0.8.1 → transcript_viewer-0.10.0}/.github/workflows/publish.yml +0 -0
- {transcript_viewer-0.8.1 → transcript_viewer-0.10.0}/.github/workflows/test.yml +0 -0
- {transcript_viewer-0.8.1 → transcript_viewer-0.10.0}/.gitignore +0 -0
- {transcript_viewer-0.8.1 → transcript_viewer-0.10.0}/src/transcript_viewer/__init__.py +0 -0
- {transcript_viewer-0.8.1 → transcript_viewer-0.10.0}/src/transcript_viewer/ai.py +0 -0
- {transcript_viewer-0.8.1 → transcript_viewer-0.10.0}/src/transcript_viewer/cli.py +0 -0
- {transcript_viewer-0.8.1 → transcript_viewer-0.10.0}/src/transcript_viewer/config.py +0 -0
- {transcript_viewer-0.8.1 → transcript_viewer-0.10.0}/src/transcript_viewer/library.py +0 -0
- {transcript_viewer-0.8.1 → transcript_viewer-0.10.0}/src/transcript_viewer/store.py +0 -0
- {transcript_viewer-0.8.1 → transcript_viewer-0.10.0}/src/transcript_viewer/viewer.py +0 -0
- {transcript_viewer-0.8.1 → transcript_viewer-0.10.0}/tests/fixtures/big-command.json +0 -0
- {transcript_viewer-0.8.1 → transcript_viewer-0.10.0}/tests/page.test.js +0 -0
- {transcript_viewer-0.8.1 → transcript_viewer-0.10.0}/tests/test_ai.py +0 -0
- {transcript_viewer-0.8.1 → transcript_viewer-0.10.0}/tests/test_cli.py +0 -0
- {transcript_viewer-0.8.1 → transcript_viewer-0.10.0}/tests/test_config.py +0 -0
- {transcript_viewer-0.8.1 → transcript_viewer-0.10.0}/tests/test_corpus.py +0 -0
- {transcript_viewer-0.8.1 → transcript_viewer-0.10.0}/tests/test_library.py +0 -0
- {transcript_viewer-0.8.1 → transcript_viewer-0.10.0}/tests/test_page.py +0 -0
- {transcript_viewer-0.8.1 → transcript_viewer-0.10.0}/tests/test_readme.py +0 -0
- {transcript_viewer-0.8.1 → transcript_viewer-0.10.0}/tests/test_store.py +0 -0
- {transcript_viewer-0.8.1 → transcript_viewer-0.10.0}/tests/test_stream.py +0 -0
- {transcript_viewer-0.8.1 → transcript_viewer-0.10.0}/tests/test_style.py +0 -0
- {transcript_viewer-0.8.1 → transcript_viewer-0.10.0}/tests/test_tidiness.py +0 -0
- {transcript_viewer-0.8.1 → transcript_viewer-0.10.0}/tests/test_viewer.py +0 -0
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: transcript-viewer
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.10.0
|
|
4
4
|
Summary: Browse agent transcripts in a local, dependency-free web viewer.
|
|
5
5
|
License: MIT
|
|
6
6
|
Requires-Python: >=3.12
|
|
7
|
-
Requires-Dist: atif-make>=0.
|
|
7
|
+
Requires-Dist: atif-make>=0.7.0
|
|
8
8
|
Provides-Extra: ai
|
|
9
9
|
Requires-Dist: anthropic>=0.40; extra == 'ai'
|
|
10
10
|
Provides-Extra: parquet
|
|
@@ -162,7 +162,13 @@ of yours, keeps its file.
|
|
|
162
162
|
|
|
163
163
|
Some datasets ship as a single file holding many runs — ATBench publishes a
|
|
164
164
|
thousand agent trajectories as one JSON array, METR's as Parquet shards
|
|
165
|
-
(install with `uv tool install "transcript-viewer[parquet]"` to read those)
|
|
165
|
+
(install with `uv tool install "transcript-viewer[parquet]"` to read those),
|
|
166
|
+
and an Inspect `.eval` log holds one run per sample.
|
|
167
|
+
|
|
168
|
+
A fetch takes at most 32 GB at once. That is a backstop, not a judgement:
|
|
169
|
+
published corpora are routinely several gigabytes, and what actually guards
|
|
170
|
+
against an accident is the picker, which measures what you have ticked and
|
|
171
|
+
shows it before anything downloads. Those are opened rather than
|
|
166
172
|
listed: the file is split into a transcript apiece and each is indexed on its
|
|
167
173
|
own, so a download of one file becomes a thousand sessions you can read. The
|
|
168
174
|
pieces are kept beside what they came from, so removing the folder removes them
|
|
@@ -149,7 +149,13 @@ of yours, keeps its file.
|
|
|
149
149
|
|
|
150
150
|
Some datasets ship as a single file holding many runs — ATBench publishes a
|
|
151
151
|
thousand agent trajectories as one JSON array, METR's as Parquet shards
|
|
152
|
-
(install with `uv tool install "transcript-viewer[parquet]"` to read those)
|
|
152
|
+
(install with `uv tool install "transcript-viewer[parquet]"` to read those),
|
|
153
|
+
and an Inspect `.eval` log holds one run per sample.
|
|
154
|
+
|
|
155
|
+
A fetch takes at most 32 GB at once. That is a backstop, not a judgement:
|
|
156
|
+
published corpora are routinely several gigabytes, and what actually guards
|
|
157
|
+
against an accident is the picker, which measures what you have ticked and
|
|
158
|
+
shows it before anything downloads. Those are opened rather than
|
|
153
159
|
listed: the file is split into a transcript apiece and each is indexed on its
|
|
154
160
|
own, so a download of one file becomes a thousand sessions you can read. The
|
|
155
161
|
pieces are kept beside what they came from, so removing the folder removes them
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "transcript-viewer"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.10.0"
|
|
4
4
|
description = "Browse agent transcripts in a local, dependency-free web viewer."
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.12"
|
|
@@ -10,7 +10,7 @@ license = { text = "MIT" }
|
|
|
10
10
|
# The floor is not politeness: this viewer imports atif_make.container, which
|
|
11
11
|
# is how a benchmark published as one JSON array of a thousand runs becomes a
|
|
12
12
|
# thousand sessions. An older atif-make fails to import at all.
|
|
13
|
-
dependencies = ["atif-make>=0.
|
|
13
|
+
dependencies = ["atif-make>=0.7.0"]
|
|
14
14
|
|
|
15
15
|
[project.optional-dependencies]
|
|
16
16
|
# Claude-backed explanations are opt-in, so the default install stays
|
|
@@ -312,8 +312,10 @@ def scan(roots: list[Path] | None = None, origin: str = "scanned") -> list[Entry
|
|
|
312
312
|
sorted(where.rglob("*.jsonl"))
|
|
313
313
|
+ sorted(where.rglob("*.har"))
|
|
314
314
|
+ sorted(where.rglob("*.json"))
|
|
315
|
+
+ sorted(where.rglob("*.md"))
|
|
315
316
|
# Not a log, but a container of them, opened above.
|
|
316
317
|
+ sorted(where.rglob("*.parquet"))
|
|
318
|
+
+ sorted(where.rglob("*.eval"))
|
|
317
319
|
)
|
|
318
320
|
for path in candidates:
|
|
319
321
|
resolved = path.resolve()
|
|
@@ -58,10 +58,13 @@ HOSTS = {
|
|
|
58
58
|
# in: a bucket of agent transcripts is far more likely to hold one zip per
|
|
59
59
|
# session than loose JSONL, so excluding archives made S3 useless for exactly
|
|
60
60
|
# the case it was added for.
|
|
61
|
-
|
|
61
|
+
# A transcript is not always JSON: some corpora ship the readable rendering.
|
|
62
|
+
LOGS = (".jsonl", ".json", ".har", ".md", ".markdown")
|
|
62
63
|
# A published dataset is a container of runs rather than a log, but it is still
|
|
63
64
|
# a thing worth fetching: METR's transcripts come as Parquet shards.
|
|
64
|
-
|
|
65
|
+
# An Inspect .eval is a zip of JSON, and a corpus of them is where most
|
|
66
|
+
# control research keeps its transcripts.
|
|
67
|
+
DATASETS = (".parquet", ".eval")
|
|
65
68
|
ARCHIVES = (".zip", ".tgz", ".tar", ".tar.gz", ".tar.bz2", ".tar.xz", ".gz")
|
|
66
69
|
SUFFIXES = LOGS + DATASETS + ARCHIVES
|
|
67
70
|
|
|
@@ -87,7 +90,13 @@ AWS_TIMEOUT = 300
|
|
|
87
90
|
BROWSE_LIMIT = 3_000
|
|
88
91
|
|
|
89
92
|
MAX_FILES = 2_000
|
|
90
|
-
|
|
93
|
+
# A ceiling on one fetch, not a judgement about what is worth having: published
|
|
94
|
+
# corpora are routinely several gigabytes — MonitoringBench ships as a single
|
|
95
|
+
# 2.5 GB archive — and two was refusing them. What still guards against an
|
|
96
|
+
# accident is the picker, which measures a selection and shows it before
|
|
97
|
+
# anything downloads; this is the backstop for the bucket nobody meant to take
|
|
98
|
+
# whole, which in practice is the 144 GB one.
|
|
99
|
+
MAX_TOTAL_BYTES = 32 * 1024**3
|
|
91
100
|
TIMEOUT = 60
|
|
92
101
|
|
|
93
102
|
|
|
@@ -578,7 +587,8 @@ def _sized(plan: "Plan") -> "Plan":
|
|
|
578
587
|
if known > MAX_TOTAL_BYTES:
|
|
579
588
|
raise FetchError(
|
|
580
589
|
f"That is {known / 1024**3:.1f} GB, over the "
|
|
581
|
-
f"{MAX_TOTAL_BYTES // 1024**3} GB limit."
|
|
590
|
+
f"{MAX_TOTAL_BYTES // 1024**3} GB limit for one fetch. "
|
|
591
|
+
f"Pick fewer folders, or take it in parts."
|
|
582
592
|
)
|
|
583
593
|
return plan
|
|
584
594
|
|
|
@@ -1728,6 +1728,12 @@ const EXAMPLES=[
|
|
|
1728
1728
|
{name:"Redwood agent transcripts",
|
|
1729
1729
|
url:"s3://rr-agent-transcripts",
|
|
1730
1730
|
note:"needs an AWS profile set in Settings"},
|
|
1731
|
+
{name:"Redwood sabotage trajectories",
|
|
1732
|
+
url:"s3://rr-strajs",
|
|
1733
|
+
note:"136 transcripts, honest and attack in pairs — needs an AWS profile"},
|
|
1734
|
+
{name:"MonitoringBench",
|
|
1735
|
+
url:"https://huggingface.co/datasets/neur26anonsub/ctrldataset2026",
|
|
1736
|
+
note:"2,644 attack runs as Inspect logs — the archive alone is 2.5 GB"},
|
|
1731
1737
|
];
|
|
1732
1738
|
|
|
1733
1739
|
function drawExamples(){
|
|
@@ -215,7 +215,11 @@ def test_a_dataset_url_lists_its_convertible_files(monkeypatch):
|
|
|
215
215
|
)
|
|
216
216
|
assert service == "hf"
|
|
217
217
|
assert label == "owner--name"
|
|
218
|
-
assert [f.name for f in files] == [
|
|
218
|
+
assert [f.name for f in files] == [
|
|
219
|
+
"a/transcript.jsonl",
|
|
220
|
+
"a/description.md",
|
|
221
|
+
"b/metadata.json",
|
|
222
|
+
]
|
|
219
223
|
assert all("/resolve/main/" in f.url for f in files)
|
|
220
224
|
assert seen["auth"] == ["tok"], "the token was not sent"
|
|
221
225
|
|
|
@@ -310,11 +314,23 @@ def test_a_blob_url_is_rewritten_to_raw(monkeypatch):
|
|
|
310
314
|
|
|
311
315
|
|
|
312
316
|
def test_nothing_convertible_is_said_plainly(monkeypatch):
|
|
313
|
-
|
|
317
|
+
# Markdown is fetchable — some corpora publish transcripts as nothing else —
|
|
318
|
+
# so the refusal is tested with something that could not be one.
|
|
319
|
+
_stub(monkeypatch, {"api/datasets": [{"type": "file", "path": "diagram.png"}]})
|
|
314
320
|
with pytest.raises(fetch.FetchError, match="Nothing convertible"):
|
|
315
321
|
fetch.plan("https://huggingface.co/datasets/owner/name", {})
|
|
316
322
|
|
|
317
323
|
|
|
324
|
+
def test_a_transcript_published_as_markdown_is_worth_fetching(monkeypatch):
|
|
325
|
+
"""A bucket of 136 of these was unreachable while .md was filtered out."""
|
|
326
|
+
_stub(
|
|
327
|
+
monkeypatch,
|
|
328
|
+
{"api/datasets": [{"type": "file", "path": "run/transcript_attack.md"}]},
|
|
329
|
+
)
|
|
330
|
+
plan = fetch.plan("https://huggingface.co/datasets/owner/name", {})
|
|
331
|
+
assert [f.name for f in plan.files] == ["run/transcript_attack.md"]
|
|
332
|
+
|
|
333
|
+
|
|
318
334
|
def test_too_many_files_is_refused_before_downloading(monkeypatch):
|
|
319
335
|
_stub(
|
|
320
336
|
monkeypatch,
|
|
@@ -489,7 +505,7 @@ def test_an_s3_url_lists_what_is_convertible(monkeypatch):
|
|
|
489
505
|
plan = fetch.plan("s3://rr-agent-transcripts/runs", {"aws": "rw-eng"})
|
|
490
506
|
assert plan.service == "s3"
|
|
491
507
|
assert plan.label == "rr-agent-transcripts"
|
|
492
|
-
assert [f.name for f in plan.files] == ["a.jsonl", "deep/b.jsonl"]
|
|
508
|
+
assert [f.name for f in plan.files] == ["a.jsonl", "notes.md", "deep/b.jsonl"]
|
|
493
509
|
assert plan.web == "s3://rr-agent-transcripts/runs"
|
|
494
510
|
|
|
495
511
|
|
|
@@ -604,7 +620,10 @@ def test_an_archive_counts_as_something_worth_fetching(monkeypatch):
|
|
|
604
620
|
]
|
|
605
621
|
}))
|
|
606
622
|
plan = fetch.plan("s3://rr-agent-transcripts/chippy/abc", {"aws": "rw-eng"})
|
|
607
|
-
|
|
623
|
+
# The Markdown comes too: a corpus that publishes transcripts as Markdown
|
|
624
|
+
# and nothing else was unreachable while it was filtered out. One that is
|
|
625
|
+
# not a transcript simply does not index.
|
|
626
|
+
assert [f.name for f in plan.files] == ["transcripts.zip", "notes.md"]
|
|
608
627
|
|
|
609
628
|
|
|
610
629
|
def test_the_listing_is_capped_rather_than_exhaustive(monkeypatch):
|
|
@@ -650,7 +669,8 @@ def test_s3_browsing_returns_folders_and_files(monkeypatch):
|
|
|
650
669
|
nodes = fetch.browse("s3://bucket", "chippy", {"aws": "rw-eng"})
|
|
651
670
|
assert "--delimiter" in seen["command"]
|
|
652
671
|
assert [(n.kind, n.name) for n in nodes] == [
|
|
653
|
-
("folder", "abc"), ("folder", "def"),
|
|
672
|
+
("folder", "abc"), ("folder", "def"),
|
|
673
|
+
("file", "loose.zip"), ("file", "readme.md"),
|
|
654
674
|
]
|
|
655
675
|
assert [n.path for n in nodes][0] == "chippy/abc"
|
|
656
676
|
|
|
@@ -667,7 +687,9 @@ def test_github_browsing_reads_one_level(monkeypatch):
|
|
|
667
687
|
{"type": "file", "path": "logs/notes.md", "size": 3},
|
|
668
688
|
]})
|
|
669
689
|
nodes = fetch.browse("https://github.com/owner/name/tree/main/logs", "", {})
|
|
670
|
-
assert [(n.kind, n.name) for n in nodes] == [
|
|
690
|
+
assert [(n.kind, n.name) for n in nodes] == [
|
|
691
|
+
("folder", "inner"), ("file", "a.jsonl"), ("file", "notes.md"),
|
|
692
|
+
]
|
|
671
693
|
|
|
672
694
|
|
|
673
695
|
def test_hf_browsing_reads_one_level(monkeypatch):
|
|
@@ -44,11 +44,11 @@ wheels = [
|
|
|
44
44
|
|
|
45
45
|
[[package]]
|
|
46
46
|
name = "atif-make"
|
|
47
|
-
version = "0.
|
|
47
|
+
version = "0.7.0"
|
|
48
48
|
source = { registry = "https://pypi.org/simple" }
|
|
49
|
-
sdist = { url = "https://files.pythonhosted.org/packages/
|
|
49
|
+
sdist = { url = "https://files.pythonhosted.org/packages/6a/ed/b1fbadcc65d59186442517059beffe7c4a590be85d17308d9143278a2104/atif_make-0.7.0.tar.gz", hash = "sha256:858966e102e772f35ffc3fdcd25a11cb52759190233b41cead3878dc607ff4ce", size = 350496, upload-time = "2026-08-24T21:49:42.323Z" }
|
|
50
50
|
wheels = [
|
|
51
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
51
|
+
{ url = "https://files.pythonhosted.org/packages/14/7b/d135b4b2bec98599cb65166f278487f87d9842492c2f13e8a1385c6a3e85/atif_make-0.7.0-py3-none-any.whl", hash = "sha256:ef1601b5d2ac3e07b9e9e97ee0a2e0856b953b4ba317633e6f590747b450659e", size = 63987, upload-time = "2026-08-24T21:49:41.087Z" },
|
|
52
52
|
]
|
|
53
53
|
|
|
54
54
|
[package.optional-dependencies]
|
|
@@ -388,7 +388,7 @@ wheels = [
|
|
|
388
388
|
|
|
389
389
|
[[package]]
|
|
390
390
|
name = "transcript-viewer"
|
|
391
|
-
version = "0.
|
|
391
|
+
version = "0.10.0"
|
|
392
392
|
source = { editable = "." }
|
|
393
393
|
dependencies = [
|
|
394
394
|
{ name = "atif-make" },
|
|
@@ -410,7 +410,7 @@ dev = [
|
|
|
410
410
|
[package.metadata]
|
|
411
411
|
requires-dist = [
|
|
412
412
|
{ name = "anthropic", marker = "extra == 'ai'", specifier = ">=0.40" },
|
|
413
|
-
{ name = "atif-make", specifier = ">=0.
|
|
413
|
+
{ name = "atif-make", specifier = ">=0.7.0" },
|
|
414
414
|
{ name = "atif-make", extras = ["parquet"], marker = "extra == 'parquet'", specifier = ">=0.5.0" },
|
|
415
415
|
]
|
|
416
416
|
provides-extras = ["parquet", "ai"]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|