cc-transcript 13.0.0__tar.gz → 13.2.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.
- {cc_transcript-13.0.0 → cc_transcript-13.2.0}/Cargo.lock +186 -0
- {cc_transcript-13.0.0 → cc_transcript-13.2.0}/PKG-INFO +1 -1
- {cc_transcript-13.0.0 → cc_transcript-13.2.0}/cc_transcript/__init__.py +1 -0
- {cc_transcript-13.0.0 → cc_transcript-13.2.0}/cc_transcript/command.py +134 -2
- cc_transcript-13.2.0/cc_transcript/heartbeats.py +117 -0
- {cc_transcript-13.0.0 → cc_transcript-13.2.0}/pyproject.toml +1 -1
- {cc_transcript-13.0.0 → cc_transcript-13.2.0}/rust/Cargo.toml +10 -0
- cc_transcript-13.2.0/rust/benches/parse.rs +77 -0
- {cc_transcript-13.0.0 → cc_transcript-13.2.0}/rust/src/lib.rs +1 -1
- {cc_transcript-13.0.0 → cc_transcript-13.2.0}/Cargo.toml +0 -0
- {cc_transcript-13.0.0 → cc_transcript-13.2.0}/LICENSE +0 -0
- {cc_transcript-13.0.0 → cc_transcript-13.2.0}/README.md +0 -0
- {cc_transcript-13.0.0 → cc_transcript-13.2.0}/cc_transcript/__main__.py +0 -0
- {cc_transcript-13.0.0 → cc_transcript-13.2.0}/cc_transcript/_parser_rs.pyi +0 -0
- {cc_transcript-13.0.0 → cc_transcript-13.2.0}/cc_transcript/activity.py +0 -0
- {cc_transcript-13.0.0 → cc_transcript-13.2.0}/cc_transcript/activity_probe.py +0 -0
- {cc_transcript-13.0.0 → cc_transcript-13.2.0}/cc_transcript/backend.py +0 -0
- {cc_transcript-13.0.0 → cc_transcript-13.2.0}/cc_transcript/builders.py +0 -0
- {cc_transcript-13.0.0 → cc_transcript-13.2.0}/cc_transcript/cli.py +0 -0
- {cc_transcript-13.0.0 → cc_transcript-13.2.0}/cc_transcript/context.py +0 -0
- {cc_transcript-13.0.0 → cc_transcript-13.2.0}/cc_transcript/corrections.py +0 -0
- {cc_transcript-13.0.0 → cc_transcript-13.2.0}/cc_transcript/corrections_cli.py +0 -0
- {cc_transcript-13.0.0 → cc_transcript-13.2.0}/cc_transcript/cost.py +0 -0
- {cc_transcript-13.0.0 → cc_transcript-13.2.0}/cc_transcript/decisions.py +0 -0
- {cc_transcript-13.0.0 → cc_transcript-13.2.0}/cc_transcript/discovery.py +0 -0
- {cc_transcript-13.0.0 → cc_transcript-13.2.0}/cc_transcript/disktruth.py +0 -0
- {cc_transcript-13.0.0 → cc_transcript-13.2.0}/cc_transcript/evidence.py +0 -0
- {cc_transcript-13.0.0 → cc_transcript-13.2.0}/cc_transcript/extract/__init__.py +0 -0
- {cc_transcript-13.0.0 → cc_transcript-13.2.0}/cc_transcript/extract/correct.py +0 -0
- {cc_transcript-13.0.0 → cc_transcript-13.2.0}/cc_transcript/facts.py +0 -0
- {cc_transcript-13.0.0 → cc_transcript-13.2.0}/cc_transcript/filterspec.py +0 -0
- {cc_transcript-13.0.0 → cc_transcript-13.2.0}/cc_transcript/ids.py +0 -0
- {cc_transcript-13.0.0 → cc_transcript-13.2.0}/cc_transcript/judge/__init__.py +0 -0
- {cc_transcript-13.0.0 → cc_transcript-13.2.0}/cc_transcript/judge/llm.py +0 -0
- {cc_transcript-13.0.0 → cc_transcript-13.2.0}/cc_transcript/judge/similar.py +0 -0
- {cc_transcript-13.0.0 → cc_transcript-13.2.0}/cc_transcript/judge/verdicts.py +0 -0
- {cc_transcript-13.0.0 → cc_transcript-13.2.0}/cc_transcript/ledger.py +0 -0
- {cc_transcript-13.0.0 → cc_transcript-13.2.0}/cc_transcript/mining/__init__.py +0 -0
- {cc_transcript-13.0.0 → cc_transcript-13.2.0}/cc_transcript/mining/candidates.py +0 -0
- {cc_transcript-13.0.0 → cc_transcript-13.2.0}/cc_transcript/mining/confidence.py +0 -0
- {cc_transcript-13.0.0 → cc_transcript-13.2.0}/cc_transcript/mining/engine.py +0 -0
- {cc_transcript-13.0.0 → cc_transcript-13.2.0}/cc_transcript/mining/filterspec.py +0 -0
- {cc_transcript-13.0.0 → cc_transcript-13.2.0}/cc_transcript/mining/formats.py +0 -0
- {cc_transcript-13.0.0 → cc_transcript-13.2.0}/cc_transcript/mining/sampling.py +0 -0
- {cc_transcript-13.0.0 → cc_transcript-13.2.0}/cc_transcript/mining/signals.py +0 -0
- {cc_transcript-13.0.0 → cc_transcript-13.2.0}/cc_transcript/mining/sourcekind.py +0 -0
- {cc_transcript-13.0.0 → cc_transcript-13.2.0}/cc_transcript/mining/spec.py +0 -0
- {cc_transcript-13.0.0 → cc_transcript-13.2.0}/cc_transcript/mining/store.py +0 -0
- {cc_transcript-13.0.0 → cc_transcript-13.2.0}/cc_transcript/models.py +0 -0
- {cc_transcript-13.0.0 → cc_transcript-13.2.0}/cc_transcript/nlp.py +0 -0
- {cc_transcript-13.0.0 → cc_transcript-13.2.0}/cc_transcript/notifications.py +0 -0
- {cc_transcript-13.0.0 → cc_transcript-13.2.0}/cc_transcript/parser.py +0 -0
- {cc_transcript-13.0.0 → cc_transcript-13.2.0}/cc_transcript/py.typed +0 -0
- {cc_transcript-13.0.0 → cc_transcript-13.2.0}/cc_transcript/query.py +0 -0
- {cc_transcript-13.0.0 → cc_transcript-13.2.0}/cc_transcript/render.py +0 -0
- {cc_transcript-13.0.0 → cc_transcript-13.2.0}/cc_transcript/sentiment/__init__.py +0 -0
- {cc_transcript-13.0.0 → cc_transcript-13.2.0}/cc_transcript/sentiment/buckets.py +0 -0
- {cc_transcript-13.0.0 → cc_transcript-13.2.0}/cc_transcript/sentiment/data/afinn-en-165.tsv +0 -0
- {cc_transcript-13.0.0 → cc_transcript-13.2.0}/cc_transcript/sentiment/data/domain_overrides.tsv +0 -0
- {cc_transcript-13.0.0 → cc_transcript-13.2.0}/cc_transcript/sentiment/data/en-ewt.udpipe +0 -0
- {cc_transcript-13.0.0 → cc_transcript-13.2.0}/cc_transcript/sentiment/engine.py +0 -0
- {cc_transcript-13.0.0 → cc_transcript-13.2.0}/cc_transcript/sentiment/lexicon.py +0 -0
- {cc_transcript-13.0.0 → cc_transcript-13.2.0}/cc_transcript/sentiment/scorespec.py +0 -0
- {cc_transcript-13.0.0 → cc_transcript-13.2.0}/cc_transcript/store.py +0 -0
- {cc_transcript-13.0.0 → cc_transcript-13.2.0}/cc_transcript/tools.py +0 -0
- {cc_transcript-13.0.0 → cc_transcript-13.2.0}/cc_transcript/watch.py +0 -0
- {cc_transcript-13.0.0 → cc_transcript-13.2.0}/rust/build.rs +0 -0
- {cc_transcript-13.0.0 → cc_transcript-13.2.0}/rust/data/command_prefix_pins.tsv +0 -0
- {cc_transcript-13.0.0 → cc_transcript-13.2.0}/rust/rustfmt.toml +0 -0
- {cc_transcript-13.0.0 → cc_transcript-13.2.0}/rust/src/activity.rs +0 -0
- {cc_transcript-13.0.0 → cc_transcript-13.2.0}/rust/src/command.rs +0 -0
- {cc_transcript-13.0.0 → cc_transcript-13.2.0}/rust/src/event.rs +0 -0
- {cc_transcript-13.0.0 → cc_transcript-13.2.0}/rust/src/filter.rs +0 -0
- {cc_transcript-13.0.0 → cc_transcript-13.2.0}/rust/src/generated/command.rs +0 -0
- {cc_transcript-13.0.0 → cc_transcript-13.2.0}/rust/src/generated/mining.rs +0 -0
- {cc_transcript-13.0.0 → cc_transcript-13.2.0}/rust/src/generated/mod.rs +0 -0
- {cc_transcript-13.0.0 → cc_transcript-13.2.0}/rust/src/generated/protocol.rs +0 -0
- {cc_transcript-13.0.0 → cc_transcript-13.2.0}/rust/src/lexicon.rs +0 -0
- {cc_transcript-13.0.0 → cc_transcript-13.2.0}/rust/src/mining.rs +0 -0
- {cc_transcript-13.0.0 → cc_transcript-13.2.0}/rust/src/model.rs +0 -0
- {cc_transcript-13.0.0 → cc_transcript-13.2.0}/rust/src/nlp.rs +0 -0
- {cc_transcript-13.0.0 → cc_transcript-13.2.0}/rust/src/parse.rs +0 -0
- {cc_transcript-13.0.0 → cc_transcript-13.2.0}/rust/src/protocol.rs +0 -0
- {cc_transcript-13.0.0 → cc_transcript-13.2.0}/rust/src/python.rs +0 -0
- {cc_transcript-13.0.0 → cc_transcript-13.2.0}/rust/src/score.rs +0 -0
- {cc_transcript-13.0.0 → cc_transcript-13.2.0}/rust/src/types.rs +0 -0
- {cc_transcript-13.0.0 → cc_transcript-13.2.0}/rust/src/value.rs +0 -0
|
@@ -30,6 +30,18 @@ dependencies = [
|
|
|
30
30
|
"memchr",
|
|
31
31
|
]
|
|
32
32
|
|
|
33
|
+
[[package]]
|
|
34
|
+
name = "anes"
|
|
35
|
+
version = "0.1.6"
|
|
36
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
37
|
+
checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299"
|
|
38
|
+
|
|
39
|
+
[[package]]
|
|
40
|
+
name = "anstyle"
|
|
41
|
+
version = "1.0.14"
|
|
42
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
43
|
+
checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000"
|
|
44
|
+
|
|
33
45
|
[[package]]
|
|
34
46
|
name = "autocfg"
|
|
35
47
|
version = "1.5.1"
|
|
@@ -60,6 +72,12 @@ version = "1.11.1"
|
|
|
60
72
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
61
73
|
checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33"
|
|
62
74
|
|
|
75
|
+
[[package]]
|
|
76
|
+
name = "cast"
|
|
77
|
+
version = "0.3.0"
|
|
78
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
79
|
+
checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5"
|
|
80
|
+
|
|
63
81
|
[[package]]
|
|
64
82
|
name = "cc"
|
|
65
83
|
version = "1.2.63"
|
|
@@ -75,6 +93,7 @@ name = "cc_transcript_parser"
|
|
|
75
93
|
version = "0.9.0"
|
|
76
94
|
dependencies = [
|
|
77
95
|
"chrono",
|
|
96
|
+
"criterion",
|
|
78
97
|
"crossbeam-channel",
|
|
79
98
|
"memchr",
|
|
80
99
|
"num_cpus",
|
|
@@ -113,6 +132,58 @@ dependencies = [
|
|
|
113
132
|
"num-traits",
|
|
114
133
|
]
|
|
115
134
|
|
|
135
|
+
[[package]]
|
|
136
|
+
name = "ciborium"
|
|
137
|
+
version = "0.2.2"
|
|
138
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
139
|
+
checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e"
|
|
140
|
+
dependencies = [
|
|
141
|
+
"ciborium-io",
|
|
142
|
+
"ciborium-ll",
|
|
143
|
+
"serde",
|
|
144
|
+
]
|
|
145
|
+
|
|
146
|
+
[[package]]
|
|
147
|
+
name = "ciborium-io"
|
|
148
|
+
version = "0.2.2"
|
|
149
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
150
|
+
checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757"
|
|
151
|
+
|
|
152
|
+
[[package]]
|
|
153
|
+
name = "ciborium-ll"
|
|
154
|
+
version = "0.2.2"
|
|
155
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
156
|
+
checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9"
|
|
157
|
+
dependencies = [
|
|
158
|
+
"ciborium-io",
|
|
159
|
+
"half",
|
|
160
|
+
]
|
|
161
|
+
|
|
162
|
+
[[package]]
|
|
163
|
+
name = "clap"
|
|
164
|
+
version = "4.6.1"
|
|
165
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
166
|
+
checksum = "1ddb117e43bbf7dacf0a4190fef4d345b9bad68dfc649cb349e7d17d28428e51"
|
|
167
|
+
dependencies = [
|
|
168
|
+
"clap_builder",
|
|
169
|
+
]
|
|
170
|
+
|
|
171
|
+
[[package]]
|
|
172
|
+
name = "clap_builder"
|
|
173
|
+
version = "4.6.0"
|
|
174
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
175
|
+
checksum = "714a53001bf66416adb0e2ef5ac857140e7dc3a0c48fb28b2f10762fc4b5069f"
|
|
176
|
+
dependencies = [
|
|
177
|
+
"anstyle",
|
|
178
|
+
"clap_lex",
|
|
179
|
+
]
|
|
180
|
+
|
|
181
|
+
[[package]]
|
|
182
|
+
name = "clap_lex"
|
|
183
|
+
version = "1.1.0"
|
|
184
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
185
|
+
checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9"
|
|
186
|
+
|
|
116
187
|
[[package]]
|
|
117
188
|
name = "crc32fast"
|
|
118
189
|
version = "1.5.0"
|
|
@@ -122,6 +193,40 @@ dependencies = [
|
|
|
122
193
|
"cfg-if",
|
|
123
194
|
]
|
|
124
195
|
|
|
196
|
+
[[package]]
|
|
197
|
+
name = "criterion"
|
|
198
|
+
version = "0.5.1"
|
|
199
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
200
|
+
checksum = "f2b12d017a929603d80db1831cd3a24082f8137ce19c69e6447f54f5fc8d692f"
|
|
201
|
+
dependencies = [
|
|
202
|
+
"anes",
|
|
203
|
+
"cast",
|
|
204
|
+
"ciborium",
|
|
205
|
+
"clap",
|
|
206
|
+
"criterion-plot",
|
|
207
|
+
"is-terminal",
|
|
208
|
+
"itertools",
|
|
209
|
+
"num-traits",
|
|
210
|
+
"once_cell",
|
|
211
|
+
"oorandom",
|
|
212
|
+
"regex",
|
|
213
|
+
"serde",
|
|
214
|
+
"serde_derive",
|
|
215
|
+
"serde_json",
|
|
216
|
+
"tinytemplate",
|
|
217
|
+
"walkdir",
|
|
218
|
+
]
|
|
219
|
+
|
|
220
|
+
[[package]]
|
|
221
|
+
name = "criterion-plot"
|
|
222
|
+
version = "0.5.0"
|
|
223
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
224
|
+
checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1"
|
|
225
|
+
dependencies = [
|
|
226
|
+
"cast",
|
|
227
|
+
"itertools",
|
|
228
|
+
]
|
|
229
|
+
|
|
125
230
|
[[package]]
|
|
126
231
|
name = "crossbeam-channel"
|
|
127
232
|
version = "0.5.15"
|
|
@@ -156,6 +261,12 @@ version = "0.8.21"
|
|
|
156
261
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
157
262
|
checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
|
|
158
263
|
|
|
264
|
+
[[package]]
|
|
265
|
+
name = "crunchy"
|
|
266
|
+
version = "0.2.4"
|
|
267
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
268
|
+
checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5"
|
|
269
|
+
|
|
159
270
|
[[package]]
|
|
160
271
|
name = "either"
|
|
161
272
|
version = "1.16.0"
|
|
@@ -259,6 +370,17 @@ dependencies = [
|
|
|
259
370
|
"wasip2",
|
|
260
371
|
]
|
|
261
372
|
|
|
373
|
+
[[package]]
|
|
374
|
+
name = "half"
|
|
375
|
+
version = "2.7.1"
|
|
376
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
377
|
+
checksum = "6ea2d84b969582b4b1864a92dc5d27cd2b77b622a8d79306834f1be5ba20d84b"
|
|
378
|
+
dependencies = [
|
|
379
|
+
"cfg-if",
|
|
380
|
+
"crunchy",
|
|
381
|
+
"zerocopy",
|
|
382
|
+
]
|
|
383
|
+
|
|
262
384
|
[[package]]
|
|
263
385
|
name = "hashbrown"
|
|
264
386
|
version = "0.17.1"
|
|
@@ -303,6 +425,26 @@ dependencies = [
|
|
|
303
425
|
"hashbrown",
|
|
304
426
|
]
|
|
305
427
|
|
|
428
|
+
[[package]]
|
|
429
|
+
name = "is-terminal"
|
|
430
|
+
version = "0.4.17"
|
|
431
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
432
|
+
checksum = "3640c1c38b8e4e43584d8df18be5fc6b0aa314ce6ebf51b53313d4306cca8e46"
|
|
433
|
+
dependencies = [
|
|
434
|
+
"hermit-abi",
|
|
435
|
+
"libc",
|
|
436
|
+
"windows-sys",
|
|
437
|
+
]
|
|
438
|
+
|
|
439
|
+
[[package]]
|
|
440
|
+
name = "itertools"
|
|
441
|
+
version = "0.10.5"
|
|
442
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
443
|
+
checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473"
|
|
444
|
+
dependencies = [
|
|
445
|
+
"either",
|
|
446
|
+
]
|
|
447
|
+
|
|
306
448
|
[[package]]
|
|
307
449
|
name = "itoa"
|
|
308
450
|
version = "1.0.18"
|
|
@@ -400,6 +542,12 @@ version = "1.21.4"
|
|
|
400
542
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
401
543
|
checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
|
|
402
544
|
|
|
545
|
+
[[package]]
|
|
546
|
+
name = "oorandom"
|
|
547
|
+
version = "11.1.5"
|
|
548
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
549
|
+
checksum = "d6790f58c7ff633d8771f42965289203411a5e5c68388703c06e14f24770b41e"
|
|
550
|
+
|
|
403
551
|
[[package]]
|
|
404
552
|
name = "percent-encoding"
|
|
405
553
|
version = "2.3.2"
|
|
@@ -702,6 +850,15 @@ version = "1.0.22"
|
|
|
702
850
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
703
851
|
checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
|
|
704
852
|
|
|
853
|
+
[[package]]
|
|
854
|
+
name = "same-file"
|
|
855
|
+
version = "1.0.6"
|
|
856
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
857
|
+
checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
|
|
858
|
+
dependencies = [
|
|
859
|
+
"winapi-util",
|
|
860
|
+
]
|
|
861
|
+
|
|
705
862
|
[[package]]
|
|
706
863
|
name = "serde"
|
|
707
864
|
version = "1.0.228"
|
|
@@ -927,6 +1084,16 @@ dependencies = [
|
|
|
927
1084
|
"syn 2.0.117",
|
|
928
1085
|
]
|
|
929
1086
|
|
|
1087
|
+
[[package]]
|
|
1088
|
+
name = "tinytemplate"
|
|
1089
|
+
version = "1.2.1"
|
|
1090
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1091
|
+
checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc"
|
|
1092
|
+
dependencies = [
|
|
1093
|
+
"serde",
|
|
1094
|
+
"serde_json",
|
|
1095
|
+
]
|
|
1096
|
+
|
|
930
1097
|
[[package]]
|
|
931
1098
|
name = "tinyvec"
|
|
932
1099
|
version = "1.11.0"
|
|
@@ -1045,6 +1212,16 @@ version = "0.9.5"
|
|
|
1045
1212
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1046
1213
|
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
|
|
1047
1214
|
|
|
1215
|
+
[[package]]
|
|
1216
|
+
name = "walkdir"
|
|
1217
|
+
version = "2.5.0"
|
|
1218
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1219
|
+
checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b"
|
|
1220
|
+
dependencies = [
|
|
1221
|
+
"same-file",
|
|
1222
|
+
"winapi-util",
|
|
1223
|
+
]
|
|
1224
|
+
|
|
1048
1225
|
[[package]]
|
|
1049
1226
|
name = "wasi"
|
|
1050
1227
|
version = "0.11.1+wasi-snapshot-preview1"
|
|
@@ -1114,6 +1291,15 @@ dependencies = [
|
|
|
1114
1291
|
"rustls-pki-types",
|
|
1115
1292
|
]
|
|
1116
1293
|
|
|
1294
|
+
[[package]]
|
|
1295
|
+
name = "winapi-util"
|
|
1296
|
+
version = "0.1.11"
|
|
1297
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1298
|
+
checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
|
|
1299
|
+
dependencies = [
|
|
1300
|
+
"windows-sys",
|
|
1301
|
+
]
|
|
1302
|
+
|
|
1117
1303
|
[[package]]
|
|
1118
1304
|
name = "windows-sys"
|
|
1119
1305
|
version = "0.52.0"
|
|
@@ -10,7 +10,7 @@ prefix (``"git commit"``, ``"docker compose"``).
|
|
|
10
10
|
from __future__ import annotations
|
|
11
11
|
|
|
12
12
|
import re
|
|
13
|
-
from dataclasses import dataclass
|
|
13
|
+
from dataclasses import dataclass, field
|
|
14
14
|
from functools import cached_property, lru_cache
|
|
15
15
|
from itertools import dropwhile
|
|
16
16
|
from typing import TYPE_CHECKING
|
|
@@ -19,7 +19,7 @@ import tree_sitter_bash as tsbash
|
|
|
19
19
|
from tree_sitter import Language, Node, Parser
|
|
20
20
|
|
|
21
21
|
if TYPE_CHECKING:
|
|
22
|
-
from collections.abc import Callable, Iterator, Sequence
|
|
22
|
+
from collections.abc import Callable, Iterator, Mapping, Sequence
|
|
23
23
|
|
|
24
24
|
BASH_PARSER = Parser(Language(tsbash.language()))
|
|
25
25
|
|
|
@@ -52,6 +52,10 @@ WRAPPER_COMMANDS = frozenset({"sudo", "env", "time", "timeout", "nice", "nohup",
|
|
|
52
52
|
|
|
53
53
|
ASSIGNMENT_RE = re.compile(r"^\w+=")
|
|
54
54
|
|
|
55
|
+
PIPE_GAP_RE = re.compile(r"\s*\|&?\s*")
|
|
56
|
+
|
|
57
|
+
REDIRECT_OP_TYPES = frozenset({"file_descriptor", ">", ">>", "<", "<<", ">&", "<&", ">|"})
|
|
58
|
+
|
|
55
59
|
|
|
56
60
|
@dataclass(frozen=True)
|
|
57
61
|
class Redirect:
|
|
@@ -74,6 +78,7 @@ class Command:
|
|
|
74
78
|
args: tuple[str, ...]
|
|
75
79
|
env: tuple[tuple[str, str], ...] = ()
|
|
76
80
|
redirects: tuple[Redirect, ...] = ()
|
|
81
|
+
span: tuple[int, int] | None = field(default=None, compare=False, repr=False)
|
|
77
82
|
|
|
78
83
|
@classmethod
|
|
79
84
|
def parse(cls, raw: str) -> Command | None:
|
|
@@ -131,6 +136,7 @@ class Command:
|
|
|
131
136
|
args=argv[1:],
|
|
132
137
|
env=self.env,
|
|
133
138
|
redirects=self.redirects,
|
|
139
|
+
span=self.span,
|
|
134
140
|
)
|
|
135
141
|
|
|
136
142
|
@property
|
|
@@ -252,6 +258,56 @@ class CommandLine:
|
|
|
252
258
|
def q(self) -> CommandLineQuery:
|
|
253
259
|
return CommandLineQuery(self)
|
|
254
260
|
|
|
261
|
+
@cached_property
|
|
262
|
+
def occurrences(self) -> tuple[Occurrence, ...]:
|
|
263
|
+
"""One ``Occurrence`` per part, in line order."""
|
|
264
|
+
return tuple(Occurrence(self, index) for index in range(len(self.parts)))
|
|
265
|
+
|
|
266
|
+
def splice(self, replacements: Mapping[int, str]) -> str:
|
|
267
|
+
"""Swap each indexed command's byte span for its replacement, preserving every other byte.
|
|
268
|
+
|
|
269
|
+
Args:
|
|
270
|
+
replacements: Command index → replacement text. Operates on
|
|
271
|
+
``raw.encode()``; untouched bytes (operators, redirects,
|
|
272
|
+
heredoc bodies, comments) pass through verbatim.
|
|
273
|
+
|
|
274
|
+
Returns:
|
|
275
|
+
The rewritten line, decoded once at the end.
|
|
276
|
+
|
|
277
|
+
Raises:
|
|
278
|
+
ValueError: If an indexed command has no ``span``, or the spans are
|
|
279
|
+
out of order or overlap.
|
|
280
|
+
"""
|
|
281
|
+
source = self.raw.encode()
|
|
282
|
+
out = bytearray()
|
|
283
|
+
cursor = 0
|
|
284
|
+
for index in sorted(replacements):
|
|
285
|
+
span = self.parts[index][0].span
|
|
286
|
+
if span is None:
|
|
287
|
+
raise ValueError(f"command at index {index} has no span")
|
|
288
|
+
start, end = span
|
|
289
|
+
if start < cursor:
|
|
290
|
+
raise ValueError(f"span {span} at index {index} overlaps or precedes cursor {cursor}")
|
|
291
|
+
out += source[cursor:start]
|
|
292
|
+
out += replacements[index].encode()
|
|
293
|
+
cursor = end
|
|
294
|
+
out += source[cursor:]
|
|
295
|
+
return out.decode()
|
|
296
|
+
|
|
297
|
+
def rewrite_occurrences(self, to: Callable[[Occurrence], str | None]) -> str | None:
|
|
298
|
+
"""Map ``to`` over each occurrence and splice in its non-``None`` results.
|
|
299
|
+
|
|
300
|
+
Args:
|
|
301
|
+
to: Maps an ``Occurrence`` to replacement text, or ``None`` to leave
|
|
302
|
+
that command untouched.
|
|
303
|
+
|
|
304
|
+
Returns:
|
|
305
|
+
The spliced line, or ``None`` when ``to`` returned ``None`` for every
|
|
306
|
+
occurrence.
|
|
307
|
+
"""
|
|
308
|
+
replacements = {occ.index: text for occ in self.occurrences if (text := to(occ)) is not None}
|
|
309
|
+
return self.splice(replacements) if replacements else None
|
|
310
|
+
|
|
255
311
|
@staticmethod
|
|
256
312
|
def node_text(node: Node) -> str:
|
|
257
313
|
return node.text.decode() if node.text else ""
|
|
@@ -330,12 +386,16 @@ class CommandLine:
|
|
|
330
386
|
case _:
|
|
331
387
|
pass
|
|
332
388
|
|
|
389
|
+
content = [c for c in node.children if c.type != "file_redirect"]
|
|
390
|
+
span = (content[0].start_byte, content[-1].end_byte) if content else (node.start_byte, node.end_byte)
|
|
391
|
+
|
|
333
392
|
return Command(
|
|
334
393
|
raw=CommandLine.node_text(node),
|
|
335
394
|
executable=executable,
|
|
336
395
|
args=tuple(args),
|
|
337
396
|
env=tuple(env),
|
|
338
397
|
redirects=tuple(redirects),
|
|
398
|
+
span=span,
|
|
339
399
|
)
|
|
340
400
|
|
|
341
401
|
@staticmethod
|
|
@@ -352,13 +412,26 @@ class CommandLine:
|
|
|
352
412
|
parts.extend(sub)
|
|
353
413
|
return parts
|
|
354
414
|
|
|
415
|
+
@staticmethod
|
|
416
|
+
def redirect_absorbed_word(node: Node) -> bool:
|
|
417
|
+
"""Whether a ``file_redirect`` swallowed a command word past its target.
|
|
418
|
+
|
|
419
|
+
Tree-sitter-bash folds a word that trails a redirect (the ``b`` in
|
|
420
|
+
``echo a >out b``) into the ``file_redirect`` node beside the target, so
|
|
421
|
+
a redirect carrying more than one target word split the command's
|
|
422
|
+
arguments — leaving it no contiguous byte span to splice.
|
|
423
|
+
"""
|
|
424
|
+
return sum(c.type not in REDIRECT_OP_TYPES for c in node.children) > 1
|
|
425
|
+
|
|
355
426
|
@staticmethod
|
|
356
427
|
def walk_redirected(node: Node) -> list[tuple[Command, str | None]]:
|
|
357
428
|
redirects: list[Redirect] = []
|
|
358
429
|
inner_parts: list[tuple[Command, str | None]] = []
|
|
430
|
+
broken = False
|
|
359
431
|
for child in node.children:
|
|
360
432
|
if child.type == "file_redirect":
|
|
361
433
|
redirects.append(CommandLine.extract_redirect(child))
|
|
434
|
+
broken = broken or CommandLine.redirect_absorbed_word(child)
|
|
362
435
|
else:
|
|
363
436
|
inner_parts.extend(CommandLine.walk_node(child))
|
|
364
437
|
if redirects and inner_parts:
|
|
@@ -370,6 +443,7 @@ class CommandLine:
|
|
|
370
443
|
args=cmd.args,
|
|
371
444
|
env=cmd.env,
|
|
372
445
|
redirects=(*cmd.redirects, *redirects),
|
|
446
|
+
span=None if broken else cmd.span,
|
|
373
447
|
),
|
|
374
448
|
op,
|
|
375
449
|
)
|
|
@@ -382,6 +456,7 @@ class CommandLine:
|
|
|
382
456
|
executable="",
|
|
383
457
|
args=(),
|
|
384
458
|
redirects=tuple(redirects),
|
|
459
|
+
span=(node.start_byte, node.end_byte),
|
|
385
460
|
),
|
|
386
461
|
None,
|
|
387
462
|
)
|
|
@@ -407,6 +482,63 @@ class CommandLine:
|
|
|
407
482
|
return parts
|
|
408
483
|
|
|
409
484
|
|
|
485
|
+
@dataclass(frozen=True)
|
|
486
|
+
class Occurrence:
|
|
487
|
+
"""One command of a ``CommandLine`` with its position and joining context.
|
|
488
|
+
|
|
489
|
+
Pairs the ``line`` with this command's ``index`` into ``line.parts``,
|
|
490
|
+
exposing the command, the operators on either side, and whether it sits in a
|
|
491
|
+
pipeline. Hook packs map over ``CommandLine.occurrences`` and return
|
|
492
|
+
replacement text per occurrence to rewrite individual commands in place.
|
|
493
|
+
"""
|
|
494
|
+
|
|
495
|
+
line: CommandLine
|
|
496
|
+
index: int
|
|
497
|
+
|
|
498
|
+
@property
|
|
499
|
+
def command(self) -> Command:
|
|
500
|
+
"""The command at this occurrence's index."""
|
|
501
|
+
return self.line.parts[self.index][0]
|
|
502
|
+
|
|
503
|
+
@property
|
|
504
|
+
def prev_op(self) -> str | None:
|
|
505
|
+
"""The operator joining the previous command to this one; ``None`` at index 0."""
|
|
506
|
+
return self.line.parts[self.index - 1][1] if self.index > 0 else None
|
|
507
|
+
|
|
508
|
+
@property
|
|
509
|
+
def next_op(self) -> str | None:
|
|
510
|
+
"""The operator joining this command to the next; ``None`` for the final command."""
|
|
511
|
+
return self.line.parts[self.index][1]
|
|
512
|
+
|
|
513
|
+
@cached_property
|
|
514
|
+
def piped(self) -> bool:
|
|
515
|
+
"""Whether this command sits on either side of a pipe.
|
|
516
|
+
|
|
517
|
+
``True`` when a neighboring operator is ``|``. Tree-sitter records no
|
|
518
|
+
operator token for ``|&``, and newline-separated statements also carry a
|
|
519
|
+
``None`` operator, so a ``None`` neighbor falls back to inspecting the
|
|
520
|
+
decoded source gap toward that neighbor: it counts as piped only when the
|
|
521
|
+
gap is exactly one pipe operator token (``|`` or ``|&``) surrounded by
|
|
522
|
+
whitespace. Anything else in the gap — a comment, a redirect target, a
|
|
523
|
+
heredoc body, ``||``, an intervening statement — is not a pipe.
|
|
524
|
+
"""
|
|
525
|
+
if self.prev_op == "|" or self.next_op == "|":
|
|
526
|
+
return True
|
|
527
|
+
span = self.command.span
|
|
528
|
+
if span is None:
|
|
529
|
+
return False
|
|
530
|
+
source = self.line.raw.encode()
|
|
531
|
+
if self.next_op is None and self.index + 1 < len(self.line.parts):
|
|
532
|
+
nxt = self.line.parts[self.index + 1][0].span
|
|
533
|
+
if nxt is not None and PIPE_GAP_RE.fullmatch(source[span[1] : nxt[0]].decode()):
|
|
534
|
+
return True
|
|
535
|
+
if self.prev_op is None and self.index > 0:
|
|
536
|
+
prev = self.line.parts[self.index - 1][0].span
|
|
537
|
+
if prev is not None and PIPE_GAP_RE.fullmatch(source[prev[1] : span[0]].decode()):
|
|
538
|
+
return True
|
|
539
|
+
return False
|
|
540
|
+
|
|
541
|
+
|
|
410
542
|
@dataclass(frozen=True)
|
|
411
543
|
class CommandLineQuery:
|
|
412
544
|
"""Predicate helpers for inspecting a parsed ``CommandLine``.
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
"""Per-(session, event) dispatch heartbeats — proof a hook event reached dispatch at all.
|
|
2
|
+
|
|
3
|
+
The decision ledger records only hooks that *fired*, so it cannot distinguish "this event
|
|
4
|
+
was never dispatched" from "it dispatched and matched nothing". A heartbeat is written
|
|
5
|
+
unconditionally at dispatch entry, before matching, keyed by ``(session_id, event)`` and
|
|
6
|
+
counting hits — so a missing beat for an event a session should emit is an unambiguous
|
|
7
|
+
wiring gap. It shares the ``decisions.db`` file with
|
|
8
|
+
:class:`~cc_transcript.decisions.DecisionLog` in a separate ``dispatch_heartbeats`` table,
|
|
9
|
+
so the vendored, Go-byte-compared ``decisions`` schema is untouched. The upsert keeps at
|
|
10
|
+
most one row per ``(session, event)``, so the table stays small however hot the path.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
|
|
15
|
+
import sqlite3
|
|
16
|
+
from dataclasses import dataclass
|
|
17
|
+
from pathlib import Path
|
|
18
|
+
from typing import TYPE_CHECKING, Self
|
|
19
|
+
|
|
20
|
+
if TYPE_CHECKING:
|
|
21
|
+
from cc_transcript.ids import SessionId
|
|
22
|
+
|
|
23
|
+
HEARTBEATS_DDL = """\
|
|
24
|
+
CREATE TABLE IF NOT EXISTS dispatch_heartbeats (
|
|
25
|
+
session_id TEXT NOT NULL,
|
|
26
|
+
event TEXT NOT NULL,
|
|
27
|
+
first_ts_ms INTEGER NOT NULL,
|
|
28
|
+
last_ts_ms INTEGER NOT NULL,
|
|
29
|
+
count INTEGER NOT NULL DEFAULT 1,
|
|
30
|
+
PRIMARY KEY (session_id, event)
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
CREATE INDEX IF NOT EXISTS idx_heartbeats_session ON dispatch_heartbeats (session_id);
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
FILENAME = "decisions.db"
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
@dataclass(frozen=True, slots=True)
|
|
40
|
+
class Heartbeat:
|
|
41
|
+
"""One ``(session, event)`` dispatch heartbeat.
|
|
42
|
+
|
|
43
|
+
Attributes:
|
|
44
|
+
session_id: The Claude session the event dispatched in.
|
|
45
|
+
event: The Claude Code event name, e.g. ``PreToolUse`` or ``Stop``.
|
|
46
|
+
first_ts_ms: Millisecond timestamp of the first dispatch this session.
|
|
47
|
+
last_ts_ms: Millisecond timestamp of the most recent dispatch.
|
|
48
|
+
count: How many times the event has dispatched this session.
|
|
49
|
+
"""
|
|
50
|
+
|
|
51
|
+
session_id: SessionId
|
|
52
|
+
event: str
|
|
53
|
+
first_ts_ms: int
|
|
54
|
+
last_ts_ms: int
|
|
55
|
+
count: int
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
class HeartbeatLog:
|
|
59
|
+
"""The ``dispatch_heartbeats`` table in ``~/.cc-transcript/decisions.db``.
|
|
60
|
+
|
|
61
|
+
Opened in WAL mode with a busy timeout, matching the decision ledger it shares a file
|
|
62
|
+
with. ``beat`` upserts one ``(session, event)`` row per dispatch, so a session holds at
|
|
63
|
+
most one row per event. Requires a local disk — WAL does not work over NFS.
|
|
64
|
+
|
|
65
|
+
Example:
|
|
66
|
+
>>> log = HeartbeatLog.open()
|
|
67
|
+
>>> log.beat(session_id, "PreToolUse", ts_ms)
|
|
68
|
+
>>> log.for_session(session_id)
|
|
69
|
+
"""
|
|
70
|
+
|
|
71
|
+
def __init__(self, conn: sqlite3.Connection) -> None:
|
|
72
|
+
self.conn = conn
|
|
73
|
+
|
|
74
|
+
@classmethod
|
|
75
|
+
def open(cls, path: Path | None = None) -> Self:
|
|
76
|
+
"""Opens (creating if needed) the heartbeat table at ``path`` (defaults to ``decisions.db``)."""
|
|
77
|
+
path = path or Path.home() / ".cc-transcript" / FILENAME
|
|
78
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
79
|
+
conn = sqlite3.connect(path, autocommit=True)
|
|
80
|
+
conn.row_factory = sqlite3.Row
|
|
81
|
+
conn.execute("PRAGMA journal_mode = WAL")
|
|
82
|
+
conn.execute("PRAGMA busy_timeout = 2000")
|
|
83
|
+
conn.executescript(HEARTBEATS_DDL)
|
|
84
|
+
return cls(conn)
|
|
85
|
+
|
|
86
|
+
def beat(self, session_id: SessionId, event: str, ts_ms: int) -> None:
|
|
87
|
+
"""Records one dispatch of ``event`` in ``session_id`` at ``ts_ms`` (upsert; ``count += 1``)."""
|
|
88
|
+
self.conn.execute(
|
|
89
|
+
"INSERT INTO dispatch_heartbeats (session_id, event, first_ts_ms, last_ts_ms, count) "
|
|
90
|
+
"VALUES (?, ?, ?, ?, 1) "
|
|
91
|
+
"ON CONFLICT (session_id, event) DO UPDATE SET last_ts_ms = excluded.last_ts_ms, count = count + 1",
|
|
92
|
+
(session_id, event, ts_ms, ts_ms),
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
def for_session(self, session_id: SessionId) -> tuple[Heartbeat, ...]:
|
|
96
|
+
"""Every ``(session, event)`` beat for ``session_id``, ordered by first dispatch."""
|
|
97
|
+
return tuple(
|
|
98
|
+
Heartbeat(
|
|
99
|
+
session_id=row["session_id"],
|
|
100
|
+
event=row["event"],
|
|
101
|
+
first_ts_ms=row["first_ts_ms"],
|
|
102
|
+
last_ts_ms=row["last_ts_ms"],
|
|
103
|
+
count=row["count"],
|
|
104
|
+
)
|
|
105
|
+
for row in self.conn.execute(
|
|
106
|
+
"SELECT * FROM dispatch_heartbeats WHERE session_id = ? ORDER BY first_ts_ms, event", (session_id,)
|
|
107
|
+
)
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
def events_seen(self, session_id: SessionId) -> frozenset[str]:
|
|
111
|
+
"""The set of event names that have dispatched at least once in ``session_id``."""
|
|
112
|
+
return frozenset(
|
|
113
|
+
row["event"]
|
|
114
|
+
for row in self.conn.execute(
|
|
115
|
+
"SELECT event FROM dispatch_heartbeats WHERE session_id = ?", (session_id,)
|
|
116
|
+
)
|
|
117
|
+
)
|
|
@@ -46,3 +46,13 @@ udpipe-rs = { version = "0.2", optional = true }
|
|
|
46
46
|
|
|
47
47
|
[build-dependencies]
|
|
48
48
|
pyo3-build-config = { version = "0.28", features = ["resolve-config"], optional = true }
|
|
49
|
+
|
|
50
|
+
[dev-dependencies]
|
|
51
|
+
# Criterion for the v14 P0 parse/filter/activity baseline (rust/benches/parse.rs).
|
|
52
|
+
# default-features off: skip the plotters/HTML report machinery; estimates.json
|
|
53
|
+
# (the medians BASELINE.json reads) is written by the core regardless.
|
|
54
|
+
criterion = { version = "0.5", default-features = false, features = ["cargo_bench_support"] }
|
|
55
|
+
|
|
56
|
+
[[bench]]
|
|
57
|
+
name = "parse"
|
|
58
|
+
harness = false
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
//! v14 P0 baseline benches: parse, parse+filter, session-activity over the corpus at
|
|
2
|
+
//! `CC_BENCH_CORPUS` (default `../.fixtures/corpus`; build via scripts/gen_corpus.py).
|
|
3
|
+
|
|
4
|
+
use std::fs;
|
|
5
|
+
use std::path::{Path, PathBuf};
|
|
6
|
+
|
|
7
|
+
use criterion::{criterion_group, criterion_main, Criterion, Throughput};
|
|
8
|
+
|
|
9
|
+
use _parser_rs::activity::{session_activity, ActivityOpts};
|
|
10
|
+
use _parser_rs::filter::{compile_spec, spec_keep};
|
|
11
|
+
use _parser_rs::parse::parse_bytes;
|
|
12
|
+
|
|
13
|
+
// spec_to_json of cli.py SIGNAL_SPEC (cc_transcript/builders.py): keep_only + drop_junk + drop_synthetic + drop_sidechain + drop_empty x2.
|
|
14
|
+
const FILTER_SPEC_JSON: &str = r#"{"clauses":[{"predicate":{"kind":"KindIs","kinds":["assistant","user"]},"action":"drop","applies_to":[],"negate":true,"label":null},{"predicate":{"kind":"TextMatchesAny","groups":[["xml_tags","<(?:system[_-](?:instruction|reminder)|local-command-(?:stdout|stderr|caveat)|command-(?:name|message|args)|task-notification|persisted-output|output-file)\\b"],["local_command_caveat","Caveat: The messages below were generated by the user while running local commands\\."],["remaining_tasks_ack","REMAINING_TASKS_ACKNOWLEDGED"],["angle_token","<<[a-z][a-z0-9-]*>>"],["skill_base_dir","^Base directory for this skill:"],["hash_banner","(?:###\\s+[\\w][\\w \\-]{0,30}\\s+){3,}###"],["xml_tags_extra","\\A\\s*<(?:teammate-message|scheduled-task)(?:[\\s/>]|$)"],["augment_agent","\\A\\s*# Augment Agent(?:\\s|$)"],["role_reminder","\\A\\s*\\[Role Rem(?-i:[Ii])nder(?:[\\s:\\]]|$)"],["command_echo","\\A\\s*<bash-(?:input|stdout|stderr)\\b"]],"ignore_case":true},"action":"drop","applies_to":["user"],"negate":false,"label":null},{"predicate":{"kind":"ModelIs","models":["<synthetic>"]},"action":"drop","applies_to":["assistant"],"negate":false,"label":null},{"predicate":{"kind":"MetaFlag","flag":"is_sidechain"},"action":"drop","applies_to":[],"negate":false,"label":null},{"predicate":{"kind":"TextEmpty","consider_tool_use":false},"action":"drop","applies_to":["user"],"negate":false,"label":null},{"predicate":{"kind":"TextEmpty","consider_tool_use":true},"action":"drop","applies_to":["assistant"],"negate":false,"label":null}]}"#;
|
|
15
|
+
|
|
16
|
+
fn corpus_dir() -> PathBuf {
|
|
17
|
+
std::env::var_os("CC_BENCH_CORPUS")
|
|
18
|
+
.map(PathBuf::from)
|
|
19
|
+
.unwrap_or_else(|| PathBuf::from("../.fixtures/corpus"))
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
fn collect_jsonl(dir: &Path, out: &mut Vec<PathBuf>) {
|
|
23
|
+
let Ok(entries) = fs::read_dir(dir) else { return };
|
|
24
|
+
for entry in entries.flatten() {
|
|
25
|
+
let path = entry.path();
|
|
26
|
+
if path.is_dir() {
|
|
27
|
+
collect_jsonl(&path, out);
|
|
28
|
+
} else if path.extension().and_then(|e| e.to_str()) == Some("jsonl") {
|
|
29
|
+
out.push(path);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
fn largest_corpus_file() -> Vec<u8> {
|
|
35
|
+
let dir = corpus_dir();
|
|
36
|
+
let mut files = Vec::new();
|
|
37
|
+
collect_jsonl(&dir, &mut files);
|
|
38
|
+
let largest = files
|
|
39
|
+
.into_iter()
|
|
40
|
+
.max_by_key(|p| fs::metadata(p).map(|m| m.len()).unwrap_or(0))
|
|
41
|
+
.unwrap_or_else(|| {
|
|
42
|
+
panic!(
|
|
43
|
+
"no .jsonl transcripts under {} — run: uv run --no-sync python scripts/gen_corpus.py",
|
|
44
|
+
dir.display()
|
|
45
|
+
)
|
|
46
|
+
});
|
|
47
|
+
fs::read(&largest).unwrap_or_else(|e| panic!("failed to read corpus file {}: {e}", largest.display()))
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
fn bench_parse(c: &mut Criterion) {
|
|
51
|
+
let bytes = largest_corpus_file();
|
|
52
|
+
let spec = compile_spec(FILTER_SPEC_JSON).expect("filter spec compiles");
|
|
53
|
+
let mut group = c.benchmark_group("parse");
|
|
54
|
+
group.sample_size(20);
|
|
55
|
+
group.throughput(Throughput::Bytes(bytes.len() as u64));
|
|
56
|
+
group.bench_function("parse_corpus", |b| {
|
|
57
|
+
b.iter(|| parse_bytes(&bytes, |_| true).expect("corpus parses"))
|
|
58
|
+
});
|
|
59
|
+
group.bench_function("filter_spec", |b| {
|
|
60
|
+
b.iter(|| parse_bytes(&bytes, |entry| spec_keep(&spec, entry)).expect("corpus parses"))
|
|
61
|
+
});
|
|
62
|
+
group.finish();
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
fn bench_activity(c: &mut Criterion) {
|
|
66
|
+
let bytes = largest_corpus_file();
|
|
67
|
+
let entries = parse_bytes(&bytes, |_| true).expect("corpus parses");
|
|
68
|
+
let opts = ActivityOpts::default();
|
|
69
|
+
let mut group = c.benchmark_group("activity");
|
|
70
|
+
group.sample_size(20);
|
|
71
|
+
group.throughput(Throughput::Elements(entries.len() as u64));
|
|
72
|
+
group.bench_function("session_activity", |b| b.iter(|| session_activity(&entries, &opts)));
|
|
73
|
+
group.finish();
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
criterion_group!(benches, bench_parse, bench_activity);
|
|
77
|
+
criterion_main!(benches);
|
|
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
|
|
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
|
{cc_transcript-13.0.0 → cc_transcript-13.2.0}/cc_transcript/sentiment/data/domain_overrides.tsv
RENAMED
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|