cyanwingsbird commited on
Commit
b319956
·
verified ·
1 Parent(s): 11d3bd3

Upload folder using huggingface_hub

Browse files
.env.example ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Config for HumOmni Track 2 — copy to .env and fill the secrets. .env is gitignored.
2
+ # Two providers: OpenRouter (the VLM backbone) and Vertex AI (the Gemini entailment judge).
3
+
4
+ # --- OpenRouter: the VLM backbone (Gemini is NOT on OpenRouter, so not the judge) ---
5
+ OPENROUTER_API_KEY=sk-or-...your-key-here...
6
+ VLM_MODEL=qwen/qwen3.7-plus
7
+
8
+ # --- Vertex AI (express mode, REST): the Gemini 3.1 Flash-Lite judge (entailment dedup + evaluation) ---
9
+ # Auth = a Vertex AI EXPRESS-mode API key (a plain REST generateContent call; no SDK / service account).
10
+ VERTEX_KEY=...your-vertex-express-api-key...
11
+ # Vertex model id (NO `google/` prefix, NO date — judges.py normalizes those).
12
+ JUDGE_MODEL=gemini-3.1-flash-lite
13
+ # Optional tuning (Vertex can be slow): per-call timeout (s), aggregate rate cap, regional host override
14
+ VERTEX_TIMEOUT_S=120
15
+ JUDGE_RPM=280
16
+ # VERTEX_BASE_URL=https://us-central1-aiplatform.googleapis.com/v1
17
+
18
+ # Optional: silences the HF Hub unauthenticated-usage warning
19
+ HF_TOKEN=hf_...your-hf-token-here...
.gitattributes CHANGED
@@ -33,3 +33,4 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ paper_experiments/caches/judge_cache.json filter=lfs diff=lfs merge=lfs -text
.gitignore ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Secrets — never commit the real .env (.env.example is the committed template)
2
+ .env
3
+ # Python
4
+ .venv/
5
+ __pycache__/
6
+ *.pyc
7
+ *.egg-info/
8
+ build/
9
+ # Data and regenerable caches (large)
10
+ /data
11
+ data/
12
+ cache/
13
+ # Outputs
14
+ submissions/
15
+ *.jsonl
16
+ *.log
17
+ # The reproduction pack IS tracked despite the rules above (it makes every reported number reproducible offline)
18
+ !paper_experiments/**
README.md ADDED
@@ -0,0 +1,90 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # HumOmni 2026 · Track 2 (ProactivEval) — proactive streaming video QA
2
+
3
+ Watch a video at 2 fps, given one question at t=0, and decide **when** to speak and **what** to
4
+ say — causally (using only frames with timestamp ≤ t). Ranking score = **0.9·PAUC + 0.1·(1−duplicate)**;
5
+ hidden judge = Gemini 3.1 Flash-Lite. Baseline MMDuet2 = 53.3; public leader at the time = 60.21.
6
+
7
+ This repository is the **Phase-1 system**: a causal streaming orchestrator around a hosted VLM
8
+ (`qwen/qwen3.7-plus` via OpenRouter). No training and no local large models — it runs on a single 8 GB GPU.
9
+
10
+ ## The method in one paragraph
11
+ The hidden judge is **recall-only** — it asks whether the accumulated response text *covers* the
12
+ reference answer's key points, with **no precision penalty** for extra detail. So the winning strategy is
13
+ **comprehensive coverage**: fire on every frame (gate off), prompt the VLM to describe the scene
14
+ exhaustively and read all on-screen text verbatim, and strip only true repeats with an LLM-entailment
15
+ dedup. The final config queries a small **prompt ensemble** per frame — `v5` (exhaustive + OCR) + `v3`
16
+ (comprehensive) + `v8` (few-shot, teach-by-example) — and merges their drafts. The big lever was the
17
+ **prompt**, not timing or a bigger model.
18
+
19
+ ## Result (WEB dev set, faithful Gemini judge, untouched holdout `gold[400:500]`)
20
+ | stage | score | note |
21
+ |---|---|---|
22
+ | baseline (focused prompt + throttle gate) | ~0.61 | the original leaderboard submission |
23
+ | + gate-off (fire every frame) | 0.625 | throttling loses coverage |
24
+ | + comprehensive prompt (`v3`) | 0.733 | recall-only judge ⇒ coverage wins |
25
+ | + exhaustive + OCR (`v5`), recent_k=16 | 0.7761 | best single prompt |
26
+ | + `v5`+`v3` ensemble @ recent_k=32 | 0.7909 | robust 2× config |
27
+ | + `v8` few-shot (`v5`+`v3`+`v8` @ k40) | **0.8018** | **the final submission** (gate off) |
28
+ | + perception gate (novelty ≥ 16, silence 3 s) | 0.7853 | optional lower-cost operating point (−51% calls) |
29
+
30
+ **Caveat:** WEB-dev is the leakage-free dev set, **not** the phase1 test set (which has no ground truth).
31
+ The `v8` gain is partly holdout-overfit — the robust gain over `v5`+`v3` is ~+0.002–0.005. Our PAUC harness
32
+ is verified bit-exact against the official scorer, and the grader is confirmed Gemini 3.1 Flash-Lite.
33
+
34
+ ## Project layout
35
+ ```
36
+ src/humomni/
37
+ core/ streaming_driver · context · pauc_eval · judges · emb_cache · env_util · validate_submission
38
+ phase1/ vlm_client · dedup · policy_api · policies · perception_gate
39
+ tuning/ cache · faithful_eval
40
+ scripts/ build_submission.py · run_inference.py · infer_simple.py
41
+ tests/ test_causality.py · test_inference_parity.py
42
+ config.json .env.example pyproject.toml
43
+ ```
44
+ Run every command **from the repo root** — modules use repo-root-relative `data/` and `cache/` paths.
45
+
46
+ ## Setup
47
+ ```bash
48
+ python -m venv .venv && .venv\Scripts\activate # Windows (use: source .venv/bin/activate on *nix)
49
+ pip install -e . # installs humomni.* + deps (pyproject.toml)
50
+ cp .env.example .env # then fill in the keys (below)
51
+ ```
52
+ `.env` needs `OPENROUTER_API_KEY` (the VLM) and `VERTEX_KEY` (the Gemini judge used for the LLM-entailment
53
+ dedup) — only for regenerating decisions from frames; the offline replay needs no keys. Put the test set at
54
+ `data/phase1/data/<id>/` — frames `0.5.jpg, 1.0.jpg, …` plus `question.json` per sample.
55
+
56
+ ## Build the submission — one command
57
+ `config.json` holds the entire method (`"ensemble": ["v5","v3","v8"] @ 40`, `dedup`/`theta`, and the
58
+ perception `"gate"` disabled by default — `use_gate:false` gives the shipped max-score point; enabling it
59
+ at novelty ≥ 16 / silence 3 s selects a lower-cost operating point):
60
+ ```bash
61
+ python scripts/build_submission.py --out submissions/tryanderror2.jsonl
62
+ ```
63
+ For each ensemble prompt it ensures a per-prompt VLM decision cache — **building it from the test frames
64
+ if missing** (paid: ~1 VLM call per prompt per frame; resumable, skips cached frames) — then replays the
65
+ merge + LLM-entailment dedup and **validates** the output (all 500 ids present, times multiples of 0.5).
66
+ Re-runs are free once the caches exist.
67
+
68
+ The live equivalent produces byte-identical output (paid on every run — use when caches are absent):
69
+ ```bash
70
+ python scripts/run_inference.py --policy api --workers 8 --out submissions/tryanderror2.jsonl
71
+ python scripts/infer_simple.py --policy api --out submissions/tryanderror2.jsonl # minimal twin
72
+ ```
73
+ Both drive the same `APIPolicy` from `config.json`, so all three commands point at the same method
74
+ (live ≡ replay). Upload is manual: the file must be named exactly your Team ID.
75
+
76
+ ## Tests
77
+ ```bash
78
+ python tests/test_causality.py # causality assert fires on shuffled / out-of-order frames
79
+ python tests/test_inference_parity.py # run_inference == infer_simple; ensemble multi-emit per frame
80
+ ```
81
+
82
+ ## Compliance
83
+ Track 2 permits any open-source or commercial LLM API (organizer clarification, 2026-07-07), with one
84
+ condition: **when generating a response at time t, only frames at time ≤ t may be used as visual input.**
85
+ This system enforces that structurally (below) and tests it.
86
+
87
+ ## The one hard rule — causality
88
+ Acting at time *t* uses **only** frames with timestamp ≤ *t*. Enforced structurally in
89
+ `humomni.core.streaming_driver.drive` (`assert t > last_t`) and guarded by `tests/test_causality.py`
90
+ (the shuffled-frame test). Keep it green always.
config.json ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "theta": 0.6,
3
+ "gate": {
4
+ "use_gate": false,
5
+ "novelty": 16,
6
+ "max_silence_s": 3.0,
7
+ "warmup_s": 1.0
8
+ },
9
+ "dedup": {
10
+ "sim_high": 0.97,
11
+ "sim_low": 0.55,
12
+ "recent_k": 16,
13
+ "use_llm": true
14
+ },
15
+ "ensemble": {
16
+ "prompts": [
17
+ "v5",
18
+ "v3",
19
+ "v8"
20
+ ],
21
+ "recent_k": 40
22
+ }
23
+ }
paper_experiments/README.md ADDED
@@ -0,0 +1,45 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Paper experiments — one command per table
2
+
3
+ Reproduces every number in the report's tables, **deterministically and offline**: the per-frame VLM
4
+ decisions (holdout videos) and all LLM-judge results are shipped as caches, so a rerun makes **zero API
5
+ calls** and needs **no keys**.
6
+
7
+ ## Setup (once)
8
+
9
+ ```bash
10
+ pip install -e . # from the repo root; first run downloads the MiniLM embedder (~90 MB)
11
+ ```
12
+
13
+ ## Run
14
+
15
+ ```bash
16
+ python paper_experiments/run.py table1 # Table 1 — main progression (score AND cost)
17
+ python paper_experiments/run.py table2 # Table 2 — ensemble ablation (calls per frame)
18
+ python paper_experiments/run.py table3 # Table 3 — dedup ablation
19
+ python paper_experiments/run.py gate # Table 4 — perception-gate cost/score frontier
20
+ python paper_experiments/run.py sensitivity # few-shot example-sensitivity check
21
+ python paper_experiments/run.py all
22
+ ```
23
+
24
+ Each line prints `score` (0.9·PAUC + 0.1·(1−dup)), `PAUC`, `dup`, mean emissions per video, and the
25
+ cost: mean **VLM calls per video** (+ an approximate US$ figure at our measured ≈$25 / 17.3k calls).
26
+ Runs in a few minutes on CPU.
27
+
28
+ ## What is shipped, and why the numbers are exact
29
+
30
+ | file | content |
31
+ |---|---|
32
+ | `data/gold_holdout.json` | the 100 untouched holdout items (public WEB split, items 400–499) |
33
+ | `caches/web_{base,comp,exh,fs,fs_alt}.jsonl` | per-frame VLM decisions for each prompt (temperature 0), holdout videos only |
34
+ | `caches/judge_cache.json` | every LLM-judge / entailment result, keyed by input hash |
35
+
36
+ `run.py` replays the cached decisions through the *same* merge + entailment-dedup code the live system
37
+ runs (`humomni.phase1.dedup.EntailmentGuard`), then scores with the official-protocol harness
38
+ (`humomni.tuning.faithful_eval`). Since the VLM decisions, judge results, and dedup logic are all
39
+ deterministic replays, the printed numbers match the report exactly.
40
+
41
+ To regenerate the VLM caches from scratch instead (paid; OpenRouter + Vertex keys in `.env`):
42
+ `python -m humomni.tuning.cache --func vlm` with the corresponding prompt — see the repo README.
43
+
44
+ Prompt naming: EXH / COMP / FS in the report = `v5` / `v3` / `v8` in the code; `fs_alt` = `v10`
45
+ (the example-swapped variant used in the sensitivity check).
paper_experiments/caches/judge_cache.json ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:05bb3868ccce66c7b0a7776071ac4853ef2642f5b3a406fb529dd35c636051ef
3
+ size 24284720
paper_experiments/caches/novelty_holdout.json ADDED
@@ -0,0 +1 @@
 
 
1
+ {"09RFL5SKVjM.22.mp4": {"0.5": 255.0, "1.0": 20.33159637451172, "1.5": 17.13237762451172, "2.0": 8.304253578186035, "2.5": 24.09418487548828, "3.0": 13.19921875, "3.5": 2.760850667953491, "4.0": 2.5807292461395264, "4.5": 2.493055582046509, "5.0": 115.04513549804688, "5.5": 28.60112762451172, "6.0": 9.843316078186035, "6.5": 28.15407943725586, "7.0": 48.97005081176758, "7.5": 33.58984375, "8.0": 34.88584899902344, "8.5": 36.06944274902344, "9.0": 35.95790100097656, "9.5": 37.94270706176758, "10.0": 44.32334899902344, "10.5": 39.87977600097656, "11.0": 20.90234375, "11.5": 6.950520992279053, "12.0": 6.90928840637207, "12.5": 8.03428840637207, "13.0": 8.338541984558105}, "0IKKzYIK-AY.6.mp4": {"0.5": 255.0, "1.0": 31.18619728088379, "1.5": 67.21050262451172, "2.0": 1.4904513359069824, "2.5": 1.1818575859069824, "3.0": 1.328125, "3.5": 1.2864583730697632, "4.0": 80.57595825195312, "4.5": 40.58376693725586, "5.0": 39.92491149902344, "5.5": 40.35459899902344, "6.0": 43.63498306274414, "6.5": 45.05512237548828, "7.0": 74.87456512451172, "7.5": 33.19921875, "8.0": 26.33420181274414, "8.5": 31.59809112548828, "9.0": 59.23263931274414, "9.5": 38.55078125, "10.0": 61.86892318725586, "10.5": 10.82204818725586, "11.0": 7.750433921813965}, "0KoYCn3MQd0.9.mp4": {"0.5": 255.0, "1.0": 9.47482681274414, "1.5": 8.462239265441895, "2.0": 6.943142414093018, "2.5": 7.166666507720947, "3.0": 8.52647590637207, "3.5": 8.50607681274414, "4.0": 8.66319465637207, "4.5": 6.813802242279053, "5.0": 50.87022399902344, "5.5": 17.16275978088379, "6.0": 15.55295181274414, "6.5": 14.86892318725586, "7.0": 24.54817771911621, "7.5": 28.35460090637207, "8.0": 23.32204818725586, "8.5": 48.45833206176758, "9.0": 5.842447757720947, "9.5": 11.71961784362793, "10.0": 12.42404556274414, "10.5": 11.69140625, "11.0": 9.88194465637207, "11.5": 27.17317771911621, "12.0": 4.001302242279053, "12.5": 3.24609375, "13.0": 2.250433921813965, "13.5": 1.5512152910232544, "14.0": 52.96006774902344, "14.5": 28.20138931274414, "15.0": 30.06900978088379, "15.5": 26.30989646911621, "16.0": 27.10112762451172, "16.5": 47.13194274902344, "17.0": 2.0130207538604736, "17.5": 6.048177242279053, "18.0": 8.12109375, "18.5": 3.9357638359069824, "19.0": 35.83506774902344, "19.5": 12.04513931274414, "20.0": 10.99609375, "20.5": 10.226996421813965, "21.0": 9.51258659362793}, "0YSLASX3Iq8.3.mp4": {"0.5": 255.0, "1.0": 13.726996421813965, "1.5": 20.59418487548828, "2.0": 36.46527862548828, "2.5": 28.25390625, "3.0": 14.9765625, "3.5": 21.18272590637207, "4.0": 16.74001693725586, "4.5": 25.85416603088379, "5.0": 26.15277862548828, "5.5": 10.937933921813965, "6.0": 15.945746421813965, "6.5": 10.851996421813965, "7.0": 14.137152671813965, "7.5": 14.27951431274414, "8.0": 26.92491340637207, "8.5": 20.48220443725586, "9.0": 20.97612762451172, "9.5": 6.976128578186035, "10.0": 21.72309112548828, "10.5": 24.42751693725586, "11.0": 24.03949737548828, "11.5": 11.44227409362793, "12.0": 10.72439193725586, "12.5": 11.773003578186035, "13.0": 26.67144012451172, "13.5": 21.046875, "14.0": 10.750433921813965, "14.5": 6.41102409362793, "15.0": 6.671875, "15.5": 13.13454818725586, "16.0": 12.226996421813965, "16.5": 65.49478912353516, "17.0": 27.65277862548828, "17.5": 10.71310806274414, "18.0": 21.07725715637207, "18.5": 10.33116340637207, "19.0": 18.03732681274414, "19.5": 25.07725715637207, "20.0": 15.11631965637207, "20.5": 26.51302146911621, "21.0": 74.42274475097656, "21.5": 9.166666984558105, "22.0": 16.64887237548828, "22.5": 13.812066078186035, "23.0": 23.19921875, "23.5": 28.88107681274414, "24.0": 15.370659828186035, "24.5": 13.680989265441895, "25.0": 17.75911521911621, "25.5": 17.49045181274414, "26.0": 15.234375, "26.5": 13.46006965637207, "27.0": 24.81857681274414, "27.5": 13.005208015441895, "28.0": 8.023003578186035, "28.5": 3.7122395038604736, "29.0": 11.558159828186035, "29.5": 31.16970443725586, "30.0": 15.082465171813965, "30.5": 5.002170085906982, "31.0": 8.414496421813965, "31.5": 21.95225715637207, "32.0": 89.68966674804688, "32.5": 8.171441078186035, "33.0": 10.457465171813965, "33.5": 12.078125, "34.0": 10.99435806274414}, "0fbebx_l2Xw.4.mp4": {"0.5": 255.0, "1.0": 0.02690972201526165, "1.5": 1.9895833730697632, "2.0": 0.0928819477558136, "2.5": 0.0451388880610466, "3.0": 0.02604166604578495, "3.5": 8.627604484558105, "4.0": 0.009548611007630825, "4.5": 1.5177950859069824, "5.0": 0.0164930559694767, "5.5": 0.0026041667442768812, "6.0": 0.4830729067325592, "6.5": 7.781683921813965, "7.0": 0.010416666977107525, "7.5": 0.0047743055038154125, "8.0": 3.757378578186035, "8.5": 0.02864583395421505, "9.0": 0.234375, "9.5": 0.002170138992369175, "10.0": 0.4722222089767456, "10.5": 0.02690972201526165, "11.0": 0.0065104165114462376, "11.5": 0.005642361007630825, "12.0": 0.0017361111240461469, "12.5": 0.0, "13.0": 0.005642361007630825, "13.5": 0.009114583022892475, "14.0": 0.0065104165114462376, "14.5": 31.75868034362793, "15.0": 4.2265625, "15.5": 4.767361164093018, "16.0": 5.82725715637207, "16.5": 21.38020896911621, "17.0": 2.305555582046509, "17.5": 21.73567771911621, "18.0": 2.868055582046509, "18.5": 1.8441840410232544, "19.0": 32.78559112548828, "19.5": 0.1818576455116272, "20.0": 0.1488715261220932, "20.5": 35.7265625, "21.0": 6.101996421813965, "21.5": 5.26258659362793, "22.0": 6.866753578186035, "22.5": 12.76171875, "23.0": 15.006510734558105, "23.5": 13.987847328186035, "24.0": 11.94357681274414, "24.5": 9.78515625, "25.0": 8.555989265441895, "25.5": 2.559027671813965, "26.0": 0.3980034589767456, "26.5": 3.3376736640930176, "27.0": 19.39756965637207, "27.5": 25.13454818725586, "28.0": 17.16579818725586, "28.5": 13.921441078186035, "29.0": 9.089409828186035, "29.5": 7.028645992279053}, "0lbseqh9fsg.35.mp4": {"0.5": 255.0, "1.0": 14.947916984558105, "1.5": 15.18055534362793, "2.0": 15.676215171813965, "2.5": 16.24913215637207, "3.0": 16.18055534362793, "3.5": 16.26475715637207, "4.0": 16.05425262451172, "4.5": 45.34461975097656, "5.0": 4.90147590637207, "5.5": 4.87803840637207, "6.0": 4.813802242279053, "6.5": 5.02256965637207, "7.0": 4.90147590637207, "7.5": 5.092447757720947, "8.0": 5.263454914093018, "8.5": 5.05381965637207, "9.0": 40.58029556274414, "9.5": 7.583767414093018, "10.0": 7.952691078186035, "10.5": 7.87196159362793, "11.0": 8.098958015441895, "11.5": 7.875433921813965, "12.0": 79.27603912353516, "12.5": 1.265625, "13.0": 1.2769097089767456}, "0pHWbrdttl8.15.mp4": {"0.5": 255.0, "1.0": 14.56727409362793, "1.5": 9.471354484558105, "2.0": 5.86631965637207, "2.5": 2.863715171813965, "3.0": 4.611979007720947, "3.5": 49.32465362548828, "4.0": 50.83203125, "4.5": 15.70920181274414, "5.0": 14.12196159362793, "5.5": 55.78081512451172, "6.0": 0.1284722238779068, "6.5": 0.1449652761220932, "7.0": 0.1597222238779068, "7.5": 0.1801215261220932, "8.0": 0.1241319477558136, "8.5": 0.1423611044883728, "9.0": 0.3923611044883728, "9.5": 18.85243034362793, "10.0": 8.61501693725586, "10.5": 20.30989646911621, "11.0": 0.6809895634651184, "11.5": 1.1605902910232544, "12.0": 0.7986111044883728, "12.5": 0.9292534589767456, "13.0": 2.8068575859069824, "13.5": 5.650173664093018, "14.0": 5.152777671813965, "14.5": 5.17881965637207, "15.0": 2.182725667953491, "15.5": 1.8151041269302368, "16.0": 0.9561631679534912, "16.5": 0.4717881977558136, "17.0": 0.6857638955116272, "17.5": 0.8637152910232544, "18.0": 1.0755208730697632, "18.5": 1.0125868320465088, "19.0": 0.8463541865348816, "19.5": 3.6419270038604736, "20.0": 32.78602600097656, "20.5": 16.63585090637207, "21.0": 6.017795085906982, "21.5": 12.326823234558105, "22.0": 13.75868034362793, "22.5": 82.69357299804688}, "0tdkFPQri14.15.mp4": {"0.5": 255.0, "1.0": 5.466145992279053, "1.5": 5.421875, "2.0": 5.50868034362793, "2.5": 51.87109375, "3.0": 12.645833015441895, "3.5": 13.59375, "4.0": 13.523871421813965, "4.5": 13.86154556274414, "5.0": 13.898871421813965, "5.5": 14.27517318725586, "6.0": 16.51692771911621, "6.5": 18.90277862548828, "7.0": 60.35069274902344, "7.5": 2.390191078186035, "8.0": 29.25434112548828, "8.5": 2.946180582046509, "9.0": 3.297308921813965, "9.5": 2.8771700859069824, "10.0": 76.8203125, "10.5": 44.01909637451172, "11.0": 50.73307418823242, "11.5": 16.66362762451172, "12.0": 7.023871421813965, "12.5": 43.39453125, "13.0": 3.094618082046509, "13.5": 3.085069417953491, "14.0": 36.42881774902344, "14.5": 3.635850667953491, "15.0": 177.77734375, "15.5": 77.00650787353516, "16.0": 117.47786712646484, "16.5": 41.65234375, "17.0": 17.54817771911621, "17.5": 61.79123306274414}, "0vklWHzRAos.9.mp4": {"0.5": 255.0, "1.0": 0.6193576455116272, "1.5": 0.61328125, "2.0": 0.3489583432674408, "2.5": 0.4001736044883728, "3.0": 0.4296875, "3.5": 0.1818576455116272, "4.0": 0.2309027761220932, "4.5": 0.1961805522441864, "5.0": 8.8984375, "5.5": 0.6267361044883728, "6.0": 0.3120659589767456, "6.5": 0.4496527910232544, "7.0": 57.59939193725586, "7.5": 19.41579818725586, "8.0": 0.5694444179534912, "8.5": 0.4826388955116272, "9.0": 0.1323784738779068, "9.5": 65.67881774902344, "10.0": 0.2126736044883728, "10.5": 2.495659828186035, "11.0": 5.229166507720947}, "1rtBD13QbXE.16.mp4": {"0.5": 255.0, "1.0": 5.99913215637207, "1.5": 4.09765625, "2.0": 4.701388835906982, "2.5": 6.06727409362793, "3.0": 5.853298664093018, "3.5": 4.578558921813965, "4.0": 57.87456512451172, "4.5": 24.51736068725586, "5.0": 18.01953125, "5.5": 12.84461784362793, "6.0": 22.39800262451172, "6.5": 6.578125, "7.0": 1.8932291269302368, "7.5": 3.932725667953491, "8.0": 19.02690887451172, "8.5": 17.84027862548828, "9.0": 4.228298664093018, "9.5": 49.42057418823242, "10.0": 2.069444417953491, "10.5": 2.2135417461395264, "11.0": 3.659288167953491}, "2FmQgQW8clw.1.mp4": {"0.5": 255.0, "1.0": 8.45876693725586, "1.5": 7.798611164093018, "2.0": 8.049479484558105, "2.5": 62.39540100097656, "3.0": 9.733941078186035, "3.5": 12.0546875, "4.0": 14.89626693725586, "4.5": 20.98741340637207, "5.0": 116.23611450195312, "5.5": 5.836371421813965, "6.0": 4.944010257720947, "6.5": 4.40234375, "7.0": 5.337239742279053, "7.5": 9.364583015441895, "8.0": 6.859808921813965, "8.5": 7.072048664093018, "9.0": 6.772135257720947, "9.5": 136.51910400390625, "10.0": 1.4921875, "10.5": 1.7604166269302368, "11.0": 1.671875, "11.5": 1.4318575859069824, "12.0": 1.5112847089767456, "12.5": 1.7252603769302368}, "2ZcsoP1AUTI.6.mp4": {"0.5": 255.0, "1.0": 3.191840171813965, "1.5": 96.85720825195312, "2.0": 11.23263931274414, "2.5": 8.296441078186035, "3.0": 6.538628578186035, "3.5": 7.428385257720947, "4.0": 8.187933921813965, "4.5": 9.09157943725586, "5.0": 8.22873306274414, "5.5": 7.613715171813965, "6.0": 6.917534828186035, "6.5": 18.31684112548828, "7.0": 23.81380271911621, "7.5": 30.15234375, "8.0": 24.53645896911621, "8.5": 35.64453125, "9.0": 29.15798568725586, "9.5": 88.56987762451172, "10.0": 2.875868082046509, "10.5": 3.647569417953491, "11.0": 2.03125, "11.5": 4.145833492279053, "12.0": 6.441840171813965, "12.5": 4.425347328186035, "13.0": 2.569444417953491, "13.5": 8.309895515441895, "14.0": 6.05859375, "14.5": 4.79774284362793, "15.0": 2.288628578186035, "15.5": 2.325086832046509, "16.0": 2.3645832538604736, "16.5": 3.280381917953491, "17.0": 7.644965171813965, "17.5": 77.35025787353516, "18.0": 20.06597137451172, "18.5": 37.47222137451172, "19.0": 31.16579818725586, "19.5": 27.20703125, "20.0": 26.26996612548828, "20.5": 32.98090362548828, "21.0": 28.82682228088379, "21.5": 35.64583206176758, "22.0": 40.30642318725586, "22.5": 40.32465362548828}, "2iOaljmFfb0.4.mp4": {"0.5": 255.0, "1.0": 84.89322662353516, "1.5": 3.8650174140930176, "2.0": 3.421006917953491, "2.5": 3.444878578186035, "3.0": 6.398003578186035, "3.5": 3.163628578186035, "4.0": 5.18272590637207, "4.5": 3.6953125, "5.0": 6.689236164093018, "5.5": 4.203558921813965, "6.0": 5.217013835906982, "6.5": 1.5902777910232544, "7.0": 1.5681424140930176, "7.5": 2.118055582046509, "8.0": 2.5859375, "8.5": 4.47352409362793, "9.0": 2.655816078186035, "9.5": 1.8485243320465088, "10.0": 2.6497395038604736, "10.5": 2.7981770038604736, "11.0": 2.14453125, "11.5": 72.38107299804688, "12.0": 0.8420138955116272, "12.5": 31.61067771911621, "13.0": 59.79036331176758, "13.5": 31.2734375, "14.0": 1.6124131679534912, "14.5": 80.27083587646484, "15.0": 32.65147399902344, "15.5": 31.84809112548828}, "2l9I3UhWP10.8.mp4": {"0.5": 255.0, "1.0": 1.5720486640930176, "1.5": 9.929253578186035, "2.0": 1.7981771230697632, "2.5": 0.9635416865348816, "3.0": 4.08116340637207, "3.5": 3.9114582538604736, "4.0": 1.4822049140930176, "4.5": 89.00650787353516, "5.0": 20.20963478088379, "5.5": 23.11024284362793, "6.0": 17.61111068725586, "6.5": 12.96701431274414, "7.0": 13.85546875, "7.5": 9.940972328186035, "8.0": 12.30381965637207, "8.5": 11.88888931274414, "9.0": 11.72743034362793, "9.5": 11.37282943725586, "10.0": 97.89236450195312, "10.5": 9.53732681274414, "11.0": 2.7404513359069824, "11.5": 3.522569417953491, "12.0": 2.41796875, "12.5": 2.366753578186035, "13.0": 2.672743082046509, "13.5": 2.2122395038604736, "14.0": 3.7526042461395264, "14.5": 1.8771700859069824, "15.0": 46.83420181274414, "15.5": 50.26779556274414, "16.0": 32.21614456176758, "16.5": 27.29600715637207, "17.0": 28.85416603088379, "17.5": 34.18663024902344, "18.0": 47.06987762451172, "18.5": 23.17925262451172, "19.0": 13.105034828186035, "19.5": 24.18967056274414, "20.0": 29.01128387451172, "20.5": 11.25564193725586, "21.0": 40.37543487548828, "21.5": 34.57551956176758, "22.0": 112.46050262451172}, "2uthP1KSZMc.11.mp4": {"0.5": 255.0, "1.0": 10.265191078186035, "1.5": 10.58376693725586, "2.0": 10.511284828186035, "2.5": 10.60763931274414, "3.0": 10.998698234558105, "3.5": 11.07986068725586, "4.0": 11.41796875, "4.5": 11.38585090637207, "5.0": 12.16623306274414, "5.5": 30.37543487548828, "6.0": 39.71744918823242, "6.5": 5.615451335906982, "7.0": 5.493923664093018, "7.5": 5.670138835906982, "8.0": 5.7421875, "8.5": 5.73741340637207, "9.0": 5.804253578186035, "9.5": 5.673177242279053, "10.0": 5.689670085906982, "10.5": 7.140625, "11.0": 13.582465171813965, "11.5": 17.28559112548828, "12.0": 14.52560806274414, "12.5": 6.416232585906982}, "3EBMpTiWAg8.11.mp4": {"0.5": 255.0, "1.0": 21.35807228088379, "1.5": 81.77474212646484, "2.0": 37.61892318725586, "2.5": 33.75303649902344, "3.0": 38.36762237548828, "3.5": 52.28168487548828, "4.0": 40.72092056274414, "4.5": 38.70269012451172, "5.0": 20.45442771911621, "5.5": 20.44053840637207, "6.0": 30.38888931274414, "6.5": 28.72916603088379, "7.0": 19.65494728088379, "7.5": 17.42230987548828, "8.0": 62.28428649902344, "8.5": 53.39713668823242, "9.0": 6.042534828186035, "9.5": 37.34418487548828, "10.0": 34.70790100097656, "10.5": 102.98133850097656, "11.0": 3.409722328186035, "11.5": 5.240451335906982, "12.0": 1.4635416269302368, "12.5": 1.0642361640930176}, "4EDw7gydFBk.25.mp4": {"0.5": 255.0, "1.0": 7.692708492279053, "1.5": 8.13194465637207, "2.0": 7.572916507720947, "2.5": 62.14540100097656, "3.0": 30.61154556274414, "3.5": 23.24565887451172, "4.0": 31.94965362548828, "4.5": 30.90234375, "5.0": 29.62890625, "5.5": 22.49479103088379, "6.0": 28.52560806274414, "6.5": 29.40321159362793, "7.0": 23.58203125, "7.5": 15.8828125, "8.0": 55.50824737548828, "8.5": 9.17361068725586, "9.0": 7.66796875, "9.5": 10.29296875, "10.0": 6.956597328186035, "10.5": 2.804253578186035, "11.0": 7.911458492279053, "11.5": 5.40711784362793, "12.0": 3.8515625, "12.5": 5.40234375, "13.0": 6.930121421813965}, "4vZ7NszztMs.9.mp4": {"0.5": 255.0, "1.0": 72.75086975097656, "1.5": 28.74565887451172, "2.0": 28.04340362548828, "2.5": 27.65147590637207, "3.0": 26.60850715637207, "3.5": 24.80208396911621, "4.0": 23.52256965637207, "4.5": 22.60286521911621, "5.0": 21.44140625, "5.5": 27.96050262451172, "6.0": 82.71137237548828, "6.5": 5.11024284362793, "7.0": 3.405816078186035, "7.5": 27.11371612548828, "8.0": 59.71354293823242, "8.5": 23.95138931274414, "9.0": 22.58680534362793, "9.5": 44.09288024902344, "10.0": 48.30859375, "10.5": 32.47352600097656, "11.0": 32.93446350097656, "11.5": 32.24175262451172, "12.0": 75.35807037353516, "12.5": 32.05642318725586, "13.0": 31.97092056274414, "13.5": 21.25043487548828, "14.0": 21.25954818725586, "14.5": 39.1484375}, "61fybvkZiDE.2.mp4": {"0.5": 255.0, "1.0": 17.13671875, "1.5": 57.3515625, "2.0": 34.78125, "2.5": 36.17578125, "3.0": 32.66276168823242, "3.5": 31.0234375, "4.0": 53.47048568725586, "4.5": 0.1059027761220932, "5.0": 0.1154513880610466, "5.5": 0.1154513880610466, "6.0": 28.03342056274414, "6.5": 6.848090171813965, "7.0": 2.331597328186035, "7.5": 0.63671875, "8.0": 45.82595443725586, "8.5": 26.87586784362793, "9.0": 28.37847137451172, "9.5": 28.7734375, "10.0": 47.09505081176758, "10.5": 25.03515625, "11.0": 27.87152862548828, "11.5": 25.44748306274414, "12.0": 21.05859375, "12.5": 19.39409637451172, "13.0": 13.17795181274414, "13.5": 63.28689193725586, "14.0": 38.22309112548828, "14.5": 15.390191078186035}, "6He8k52im8I.6.mp4": {"0.5": 255.0, "1.0": 51.43923568725586, "1.5": 18.63498306274414, "2.0": 60.60980987548828, "2.5": 72.01996612548828, "3.0": 66.29080200195312, "3.5": 80.66709899902344, "4.0": 22.62239646911621, "4.5": 60.78385543823242, "5.0": 11.886284828186035, "5.5": 29.78125, "6.0": 22.50998306274414, "6.5": 5.980902671813965, "7.0": 6.271701335906982, "7.5": 22.44400978088379, "8.0": 5.467013835906982, "8.5": 5.40321159362793, "9.0": 4.2734375, "9.5": 4.236545085906982, "10.0": 6.368489742279053, "10.5": 28.58072853088379, "11.0": 10.91319465637207, "11.5": 12.463541984558105, "12.0": 7.373263835906982, "12.5": 7.49609375, "13.0": 9.19835090637207, "13.5": 82.95008850097656, "14.0": 59.74045181274414}, "6mLBr_ZxTj4.44.mp4": {"0.5": 255.0, "1.0": 8.28993034362793, "1.5": 16.3203125, "2.0": 10.625, "2.5": 86.16580200195312, "3.0": 1.19140625, "3.5": 2.7634549140930176, "4.0": 2.222222328186035, "4.5": 1.0885416269302368, "5.0": 3.008246421813965, "5.5": 103.97525787353516, "6.0": 1.6918402910232544, "6.5": 1.8663194179534912, "7.0": 1.9496527910232544, "7.5": 3.004774332046509, "8.0": 53.09809112548828, "8.5": 1.4084200859069824, "9.0": 3.109808921813965, "9.5": 1.6953125, "10.0": 2.582899332046509, "10.5": 2.8815104961395264, "11.0": 57.62282943725586, "11.5": 25.77907943725586}, "6qTEzEcD3No.5.mp4": {"0.5": 255.0, "1.0": 59.02994918823242, "1.5": 7.668402671813965, "2.0": 6.0, "2.5": 59.17881774902344, "3.0": 6.80164909362793, "3.5": 99.52734375, "4.0": 1.3984375, "4.5": 1.4422743320465088, "5.0": 63.03689193725586, "5.5": 57.50520706176758, "6.0": 6.24913215637207, "6.5": 2.909288167953491, "7.0": 1.8298611640930176, "7.5": 5.16102409362793, "8.0": 6.680121421813965, "8.5": 13.36545181274414, "9.0": 67.98654174804688, "9.5": 2.598524332046509, "10.0": 2.551649332046509, "10.5": 2.4279513359069824, "11.0": 58.55512237548828, "11.5": 0.7243923544883728, "12.0": 0.7643229365348816, "12.5": 1.203125}, "6ureBD2VY1c.3.mp4": {"0.5": 255.0, "1.0": 17.69270896911621, "1.5": 21.14019012451172, "2.0": 20.08897590637207, "2.5": 19.59939193725586, "3.0": 17.00390625, "3.5": 10.19704818725586, "4.0": 28.54557228088379, "4.5": 23.84895896911621, "5.0": 32.0390625, "5.5": 24.40407943725586, "6.0": 19.78385353088379, "6.5": 61.38498306274414, "7.0": 49.94053649902344, "7.5": 15.23654556274414, "8.0": 21.00086784362793, "8.5": 23.43880271911621, "9.0": 41.28298568725586, "9.5": 47.83203125, "10.0": 39.23611068725586, "10.5": 33.40668487548828, "11.0": 77.82855987548828, "11.5": 43.11458206176758, "12.0": 57.25390625, "12.5": 50.87673568725586, "13.0": 39.49131774902344, "13.5": 46.72569274902344, "14.0": 97.27560424804688, "14.5": 30.12413215637207, "15.0": 43.88628387451172, "15.5": 47.88454818725586, "16.0": 29.45529556274414, "16.5": 33.31423568725586, "17.0": 19.36805534362793, "17.5": 22.76432228088379, "18.0": 41.96440887451172, "18.5": 31.51128387451172, "19.0": 19.515625, "19.5": 10.126301765441895, "20.0": 12.413628578186035, "20.5": 37.10243225097656, "21.0": 31.49956512451172}, "7dyvxojt3o8.51.mp4": {"0.5": 255.0, "1.0": 18.65798568725586, "1.5": 20.13498306274414, "2.0": 78.90755462646484, "2.5": 4.59071159362793, "3.0": 3.90234375, "3.5": 4.205295085906982, "4.0": 3.887586832046509, "4.5": 69.17621612548828, "5.0": 10.40017318725586, "5.5": 9.54210090637207, "6.0": 8.105902671813965, "6.5": 118.41580200195312, "7.0": 173.8190155029297, "7.5": 25.60112762451172, "8.0": 20.08637237548828, "8.5": 21.59635353088379, "9.0": 20.80381965637207, "9.5": 22.56857681274414, "10.0": 21.44618034362793, "10.5": 22.81467056274414, "11.0": 63.38975524902344, "11.5": 7.5859375, "12.0": 7.062933921813965, "12.5": 7.658854007720947, "13.0": 6.950520992279053, "13.5": 7.655816078186035, "14.0": 7.012152671813965, "14.5": 7.569878578186035, "15.0": 7.504340171813965, "15.5": 17.30902862548828, "16.0": 70.765625, "16.5": 17.37543487548828, "17.0": 19.42708396911621, "17.5": 18.14670181274414}, "7k3S6IsDZ28.27.mp4": {"0.5": 255.0, "1.0": 15.906683921813965, "1.5": 68.56814575195312, "2.0": 22.1171875, "2.5": 13.75607681274414, "3.0": 14.05772590637207, "3.5": 13.694878578186035, "4.0": 13.58897590637207, "4.5": 17.48654556274414, "5.0": 85.91493225097656, "5.5": 54.96788024902344, "6.0": 33.81901168823242, "6.5": 25.56206512451172, "7.0": 26.26779556274414, "7.5": 27.06944465637207, "8.0": 30.24913215637207, "8.5": 32.20138931274414, "9.0": 29.10329818725586, "9.5": 70.04600524902344, "10.0": 35.32465362548828, "10.5": 34.27300262451172, "11.0": 33.64366149902344, "11.5": 33.89887237548828}, "84GzJD_ARp0.4.mp4": {"0.5": 255.0, "1.0": 1.3020833730697632, "1.5": 1.9691840410232544, "2.0": 1.1571180820465088, "2.5": 1.4370659589767456, "3.0": 1.5776909589767456, "3.5": 15.576823234558105, "4.0": 1.5434027910232544, "4.5": 1.6879340410232544, "5.0": 0.3919270932674408, "5.5": 55.06857681274414, "6.0": 54.62717056274414, "6.5": 35.13498306274414, "7.0": 33.79340362548828, "7.5": 6.258246421813965, "8.0": 3.8645832538604736, "8.5": 42.50520706176758, "9.0": 7.998697757720947, "9.5": 10.852864265441895, "10.0": 7.07421875, "10.5": 7.215277671813965, "11.0": 6.5703125, "11.5": 22.18359375}, "8GnsEFk5HaI.28.mp4": {"0.5": 255.0, "1.0": 1.2907986640930176, "1.5": 1.3307291269302368, "2.0": 1.2638888359069824, "2.5": 1.2790799140930176, "3.0": 1.1818575859069824, "3.5": 1.2508680820465088, "4.0": 1.1449652910232544, "4.5": 1.2638888359069824, "5.0": 1.1749131679534912, "5.5": 1.1527777910232544, "6.0": 1.3624131679534912, "6.5": 1.1675347089767456, "7.0": 1.203125, "7.5": 1.0360243320465088, "8.0": 1.1527777910232544, "8.5": 1.0989583730697632, "9.0": 0.9752604365348816, "9.5": 40.41926956176758, "10.0": 0.0, "10.5": 0.0, "11.0": 0.0828993022441864, "11.5": 0.2699652910232544, "12.0": 0.0017361111240461469, "12.5": 0.0, "13.0": 0.0, "13.5": 0.0, "14.0": 0.0, "14.5": 0.0, "15.0": 0.0, "15.5": 0.0, "16.0": 56.15234375, "16.5": 62.86371612548828, "17.0": 48.9921875, "17.5": 29.47525978088379, "18.0": 37.55946350097656, "18.5": 36.203125, "19.0": 26.50217056274414, "19.5": 30.68359375, "20.0": 29.79296875, "20.5": 33.23003387451172, "21.0": 39.91666793823242, "21.5": 33.44140625, "22.0": 36.90060806274414, "22.5": 28.99913215637207, "23.0": 35.61328125, "23.5": 37.58680725097656, "24.0": 31.53298568725586, "24.5": 59.44010543823242, "25.0": 1.8559027910232544, "25.5": 1.7890625, "26.0": 1.7834200859069824, "26.5": 1.7717013359069824, "27.0": 1.6892361640930176, "27.5": 1.8971353769302368, "28.0": 46.48350524902344, "28.5": 0.4084201455116272, "29.0": 0.4809027910232544, "29.5": 0.6276041865348816, "30.0": 0.4010416567325592, "30.5": 0.5768229365348816, "31.0": 0.5555555820465088, "31.5": 0.5329861044883728, "32.0": 0.7903645634651184, "32.5": 3.4986979961395264, "33.0": 0.7235243320465088, "33.5": 0.6436631679534912}, "8TJF2VwFgD8.8.mp4": {"0.5": 255.0, "1.0": 3.6080729961395264, "1.5": 2.266493082046509, "2.0": 86.73741149902344, "2.5": 1.2669271230697632, "3.0": 2.0182292461395264, "3.5": 0.2799479067325592, "4.0": 4.941840171813965, "4.5": 90.23871612548828, "5.0": 18.04730987548828, "5.5": 11.92578125, "6.0": 8.130208015441895, "6.5": 45.53298568725586, "7.0": 6.25, "7.5": 9.829426765441895, "8.0": 11.749566078186035, "8.5": 25.78211784362793, "9.0": 11.848958015441895, "9.5": 13.61154556274414, "10.0": 15.13975715637207, "10.5": 9.94835090637207, "11.0": 12.934895515441895, "11.5": 23.08680534362793, "12.0": 21.42795181274414}, "8VR6Ao97vNc.1.mp4": {"0.5": 255.0, "1.0": 131.77040100097656, "1.5": 0.0, "2.0": 122.08246612548828, "2.5": 29.10677146911621, "3.0": 43.98871612548828, "3.5": 6.014322757720947, "4.0": 7.83897590637207, "4.5": 0.7291666865348816, "5.0": 0.01519097201526165, "5.5": 0.1623263955116272, "6.0": 0.009114583022892475, "6.5": 0.0008680555620230734, "7.0": 29.94053840637207, "7.5": 3.7552082538604736, "8.0": 3.678819417953491, "8.5": 23.18359375, "9.0": 4.486979007720947, "9.5": 81.40538024902344, "10.0": 7.430121421813965, "10.5": 4.388888835906982, "11.0": 9.444010734558105, "11.5": 93.08506774902344}, "B_A9FCsWnlI.6.mp4": {"0.5": 255.0, "1.0": 45.42578125, "1.5": 59.26996612548828, "2.0": 63.36415100097656, "2.5": 43.88715362548828, "3.0": 57.57899475097656, "3.5": 66.32769012451172, "4.0": 56.80859375, "4.5": 42.98828125, "5.0": 27.16449737548828, "5.5": 53.20182418823242, "6.0": 63.73307418823242, "6.5": 5.683159828186035, "7.0": 3.164930582046509, "7.5": 58.39800262451172, "8.0": 16.86805534362793, "8.5": 10.055989265441895, "9.0": 78.40972137451172, "9.5": 5.971354007720947, "10.0": 7.962673664093018, "10.5": 60.734375, "11.0": 22.66753387451172, "11.5": 24.93185806274414, "12.0": 31.24392318725586, "12.5": 62.12543487548828, "13.0": 10.25217056274414, "13.5": 66.58810424804688, "14.0": 12.420573234558105, "14.5": 75.84852600097656, "15.0": 34.60590362548828, "15.5": 37.62890625, "16.0": 26.45659637451172, "16.5": 24.79123306274414, "17.0": 66.54080200195312, "17.5": 43.29209899902344}, "CJ-ER0GdJd8.36.mp4": {"0.5": 255.0, "1.0": 9.46875, "1.5": 8.83203125, "2.0": 5.66015625, "2.5": 9.15407943725586, "3.0": 15.48046875, "3.5": 16.46354103088379, "4.0": 18.15928840637207, "4.5": 18.01736068725586, "5.0": 15.51085090637207, "5.5": 14.733073234558105, "6.0": 6.8046875, "6.5": 6.046875, "7.0": 53.54079818725586, "7.5": 2.885850667953491, "8.0": 8.18359375, "8.5": 3.136284828186035, "9.0": 51.78081512451172, "9.5": 34.81944274902344, "10.0": 43.61675262451172, "10.5": 39.27907943725586, "11.0": 39.47395706176758, "11.5": 35.81336975097656, "12.0": 40.67665100097656, "12.5": 38.27083206176758, "13.0": 37.32421875, "13.5": 30.24131965637207, "14.0": 56.34461975097656}, "CJorIIaGAYI.5.mp4": {"0.5": 255.0, "1.0": 8.484808921813965, "1.5": 10.811198234558105, "2.0": 12.02126693725586, "2.5": 114.94834899902344, "3.0": 0.0026041667442768812, "3.5": 151.0026092529297, "4.0": 14.95920181274414, "4.5": 97.234375, "5.0": 0.0264756940305233, "5.5": 0.0078125, "6.0": 73.53125, "6.5": 25.45355987548828, "7.0": 21.91927146911621, "7.5": 29.84635353088379, "8.0": 30.56814193725586, "8.5": 38.0625, "9.0": 31.28342056274414, "9.5": 30.98784637451172, "10.0": 36.48828125, "10.5": 26.94314193725586, "11.0": 23.66145896911621, "11.5": 12.823784828186035, "12.0": 15.64366340637207, "12.5": 28.14236068725586, "13.0": 104.625, "13.5": 43.88584899902344, "14.0": 54.56857681274414}, "ClksIvHsYcc.24.mp4": {"0.5": 255.0, "1.0": 7.513888835906982, "1.5": 34.09592056274414, "2.0": 7.573784828186035, "2.5": 8.829426765441895, "3.0": 5.505208492279053, "3.5": 36.38975524902344, "4.0": 4.0859375, "4.5": 3.413628578186035, "5.0": 41.20746612548828, "5.5": 3.4244792461395264, "6.0": 2.5447049140930176, "6.5": 2.3927950859069824, "7.0": 2.8454861640930176, "7.5": 1.2183159589767456, "8.0": 43.40234375, "8.5": 18.67795181274414, "9.0": 15.915364265441895, "9.5": 43.96440887451172, "10.0": 40.20963668823242, "10.5": 21.19184112548828, "11.0": 44.81119918823242, "11.5": 20.25, "12.0": 25.70746612548828, "12.5": 16.12022590637207, "13.0": 20.55078125, "13.5": 89.98480987548828, "14.0": 1.4131944179534912}, "E2WelDikd4s.50.mp4": {"0.5": 255.0, "1.0": 3.081163167953491, "1.5": 3.1310763359069824, "2.0": 3.1510417461395264, "2.5": 3.2747395038604736, "3.0": 3.215277671813965, "3.5": 3.265191078186035, "4.0": 3.348090171813965, "4.5": 3.211805582046509, "5.0": 2.5256075859069824, "5.5": 1.6883680820465088, "6.0": 39.57595443725586, "6.5": 129.3580780029297, "7.0": 26.734375, "7.5": 16.83854103088379, "8.0": 7.487847328186035, "8.5": 8.19444465637207, "9.0": 8.397135734558105, "9.5": 19.58637237548828, "10.0": 8.3515625, "10.5": 2.6940104961395264, "11.0": 2.5611979961395264, "11.5": 2.1471354961395264, "12.0": 1.3250868320465088, "12.5": 102.27387237548828, "13.0": 42.43793487548828, "13.5": 5.08203125, "14.0": 6.51953125, "14.5": 7.21788215637207, "15.0": 6.98133659362793, "15.5": 6.609375, "16.0": 6.658420085906982, "16.5": 6.468316078186035, "17.0": 5.891058921813965, "17.5": 5.863715171813965, "18.0": 5.789496421813965, "18.5": 4.48741340637207, "19.0": 10.45486068725586}, "EwItjBAwCbQ.37.mp4": {"0.5": 255.0, "1.0": 4.931423664093018, "1.5": 4.533420085906982, "2.0": 5.763020992279053, "2.5": 4.5546875, "3.0": 4.396701335906982, "3.5": 5.1484375, "4.0": 4.58116340637207, "4.5": 4.816840171813965, "5.0": 2.2786457538604736, "5.5": 3.979600667953491, "6.0": 1.9939236640930176, "6.5": 3.2591145038604736, "7.0": 0.4205729067325592, "7.5": 3.003038167953491, "8.0": 2.727430582046509, "8.5": 3.089409828186035, "9.0": 2.8728299140930176, "9.5": 3.86328125, "10.0": 2.546006917953491, "10.5": 2.729600667953491, "11.0": 1.2230902910232544, "11.5": 37.66102600097656, "12.0": 25.13802146911621, "12.5": 20.55078125, "13.0": 17.65451431274414, "13.5": 15.64756965637207, "14.0": 13.988715171813965, "14.5": 17.20442771911621, "15.0": 5.973090171813965, "15.5": 3.430121421813965, "16.0": 5.03125, "16.5": 3.975694417953491, "17.0": 25.90581512451172, "17.5": 11.86892318725586, "18.0": 10.164496421813965, "18.5": 8.940972328186035, "19.0": 8.572916984558105, "19.5": 4.256076335906982}, "FL9WvBWs73I.18.mp4": {"0.5": 255.0, "1.0": 8.55164909362793, "1.5": 11.50303840637207, "2.0": 16.50824737548828, "2.5": 17.24609375, "3.0": 11.042534828186035, "3.5": 61.37630081176758, "4.0": 15.840277671813965, "4.5": 22.90711784362793, "5.0": 27.65364646911621, "5.5": 20.50173568725586, "6.0": 18.08767318725586, "6.5": 42.41796875, "7.0": 0.5855034589767456, "7.5": 0.5677083134651184, "8.0": 0.9136284589767456, "8.5": 0.55859375, "9.0": 1.5594618320465088, "9.5": 1.0173611640930176, "10.0": 1.6215277910232544, "10.5": 1.890625, "11.0": 64.79036712646484, "11.5": 22.66232681274414, "12.0": 27.58897590637207, "12.5": 15.97569465637207, "13.0": 65.21701049804688, "13.5": 18.48524284362793, "14.0": 18.3203125, "14.5": 20.35807228088379, "15.0": 20.63498306274414, "15.5": 10.846354484558105, "16.0": 9.35720443725586, "16.5": 8.977864265441895, "17.0": 6.49913215637207, "17.5": 21.64192771911621, "18.0": 15.715277671813965, "18.5": 16.45008659362793, "19.0": 10.14236068725586, "19.5": 10.01171875, "20.0": 5.082465171813965, "20.5": 4.908854007720947, "21.0": 3.648871421813965, "21.5": 4.562933921813965, "22.0": 36.62760543823242, "22.5": 2.4895832538604736, "23.0": 2.182725667953491, "23.5": 3.9596354961395264, "24.0": 2.984375, "24.5": 1.5164930820465088}, "FYvIo4ylZus.1.mp4": {"0.5": 255.0, "1.0": 10.085503578186035, "1.5": 10.18836784362793, "2.0": 7.785590171813965, "2.5": 5.11631965637207, "3.0": 2.7829861640930176, "3.5": 3.383246421813965, "4.0": 3.550347328186035, "4.5": 4.112847328186035, "5.0": 22.94661521911621, "5.5": 4.75477409362793, "6.0": 5.463107585906982, "6.5": 1.7934027910232544, "7.0": 1.0390625, "7.5": 1.3424478769302368, "8.0": 1.8845486640930176, "8.5": 24.22829818725586, "9.0": 5.21484375, "9.5": 10.819878578186035, "10.0": 19.52690887451172, "10.5": 12.50303840637207, "11.0": 4.10633659362793, "11.5": 3.6184895038604736, "12.0": 5.50868034362793, "12.5": 21.06857681274414, "13.0": 1.1605902910232544, "13.5": 0.8098958134651184, "14.0": 2.831597328186035, "14.5": 5.993489742279053, "15.0": 2.1158854961395264, "15.5": 0.6202256679534912, "16.0": 15.018229484558105, "16.5": 2.0247395038604736, "17.0": 3.0920138359069824, "17.5": 1.8671875, "18.0": 1.6085069179534912, "18.5": 2.4670138359069824, "19.0": 3.0017361640930176, "19.5": 2.164496421813965, "20.0": 7.855902671813965, "20.5": 13.429253578186035}, "F_0Nud7Wa8w.7.mp4": {"0.5": 255.0, "1.0": 6.666232585906982, "1.5": 14.677083015441895, "2.0": 62.33984375, "2.5": 44.52777862548828, "3.0": 8.586371421813965, "3.5": 9.214409828186035, "4.0": 30.42317771911621, "4.5": 71.34418487548828, "5.0": 2.7248263359069824, "5.5": 2.8025174140930176, "6.0": 80.99262237548828, "6.5": 45.98350524902344, "7.0": 49.13628387451172, "7.5": 34.54817581176758, "8.0": 25.83767318725586, "8.5": 29.72569465637207, "9.0": 31.04774284362793, "9.5": 38.13454818725586, "10.0": 33.00651168823242, "10.5": 23.67751693725586, "11.0": 6.682291507720947, "11.5": 8.42274284362793, "12.0": 12.84678840637207, "12.5": 19.16493034362793, "13.0": 52.03428649902344, "13.5": 25.72699737548828, "14.0": 29.16623306274414, "14.5": 29.53255271911621, "15.0": 31.99175262451172}, "FhmTwZGt97Y.22.mp4": {"0.5": 255.0, "1.0": 29.25, "1.5": 39.37196350097656, "2.0": 70.85112762451172, "2.5": 53.35546875, "3.0": 55.73003387451172, "3.5": 25.82508659362793, "4.0": 34.00043487548828, "4.5": 39.59288024902344, "5.0": 35.10720443725586, "5.5": 19.7890625, "6.0": 79.9765625, "6.5": 19.71657943725586, "7.0": 21.92578125, "7.5": 19.12282943725586, "8.0": 10.942708015441895, "8.5": 16.97786521911621, "9.0": 12.215277671813965, "9.5": 28.3359375, "10.0": 14.730902671813965, "10.5": 28.29253387451172, "11.0": 16.95355987548828, "11.5": 45.5625}, "GBERmCAKFic.12.mp4": {"0.5": 255.0, "1.0": 0.9127604365348816, "1.5": 0.5286458134651184, "2.0": 0.4704861044883728, "2.5": 0.4583333432674408, "3.0": 0.2955729067325592, "3.5": 0.3754340410232544, "4.0": 83.7578125, "4.5": 0.4144965410232544, "5.0": 0.7625868320465088, "5.5": 11.65842056274414, "6.0": 7.93836784362793, "6.5": 53.17100524902344, "7.0": 20.6015625, "7.5": 19.70225715637207, "8.0": 5.668402671813965, "8.5": 37.47699737548828, "9.0": 15.880208015441895, "9.5": 47.41796875, "10.0": 8.090277671813965, "10.5": 9.777777671813965, "11.0": 1.5108506679534912}, "GFhJFhBUc-Y.7.mp4": {"0.5": 255.0, "1.0": 13.467448234558105, "1.5": 7.164496421813965, "2.0": 85.03385162353516, "2.5": 1.8823784589767456, "3.0": 0.9986979365348816, "3.5": 1.0347222089767456, "4.0": 0.9483506679534912, "4.5": 0.8849826455116272, "5.0": 62.2734375, "5.5": 9.201823234558105, "6.0": 27.72048568725586, "6.5": 21.96788215637207, "7.0": 27.54253387451172, "7.5": 26.74696159362793, "8.0": 29.33940887451172, "8.5": 34.23307418823242, "9.0": 36.62543487548828, "9.5": 31.58289909362793, "10.0": 28.14322853088379, "10.5": 38.20659637451172, "11.0": 22.17838478088379, "11.5": 56.03819274902344, "12.0": 7.18663215637207, "12.5": 6.996527671813965, "13.0": 2.2291667461395264, "13.5": 3.850694417953491, "14.0": 4.45225715637207, "14.5": 0.9088541865348816, "15.0": 8.80381965637207}, "Gw1fS2Nze5s.1.mp4": {"0.5": 255.0, "1.0": 0.5625, "1.5": 0.14453125, "2.0": 0.02604166604578495, "2.5": 0.014322916977107525, "3.0": 0.0, "3.5": 0.006076388992369175, "4.0": 0.0008680555620230734, "4.5": 0.0013020833721384406, "5.0": 0.0004340277810115367, "5.5": 0.0052083334885537624, "6.0": 0.5190972089767456, "6.5": 0.0855034738779068, "7.0": 0.0069444444961845875, "7.5": 0.0065104165114462376, "8.0": 0.0, "8.5": 59.28819274902344, "9.0": 47.26432418823242, "9.5": 33.83246612548828, "10.0": 34.32161331176758, "10.5": 33.45051956176758, "11.0": 17.29861068725586, "11.5": 39.38151168823242, "12.0": 23.87803840637207, "12.5": 23.98741340637207, "13.0": 36.24262237548828, "13.5": 41.92578125, "14.0": 5.519965171813965, "14.5": 8.06857681274414, "15.0": 8.84592056274414, "15.5": 11.635416984558105, "16.0": 7.04210090637207, "16.5": 5.26649284362793, "17.0": 24.37065887451172, "17.5": 21.50868034362793, "18.0": 21.96657943725586, "18.5": 12.050347328186035, "19.0": 18.22309112548828}, "GzD9IDWLodk.15.mp4": {"0.5": 255.0, "1.0": 0.0008680555620230734, "1.5": 59.51519012451172, "2.0": 4.905816078186035, "2.5": 3.9474825859069824, "3.0": 2.2018229961395264, "3.5": 1.8302950859069824, "4.0": 2.0529513359069824, "4.5": 5.941840171813965, "5.0": 3.6497395038604736, "5.5": 4.105902671813965, "6.0": 5.380642414093018, "6.5": 57.14973831176758, "7.0": 0.2291666716337204, "7.5": 0.08203125, "8.0": 0.0030381944961845875, "8.5": 56.36675262451172, "9.0": 5.896267414093018, "9.5": 4.885416507720947, "10.0": 5.531683921813965, "10.5": 8.24045181274414}, "Itob3CQDzR4.18.mp4": {"0.5": 255.0, "1.0": 2.2890625, "1.5": 2.539496421813965, "2.0": 47.79991149902344, "2.5": 45.08984375, "3.0": 3.1927082538604736, "3.5": 3.256944417953491, "4.0": 0.7534722089767456, "4.5": 0.6848958134651184, "5.0": 5.84071159362793, "5.5": 10.798176765441895, "6.0": 14.27647590637207, "6.5": 20.53255271911621, "7.0": 22.40711784362793, "7.5": 24.56770896911621, "8.0": 20.45572853088379, "8.5": 15.626301765441895, "9.0": 11.418402671813965, "9.5": 6.36414909362793, "10.0": 4.220486164093018, "10.5": 2.3658854961395264, "11.0": 1.4236111640930176, "11.5": 3.01171875, "12.0": 3.035590171813965, "12.5": 12.67578125, "13.0": 31.50694465637207, "13.5": 42.96788024902344, "14.0": 1.8138021230697632, "14.5": 0.8194444179534912, "15.0": 0.953125, "15.5": 52.29253387451172}, "JljoS47Qf_s.20.mp4": {"0.5": 255.0, "1.0": 18.64366340637207, "1.5": 32.90581512451172, "2.0": 12.626301765441895, "2.5": 23.44444465637207, "3.0": 22.41970443725586, "3.5": 10.62977409362793, "4.0": 19.47873306274414, "4.5": 15.29079818725586, "5.0": 11.805121421813965, "5.5": 2.096788167953491, "6.0": 60.5390625, "6.5": 5.713107585906982, "7.0": 8.442708015441895, "7.5": 0.7113715410232544, "8.0": 17.64019012451172, "8.5": 9.359375, "9.0": 11.79210090637207, "9.5": 1.5130208730697632, "10.0": 42.55165100097656, "10.5": 24.94791603088379, "11.0": 30.80815887451172, "11.5": 0.6467013955116272, "12.0": 10.49522590637207, "12.5": 45.8359375, "13.0": 28.39279556274414, "13.5": 26.47743034362793, "14.0": 2.0403645038604736}, "K78OKshZKIA.2.mp4": {"0.5": 255.0, "1.0": 15.6328125, "1.5": 87.70355987548828, "2.0": 0.6358506679534912, "2.5": 1.1888021230697632, "3.0": 1.2947049140930176, "3.5": 0.7469618320465088, "4.0": 1.0177950859069824, "4.5": 1.0460069179534912, "5.0": 0.4865451455116272, "5.5": 1.2300347089767456, "6.0": 0.6527777910232544, "6.5": 0.3532986044883728, "7.0": 0.7916666865348816, "7.5": 0.4609375, "8.0": 0.5842013955116272, "8.5": 0.9544270634651184, "9.0": 0.4105902910232544, "9.5": 0.2252604216337204, "10.0": 1.4322916269302368, "10.5": 1.4292534589767456, "11.0": 1.4236111640930176, "11.5": 0.8246527910232544, "12.0": 1.2873263359069824, "12.5": 81.31510162353516, "13.0": 49.44357681274414, "13.5": 29.15928840637207, "14.0": 11.31467056274414, "14.5": 39.41232681274414, "15.0": 2.2096354961395264, "15.5": 10.70138931274414, "16.0": 9.125, "16.5": 35.66926956176758, "17.0": 3.99609375, "17.5": 2.773871421813965, "18.0": 6.635416507720947, "18.5": 45.69618225097656, "19.0": 5.384548664093018, "19.5": 5.248263835906982, "20.0": 47.1484375, "20.5": 26.9375, "21.0": 17.28385353088379, "21.5": 24.90104103088379, "22.0": 30.62586784362793, "22.5": 29.28125, "23.0": 10.20138931274414, "23.5": 58.10980987548828, "24.0": 41.81553649902344, "24.5": 11.600260734558105, "25.0": 6.266058921813965, "25.5": 12.99392318725586, "26.0": 12.191840171813965}, "KGTsMFEgpFI.3.mp4": {"0.5": 255.0, "1.0": 38.3046875, "1.5": 30.20355987548828, "2.0": 36.13932418823242, "2.5": 21.40234375, "3.0": 24.23914909362793, "3.5": 46.27517318725586, "4.0": 5.913628578186035, "4.5": 7.46788215637207, "5.0": 5.27256965637207, "5.5": 12.58116340637207, "6.0": 31.265625, "6.5": 20.98611068725586, "7.0": 23.04253387451172, "7.5": 21.38541603088379, "8.0": 21.81380271911621, "8.5": 22.21875, "9.0": 27.85416603088379, "9.5": 24.03255271911621, "10.0": 38.00173568725586, "10.5": 32.61805725097656, "11.0": 28.34288215637207, "11.5": 25.7109375}, "Kd3vC0lL9C4.57.mp4": {"0.5": 255.0, "1.0": 17.40234375, "1.5": 7.96397590637207, "2.0": 11.90625, "2.5": 31.50868034362793, "3.0": 7.352864742279053, "3.5": 4.472222328186035, "4.0": 7.244357585906982, "4.5": 15.264323234558105, "5.0": 30.59461784362793, "5.5": 9.934895515441895, "6.0": 9.53211784362793, "6.5": 5.352864742279053, "7.0": 7.45616340637207, "7.5": 5.427517414093018, "8.0": 17.90885353088379, "8.5": 43.24348831176758, "9.0": 27.18359375, "9.5": 11.44835090637207, "10.0": 44.88107681274414, "10.5": 14.280816078186035, "11.0": 32.19791793823242, "11.5": 14.509114265441895, "12.0": 17.90494728088379, "12.5": 47.17404556274414, "13.0": 19.11458396911621, "13.5": 7.57508659362793, "14.0": 2.6575520038604736, "14.5": 3.0915799140930176, "15.0": 2.4626736640930176}, "KqUZIzkIWaY.7.mp4": {"0.5": 255.0, "1.0": 6.02256965637207, "1.5": 24.85807228088379, "2.0": 39.23524475097656, "2.5": 15.83984375, "3.0": 30.29340362548828, "3.5": 32.48871612548828, "4.0": 43.77430725097656, "4.5": 54.37456512451172, "5.0": 3.1315104961395264, "5.5": 5.408420085906982, "6.0": 1.3519965410232544, "6.5": 1.3576388359069824, "7.0": 69.54166412353516, "7.5": 60.38845443725586, "8.0": 38.78645706176758, "8.5": 27.92621612548828, "9.0": 25.86284637451172, "9.5": 14.07204818725586, "10.0": 17.96657943725586, "10.5": 17.2421875, "11.0": 5.46571159362793, "11.5": 21.59461784362793, "12.0": 9.376301765441895, "12.5": 31.61067771911621, "13.0": 21.19357681274414}, "LbJ7VmR7Y9g.8.mp4": {"0.5": 255.0, "1.0": 47.96354293823242, "1.5": 6.46484375, "2.0": 7.654079914093018, "2.5": 7.126302242279053, "3.0": 7.259114742279053, "3.5": 6.35243034362793, "4.0": 7.006076335906982, "4.5": 9.27734375, "5.0": 6.576822757720947, "5.5": 7.666666507720947, "6.0": 50.75390625, "6.5": 15.96006965637207, "7.0": 22.84114646911621, "7.5": 22.63498306274414, "8.0": 20.859375, "8.5": 17.07769012451172, "9.0": 10.34765625, "9.5": 17.09635353088379, "10.0": 10.108073234558105, "10.5": 9.69357681274414, "11.0": 12.753472328186035, "11.5": 60.89236068725586, "12.0": 43.3203125, "12.5": 30.14019012451172, "13.0": 34.76041793823242, "13.5": 25.07465362548828, "14.0": 33.40451431274414}, "LjKTQ9RKdaM.1.mp4": {"0.5": 255.0, "1.0": 82.78298950195312, "1.5": 9.29296875, "2.0": 9.2890625, "2.5": 9.18967056274414, "3.0": 9.040364265441895, "3.5": 8.039496421813965, "4.0": 6.901041507720947, "4.5": 6.924479007720947, "5.0": 8.32595443725586, "5.5": 7.661892414093018, "6.0": 6.678385257720947, "6.5": 59.71050262451172, "7.0": 3.8029513359069824, "7.5": 4.95008659362793, "8.0": 2.881944417953491, "8.5": 6.089409828186035, "9.0": 2.76171875, "9.5": 3.715277671813965, "10.0": 1.3654513359069824, "10.5": 0.4982638955116272, "11.0": 3.593316078186035, "11.5": 1.4123263359069824}, "LpUyGPg_Dc8.8.mp4": {"0.5": 255.0, "1.0": 9.480034828186035, "1.5": 13.042534828186035, "2.0": 27.43315887451172, "2.5": 32.19487762451172, "3.0": 17.10546875, "3.5": 31.47439193725586, "4.0": 62.8125, "4.5": 8.16102409362793, "5.0": 4.81640625, "5.5": 1.6792534589767456, "6.0": 0.6840277910232544, "6.5": 0.8767361044883728, "7.0": 1.1128472089767456, "7.5": 1.4796006679534912, "8.0": 1.04296875, "8.5": 1.5590277910232544, "9.0": 51.12890625, "9.5": 10.06944465637207, "10.0": 15.188801765441895, "10.5": 11.951823234558105, "11.0": 12.74826431274414, "11.5": 15.15234375, "12.0": 15.6640625, "12.5": 22.67621612548828, "13.0": 24.34244728088379, "13.5": 25.04817771911621, "14.0": 30.23654556274414, "14.5": 33.67057418823242, "15.0": 29.77126693725586, "15.5": 24.20052146911621, "16.0": 27.82508659362793, "16.5": 32.2578125, "17.0": 19.84939193725586, "17.5": 33.05598831176758, "18.0": 28.953125, "18.5": 44.53689193725586, "19.0": 33.02040100097656, "19.5": 30.49131965637207, "20.0": 26.47135353088379, "20.5": 36.95355987548828, "21.0": 31.20920181274414}, "MtuZyKJPjAM.4.mp4": {"0.5": 255.0, "1.0": 4.173611164093018, "1.5": 3.2434895038604736, "2.0": 69.62673950195312, "2.5": 16.20008659362793, "3.0": 10.494791984558105, "3.5": 13.62326431274414, "4.0": 9.81336784362793, "4.5": 10.87109375, "5.0": 12.846354484558105, "5.5": 9.346354484558105, "6.0": 10.33116340637207, "6.5": 7.748697757720947, "7.0": 6.853298664093018, "7.5": 4.01258659362793, "8.0": 65.79122924804688, "8.5": 1.8020833730697632, "9.0": 2.0807292461395264, "9.5": 2.2005207538604736, "10.0": 2.58984375, "10.5": 2.7942707538604736, "11.0": 3.7486979961395264, "11.5": 2.973524332046509, "12.0": 3.167534828186035, "12.5": 4.444878578186035, "13.0": 34.56119918823242, "13.5": 23.21484375, "14.0": 25.59809112548828, "14.5": 24.10199737548828, "15.0": 19.62673568725586, "15.5": 16.74565887451172, "16.0": 9.70529556274414, "16.5": 4.508246421813965, "17.0": 38.62152862548828, "17.5": 33.11241149902344, "18.0": 20.96657943725586, "18.5": 33.34852600097656, "19.0": 33.74956512451172, "19.5": 38.79296875}, "O7aiG0D0qo0.25.mp4": {"0.5": 255.0, "1.0": 2.918402671813965, "1.5": 17.12065887451172, "2.0": 89.55902862548828, "2.5": 5.089409828186035, "3.0": 3.9296875, "3.5": 2.68359375, "4.0": 1.3415799140930176, "4.5": 1.4127603769302368, "5.0": 1.5078125, "5.5": 1.2643228769302368, "6.0": 1.0993924140930176, "6.5": 3.5104167461395264, "7.0": 109.93706512451172, "7.5": 39.49088668823242, "8.0": 26.38237762451172, "8.5": 4.99913215637207, "9.0": 0.3619791567325592, "9.5": 0.03125, "10.0": 4.386284828186035, "10.5": 7.7109375, "11.0": 28.62803840637207, "11.5": 87.50868225097656, "12.0": 12.60069465637207}, "PXMngeRSdsE.25.mp4": {"0.5": 255.0, "1.0": 25.24479103088379, "1.5": 21.46006965637207, "2.0": 22.3125, "2.5": 13.391926765441895, "3.0": 0.7513020634651184, "3.5": 31.51953125, "4.0": 24.16927146911621, "4.5": 3.5759549140930176, "5.0": 2.616319417953491, "5.5": 1.8294271230697632, "6.0": 17.04296875, "6.5": 60.65972137451172, "7.0": 0.4244791567325592, "7.5": 22.11762237548828, "8.0": 41.88324737548828, "8.5": 12.52647590637207, "9.0": 26.20486068725586, "9.5": 19.73697853088379, "10.0": 35.85980987548828, "10.5": 22.21484375, "11.0": 29.85199737548828, "11.5": 32.01953125, "12.0": 24.82335090637207, "12.5": 29.90060806274414, "13.0": 22.82855987548828, "13.5": 14.88368034362793, "14.0": 18.36805534362793, "14.5": 29.02951431274414, "15.0": 9.778645515441895, "15.5": 7.328125, "16.0": 6.98741340637207, "16.5": 5.947048664093018, "17.0": 9.003472328186035}, "PvvMz5FBofo.6.mp4": {"0.5": 255.0, "1.0": 51.85546875, "1.5": 64.55381774902344, "2.0": 32.73307418823242, "2.5": 28.58506965637207, "3.0": 31.6796875, "3.5": 33.48784637451172, "4.0": 60.96267318725586, "4.5": 1.1037325859069824, "5.0": 0.8333333134651184, "5.5": 0.5924479365348816, "6.0": 2.22265625, "6.5": 4.657986164093018, "7.0": 6.256076335906982, "7.5": 8.19357681274414, "8.0": 6.905816078186035, "8.5": 52.99392318725586, "9.0": 25.35069465637207, "9.5": 22.51822853088379, "10.0": 22.53732681274414, "10.5": 21.90581512451172, "11.0": 20.92795181274414, "11.5": 29.98003387451172}, "RdhTvVPcoIo.12.mp4": {"0.5": 255.0, "1.0": 21.29340362548828, "1.5": 18.43663215637207, "2.0": 7.68836784362793, "2.5": 6.394965171813965, "3.0": 10.710503578186035, "3.5": 11.09765625, "4.0": 48.9375, "4.5": 2.671441078186035, "5.0": 2.8025174140930176, "5.5": 52.2265625, "6.0": 32.15147399902344, "6.5": 8.96267318725586, "7.0": 123.13237762451172, "7.5": 12.28732681274414, "8.0": 76.65277862548828, "8.5": 68.47482299804688, "9.0": 97.88715362548828, "9.5": 8.1875, "10.0": 77.69791412353516, "10.5": 8.140625, "11.0": 33.97569274902344, "11.5": 1.09765625, "12.0": 2.0603299140930176, "12.5": 2.2161457538604736, "13.0": 1.6584200859069824}, "RjC6aV1SNIQ.3.mp4": {"0.5": 255.0, "1.0": 10.66493034362793, "1.5": 39.99826431274414, "2.0": 20.15798568725586, "2.5": 18.49392318725586, "3.0": 10.43272590637207, "3.5": 10.67404556274414, "4.0": 9.051215171813965, "4.5": 8.855034828186035, "5.0": 9.778645515441895, "5.5": 12.009114265441895, "6.0": 4.344183921813965, "6.5": 5.054253578186035, "7.0": 8.13975715637207, "7.5": 10.26171875, "8.0": 14.46180534362793, "8.5": 17.28168487548828, "9.0": 26.26388931274414, "9.5": 40.14670181274414, "10.0": 35.82161331176758, "10.5": 30.95355987548828, "11.0": 28.83029556274414, "11.5": 24.81119728088379, "12.0": 27.19140625, "12.5": 41.30729293823242, "13.0": 16.61024284362793, "13.5": 18.89670181274414, "14.0": 28.11241340637207, "14.5": 29.33940887451172, "15.0": 19.22873306274414, "15.5": 15.147135734558105, "16.0": 18.63194465637207, "16.5": 29.84765625, "17.0": 12.42361068725586, "17.5": 8.97829818725586, "18.0": 9.96006965637207, "18.5": 10.30164909362793}, "VWlP9ZnqmIQ.14.mp4": {"0.5": 255.0, "1.0": 2.0364582538604736, "1.5": 1.4500868320465088, "2.0": 0.8194444179534912, "2.5": 6.615451335906982, "3.0": 71.37369537353516, "3.5": 55.62456512451172, "4.0": 28.08637237548828, "4.5": 25.37369728088379, "5.0": 65.20355987548828, "5.5": 63.64973831176758, "6.0": 35.91493225097656, "6.5": 44.05338668823242, "7.0": 74.49435424804688, "7.5": 61.21223831176758, "8.0": 29.81900978088379, "8.5": 16.91319465637207, "9.0": 65.10199737548828, "9.5": 9.01953125, "10.0": 10.737847328186035, "10.5": 8.41015625, "11.0": 8.377604484558105, "11.5": 8.14756965637207, "12.0": 7.588541507720947, "12.5": 5.23350715637207, "13.0": 8.42881965637207, "13.5": 9.76388931274414, "14.0": 7.26258659362793, "14.5": 58.74088668823242, "15.0": 30.70789909362793, "15.5": 30.29123306274414, "16.0": 34.80598831176758, "16.5": 34.02951431274414, "17.0": 33.87934112548828, "17.5": 38.98350524902344, "18.0": 32.17795181274414, "18.5": 33.65451431274414, "19.0": 22.0390625, "19.5": 18.50217056274414, "20.0": 13.05685806274414, "20.5": 18.50043487548828, "21.0": 28.07204818725586, "21.5": 37.16059112548828, "22.0": 39.81553649902344, "22.5": 19.72352409362793, "23.0": 30.81814193725586}, "WQFI_AIsYtg.15.mp4": {"0.5": 255.0, "1.0": 122.50130462646484, "1.5": 122.09201049804688, "2.0": 44.95182418823242, "2.5": 38.0078125, "3.0": 20.5625, "3.5": 50.10676956176758, "4.0": 41.70008850097656, "4.5": 55.48003387451172, "5.0": 28.65711784362793, "5.5": 43.25651168823242, "6.0": 25.54861068725586, "6.5": 19.04644012451172, "7.0": 39.10850524902344, "7.5": 33.40668487548828, "8.0": 25.91840362548828, "8.5": 36.21180725097656, "9.0": 46.17751693725586, "9.5": 52.60026168823242, "10.0": 39.10894012451172, "10.5": 41.03211975097656, "11.0": 47.8515625, "11.5": 16.84114646911621, "12.0": 11.687933921813965, "12.5": 26.16493034362793, "13.0": 29.09071159362793, "13.5": 31.37803840637207, "14.0": 20.97092056274414, "14.5": 64.67534637451172, "15.0": 63.10416793823242, "15.5": 29.88107681274414, "16.0": 8.647135734558105, "16.5": 21.26215362548828, "17.0": 29.30034637451172, "17.5": 44.63845443725586, "18.0": 43.35807418823242, "18.5": 42.53125}, "WV1-ZuBmP-E.12.mp4": {"0.5": 255.0, "1.0": 10.532551765441895, "1.5": 20.34982681274414, "2.0": 12.51388931274414, "2.5": 19.26085090637207, "3.0": 91.61675262451172, "3.5": 9.17404556274414, "4.0": 5.451388835906982, "4.5": 1.9283853769302368, "5.0": 6.234375, "5.5": 91.95790100097656, "6.0": 2.1545138359069824, "6.5": 4.558159828186035, "7.0": 2.995659828186035, "7.5": 13.22439193725586, "8.0": 26.94400978088379, "8.5": 74.63932037353516, "9.0": 4.078125, "9.5": 1.6189236640930176, "10.0": 2.6783854961395264, "10.5": 14.01649284362793, "11.0": 3.836805582046509, "11.5": 5.327691078186035, "12.0": 3.855034828186035, "12.5": 7.065972328186035, "13.0": 6.411458492279053, "13.5": 1.9322916269302368, "14.0": 5.810329914093018, "14.5": 4.654947757720947, "15.0": 70.29600524902344}, "WZcMlzsh4XM.1.mp4": {"0.5": 255.0, "1.0": 24.23958396911621, "1.5": 21.39453125, "2.0": 84.53949737548828, "2.5": 91.92534637451172, "3.0": 93.81423950195312, "3.5": 55.79470443725586, "4.0": 46.17317581176758, "4.5": 88.31423950195312, "5.0": 79.48784637451172, "5.5": 65.05165100097656, "6.0": 57.94704818725586, "6.5": 23.56163215637207, "7.0": 37.875, "7.5": 62.26692581176758, "8.0": 46.52300262451172, "8.5": 99.64540100097656, "9.0": 17.45703125, "9.5": 18.82378387451172, "10.0": 5.154513835906982, "10.5": 6.09461784362793}, "XqDwTKVeUcI.31.mp4": {"0.5": 255.0, "1.0": 1.0959200859069824, "1.5": 1.0082465410232544, "2.0": 63.27387237548828, "2.5": 4.031683921813965, "3.0": 4.052951335906982, "3.5": 45.91796875, "4.0": 35.46918487548828, "4.5": 42.62022399902344, "5.0": 14.05295181274414, "5.5": 38.93663024902344, "6.0": 30.89279556274414, "6.5": 53.30034637451172, "7.0": 20.59939193725586, "7.5": 21.77864646911621, "8.0": 20.27994728088379, "8.5": 18.84505271911621, "9.0": 13.78993034362793, "9.5": 63.11198043823242, "10.0": 19.76953125, "10.5": 21.16840362548828, "11.0": 10.122395515441895, "11.5": 37.51388931274414, "12.0": 4.34852409362793, "12.5": 16.88585090637207, "13.0": 20.18880271911621, "13.5": 8.32508659362793, "14.0": 23.74782943725586, "14.5": 4.260416507720947, "15.0": 27.86849021911621, "15.5": 23.87152862548828, "16.0": 18.43142318725586}, "YofCMYZhYcs.5.mp4": {"0.5": 255.0, "1.0": 2.019965171813965, "1.5": 2.1657986640930176, "2.0": 1.515625, "2.5": 2.5295138359069824, "3.0": 2.1809895038604736, "3.5": 1.4735243320465088, "4.0": 1.4665799140930176, "4.5": 3.35546875, "5.0": 3.5217013359069824, "5.5": 3.8728299140930176, "6.0": 2.9192707538604736, "6.5": 75.11024475097656, "7.0": 2.1267361640930176, "7.5": 1.4236111640930176, "8.0": 0.7161458134651184, "8.5": 0.9895833134651184, "9.0": 0.8038194179534912, "9.5": 0.5503472089767456, "10.0": 1.109375, "10.5": 0.8072916865348816, "11.0": 1.1276041269302368, "11.5": 0.8107638955116272, "12.0": 0.6953125, "12.5": 2.112847328186035, "13.0": 65.03689575195312, "13.5": 5.98524284362793, "14.0": 3.58984375, "14.5": 3.2092013359069824, "15.0": 2.003472328186035, "15.5": 51.77256774902344, "16.0": 3.526909828186035, "16.5": 5.10243034362793, "17.0": 4.574652671813965, "17.5": 4.173611164093018, "18.0": 5.157986164093018, "18.5": 6.80946159362793, "19.0": 9.717448234558105, "19.5": 4.392361164093018}, "ZRL2bzC2GGE.67.mp4": {"0.5": 255.0, "1.0": 2.563368082046509, "1.5": 3.1393229961395264, "2.0": 2.7942707538604736, "2.5": 3.0872395038604736, "3.0": 3.624566078186035, "3.5": 2.292534828186035, "4.0": 4.111545085906982, "4.5": 2.276909828186035, "5.0": 3.531683921813965, "5.5": 4.907552242279053, "6.0": 3.6536457538604736, "6.5": 2.065538167953491, "7.0": 2.312066078186035, "7.5": 54.73567581176758, "8.0": 21.98220443725586, "8.5": 25.20703125, "9.0": 25.26866340637207, "9.5": 25.15277862548828, "10.0": 23.78602409362793, "10.5": 22.04730987548828, "11.0": 16.98003387451172, "11.5": 3.14453125, "12.0": 1.9813368320465088, "12.5": 1.5533853769302368, "13.0": 0.9201388955116272, "13.5": 49.11979293823242, "14.0": 3.303819417953491, "14.5": 2.7912325859069824, "15.0": 4.21006965637207, "15.5": 3.6536457538604736, "16.0": 1.1653646230697632, "16.5": 3.198784828186035, "17.0": 3.6228299140930176, "17.5": 46.39583206176758}, "_8UAR6y9vtM.1.mp4": {"0.5": 255.0, "1.0": 3.6697049140930176, "1.5": 3.3927950859069824, "2.0": 8.33810806274414, "2.5": 6.469183921813965, "3.0": 7.096354007720947, "3.5": 32.88975524902344, "4.0": 9.279948234558105, "4.5": 9.64279556274414, "5.0": 10.34375, "5.5": 3.164496421813965, "6.0": 3.11328125, "6.5": 4.2890625, "7.0": 4.233941078186035, "7.5": 2.971788167953491, "8.0": 2.9557292461395264, "8.5": 3.9782986640930176, "9.0": 3.0412325859069824, "9.5": 3.0368924140930176, "10.0": 3.0065104961395264, "10.5": 3.003472328186035, "11.0": 2.9665799140930176, "11.5": 2.940972328186035, "12.0": 3.0247395038604736, "12.5": 3.194444417953491, "13.0": 3.245659828186035, "13.5": 23.77821159362793, "14.0": 37.36024475097656, "14.5": 11.294270515441895, "15.0": 10.40451431274414, "15.5": 9.60633659362793, "16.0": 18.09114646911621, "16.5": 18.20746612548828, "17.0": 18.85677146911621, "17.5": 81.15364837646484, "18.0": 23.65277862548828, "18.5": 79.44444274902344, "19.0": 41.98741149902344, "19.5": 4.622395992279053, "20.0": 5.277777671813965, "20.5": 7.127604007720947, "21.0": 8.260416984558105, "21.5": 9.29774284362793, "22.0": 11.47048568725586, "22.5": 16.421875, "23.0": 24.53819465637207, "23.5": 42.84288024902344}, "_BVVEW8y_VA.9.mp4": {"0.5": 255.0, "1.0": 5.944010257720947, "1.5": 33.33073043823242, "2.0": 30.26085090637207, "2.5": 19.49045181274414, "3.0": 17.20486068725586, "3.5": 12.07638931274414, "4.0": 73.48611450195312, "4.5": 58.58333206176758, "5.0": 46.27256774902344, "5.5": 55.05295181274414, "6.0": 41.21831512451172, "6.5": 41.63628387451172, "7.0": 39.07986068725586, "7.5": 32.67144012451172, "8.0": 39.36241149902344, "8.5": 34.32942581176758, "9.0": 48.70095443725586, "9.5": 55.27430725097656, "10.0": 35.38194274902344, "10.5": 31.74001693725586, "11.0": 32.78819274902344, "11.5": 34.38237762451172, "12.0": 19.3359375, "12.5": 29.01085090637207, "13.0": 12.61154556274414, "13.5": 0.0954861119389534, "14.0": 0.0434027761220932, "14.5": 0.006076388992369175}, "_Jh8mvvoXsM.1.mp4": {"0.5": 255.0, "1.0": 6.411458492279053, "1.5": 5.93055534362793, "2.0": 5.1640625, "2.5": 4.021267414093018, "3.0": 3.5490450859069824, "3.5": 4.753472328186035, "4.0": 5.90625, "4.5": 3.0989582538604736, "5.0": 4.915364742279053, "5.5": 2.922743082046509, "6.0": 85.09765625, "6.5": 1.6848958730697632, "7.0": 1.3363715410232544, "7.5": 1.7478299140930176, "8.0": 1.5811631679534912, "8.5": 77.5546875, "9.0": 22.85460090637207, "9.5": 21.18663215637207, "10.0": 24.54166603088379}, "_KgWye11I8g.7.mp4": {"0.5": 255.0, "1.0": 4.512152671813965, "1.5": 4.42100715637207, "2.0": 4.20616340637207, "2.5": 4.051215171813965, "3.0": 33.39626693725586, "3.5": 49.98828125, "4.0": 51.96831512451172, "4.5": 22.83854103088379, "5.0": 21.02474021911621, "5.5": 14.05164909362793, "6.0": 3.438368082046509, "6.5": 4.724826335906982, "7.0": 12.760416984558105, "7.5": 16.81510353088379, "8.0": 18.45442771911621, "8.5": 16.78515625, "9.0": 14.88368034362793, "9.5": 32.9375, "10.0": 62.97309112548828, "10.5": 27.42447853088379, "11.0": 115.21875, "11.5": 45.22959899902344, "12.0": 27.43836784362793, "12.5": 34.70182418823242, "13.0": 44.55685806274414, "13.5": 68.06510162353516, "14.0": 76.34982299804688}, "_T2A_lgn69w.37.mp4": {"0.5": 255.0, "1.0": 57.33246612548828, "1.5": 7.4609375, "2.0": 4.040364742279053, "2.5": 6.951388835906982, "3.0": 4.266927242279053, "3.5": 3.938368082046509, "4.0": 6.420572757720947, "4.5": 5.33289909362793, "5.0": 6.132378578186035, "5.5": 41.94227600097656, "6.0": 31.75868034362793, "6.5": 15.694878578186035, "7.0": 5.261284828186035, "7.5": 6.310763835906982, "8.0": 5.455295085906982, "8.5": 10.265191078186035, "9.0": 8.12282943725586, "9.5": 5.298177242279053, "10.0": 10.562933921813965, "10.5": 8.44921875, "11.0": 7.286458492279053, "11.5": 7.327691078186035, "12.0": 5.322916507720947, "12.5": 9.12282943725586, "13.0": 12.53125, "13.5": 91.05294799804688, "14.0": 21.16796875, "14.5": 27.10633659362793, "15.0": 34.34244918823242, "15.5": 41.16753387451172, "16.0": 34.734375, "16.5": 31.54296875, "17.0": 25.37282943725586, "17.5": 22.54730987548828, "18.0": 85.34071350097656, "18.5": 13.50564193725586, "19.0": 20.46180534362793, "19.5": 30.21310806274414, "20.0": 39.61024475097656, "20.5": 42.26128387451172, "21.0": 31.14756965637207, "21.5": 12.43055534362793, "22.0": 16.52821159362793, "22.5": 17.04383659362793, "23.0": 5.099826335906982, "23.5": 7.99609375, "24.0": 6.292534828186035, "24.5": 9.798176765441895, "25.0": 11.46397590637207, "25.5": 13.13368034362793, "26.0": 16.13237762451172, "26.5": 28.36111068725586, "27.0": 36.63064193725586, "27.5": 42.56380081176758, "28.0": 50.06857681274414, "28.5": 27.46224021911621, "29.0": 18.37890625, "29.5": 19.79861068725586, "30.0": 19.58116340637207, "30.5": 12.0703125, "31.0": 9.66970443725586, "31.5": 14.098958015441895, "32.0": 11.89756965637207, "32.5": 8.374566078186035}, "_nbQsnUvlo4.9.mp4": {"0.5": 255.0, "1.0": 11.56944465637207, "1.5": 10.72743034362793, "2.0": 4.806423664093018, "2.5": 14.967448234558105, "3.0": 19.05685806274414, "3.5": 22.50173568725586, "4.0": 7.302083492279053, "4.5": 4.83897590637207, "5.0": 1.2513021230697632, "5.5": 43.70963668823242, "6.0": 18.87282943725586, "6.5": 8.430989265441895, "7.0": 14.38194465637207, "7.5": 14.138020515441895, "8.0": 4.430989742279053, "8.5": 5.209201335906982, "9.0": 9.953125, "9.5": 9.013020515441895, "10.0": 43.38454818725586, "10.5": 4.467013835906982, "11.0": 8.2578125, "11.5": 10.02430534362793, "12.0": 16.16189193725586, "12.5": 6.24609375}, "aGu1HuPl3Gs.1.mp4": {"0.5": 255.0, "1.0": 2.8541667461395264, "1.5": 14.846354484558105, "2.0": 15.86935806274414, "2.5": 0.1258680522441864, "3.0": 0.0850694477558136, "3.5": 0.2005208283662796, "4.0": 0.2986111044883728, "4.5": 0.03081597201526165, "5.0": 9.656683921813965, "5.5": 9.606770515441895, "6.0": 0.009548611007630825, "6.5": 34.22699737548828, "7.0": 29.80078125, "7.5": 8.179253578186035, "8.0": 0.1532118022441864, "8.5": 0.1019965261220932, "9.0": 0.5321180820465088, "9.5": 0.1671006977558136, "10.0": 0.2052951455116272, "10.5": 0.3098958432674408, "11.0": 0.4314236044883728, "11.5": 0.1970486044883728, "12.0": 0.1423611044883728, "12.5": 0.2608506977558136, "13.0": 0.4926215410232544, "13.5": 0.3919270932674408, "14.0": 2.327256917953491, "14.5": 1.6184896230697632}, "bTMVVjv5zQ0.2.mp4": {"0.5": 255.0, "1.0": 0.4748263955116272, "1.5": 61.97005081176758, "2.0": 3.269965171813965, "2.5": 15.794270515441895, "3.0": 0.9383680820465088, "3.5": 0.94921875, "4.0": 0.8997395634651184, "4.5": 22.69400978088379, "5.0": 3.213975667953491, "5.5": 3.2356770038604736, "6.0": 3.1822917461395264, "6.5": 26.38454818725586, "7.0": 8.52430534362793, "7.5": 8.585503578186035, "8.0": 28.60677146911621, "8.5": 16.26605987548828, "9.0": 0.6879340410232544, "9.5": 65.40103912353516, "10.0": 6.123697757720947, "10.5": 5.441840171813965, "11.0": 5.001736164093018, "11.5": 3.6857638359069824, "12.0": 15.93142318725586, "12.5": 90.81466674804688}, "cN039e4NGAw.7.mp4": {"0.5": 255.0, "1.0": 11.28515625, "1.5": 37.19314193725586, "2.0": 98.52908325195312, "2.5": 15.61414909362793, "3.0": 1.0629340410232544, "3.5": 1.1345486640930176, "4.0": 0.8884548544883728, "4.5": 0.9973958134651184, "5.0": 1.1032986640930176, "5.5": 1.10546875, "6.0": 2.1341145038604736, "6.5": 1.6553819179534912, "7.0": 2.881944417953491, "7.5": 1.9552950859069824, "8.0": 102.73090362548828, "8.5": 25.75694465637207, "9.0": 23.79947853088379, "9.5": 72.89149475097656, "10.0": 1.8723958730697632, "10.5": 1.8697916269302368, "11.0": 78.60025787353516, "11.5": 19.63932228088379}, "crFioD1EelM.13.mp4": {"0.5": 255.0, "1.0": 17.33159637451172, "1.5": 40.58159637451172, "2.0": 0.9409722089767456, "2.5": 0.5642361044883728, "3.0": 0.6657986044883728, "3.5": 0.6280381679534912, "4.0": 0.6024305820465088, "4.5": 0.5, "5.0": 34.46831512451172, "5.5": 19.02994728088379, "6.0": 8.67491340637207, "6.5": 9.97960090637207, "7.0": 6.423611164093018, "7.5": 5.694878578186035, "8.0": 7.322482585906982, "8.5": 6.02734375, "9.0": 4.9140625, "9.5": 3.852430582046509, "10.0": 3.9778645038604736, "10.5": 5.623263835906982, "11.0": 74.29080200195312, "11.5": 3.59765625, "12.0": 3.5768229961395264, "12.5": 3.766058921813965}, "dR9jfG9Zr5A.2.mp4": {"0.5": 255.0, "1.0": 5.451388835906982, "1.5": 7.773871421813965, "2.0": 7.108072757720947, "2.5": 6.54296875, "3.0": 4.970486164093018, "3.5": 3.7282986640930176, "4.0": 8.452691078186035, "4.5": 7.744357585906982, "5.0": 7.298177242279053, "5.5": 8.417534828186035, "6.0": 5.17578125, "6.5": 7.547308921813965, "7.0": 8.8515625, "7.5": 8.27821159362793, "8.0": 6.255642414093018, "8.5": 3.4704861640930176, "9.0": 5.42578125, "9.5": 3.504340171813965, "10.0": 4.457465171813965, "10.5": 3.4509549140930176, "11.0": 2.0681424140930176, "11.5": 91.97916412353516, "12.0": 4.161892414093018, "12.5": 3.3919270038604736, "13.0": 3.090277671813965, "13.5": 2.711805582046509, "14.0": 61.93619918823242, "14.5": 6.23350715637207, "15.0": 7.91015625, "15.5": 6.90928840637207, "16.0": 8.32725715637207, "16.5": 7.857638835906982, "17.0": 71.85372924804688, "17.5": 69.921875, "18.0": 14.085503578186035, "18.5": 21.57421875, "19.0": 17.23871612548828, "19.5": 15.538628578186035, "20.0": 44.38975524902344, "20.5": 0.3276909589767456, "21.0": 0.3932291567325592, "21.5": 1.7374131679534912, "22.0": 0.78515625, "22.5": 73.18663024902344, "23.0": 21.66449737548828, "23.5": 17.26692771911621, "24.0": 21.76258659362793, "24.5": 23.6875}, "dUAPtHIz8Rg.5.mp4": {"0.5": 255.0, "1.0": 11.925347328186035, "1.5": 18.61935806274414, "2.0": 8.867621421813965, "2.5": 9.48611068725586, "3.0": 60.38715362548828, "3.5": 61.58290100097656, "4.0": 41.27777862548828, "4.5": 36.91362762451172, "5.0": 33.73176956176758, "5.5": 57.328125, "6.0": 40.55946350097656, "6.5": 46.99652862548828, "7.0": 26.22309112548828, "7.5": 16.93359375, "8.0": 38.01692581176758, "8.5": 22.89670181274414, "9.0": 14.32508659362793, "9.5": 11.63758659362793, "10.0": 10.952691078186035, "10.5": 21.10590362548828, "11.0": 20.07465362548828, "11.5": 13.765191078186035, "12.0": 8.45486068725586, "12.5": 7.047308921813965, "13.0": 55.69618225097656, "13.5": 17.31032943725586, "14.0": 6.295138835906982, "14.5": 13.58680534362793, "15.0": 13.74045181274414, "15.5": 5.915798664093018, "16.0": 5.32118034362793, "16.5": 4.716579914093018, "17.0": 2.370225667953491, "17.5": 3.984375, "18.0": 5.463107585906982, "18.5": 4.361979007720947, "19.0": 7.718316078186035, "19.5": 6.69921875, "20.0": 6.351128578186035, "20.5": 3.051649332046509, "21.0": 2.6497395038604736, "21.5": 4.16493034362793, "22.0": 4.65538215637207, "22.5": 51.07421875, "23.0": 17.71614646911621, "23.5": 15.839409828186035, "24.0": 17.84505271911621, "24.5": 12.67664909362793, "25.0": 16.65928840637207}, "e3aLYhXDbz8.12.mp4": {"0.5": 255.0, "1.0": 9.112847328186035, "1.5": 9.60460090637207, "2.0": 9.518229484558105, "2.5": 9.42404556274414, "3.0": 9.752604484558105, "3.5": 27.203125, "4.0": 35.50737762451172, "4.5": 1.2473958730697632, "5.0": 1.3185763359069824, "5.5": 1.2204861640930176, "6.0": 1.2560763359069824, "6.5": 1.2300347089767456, "7.0": 1.3272569179534912, "7.5": 1.2712674140930176, "8.0": 53.73698043823242, "8.5": 41.09201431274414, "9.0": 1.4053819179534912, "9.5": 2.1653645038604736, "10.0": 2.049913167953491, "10.5": 3.0455729961395264, "11.0": 3.952691078186035, "11.5": 3.516493082046509}, "eDZVdZvmG74.3.mp4": {"0.5": 255.0, "1.0": 55.16362762451172, "1.5": 0.6588541865348816, "2.0": 0.3706597089767456, "2.5": 1.2274305820465088, "3.0": 0.8337673544883728, "3.5": 0.72265625, "4.0": 58.00260543823242, "4.5": 0.6883680820465088, "5.0": 1.2847222089767456, "5.5": 19.94921875, "6.0": 30.43706512451172, "6.5": 30.79557228088379, "7.0": 37.640625, "7.5": 50.56770706176758, "8.0": 25.12890625, "8.5": 3.019965171813965, "9.0": 1.5047743320465088, "9.5": 70.96875, "10.0": 1.0368924140930176, "10.5": 0.5559895634651184, "11.0": 0.5607638955116272, "11.5": 0.3862847089767456, "12.0": 0.2664930522441864, "12.5": 0.1935763955116272, "13.0": 0.1805555522441864, "13.5": 0.1827256977558136, "14.0": 0.1336805522441864, "14.5": 68.88237762451172, "15.0": 8.46701431274414, "15.5": 1.6388888359069824, "16.0": 0.8559027910232544, "16.5": 0.3676215410232544, "17.0": 0.5147569179534912, "17.5": 0.3875868022441864, "18.0": 0.3146701455116272, "18.5": 59.31119918823242, "19.0": 0.3133680522441864, "19.5": 0.3033854067325592, "20.0": 0.3697916567325592, "20.5": 0.6640625, "21.0": 83.57378387451172}, "eairs2hMa3I.31.mp4": {"0.5": 255.0, "1.0": 54.23654556274414, "1.5": 9.29774284362793, "2.0": 4.018229007720947, "2.5": 3.2009549140930176, "3.0": 4.66015625, "3.5": 22.29904556274414, "4.0": 31.58289909362793, "4.5": 9.385416984558105, "5.0": 9.238715171813965, "5.5": 9.002604484558105, "6.0": 55.69965362548828, "6.5": 58.82551956176758, "7.0": 34.16015625, "7.5": 27.33767318725586, "8.0": 15.37022590637207, "8.5": 15.6640625, "9.0": 15.99045181274414, "9.5": 14.09592056274414, "10.0": 6.555989742279053, "10.5": 14.2109375, "11.0": 35.38064193725586, "11.5": 11.68055534362793, "12.0": 11.362847328186035, "12.5": 10.961371421813965, "13.0": 10.61024284362793, "13.5": 10.24609375, "14.0": 10.038628578186035, "14.5": 9.783854484558105, "15.0": 67.51779174804688, "15.5": 17.1796875}, "ecilxRa6m5g.4.mp4": {"0.5": 255.0, "1.0": 3.4765625, "1.5": 2.004774332046509, "2.0": 10.958333015441895, "2.5": 6.577691078186035, "3.0": 4.873263835906982, "3.5": 14.648003578186035, "4.0": 4.35069465637207, "4.5": 8.48828125, "5.0": 6.93663215637207, "5.5": 3.550347328186035, "6.0": 20.99826431274414, "6.5": 5.13194465637207, "7.0": 4.829861164093018, "7.5": 8.180121421813965, "8.0": 6.454861164093018, "8.5": 4.358941078186035, "9.0": 4.305121421813965, "9.5": 2.733506917953491, "10.0": 51.23698043823242, "10.5": 29.25824737548828, "11.0": 31.734375, "11.5": 31.42838478088379, "12.0": 14.71657943725586}, "fRqJsYel7V0.15.mp4": {"0.5": 255.0, "1.0": 45.55121612548828, "1.5": 50.42230987548828, "2.0": 48.40364456176758, "2.5": 42.98828125, "3.0": 50.34678649902344, "3.5": 28.92708396911621, "4.0": 36.36024475097656, "4.5": 32.98698043823242, "5.0": 101.29513549804688, "5.5": 46.15060806274414, "6.0": 25.58159637451172, "6.5": 42.50347137451172, "7.0": 28.1171875, "7.5": 21.17447853088379, "8.0": 25.39800262451172, "8.5": 81.29036712646484, "9.0": 2.528211832046509, "9.5": 65.46267700195312, "10.0": 42.15928649902344, "10.5": 37.88888931274414, "11.0": 37.4609375, "11.5": 54.62760543823242, "12.0": 24.39670181274414, "12.5": 23.16362762451172, "13.0": 25.95659637451172}, "foGfkZjCHm0.21.mp4": {"0.5": 255.0, "1.0": 3.6432292461395264, "1.5": 41.45269012451172, "2.0": 4.069010257720947, "2.5": 1.9665799140930176, "3.0": 1.0963541269302368, "3.5": 35.68663024902344, "4.0": 12.23524284362793, "4.5": 11.004340171813965, "5.0": 8.84201431274414, "5.5": 4.366753578186035, "6.0": 3.824652671813965, "6.5": 62.734375, "7.0": 1.92578125, "7.5": 1.1232638359069824, "8.0": 0.5703125, "8.5": 0.8485243320465088, "9.0": 0.8997395634651184, "9.5": 1.0577256679534912, "10.0": 0.5677083134651184, "10.5": 1.0377603769302368, "11.0": 0.9431423544883728, "11.5": 2.06640625, "12.0": 0.6614583134651184, "12.5": 2.995225667953491, "13.0": 1.1961805820465088, "13.5": 1.8624131679534912, "14.0": 2.00390625, "14.5": 2.85546875, "15.0": 1.3871527910232544, "15.5": 1.1111111640930176, "16.0": 1.171875, "16.5": 1.2248263359069824, "17.0": 1.3532986640930176, "17.5": 1.1345486640930176, "18.0": 1.3233506679534912, "18.5": 1.3233506679534912, "19.0": 0.4835069477558136, "19.5": 1.1410590410232544}, "g5esQcMBxek.10.mp4": {"0.5": 255.0, "1.0": 42.67838668823242, "1.5": 2.6484375, "2.0": 1.6046006679534912, "2.5": 2.434027671813965, "3.0": 0.9574652910232544, "3.5": 0.9071180820465088, "4.0": 54.12847137451172, "4.5": 2.51171875, "5.0": 7.189670085906982, "5.5": 8.653645515441895, "6.0": 80.60546875, "6.5": 12.01649284362793, "7.0": 46.27170181274414, "7.5": 5.873697757720947, "8.0": 33.25130081176758, "8.5": 29.25737762451172, "9.0": 16.00303840637207, "9.5": 11.979166984558105, "10.0": 27.36154556274414, "10.5": 40.87239456176758, "11.0": 28.41275978088379, "11.5": 57.41276168823242, "12.0": 5.71571159362793, "12.5": 64.02040100097656, "13.0": 21.30555534362793, "13.5": 11.38845443725586}, "gBBS5XqzOu4.65.mp4": {"0.5": 255.0, "1.0": 0.0603298619389534, "1.5": 0.0512152761220932, "2.0": 0.0434027761220932, "2.5": 0.078125, "3.0": 53.24088668823242, "3.5": 36.75043487548828, "4.0": 51.71527862548828, "4.5": 2.6875, "5.0": 2.667100667953491, "5.5": 2.637152671813965, "6.0": 2.71875, "6.5": 2.5768229961395264, "7.0": 44.27083206176758, "7.5": 34.35373306274414, "8.0": 36.58203125, "8.5": 2.847222328186035, "9.0": 2.820746421813965, "9.5": 2.7712674140930176, "10.0": 17.35894012451172, "10.5": 37.87977600097656, "11.0": 53.41276168823242, "11.5": 2.596788167953491, "12.0": 2.476128578186035, "12.5": 2.5256075859069824, "13.0": 2.91015625, "13.5": 58.30295181274414}, "gCtvzTSzMxY.7.mp4": {"0.5": 255.0, "1.0": 0.1241319477558136, "1.5": 0.2161458283662796, "2.0": 0.3728298544883728, "2.5": 0.2703993022441864, "3.0": 0.4461805522441864, "3.5": 0.2751736044883728, "4.0": 0.5251736044883728, "4.5": 0.8897569179534912, "5.0": 0.7222222089767456, "5.5": 0.7625868320465088, "6.0": 60.63368225097656, "6.5": 16.59505271911621, "7.0": 1.5138888359069824, "7.5": 117.46397399902344, "8.0": 2.23046875, "8.5": 2.831163167953491, "9.0": 10.674479484558105, "9.5": 15.516058921813965, "10.0": 17.75607681274414, "10.5": 12.738715171813965, "11.0": 13.381510734558105, "11.5": 11.975260734558105}, "gw3w9MLxihk.5.mp4": {"0.5": 255.0, "1.0": 4.245659828186035, "1.5": 2.225694417953491, "2.0": 6.583767414093018, "2.5": 3.535590171813965, "3.0": 3.73046875, "3.5": 4.131076335906982, "4.0": 1.5407986640930176, "4.5": 66.63628387451172, "5.0": 6.874566078186035, "5.5": 6.78211784362793, "6.0": 3.9661457538604736, "6.5": 54.47526168823242, "7.0": 34.29861068725586, "7.5": 36.18359375, "8.0": 29.65625, "8.5": 42.94314193725586, "9.0": 37.71397399902344, "9.5": 32.21050262451172, "10.0": 35.31597137451172, "10.5": 52.45529556274414, "11.0": 21.27907943725586, "11.5": 16.91059112548828, "12.0": 18.13758659362793, "12.5": 53.40277862548828}, "h4I8LFOlnOY.4.mp4": {"0.5": 255.0, "1.0": 23.44097137451172, "1.5": 6.024739742279053, "2.0": 3.312933921813965, "2.5": 3.085503578186035, "3.0": 3.0559895038604736, "3.5": 3.121961832046509, "4.0": 5.446614742279053, "4.5": 26.62109375, "5.0": 22.62717056274414, "5.5": 2.374566078186035, "6.0": 2.319444417953491, "6.5": 2.3671875, "7.0": 2.335503578186035, "7.5": 2.352430582046509, "8.0": 2.344618082046509, "8.5": 14.343316078186035, "9.0": 17.29817771911621, "9.5": 3.3415799140930176, "10.0": 2.9010417461395264, "10.5": 2.9375, "11.0": 2.7630207538604736, "11.5": 21.83724021911621, "12.0": 82.45095825195312}, "h9ubQQpBWTE.1.mp4": {"0.5": 255.0, "1.0": 61.50737762451172, "1.5": 1.6427950859069824, "2.0": 1.2565103769302368, "2.5": 63.21614456176758, "3.0": 12.769097328186035, "3.5": 6.1171875, "4.0": 7.2890625, "4.5": 6.875433921813965, "5.0": 11.86154556274414, "5.5": 3.8415799140930176, "6.0": 9.078558921813965, "6.5": 6.86414909362793, "7.0": 4.376302242279053, "7.5": 7.152777671813965, "8.0": 40.87977600097656, "8.5": 8.011284828186035, "9.0": 6.90711784362793, "9.5": 5.646267414093018, "10.0": 10.22092056274414, "10.5": 4.33203125, "11.0": 51.58333206176758, "11.5": 30.77647590637207, "12.0": 45.21961975097656, "12.5": 41.27387237548828, "13.0": 33.22352600097656, "13.5": 40.296875, "14.0": 33.84852600097656, "14.5": 28.75477409362793, "15.0": 33.43098831176758, "15.5": 66.13237762451172, "16.0": 4.740885257720947, "16.5": 2.988715171813965, "17.0": 2.0568575859069824, "17.5": 2.991319417953491, "18.0": 1.1397569179534912, "18.5": 1.5807291269302368, "19.0": 59.23176956176758}, "iU6Vq2pBI-I.59.mp4": {"0.5": 255.0, "1.0": 7.456597328186035, "1.5": 11.9140625, "2.0": 7.16883659362793, "2.5": 7.123697757720947, "3.0": 8.3046875, "3.5": 7.94227409362793, "4.0": 11.96267318725586, "4.5": 9.97569465637207, "5.0": 2.188368082046509, "5.5": 8.546441078186035, "6.0": 13.049479484558105, "6.5": 8.615885734558105, "7.0": 5.24696159362793, "7.5": 12.371527671813965, "8.0": 7.36805534362793, "8.5": 3.7174479961395264, "9.0": 4.437933921813965, "9.5": 5.333333492279053, "10.0": 3.070746421813965, "10.5": 2.6111111640930176, "11.0": 9.723958015441895, "11.5": 28.95225715637207, "12.0": 4.056423664093018, "12.5": 5.154513835906982, "13.0": 9.38194465637207, "13.5": 9.558159828186035, "14.0": 39.19184112548828, "14.5": 12.013020515441895, "15.0": 10.259114265441895, "15.5": 10.95225715637207, "16.0": 3.9466145038604736, "16.5": 10.976128578186035, "17.0": 9.66493034362793, "17.5": 3.3828125, "18.0": 4.873263835906982, "18.5": 7.128472328186035, "19.0": 5.814670085906982, "19.5": 11.65321159362793, "20.0": 9.30946159362793, "20.5": 3.206163167953491, "21.0": 4.053385257720947, "21.5": 4.40321159362793, "22.0": 9.90321159362793, "22.5": 8.966145515441895, "23.0": 74.46744537353516, "23.5": 7.766058921813965, "24.0": 3.659288167953491, "24.5": 8.308159828186035, "25.0": 4.95703125, "25.5": 9.063801765441895, "26.0": 51.87803649902344, "26.5": 1.9670138359069824, "27.0": 3.734375, "27.5": 4.80772590637207, "28.0": 3.6345486640930176}, "iz6G1svj7Ng.4.mp4": {"0.5": 255.0, "1.0": 0.0681423619389534, "1.5": 0.0173611119389534, "2.0": 14.21657943725586, "2.5": 42.76605987548828, "3.0": 10.476996421813965, "3.5": 4.802517414093018, "4.0": 29.84461784362793, "4.5": 8.97960090637207, "5.0": 31.54817771911621, "5.5": 25.69487762451172, "6.0": 52.26171875, "6.5": 11.640191078186035, "7.0": 45.55642318725586, "7.5": 1.4236111640930176, "8.0": 0.1875, "8.5": 0.7634548544883728, "9.0": 16.91145896911621, "9.5": 0.2009548544883728, "10.0": 0.1189236119389534, "10.5": 0.0698784738779068, "11.0": 0.0889756977558136, "11.5": 0.6944444179534912, "12.0": 2.722222328186035, "12.5": 0.4474826455116272}, "k7OQGhkYkqs.1.mp4": {"0.5": 255.0, "1.0": 6.10460090637207, "1.5": 7.09071159362793, "2.0": 8.07899284362793, "2.5": 8.60373306274414, "3.0": 99.80902862548828, "3.5": 183.39149475097656, "4.0": 17.47612762451172, "4.5": 6.737847328186035, "5.0": 10.577691078186035, "5.5": 1.3524305820465088, "6.0": 1.3350694179534912, "6.5": 1.3498263359069824, "7.0": 1.3064236640930176, "7.5": 1.2686631679534912, "8.0": 127.58637237548828, "8.5": 15.73133659362793, "9.0": 12.006510734558105, "9.5": 13.02734375, "10.0": 18.47048568725586, "10.5": 24.40538215637207, "11.0": 14.273871421813965, "11.5": 15.21875}, "lIS3_02wKZM.8.mp4": {"0.5": 255.0, "1.0": 8.636284828186035, "1.5": 5.071614742279053, "2.0": 5.454427242279053, "2.5": 52.33159637451172, "3.0": 3.694878578186035, "3.5": 2.075086832046509, "4.0": 1.3172743320465088, "4.5": 1.8910590410232544, "5.0": 1.8971353769302368, "5.5": 2.5681424140930176, "6.0": 66.28775787353516, "6.5": 8.02560806274414, "7.0": 4.322048664093018, "7.5": 7.013888835906982, "8.0": 8.101128578186035, "8.5": 5.558159828186035, "9.0": 58.68836975097656, "9.5": 3.254774332046509, "10.0": 2.223524332046509, "10.5": 6.384114742279053, "11.0": 6.534722328186035, "11.5": 2.7209200859069824, "12.0": 2.675347328186035, "12.5": 76.00521087646484, "13.0": 9.76085090637207, "13.5": 12.36154556274414, "14.0": 21.43272590637207, "14.5": 25.30295181274414, "15.0": 25.09982681274414, "15.5": 21.33463478088379, "16.0": 66.11631774902344, "16.5": 2.7248263359069824, "17.0": 5.981770992279053, "17.5": 4.971354007720947, "18.0": 5.236111164093018, "18.5": 4.333333492279053, "19.0": 4.216579914093018}, "m4sWjNlPlVs.43.mp4": {"0.5": 255.0, "1.0": 6.845920085906982, "1.5": 2.891493082046509, "2.0": 21.27387237548828, "2.5": 24.08203125, "3.0": 24.11111068725586, "3.5": 33.65798568725586, "4.0": 17.80121612548828, "4.5": 15.75607681274414, "5.0": 14.440972328186035, "5.5": 11.94140625, "6.0": 42.06684112548828, "6.5": 12.26649284362793, "7.0": 10.17881965637207, "7.5": 9.430989265441895, "8.0": 12.451823234558105, "8.5": 17.09461784362793, "9.0": 4.584635257720947, "9.5": 1.5008680820465088, "10.0": 18.33940887451172, "10.5": 17.98220443725586, "11.0": 41.60807418823242, "11.5": 8.44227409362793, "12.0": 7.716579914093018, "12.5": 5.70225715637207, "13.0": 6.009114742279053, "13.5": 7.34852409362793, "14.0": 17.83333396911621, "14.5": 19.45355987548828, "15.0": 13.940104484558105, "15.5": 15.47439193725586, "16.0": 28.46050262451172, "16.5": 35.90451431274414, "17.0": 15.805989265441895, "17.5": 12.736979484558105, "18.0": 19.95138931274414, "18.5": 12.34765625, "19.0": 21.59244728088379, "19.5": 14.5, "20.0": 8.61111068725586, "20.5": 11.40017318725586, "21.0": 16.39930534362793, "21.5": 8.015625, "22.0": 6.539496421813965, "22.5": 21.68229103088379, "23.0": 18.54947853088379, "23.5": 9.94835090637207, "24.0": 37.30121612548828, "24.5": 32.86198043823242, "25.0": 9.03298568725586, "25.5": 11.234375, "26.0": 13.07986068725586, "26.5": 14.74001693725586, "27.0": 13.38671875, "27.5": 9.085503578186035, "28.0": 16.86371612548828, "28.5": 28.83246612548828, "29.0": 18.73784637451172, "29.5": 12.611979484558105, "30.0": 10.19704818725586, "30.5": 15.66796875, "31.0": 11.53689193725586, "31.5": 8.01171875, "32.0": 20.59678840637207, "32.5": 26.67751693725586, "33.0": 11.88671875, "33.5": 5.76866340637207, "34.0": 13.45486068725586, "34.5": 44.12760543823242, "35.0": 31.81032943725586}, "mtP9H7AClk8.7.mp4": {"0.5": 255.0, "1.0": 6.669270992279053, "1.5": 58.57291793823242, "2.0": 27.40364646911621, "2.5": 4.982204914093018, "3.0": 30.36979103088379, "3.5": 6.63671875, "4.0": 1.7938368320465088, "4.5": 42.98307418823242, "5.0": 41.50520706176758, "5.5": 5.181857585906982, "6.0": 3.94921875, "6.5": 2.28125, "7.0": 63.04383850097656, "7.5": 0.0177951380610466, "8.0": 64.33246612548828, "8.5": 0.0360243059694767, "9.0": 45.60329818725586, "9.5": 57.35459899902344, "10.0": 0.03081597201526165, "10.5": 69.90190887451172, "11.0": 0.0503472238779068, "11.5": 0.00737847201526165, "12.0": 48.83376693725586}, "muUcz5aYTMs.3.mp4": {"0.5": 255.0, "1.0": 3.7795138359069824, "1.5": 2.2517361640930176, "2.0": 190.34722900390625, "2.5": 6.490451335906982, "3.0": 10.464409828186035, "3.5": 14.159722328186035, "4.0": 3.967881917953491, "4.5": 1.5052083730697632, "5.0": 3.440972328186035, "5.5": 7.08116340637207, "6.0": 1.2035590410232544, "6.5": 0.2252604216337204, "7.0": 0.1202256977558136, "7.5": 0.0881076380610466, "8.0": 4.154947757720947, "8.5": 2.311631917953491, "9.0": 0.8828125, "9.5": 7.137152671813965, "10.0": 0.8116319179534912, "10.5": 0.0750868022441864, "11.0": 178.32464599609375, "11.5": 2.894097328186035, "12.0": 4.257378578186035}, "my2KTkgATPU.34.mp4": {"0.5": 255.0, "1.0": 24.53385353088379, "1.5": 51.28602600097656, "2.0": 17.77256965637207, "2.5": 69.52040100097656, "3.0": 15.65407943725586, "3.5": 32.69661331176758, "4.0": 15.80295181274414, "4.5": 20.82204818725586, "5.0": 18.53081512451172, "5.5": 19.60677146911621, "6.0": 25.18793487548828, "6.5": 28.22352409362793, "7.0": 17.82335090637207, "7.5": 22.02994728088379, "8.0": 29.39322853088379, "8.5": 27.53645896911621, "9.0": 33.56987762451172, "9.5": 20.97178840637207, "10.0": 27.52647590637207, "10.5": 24.87239646911621, "11.0": 56.65017318725586, "11.5": 58.61154556274414, "12.0": 42.92448043823242, "12.5": 35.46571350097656, "13.0": 26.80685806274414, "13.5": 25.44140625, "14.0": 27.99869728088379, "14.5": 16.30381965637207, "15.0": 39.64453125, "15.5": 46.76258850097656, "16.0": 33.56076431274414, "16.5": 36.44227600097656, "17.0": 29.19270896911621, "17.5": 25.8828125, "18.0": 30.03819465637207, "18.5": 29.52951431274414, "19.0": 17.03125}, "nTGjRCHPHYw.10.mp4": {"0.5": 255.0, "1.0": 6.33116340637207, "1.5": 6.016058921813965, "2.0": 5.88671875, "2.5": 5.453125, "3.0": 5.467013835906982, "3.5": 5.302517414093018, "4.0": 5.149739742279053, "4.5": 5.158854007720947, "5.0": 4.784722328186035, "5.5": 4.237847328186035, "6.0": 4.132378578186035, "6.5": 3.5881075859069824, "7.0": 3.230902671813965, "7.5": 2.684461832046509, "8.0": 2.2013888359069824, "8.5": 10.93836784362793, "9.0": 37.17013931274414, "9.5": 4.503472328186035, "10.0": 2.9700520038604736, "10.5": 4.660590171813965, "11.0": 3.1423611640930176, "11.5": 4.427083492279053, "12.0": 4.187933921813965, "12.5": 57.63758850097656, "13.0": 12.28515625, "13.5": 12.915364265441895, "14.0": 12.549479484558105, "14.5": 11.77560806274414, "15.0": 10.10850715637207, "15.5": 7.771267414093018, "16.0": 5.693576335906982, "16.5": 3.823784828186035, "17.0": 1.9001736640930176, "17.5": 1.2556424140930176, "18.0": 0.6657986044883728, "18.5": 59.20790100097656, "19.0": 3.7018229961395264, "19.5": 2.0407986640930176, "20.0": 0.9587673544883728, "20.5": 0.9887152910232544, "21.0": 0.9223090410232544, "21.5": 1.6180555820465088, "22.0": 3.422308921813965}, "rM-QRSeVngo.1.mp4": {"0.5": 255.0, "1.0": 4.635416507720947, "1.5": 6.076388835906982, "2.0": 7.651041507720947, "2.5": 7.026909828186035, "3.0": 7.9921875, "3.5": 5.536892414093018, "4.0": 60.34505081176758, "4.5": 2.148003578186035, "5.0": 1.7261284589767456, "5.5": 1.9097222089767456, "6.0": 1.5559896230697632, "6.5": 2.620659828186035, "7.0": 2.1393229961395264, "7.5": 64.359375, "8.0": 45.39800262451172, "8.5": 2.418402671813965, "9.0": 2.180121421813965, "9.5": 2.22265625, "10.0": 2.5221354961395264, "10.5": 2.4791667461395264, "11.0": 2.553819417953491, "11.5": 2.730034828186035, "12.0": 2.663194417953491, "12.5": 2.3098957538604736, "13.0": 52.71744918823242, "13.5": 2.0208332538604736, "14.0": 1.7595486640930176, "14.5": 1.6427950859069824, "15.0": 1.9830728769302368, "15.5": 1.5611978769302368, "16.0": 1.4092881679534912, "16.5": 1.2469618320465088, "17.0": 1.1675347089767456, "17.5": 2.0182292461395264, "18.0": 2.597222328186035, "18.5": 1.2864583730697632, "19.0": 1.1393228769302368, "19.5": 2.602430582046509, "20.0": 2.0529513359069824, "20.5": 2.5876736640930176}, "rwbhbyWCR2U.8.mp4": {"0.5": 255.0, "1.0": 40.83203125, "1.5": 50.13845443725586, "2.0": 5.772135257720947, "2.5": 5.641927242279053, "3.0": 0.0859375, "3.5": 0.013888888992369175, "4.0": 0.00390625, "4.5": 0.0017361111240461469, "5.0": 0.0, "5.5": 20.19140625, "6.0": 0.0, "6.5": 0.0, "7.0": 0.1545138955116272, "7.5": 0.0017361111240461469, "8.0": 0.002170138992369175, "8.5": 0.0, "9.0": 0.0, "9.5": 0.0, "10.0": 0.0, "10.5": 0.0008680555620230734, "11.0": 0.0008680555620230734, "11.5": 19.58724021911621, "12.0": 0.9457465410232544, "12.5": 0.1497395783662796, "13.0": 0.0763888880610466, "13.5": 0.0386284738779068, "14.0": 0.00737847201526165, "14.5": 0.0052083334885537624, "15.0": 0.0065104165114462376, "15.5": 0.0}}
paper_experiments/caches/web_base.jsonl ADDED
The diff for this file is too large to render. See raw diff
 
paper_experiments/caches/web_comp.jsonl ADDED
The diff for this file is too large to render. See raw diff
 
paper_experiments/caches/web_exh.jsonl ADDED
The diff for this file is too large to render. See raw diff
 
paper_experiments/caches/web_fs.jsonl ADDED
The diff for this file is too large to render. See raw diff
 
paper_experiments/caches/web_fs_alt.jsonl ADDED
The diff for this file is too large to render. See raw diff
 
paper_experiments/data/gold_holdout.json ADDED
@@ -0,0 +1 @@
 
 
1
+ [{"video": "6ureBD2VY1c.3.mp4", "question_id": "6ureBD2VY1c.3.mp4", "answer": [{"role": "assistant", "content": "A woman is speaking in front of the camera, and a man is taking photos.", "related_timespan": [0.0, 10.24], "reply_timespan": [0.0, 11.24]}, {"role": "assistant", "content": "A woman and a man are posing in front of a movie poster, and a woman is standing in front of a white background.", "related_timespan": [10.28, 20.79], "reply_timespan": [10.28, 20.820800000000002]}], "conversation": [{"role": "user", "content": "What activities are people doing in front of the camera?", "time": 0}], "duration": 20.820800000000002}, {"video": "_KgWye11I8g.7.mp4", "question_id": "_KgWye11I8g.7.mp4", "answer": [{"role": "assistant", "content": "A black game console is displayed.", "related_timespan": [0.0, 7.51], "reply_timespan": [0.0, 8.51]}, {"role": "assistant", "content": "A black game console is displayed on the screen.", "related_timespan": [7.57, 13.88], "reply_timespan": [7.57, 13.9139]}], "conversation": [{"role": "user", "content": "What is being displayed in the video?", "time": 0}], "duration": 13.9139}, {"video": "m4sWjNlPlVs.43.mp4", "question_id": "m4sWjNlPlVs.43.mp4", "answer": [{"role": "assistant", "content": "The person is showing a purple band while the piece of paper is mentioned to be in the background.", "related_timespan": [0.0, 5.38], "reply_timespan": [0.0, 6.38]}, {"role": "assistant", "content": "A person is pointing to a piece of paper.", "related_timespan": [5.42, 10.68], "reply_timespan": [5.42, 11.68]}, {"role": "assistant", "content": "The person is drawing, unfolding, and then folding and patting the piece of paper.", "related_timespan": [10.72, 34.99], "reply_timespan": [10.72, 35.035]}], "conversation": [{"role": "user", "content": "What is the person doing with the piece of paper?", "time": 0}], "duration": 35.035}, {"video": "GzD9IDWLodk.15.mp4", "question_id": "GzD9IDWLodk.15.mp4", "answer": [{"role": "assistant", "content": "The man is looking at and talking to the camera.", "related_timespan": [0.0, 10.51], "reply_timespan": [0.0, 10.543866666666666]}], "conversation": [{"role": "user", "content": "What is the man doing?", "time": 0}], "duration": 10.543866666666666}, {"video": "2ZcsoP1AUTI.6.mp4", "question_id": "2ZcsoP1AUTI.6.mp4", "answer": [{"role": "assistant", "content": "Machines are spraying white smoke onto an airplane.", "related_timespan": [0.0, 8.74], "reply_timespan": [0.0, 9.74]}, {"role": "assistant", "content": "A man in a safety vest checks a car.", "related_timespan": [16.75, 22.39], "reply_timespan": [16.75, 22.4224]}], "conversation": [{"role": "user", "content": "What activity is being carried out on or near vehicles?", "time": 0}], "duration": 22.4224}, {"video": "bTMVVjv5zQ0.2.mp4", "question_id": "bTMVVjv5zQ0.2.mp4", "answer": [{"role": "assistant", "content": "A man in a dark brown overcoat holds a gun in his hand and aims it into the distance in the forest.", "related_timespan": [0.0, 2.04], "reply_timespan": [0.0, 3.04]}, {"role": "assistant", "content": "The man is sprawling on the ground in the woods, surrounded by a bear sniffing his body.", "related_timespan": [2.09, 3.75], "reply_timespan": [2.09, 4.75]}, {"role": "assistant", "content": "The man is lying on the ground with a bear seemingly about to attack him.", "related_timespan": [3.8, 5.71], "reply_timespan": [3.8, 6.71]}, {"role": "assistant", "content": "The man on the ground is holding a gun up to a bear walking toward him.", "related_timespan": [5.76, 7.22], "reply_timespan": [5.76, 8.219999999999999]}, {"role": "assistant", "content": "The man is lying on the ground with many wounds and blood, with a bear standing near him.", "related_timespan": [7.26, 8.72], "reply_timespan": [7.26, 9.72]}], "conversation": [{"role": "user", "content": "What is happening with the man in the forest?", "time": 0}], "duration": 12.554208333333332}, {"video": "CJorIIaGAYI.5.mp4", "question_id": "CJorIIaGAYI.5.mp4", "answer": [{"role": "assistant", "content": "The woman in a jersey with glasses and twists is dancing with a person wearing a huge baseball-like headgear and is holding a bouquet of flowers.", "related_timespan": [5.37, 14.11], "reply_timespan": [5.37, 14.147466666666666]}], "conversation": [{"role": "user", "content": "What is happening with the woman in a jersey with glasses and twists?", "time": 0}], "duration": 14.147466666666666}, {"video": "Gw1fS2Nze5s.1.mp4", "question_id": "Gw1fS2Nze5s.1.mp4", "answer": [{"role": "assistant", "content": "Player number 30 throws the ball into the basket.", "related_timespan": [8.2, 12.96], "reply_timespan": [8.2, 13.96]}, {"role": "assistant", "content": "Player number 30 throws the ball to the basket.", "related_timespan": [13.0, 18.72], "reply_timespan": [13.0, 18.76]}], "conversation": [{"role": "user", "content": "What notable action is player number 30 doing during the game?", "time": 0}], "duration": 18.76}, {"video": "2l9I3UhWP10.8.mp4", "question_id": "2l9I3UhWP10.8.mp4", "answer": [{"role": "assistant", "content": "The man is holding a box of Switch games and later a screenshot covers it.", "related_timespan": [0.0, 3.94], "reply_timespan": [0.0, 4.9399999999999995]}, {"role": "assistant", "content": "The man is holding a box of Switch games and patting it while talking to the camera.", "related_timespan": [9.74, 14.41], "reply_timespan": [9.74, 15.41]}, {"role": "assistant", "content": "The man is holding a box of Switch games on the beach.", "related_timespan": [14.45, 22.19], "reply_timespan": [14.45, 22.2222]}], "conversation": [{"role": "user", "content": "What is the man doing with the Switch game box?", "time": 0}], "duration": 22.2222}, {"video": "8VR6Ao97vNc.1.mp4", "question_id": "8VR6Ao97vNc.1.mp4", "answer": [{"role": "assistant", "content": "The video features the Apple Music logo.", "related_timespan": [0.0, 9.18], "reply_timespan": [0.0, 10.18]}], "conversation": [{"role": "user", "content": "What is featured prominently in the video?", "time": 0}], "duration": 11.553208333333332}, {"video": "FL9WvBWs73I.18.mp4", "question_id": "FL9WvBWs73I.18.mp4", "answer": [{"role": "assistant", "content": "The person is unrolling the turf.", "related_timespan": [0.0, 5.92], "reply_timespan": [0.0, 6.92]}, {"role": "assistant", "content": "The man butts the turf to the edge and taps it down.", "related_timespan": [5.96, 10.44], "reply_timespan": [5.96, 11.44]}, {"role": "assistant", "content": "The man is taping the turf down.", "related_timespan": [10.48, 12.64], "reply_timespan": [10.48, 13.64]}, {"role": "assistant", "content": "The man taps the turf down and uncurls the edge.", "related_timespan": [12.68, 21.36], "reply_timespan": [12.68, 22.36]}, {"role": "assistant", "content": "The man gets another turf.", "related_timespan": [21.4, 24.44], "reply_timespan": [21.4, 24.48]}], "conversation": [{"role": "user", "content": "What is the man doing with the turf?", "time": 0}], "duration": 24.48}, {"video": "aGu1HuPl3Gs.1.mp4", "question_id": "aGu1HuPl3Gs.1.mp4", "answer": [{"role": "assistant", "content": "The players can judge the squad rating according to the number of stars.", "related_timespan": [0.0, 1.23], "reply_timespan": [0.0, 2.23]}, {"role": "assistant", "content": "The interface shows a position layout diagram and relevant information about each player, including nationality.", "related_timespan": [6.17, 14.25], "reply_timespan": [6.17, 14.280933333333333]}], "conversation": [{"role": "user", "content": "What information is presented on the video game interface?", "time": 0}], "duration": 14.280933333333333}, {"video": "2FmQgQW8clw.1.mp4", "question_id": "2FmQgQW8clw.1.mp4", "answer": [{"role": "assistant", "content": "The woman is walking her dog in a park.", "related_timespan": [0.0, 1.84], "reply_timespan": [0.0, 2.84]}, {"role": "assistant", "content": "The woman is leading her dog up the steps on a leash.", "related_timespan": [1.88, 4.48], "reply_timespan": [1.88, 5.48]}], "conversation": [{"role": "user", "content": "What is the woman doing with her dog?", "time": 0}], "duration": 12.44}, {"video": "Kd3vC0lL9C4.57.mp4", "question_id": "Kd3vC0lL9C4.57.mp4", "answer": [{"role": "assistant", "content": "A person is wearing a white full-wrap helmet.", "related_timespan": [0.0, 2.2], "reply_timespan": [0.0, 3.2]}, {"role": "assistant", "content": "A person is wearing a yellow full-wrap helmet.", "related_timespan": [2.24, 4.37], "reply_timespan": [2.24, 5.37]}, {"role": "assistant", "content": "A person is wearing a blue full-wrap shark-shaped helmet.", "related_timespan": [4.4, 8.04], "reply_timespan": [4.4, 9.04]}, {"role": "assistant", "content": "People are wearing jumpsuits and helmets in white, purple, red, yellow, black, and gray.", "related_timespan": [11.95, 14.78], "reply_timespan": [11.95, 14.8148]}], "conversation": [{"role": "user", "content": "What are the people wearing?", "time": 0}], "duration": 14.8148}, {"video": "E2WelDikd4s.50.mp4", "question_id": "E2WelDikd4s.50.mp4", "answer": [{"role": "assistant", "content": "Green mechanical vehicles on an outdoor lawn, with water and buildings in the distance.", "related_timespan": [0.0, 5.44], "reply_timespan": [0.0, 6.44]}, {"role": "assistant", "content": "Disney's castle with fireworks in the evening sky.", "related_timespan": [5.47, 12.11], "reply_timespan": [5.47, 13.11]}, {"role": "assistant", "content": "Disney Castle with a blue sky and white clouds in the background.", "related_timespan": [12.15, 18.95], "reply_timespan": [12.15, 18.985633333333332]}], "conversation": [{"role": "user", "content": "What is shown in the photos?", "time": 0}], "duration": 18.985633333333332}, {"video": "lIS3_02wKZM.8.mp4", "question_id": "lIS3_02wKZM.8.mp4", "answer": [{"role": "assistant", "content": "The woman in a blue blouse says something to the left.", "related_timespan": [0.0, 1.94], "reply_timespan": [0.0, 2.94]}, {"role": "assistant", "content": "The woman in a blue blouse is busy with something at a table.", "related_timespan": [12.1, 15.51], "reply_timespan": [12.1, 16.509999999999998]}], "conversation": [{"role": "user", "content": "What is the woman in the blue blouse doing?", "time": 0}], "duration": 19.153203342618383}, {"video": "4vZ7NszztMs.9.mp4", "question_id": "4vZ7NszztMs.9.mp4", "answer": [{"role": "assistant", "content": "Basketball.", "related_timespan": [0.0, 14.45], "reply_timespan": [0.0, 14.481133333333334]}], "conversation": [{"role": "user", "content": "What sport is being discussed?", "time": 0}], "duration": 14.481133333333334}, {"video": "h9ubQQpBWTE.1.mp4", "question_id": "h9ubQQpBWTE.1.mp4", "answer": [{"role": "assistant", "content": "Three people sit at a table in front of microphones. The right man talks with a laptop in front of him.", "related_timespan": [0.0, 2.07], "reply_timespan": [0.0, 3.07]}, {"role": "assistant", "content": "Three people sit at a table in front of microphones. The woman talks with her hands in front of her chest.", "related_timespan": [14.78, 18.92], "reply_timespan": [14.78, 18.952266666666667]}], "conversation": [{"role": "user", "content": "What is happening at the table?", "time": 0}], "duration": 18.952266666666667}, {"video": "CJ-ER0GdJd8.36.mp4", "question_id": "CJ-ER0GdJd8.36.mp4", "answer": [{"role": "assistant", "content": "The video shows a busy street in a city with many shops and restaurants, with people walking and some on motorcycles.", "related_timespan": [0.0, 6.29], "reply_timespan": [0.0, 7.29]}, {"role": "assistant", "content": "The video shows a busy street in a city with shops and restaurants, motorcycles, and people walking and driving on the road.", "related_timespan": [8.5, 14.04], "reply_timespan": [8.5, 14.083333333333334]}], "conversation": [{"role": "user", "content": "What does the video show about the city street?", "time": 0}], "duration": 14.083333333333334}, {"video": "h4I8LFOlnOY.4.mp4", "question_id": "h4I8LFOlnOY.4.mp4", "answer": [{"role": "assistant", "content": "There is green grass, reeds, and a small wooden bridge over the water.", "related_timespan": [0.0, 4.14], "reply_timespan": [0.0, 5.14]}, {"role": "assistant", "content": "There are leaves, lotus leaves, and grass floating on the lake.", "related_timespan": [4.2, 8.31], "reply_timespan": [4.2, 9.31]}, {"role": "assistant", "content": "There are stones and fish of many colors in the lake.", "related_timespan": [8.34, 11.98], "reply_timespan": [8.34, 12.012]}], "conversation": [{"role": "user", "content": "What features can be observed in and around the water?", "time": 0}], "duration": 12.012}, {"video": "g5esQcMBxek.10.mp4", "question_id": "g5esQcMBxek.10.mp4", "answer": [{"role": "assistant", "content": "Four people are sitting on the stage and having a conversation.", "related_timespan": [0.0, 3.5], "reply_timespan": [0.0, 4.5]}, {"role": "assistant", "content": "A man in a suit and glasses sits on the stage and talks in front of a black background.", "related_timespan": [3.54, 6.44], "reply_timespan": [3.54, 7.44]}, {"role": "assistant", "content": "A man wearing a suit has a hug with a man in a black shirt, and there is a piano performance.", "related_timespan": [6.47, 10.08], "reply_timespan": [6.47, 11.08]}], "conversation": [{"role": "user", "content": "What events or activities are taking place on the stage?", "time": 0}], "duration": 13.413400000000001}, {"video": "_T2A_lgn69w.37.mp4", "question_id": "_T2A_lgn69w.37.mp4", "answer": [{"role": "assistant", "content": "The man is holding a bottle of Apple Sidra in his hand.", "related_timespan": [4.97, 12.83], "reply_timespan": [4.97, 13.83]}, {"role": "assistant", "content": "The man unscrews a bottle of Apple Sidra, pours it into a glass cup, and drinks it.", "related_timespan": [17.63, 32.4], "reply_timespan": [17.63, 32.43333333333333]}], "conversation": [{"role": "user", "content": "What does the man do with the Apple Sidra?", "time": 0}], "duration": 32.43333333333333}, {"video": "6mLBr_ZxTj4.44.mp4", "question_id": "6mLBr_ZxTj4.44.mp4", "answer": [{"role": "assistant", "content": "The dog has bumps in its ear.", "related_timespan": [2.2, 4.94], "reply_timespan": [2.2, 5.94]}, {"role": "assistant", "content": "The dog has bumps and ticks.", "related_timespan": [5.0, 7.31], "reply_timespan": [5.0, 8.309999999999999]}, {"role": "assistant", "content": "The dog has a lump on its body.", "related_timespan": [7.37, 11.28], "reply_timespan": [7.37, 11.311300000000001]}], "conversation": [{"role": "user", "content": "What issue is the dog experiencing?", "time": 0}], "duration": 11.311300000000001}, {"video": "gBBS5XqzOu4.65.mp4", "question_id": "gBBS5XqzOu4.65.mp4", "answer": [{"role": "assistant", "content": "A map of the world is being shown on the screen, and the man points to different locations on it.", "related_timespan": [7.27, 13.28], "reply_timespan": [7.27, 13.3133]}], "conversation": [{"role": "user", "content": "What is being shown on the screen?", "time": 0}], "duration": 13.3133}, {"video": "XqDwTKVeUcI.31.mp4", "question_id": "XqDwTKVeUcI.31.mp4", "answer": [{"role": "assistant", "content": "The woman is wearing a hat and sunglasses.", "related_timespan": [0.0, 1.67], "reply_timespan": [0.0, 2.67]}, {"role": "assistant", "content": "The people are dressed in old-fashioned clothing, including overalls and hats.", "related_timespan": [1.7, 4.97], "reply_timespan": [1.7, 5.97]}, {"role": "assistant", "content": "A man with a black hat and many people standing around him.", "related_timespan": [5.0, 6.03], "reply_timespan": [5.0, 7.03]}, {"role": "assistant", "content": "A man is wearing a helmet and goggles.", "related_timespan": [8.87, 10.77], "reply_timespan": [8.87, 11.77]}, {"role": "assistant", "content": "A man is in a checkered shirt and hat; another man is next to him is smoking a cigarette.", "related_timespan": [10.8, 15.9], "reply_timespan": [10.8, 15.933333333333334]}], "conversation": [{"role": "user", "content": "What kind of clothing are people wearing in the video?", "time": 0}], "duration": 15.933333333333334}, {"video": "nTGjRCHPHYw.10.mp4", "question_id": "nTGjRCHPHYw.10.mp4", "answer": [{"role": "assistant", "content": "The man is standing in front of a table with several items on it and is talking and gesturing.", "related_timespan": [8.34, 11.95], "reply_timespan": [8.34, 12.95]}, {"role": "assistant", "content": "The man is standing in front of a table with different kinds of mats on it, speaking and gesturing.", "related_timespan": [17.95, 22.19], "reply_timespan": [17.95, 22.2222]}], "conversation": [{"role": "user", "content": "What is the man doing in the video?", "time": 0}], "duration": 22.2222}, {"video": "0vklWHzRAos.9.mp4", "question_id": "0vklWHzRAos.9.mp4", "answer": [{"role": "assistant", "content": "The video game features a character card of a player's identity information.", "related_timespan": [0.0, 6.58], "reply_timespan": [0.0, 7.58]}, {"role": "assistant", "content": "The video game allows you to choose a club and shows a squad of football players with their character cards.", "related_timespan": [6.62, 10.83], "reply_timespan": [6.62, 10.875]}], "conversation": [{"role": "user", "content": "What does the video game feature?", "time": 0}], "duration": 10.875}, {"video": "EwItjBAwCbQ.37.mp4", "question_id": "EwItjBAwCbQ.37.mp4", "answer": [{"role": "assistant", "content": "The image is dimly lit.", "related_timespan": [0.0, 10.8], "reply_timespan": [0.0, 11.8]}, {"role": "assistant", "content": "The tunnel is dimly lit.", "related_timespan": [10.83, 13.77], "reply_timespan": [10.83, 14.77]}, {"role": "assistant", "content": "The background is very dim.", "related_timespan": [13.8, 16.6], "reply_timespan": [13.8, 17.6]}, {"role": "assistant", "content": "The picture is very dim.", "related_timespan": [16.63, 19.27], "reply_timespan": [16.63, 19.3]}], "conversation": [{"role": "user", "content": "How is the lighting in the scenes described in the video?", "time": 0}], "duration": 19.3}, {"video": "eDZVdZvmG74.3.mp4", "question_id": "eDZVdZvmG74.3.mp4", "answer": [{"role": "assistant", "content": "A person fills up a car with a gas pump.", "related_timespan": [0.0, 3.6], "reply_timespan": [0.0, 4.6]}, {"role": "assistant", "content": "A gas station sign and branding are shown.", "related_timespan": [3.63, 9.1], "reply_timespan": [3.63, 10.1]}, {"role": "assistant", "content": "Cars are parked and one drives away while another drives in at the gas station.", "related_timespan": [14.2, 18.2], "reply_timespan": [14.2, 19.2]}], "conversation": [{"role": "user", "content": "What happens at the gas station?", "time": 0}], "duration": 21.0}, {"video": "_BVVEW8y_VA.9.mp4", "question_id": "_BVVEW8y_VA.9.mp4", "answer": [{"role": "assistant", "content": "The girl in blue jumps from a balcony to the ground before sliding under a cart.", "related_timespan": [4.17, 7.03], "reply_timespan": [4.17, 8.030000000000001]}, {"role": "assistant", "content": "The girl in blue jumps off the ledge, and people are looking up at her.", "related_timespan": [7.07, 8.43], "reply_timespan": [7.07, 9.43]}, {"role": "assistant", "content": "The girl in blue jumps from a height and falls on a basket of oranges.", "related_timespan": [8.47, 9.63], "reply_timespan": [8.47, 10.63]}, {"role": "assistant", "content": "After hitting the basket of oranges, the girl rolls down the steps.", "related_timespan": [9.67, 11.9], "reply_timespan": [9.67, 12.9]}], "conversation": [{"role": "user", "content": "What happens when the girl in blue jumps from the height?", "time": 0}], "duration": 14.633333333333333}, {"video": "F_0Nud7Wa8w.7.mp4", "question_id": "F_0Nud7Wa8w.7.mp4", "answer": [{"role": "assistant", "content": "The woman with long blonde hair is wearing a wedding dress and standing inside a bedroom.", "related_timespan": [0.0, 3.96], "reply_timespan": [0.0, 4.96]}, {"role": "assistant", "content": "The woman with long blonde hair is sitting and talking to the camera.", "related_timespan": [4.0, 12.68], "reply_timespan": [4.0, 13.68]}, {"role": "assistant", "content": "The woman with long blonde hair is walking next to a black bench and arranging pillows.", "related_timespan": [12.72, 15.1], "reply_timespan": [12.72, 15.140125]}], "conversation": [{"role": "user", "content": "What is the woman with long blonde hair doing?", "time": 0}], "duration": 15.140125}, {"video": "gw3w9MLxihk.5.mp4", "question_id": "gw3w9MLxihk.5.mp4", "answer": [{"role": "assistant", "content": "A woman in a green T-shirt talks and sits on the lawn.", "related_timespan": [0.0, 3.84], "reply_timespan": [0.0, 4.84]}, {"role": "assistant", "content": "A man and a woman squat in a garden and eat the plant in their hands.", "related_timespan": [3.88, 5.96], "reply_timespan": [3.88, 6.96]}, {"role": "assistant", "content": "Lots of people walk in a garden next to a house.", "related_timespan": [6.01, 7.84], "reply_timespan": [6.01, 8.84]}, {"role": "assistant", "content": "A man talks with a branch of a tree in his hand surrounded by lots of people in a garden.", "related_timespan": [7.88, 10.18], "reply_timespan": [7.88, 11.18]}, {"role": "assistant", "content": "A man and a woman stand next to a tree and eat leaves in their hands.", "related_timespan": [10.22, 12.68], "reply_timespan": [10.22, 12.721041666666666]}], "conversation": [{"role": "user", "content": "What are people doing in the garden?", "time": 0}], "duration": 12.721041666666666}, {"video": "8GnsEFk5HaI.28.mp4", "question_id": "8GnsEFk5HaI.28.mp4", "answer": [{"role": "assistant", "content": "The man in a suit and tie is standing with a microphone in front of him, speaking seriously. ", "related_timespan": [8.8, 15.53], "reply_timespan": [8.8, 16.53]}, {"role": "assistant", "content": "The man in a suit and glasses is walking down the red carpet and interacting with others.", "related_timespan": [15.57, 23.93], "reply_timespan": [15.57, 24.93]}], "conversation": [{"role": "user", "content": "What is the man in a suit and tie doing?", "time": 0}], "duration": 33.6}, {"video": "3EBMpTiWAg8.11.mp4", "question_id": "3EBMpTiWAg8.11.mp4", "answer": [{"role": "assistant", "content": "A person in a black T-shirt restrains another person on the ground.", "related_timespan": [0.0, 7.57], "reply_timespan": [0.0, 8.57]}, {"role": "assistant", "content": "A man in black moves on the ground beside a person sitting in a chair.", "related_timespan": [7.61, 9.88], "reply_timespan": [7.61, 10.88]}], "conversation": [{"role": "user", "content": "What action takes place involving a person in black?", "time": 0}], "duration": 12.512500000000001}, {"video": "JljoS47Qf_s.20.mp4", "question_id": "JljoS47Qf_s.20.mp4", "answer": [{"role": "assistant", "content": "The cartoon character with blonde hair holds a stick and talks on a pink background.", "related_timespan": [0.0, 5.26], "reply_timespan": [0.0, 6.26]}, {"role": "assistant", "content": "The cartoon character with blonde hair holds a stick pointing at the screen and talks to the camera.", "related_timespan": [5.3, 9.51], "reply_timespan": [5.3, 10.51]}, {"role": "assistant", "content": "A cartoon character with blonde hair appears with a blue background.", "related_timespan": [9.55, 14.06], "reply_timespan": [9.55, 14.097416666666666]}], "conversation": [{"role": "user", "content": "What is the cartoon character with blonde hair doing?", "time": 0}], "duration": 14.097416666666666}, {"video": "iU6Vq2pBI-I.59.mp4", "question_id": "iU6Vq2pBI-I.59.mp4", "answer": [{"role": "assistant", "content": "Credit cards, specifically the citi double cash card.", "related_timespan": [0.0, 22.59], "reply_timespan": [0.0, 23.59]}, {"role": "assistant", "content": "Features of a mobile phone app related to recognizing charges on a map.", "related_timespan": [25.46, 27.83], "reply_timespan": [25.46, 27.861166666666666]}], "conversation": [{"role": "user", "content": "What is being discussed in the video?", "time": 0}], "duration": 27.861166666666666}, {"video": "0pHWbrdttl8.15.mp4", "question_id": "0pHWbrdttl8.15.mp4", "answer": [{"role": "assistant", "content": "The man is using a black detector on the car door.", "related_timespan": [0.0, 2.8], "reply_timespan": [0.0, 3.8]}, {"role": "assistant", "content": "The device appears to be a detector with changing numbers.", "related_timespan": [4.94, 19.69], "reply_timespan": [4.94, 20.69]}, {"role": "assistant", "content": "The device appears to be a detector with a screen.", "related_timespan": [19.72, 22.66], "reply_timespan": [19.72, 22.689333333333334]}], "conversation": [{"role": "user", "content": "What is the purpose of the device the man is holding?", "time": 0}], "duration": 22.689333333333334}, {"video": "YofCMYZhYcs.5.mp4", "question_id": "YofCMYZhYcs.5.mp4", "answer": [{"role": "assistant", "content": "The two men are sitting at a table talking and gesturing.", "related_timespan": [0.0, 5.84], "reply_timespan": [0.0, 6.84]}, {"role": "assistant", "content": "The men are sitting at a table, with one man talking and the other nodding.", "related_timespan": [5.88, 12.55], "reply_timespan": [5.88, 13.55]}, {"role": "assistant", "content": "The men are sitting on a couch, with one man talking and gesturing while the other listens.", "related_timespan": [12.6, 19.64], "reply_timespan": [12.6, 19.686333333333334]}], "conversation": [{"role": "user", "content": "What are the men doing in the videos?", "time": 0}], "duration": 19.686333333333334}, {"video": "k7OQGhkYkqs.1.mp4", "question_id": "k7OQGhkYkqs.1.mp4", "answer": [{"role": "assistant", "content": "A white cylinder is placed on a windowsill.", "related_timespan": [0.0, 2.25], "reply_timespan": [0.0, 3.25]}, {"role": "assistant", "content": "A white object with a black bottom.", "related_timespan": [7.38, 11.34], "reply_timespan": [7.38, 11.386375]}], "conversation": [{"role": "user", "content": "What object is being shown in the video?", "time": 0}], "duration": 11.386375}, {"video": "RjC6aV1SNIQ.3.mp4", "question_id": "RjC6aV1SNIQ.3.mp4", "answer": [{"role": "assistant", "content": "A person is preparing to pour the white stuff from a clear container into a tub.", "related_timespan": [8.88, 12.18], "reply_timespan": [8.88, 13.18]}, {"role": "assistant", "content": "A woman uses a red spatula to scrape the white stuff into the silver tub.", "related_timespan": [12.22, 15.81], "reply_timespan": [12.22, 16.810000000000002]}, {"role": "assistant", "content": "A person is stirring the white stuff in the basin.", "related_timespan": [15.85, 18.27], "reply_timespan": [15.85, 18.30995833333333]}], "conversation": [{"role": "user", "content": "What is being done with the white stuff?", "time": 0}], "duration": 18.30995833333333}, {"video": "my2KTkgATPU.34.mp4", "question_id": "my2KTkgATPU.34.mp4", "answer": [{"role": "assistant", "content": "Macaroni is being stirred in a pot.", "related_timespan": [1.84, 14.35], "reply_timespan": [1.84, 15.35]}, {"role": "assistant", "content": "People are eating macaroni at the dining table.", "related_timespan": [14.38, 18.89], "reply_timespan": [14.38, 18.9189]}], "conversation": [{"role": "user", "content": "What type of food is being prepared or eaten?", "time": 0}], "duration": 18.9189}, {"video": "VWlP9ZnqmIQ.14.mp4", "question_id": "VWlP9ZnqmIQ.14.mp4", "answer": [{"role": "assistant", "content": "The man is pulling the zipper and pulling it up.", "related_timespan": [0.0, 2.5], "reply_timespan": [0.0, 3.5]}, {"role": "assistant", "content": "A person is holding the black bag and zipping it.", "related_timespan": [2.54, 8.31], "reply_timespan": [2.54, 9.31]}, {"role": "assistant", "content": "The man is pulling the zipper and checking it.", "related_timespan": [8.34, 13.78], "reply_timespan": [8.34, 14.78]}, {"role": "assistant", "content": "A person is unzipping the bag. The man takes out a black object from the bag.", "related_timespan": [13.81, 22.92], "reply_timespan": [13.81, 22.956266666666668]}], "conversation": [{"role": "user", "content": "What is being done with the black bags?", "time": 0}], "duration": 22.956266666666668}, {"video": "LbJ7VmR7Y9g.8.mp4", "question_id": "LbJ7VmR7Y9g.8.mp4", "answer": [{"role": "assistant", "content": "They are fishing on the ship.", "related_timespan": [0.0, 5.67], "reply_timespan": [0.0, 6.67]}, {"role": "assistant", "content": "Three people are fishing on the ship.", "related_timespan": [5.71, 10.76], "reply_timespan": [5.71, 11.76]}], "conversation": [{"role": "user", "content": "What is the activity taking place on the ship?", "time": 0}], "duration": 14.222541666666666}, {"video": "61fybvkZiDE.2.mp4", "question_id": "61fybvkZiDE.2.mp4", "answer": [{"role": "assistant", "content": "The man is talking and looking at the camera.", "related_timespan": [3.28, 7.6], "reply_timespan": [3.28, 8.6]}, {"role": "assistant", "content": "The man is holding and waving a transparent model and seems very happy.", "related_timespan": [7.64, 9.32], "reply_timespan": [7.64, 10.32]}, {"role": "assistant", "content": "The man is making something on the desk.", "related_timespan": [9.36, 12.96], "reply_timespan": [9.36, 13.96]}, {"role": "assistant", "content": "The man appears to be looking at something.", "related_timespan": [13.0, 14.48], "reply_timespan": [13.0, 14.52]}], "conversation": [{"role": "user", "content": "What is the man doing while wearing a blue shirt?", "time": 0}], "duration": 14.52}, {"video": "WZcMlzsh4XM.1.mp4", "question_id": "WZcMlzsh4XM.1.mp4", "answer": [{"role": "assistant", "content": "A man is putting a clod on a big plate made of concrete.", "related_timespan": [0.0, 1.7], "reply_timespan": [0.0, 2.7]}, {"role": "assistant", "content": "A person is checking the plumbing of a sink.", "related_timespan": [1.74, 3.94], "reply_timespan": [1.74, 4.9399999999999995]}, {"role": "assistant", "content": "A person is cleaning snow in the evening.", "related_timespan": [3.97, 7.81], "reply_timespan": [3.97, 8.809999999999999]}], "conversation": [{"role": "user", "content": "What activities are being shown in the video?", "time": 0}], "duration": 10.377033333333333}, {"video": "FhmTwZGt97Y.22.mp4", "question_id": "FhmTwZGt97Y.22.mp4", "answer": [{"role": "assistant", "content": "The person is pouring liquid from a white dish into a black pot on a table.", "related_timespan": [0.0, 5.6], "reply_timespan": [0.0, 6.6]}, {"role": "assistant", "content": "The person is pouring milk from an orange bowl into a black pot.", "related_timespan": [5.64, 11.64], "reply_timespan": [5.64, 11.68]}], "conversation": [{"role": "user", "content": "What is the person doing with the liquid?", "time": 0}], "duration": 11.68}, {"video": "B_A9FCsWnlI.6.mp4", "question_id": "B_A9FCsWnlI.6.mp4", "answer": [{"role": "assistant", "content": "A man is standing on a black hoverboard and riding it down and up a ramp in a room.", "related_timespan": [1.52, 4.72], "reply_timespan": [1.52, 5.72]}, {"role": "assistant", "content": "A man is standing on a black hoverboard and riding it down and up a ramp in a room, and there is also a close-up of a black hoverboard.", "related_timespan": [4.76, 7.08], "reply_timespan": [4.76, 8.08]}, {"role": "assistant", "content": "There is a black hoverboard on the table.", "related_timespan": [13.96, 17.6], "reply_timespan": [13.96, 17.64]}], "conversation": [{"role": "user", "content": "What activities involve the hoverboard?", "time": 0}], "duration": 17.64}, {"video": "muUcz5aYTMs.3.mp4", "question_id": "muUcz5aYTMs.3.mp4", "answer": [{"role": "assistant", "content": "A light is being shown.", "related_timespan": [0.0, 1.25], "reply_timespan": [0.0, 2.25]}, {"role": "assistant", "content": "Bicycle lights in a forest setting are being discussed, as indicated by the words \"Light Output (lumens), Beam Pattern, Mounting Options.\"", "related_timespan": [1.29, 10.68], "reply_timespan": [1.29, 11.68]}, {"role": "assistant", "content": "A man is showcasing bicycle lights.", "related_timespan": [10.72, 12.14], "reply_timespan": [10.72, 12.178833333333332]}], "conversation": [{"role": "user", "content": "What is being discussed or showcased?", "time": 0}], "duration": 12.178833333333332}, {"video": "WQFI_AIsYtg.15.mp4", "question_id": "WQFI_AIsYtg.15.mp4", "answer": [{"role": "assistant", "content": "The man's hands hold a sneaker with black and white accents.", "related_timespan": [0.0, 1.0], "reply_timespan": [0.0, 2.0]}, {"role": "assistant", "content": "The man is showing the camera the sneakers and removes an insole from the shoe.", "related_timespan": [1.03, 18.42], "reply_timespan": [1.03, 18.451766666666668]}], "conversation": [{"role": "user", "content": "What are the man's actions related to the sneakers?", "time": 0}], "duration": 18.451766666666668}, {"video": "RdhTvVPcoIo.12.mp4", "question_id": "RdhTvVPcoIo.12.mp4", "answer": [{"role": "assistant", "content": "The man is drawing on a monitor.", "related_timespan": [0.0, 3.5], "reply_timespan": [0.0, 4.5]}, {"role": "assistant", "content": "The man in black is spreading his arms out wide with a cloak-like item hanging behind him.", "related_timespan": [3.53, 5.1], "reply_timespan": [3.53, 6.1]}, {"role": "assistant", "content": "A man in purple and a woman in light blue are gazing at each other happily.", "related_timespan": [5.13, 6.4], "reply_timespan": [5.13, 7.4]}, {"role": "assistant", "content": "Various characters are dancing, looking at each other, or kissing.", "related_timespan": [6.43, 10.53], "reply_timespan": [6.43, 11.53]}, {"role": "assistant", "content": "A kneeling man dressed in black catches a flying figure in a red helmet.", "related_timespan": [10.57, 13.2], "reply_timespan": [10.57, 13.233333333333333]}], "conversation": [{"role": "user", "content": "What are the people or characters doing?", "time": 0}], "duration": 13.233333333333333}, {"video": "0fbebx_l2Xw.4.mp4", "question_id": "0fbebx_l2Xw.4.mp4", "answer": [{"role": "assistant", "content": "Having fun and smiling is a recurring theme.", "related_timespan": [0.0, 14.07], "reply_timespan": [0.0, 15.07]}, {"role": "assistant", "content": "Having fun and smiling is a recurring theme.", "related_timespan": [18.5, 19.97], "reply_timespan": [18.5, 20.97]}], "conversation": [{"role": "user", "content": "What is a recurring theme in the video?", "time": 0}], "duration": 29.466666666666665}, {"video": "7dyvxojt3o8.51.mp4", "question_id": "7dyvxojt3o8.51.mp4", "answer": [{"role": "assistant", "content": "A blue pickup truck with large tires is parked on a field.", "related_timespan": [0.0, 1.68], "reply_timespan": [0.0, 2.6799999999999997]}, {"role": "assistant", "content": "A blue pickup truck with large tires is parked on a field with trees and two tents in the background.", "related_timespan": [1.72, 4.16], "reply_timespan": [1.72, 5.16]}, {"role": "assistant", "content": "A large blue truck with large tires is parked in front of a large garage.", "related_timespan": [4.2, 10.36], "reply_timespan": [4.2, 11.36]}, {"role": "assistant", "content": "A white pickup truck with a brown stripe is parked in a field with trees in the background.", "related_timespan": [10.4, 15.28], "reply_timespan": [10.4, 16.28]}, {"role": "assistant", "content": "A large truck with large wheels is driving on a dirt road with people watching.", "related_timespan": [15.32, 17.32], "reply_timespan": [15.32, 17.36]}], "conversation": [{"role": "user", "content": "What can be observed about the vehicles in the video?", "time": 0}], "duration": 17.36}, {"video": "FYvIo4ylZus.1.mp4", "question_id": "FYvIo4ylZus.1.mp4", "answer": [{"role": "assistant", "content": "A person is using scissors to cut a circle in a white object.", "related_timespan": [0.0, 4.7], "reply_timespan": [0.0, 5.7]}, {"role": "assistant", "content": "A person is applying glue to a white disc with a white cap sitting next to it.", "related_timespan": [4.74, 8.07], "reply_timespan": [4.74, 9.07]}, {"role": "assistant", "content": "A person is gluing an orb to a white disc with a white cap placed next to it.", "related_timespan": [8.11, 12.11], "reply_timespan": [8.11, 13.11]}, {"role": "assistant", "content": "A person is placing a small disc dipped in orbs over a white lid.", "related_timespan": [12.15, 15.68], "reply_timespan": [12.15, 16.68]}, {"role": "assistant", "content": "A person is holding a white object with a lid that is covered with a clear orb.", "related_timespan": [15.72, 20.62], "reply_timespan": [15.72, 20.653966666666665]}], "conversation": [{"role": "user", "content": "What actions are being performed with the white object?", "time": 0}], "duration": 20.653966666666665}, {"video": "KGTsMFEgpFI.3.mp4", "question_id": "KGTsMFEgpFI.3.mp4", "answer": [{"role": "assistant", "content": "Two men in white uniforms are running around the field.", "related_timespan": [0.0, 1.47], "reply_timespan": [0.0, 2.4699999999999998]}, {"role": "assistant", "content": "A pitcher throws the baseball, and the batter hits it and starts running.", "related_timespan": [2.87, 5.51], "reply_timespan": [2.87, 6.51]}, {"role": "assistant", "content": "Some people are running on the field, and a few people are running out of an exit in the audience.", "related_timespan": [5.54, 11.38], "reply_timespan": [5.54, 11.4114]}], "conversation": [{"role": "user", "content": "What is happening during the baseball game?", "time": 0}], "duration": 11.4114}, {"video": "0KoYCn3MQd0.9.mp4", "question_id": "0KoYCn3MQd0.9.mp4", "answer": [{"role": "assistant", "content": "Elsa is walking toward the camera while talking.", "related_timespan": [0.0, 4.7], "reply_timespan": [0.0, 5.7]}, {"role": "assistant", "content": "Elsa is walking towards a terrace.", "related_timespan": [4.74, 8.17], "reply_timespan": [4.74, 9.17]}, {"role": "assistant", "content": "Elsa's feet touch the ground and the ground begins to freeze.", "related_timespan": [13.31, 16.15], "reply_timespan": [13.31, 17.15]}, {"role": "assistant", "content": "Elsa is holding hands with Anna and then pulling them apart.", "related_timespan": [16.18, 18.39], "reply_timespan": [16.18, 19.39]}, {"role": "assistant", "content": "Elsa is on a boat with other characters, with water splashing everywhere.", "related_timespan": [18.42, 21.05], "reply_timespan": [18.42, 21.087733333333333]}], "conversation": [{"role": "user", "content": "What is Elsa doing?", "time": 0}], "duration": 21.087733333333333}, {"video": "09RFL5SKVjM.22.mp4", "question_id": "09RFL5SKVjM.22.mp4", "answer": [{"role": "assistant", "content": "The words \"HOLLYWOOD\" are captured on the mountain.", "related_timespan": [6.27, 12.85], "reply_timespan": [6.27, 12.879533333333333]}], "conversation": [{"role": "user", "content": "What can be seen in the background on the mountain?", "time": 0}], "duration": 12.879533333333333}, {"video": "cN039e4NGAw.7.mp4", "question_id": "cN039e4NGAw.7.mp4", "answer": [{"role": "assistant", "content": "The man in a black suit glances to his left, turning his head and walking on.", "related_timespan": [7.61, 9.18], "reply_timespan": [7.61, 10.18]}, {"role": "assistant", "content": "The man in a black suit is standing and walking forward side by side with a man in a white shirt.", "related_timespan": [9.21, 11.38], "reply_timespan": [9.21, 11.4114]}], "conversation": [{"role": "user", "content": "What is the man in the suit doing?", "time": 0}], "duration": 11.4114}, {"video": "foGfkZjCHm0.21.mp4", "question_id": "foGfkZjCHm0.21.mp4", "answer": [{"role": "assistant", "content": "The man is doing squats.", "related_timespan": [0.0, 3.12], "reply_timespan": [0.0, 4.12]}, {"role": "assistant", "content": "The man is lying on the floor and exercising.", "related_timespan": [3.16, 5.76], "reply_timespan": [3.16, 6.76]}, {"role": "assistant", "content": "The man is exercising, sitting on the floor speaking to the camera.", "related_timespan": [5.8, 19.28], "reply_timespan": [5.8, 19.32]}], "conversation": [{"role": "user", "content": "What is the man doing in the video?", "time": 0}], "duration": 19.32}, {"video": "mtP9H7AClk8.7.mp4", "question_id": "mtP9H7AClk8.7.mp4", "answer": [{"role": "assistant", "content": "The theme involves a deer and characters Rick and Michonne in the woods.", "related_timespan": [0.0, 4.21], "reply_timespan": [0.0, 5.21]}, {"role": "assistant", "content": "The theme involves Michonne carrying a deer and pictures of Rick and Negan.", "related_timespan": [4.25, 10.01], "reply_timespan": [4.25, 11.01]}], "conversation": [{"role": "user", "content": "What is the recurring theme from The Walking Dead in the captions?", "time": 0}], "duration": 12.011999999999999}, {"video": "rM-QRSeVngo.1.mp4", "question_id": "rM-QRSeVngo.1.mp4", "answer": [{"role": "assistant", "content": "A \"DEADLY SHOOTING\" is being reported.", "related_timespan": [0.0, 12.41], "reply_timespan": [0.0, 13.41]}], "conversation": [{"role": "user", "content": "What is being reported in the news program?", "time": 0}], "duration": 20.4204}, {"video": "ZRL2bzC2GGE.67.mp4", "question_id": "ZRL2bzC2GGE.67.mp4", "answer": [{"role": "assistant", "content": "The setting is in front of rows of racks with motorcycle helmets and jackets.", "related_timespan": [0.0, 7.21], "reply_timespan": [0.0, 8.21]}, {"role": "assistant", "content": "The setting includes a motorcycle wall in the background.", "related_timespan": [7.24, 13.01], "reply_timespan": [7.24, 14.01]}, {"role": "assistant", "content": "The setting includes racks with motorcycle helmets and jackets, as well as a web page about \"2017 MOTO GEAR GUIDES\".", "related_timespan": [13.05, 17.42], "reply_timespan": [13.05, 17.450766666666667]}], "conversation": [{"role": "user", "content": "What is the setting for the video?", "time": 0}], "duration": 17.450766666666667}, {"video": "6He8k52im8I.6.mp4", "question_id": "6He8k52im8I.6.mp4", "answer": [{"role": "assistant", "content": "People are throwing torches at a vehicle and there is a street with many people walking on it.", "related_timespan": [2.77, 5.64], "reply_timespan": [2.77, 6.64]}, {"role": "assistant", "content": "A large group of people is marching through the street at night carrying signs and banners.", "related_timespan": [5.67, 6.94], "reply_timespan": [5.67, 7.94]}, {"role": "assistant", "content": "Police officers are lining up in a row and blocking the crowd on the street.", "related_timespan": [6.97, 9.91], "reply_timespan": [6.97, 10.91]}], "conversation": [{"role": "user", "content": "What notable events occur on the street?", "time": 0}], "duration": 14.1141}, {"video": "8TJF2VwFgD8.8.mp4", "question_id": "8TJF2VwFgD8.8.mp4", "answer": [{"role": "assistant", "content": "The woman is holding a concealer and showing it to the camera.", "related_timespan": [3.84, 5.84], "reply_timespan": [3.84, 6.84]}, {"role": "assistant", "content": "The woman applies concealer to the back of her hand.", "related_timespan": [5.88, 12.04], "reply_timespan": [5.88, 12.08]}], "conversation": [{"role": "user", "content": "What is the woman doing with the concealer?", "time": 0}], "duration": 12.08}, {"video": "7k3S6IsDZ28.27.mp4", "question_id": "7k3S6IsDZ28.27.mp4", "answer": [{"role": "assistant", "content": "A person in blue is holding a framed photograph with an orange button on it.", "related_timespan": [0.0, 4.63], "reply_timespan": [0.0, 5.63]}, {"role": "assistant", "content": "A man is holding a framed photograph of a yellow square.", "related_timespan": [4.67, 11.63], "reply_timespan": [4.67, 11.666666666666666]}], "conversation": [{"role": "user", "content": "Who is holding a framed photograph?", "time": 0}], "duration": 11.666666666666666}, {"video": "LjKTQ9RKdaM.1.mp4", "question_id": "LjKTQ9RKdaM.1.mp4", "answer": [{"role": "assistant", "content": "A brown mountain bike is shown.", "related_timespan": [0.0, 6.17], "reply_timespan": [0.0, 7.17]}, {"role": "assistant", "content": "A man stands in front of a black mountain bike.", "related_timespan": [6.21, 11.59], "reply_timespan": [6.21, 11.636624999999999]}], "conversation": [{"role": "user", "content": "What is shown in the video related to mountain biking?", "time": 0}], "duration": 11.636624999999999}, {"video": "eairs2hMa3I.31.mp4", "question_id": "eairs2hMa3I.31.mp4", "answer": [{"role": "assistant", "content": "The video shows ancient coins on display.", "related_timespan": [3.27, 5.71], "reply_timespan": [3.27, 6.71]}, {"role": "assistant", "content": "A man's hand is moving a pile of coins on a table.", "related_timespan": [5.77, 10.28], "reply_timespan": [5.77, 11.28]}], "conversation": [{"role": "user", "content": "What activities involve coins in the video?", "time": 0}], "duration": 15.4154}, {"video": "0IKKzYIK-AY.6.mp4", "question_id": "0IKKzYIK-AY.6.mp4", "answer": [{"role": "assistant", "content": "The man is walking while talking to the camera in a park.", "related_timespan": [0.0, 3.44], "reply_timespan": [0.0, 4.4399999999999995]}, {"role": "assistant", "content": "The man is riding a skateboard on concrete ground and talking.", "related_timespan": [6.31, 8.38], "reply_timespan": [6.31, 9.38]}, {"role": "assistant", "content": "The man is standing on the platform of a concrete staircase and talking to the camera.", "related_timespan": [9.58, 10.78], "reply_timespan": [9.58, 10.8108]}], "conversation": [{"role": "user", "content": "What is the man doing in the video?", "time": 0}], "duration": 10.8108}, {"video": "K78OKshZKIA.2.mp4", "question_id": "K78OKshZKIA.2.mp4", "answer": [{"role": "assistant", "content": "A person is holding a purple device in a black basket inside a machine.", "related_timespan": [16.18, 19.69], "reply_timespan": [16.18, 20.69]}], "conversation": [{"role": "user", "content": "What activities are being shown related to the purple device?", "time": 0}], "duration": 25.892533333333333}, {"video": "4EDw7gydFBk.25.mp4", "question_id": "4EDw7gydFBk.25.mp4", "answer": [{"role": "assistant", "content": "The man is sitting on a sofa with an electronic device in his hand.", "related_timespan": [0.0, 2.07], "reply_timespan": [0.0, 3.07]}, {"role": "assistant", "content": "The man puts down the electronic device.", "related_timespan": [7.61, 12.78], "reply_timespan": [7.61, 12.812800000000001]}], "conversation": [{"role": "user", "content": "What is the man doing with the electronic device?", "time": 0}], "duration": 12.812800000000001}, {"video": "GBERmCAKFic.12.mp4", "question_id": "GBERmCAKFic.12.mp4", "answer": [{"role": "assistant", "content": "The video shows an apartment building at night with police tape around it, indicating a crime scene.", "related_timespan": [3.54, 9.04], "reply_timespan": [3.54, 10.04]}, {"role": "assistant", "content": "There are two people standing next to a brick wall at night, one in uniform, implying police involvement at a crime scene.", "related_timespan": [9.08, 11.01], "reply_timespan": [9.08, 11.044366666666667]}], "conversation": [{"role": "user", "content": "What incident is being reported in the video?", "time": 0}], "duration": 11.044366666666667}, {"video": "_nbQsnUvlo4.9.mp4", "question_id": "_nbQsnUvlo4.9.mp4", "answer": [{"role": "assistant", "content": "The man is holding a white helmet in his hand and then placing it on a motorbike mirror.", "related_timespan": [0.0, 4.96], "reply_timespan": [0.0, 5.96]}, {"role": "assistant", "content": "The man is holding a white helmet, reaching into it, and then bringing it to his right hand while speaking into the camera.", "related_timespan": [9.47, 12.47], "reply_timespan": [9.47, 12.5125]}], "conversation": [{"role": "user", "content": "What is the man doing with the helmet?", "time": 0}], "duration": 12.5125}, {"video": "gCtvzTSzMxY.7.mp4", "question_id": "gCtvzTSzMxY.7.mp4", "answer": [{"role": "assistant", "content": "Face ID technology is being demonstrated.", "related_timespan": [0.0, 5.64], "reply_timespan": [0.0, 6.64]}, {"role": "assistant", "content": "The woman is using face recognition technology.", "related_timespan": [5.67, 7.17], "reply_timespan": [5.67, 8.17]}], "conversation": [{"role": "user", "content": "What technology is being demonstrated or discussed?", "time": 0}], "duration": 11.378033333333333}, {"video": "LpUyGPg_Dc8.8.mp4", "question_id": "LpUyGPg_Dc8.8.mp4", "answer": [{"role": "assistant", "content": "The pipe has many leak holes in it.", "related_timespan": [0.0, 3.4], "reply_timespan": [0.0, 4.4]}, {"role": "assistant", "content": "The pipe has a small hole in the center and is rusty.", "related_timespan": [3.44, 8.44], "reply_timespan": [3.44, 9.44]}, {"role": "assistant", "content": "The end of the pipe has two holes.", "related_timespan": [8.48, 20.92], "reply_timespan": [8.48, 20.96]}], "conversation": [{"role": "user", "content": "What is notable about the pipe?", "time": 0}], "duration": 20.96}, {"video": "WV1-ZuBmP-E.12.mp4", "question_id": "WV1-ZuBmP-E.12.mp4", "answer": [{"role": "assistant", "content": "The woman uses a make-up brush to make circles on her face.", "related_timespan": [0.0, 2.47], "reply_timespan": [0.0, 3.47]}, {"role": "assistant", "content": "A woman is painting her eyebrows with an eyebrow brush.", "related_timespan": [4.87, 15.02], "reply_timespan": [4.87, 15.048366666666666]}], "conversation": [{"role": "user", "content": "What is the woman doing with makeup?", "time": 0}], "duration": 15.048366666666666}, {"video": "MtuZyKJPjAM.4.mp4", "question_id": "MtuZyKJPjAM.4.mp4", "answer": [{"role": "assistant", "content": "A ball is thrown to an athlete, and there is a scoring box indicating a tracking of scores.", "related_timespan": [0.0, 1.47], "reply_timespan": [0.0, 2.4699999999999998]}, {"role": "assistant", "content": "An athlete throws a ball and another hits it and runs, showing an ongoing play in the sports event.", "related_timespan": [7.57, 12.28], "reply_timespan": [7.57, 13.28]}, {"role": "assistant", "content": "An athlete picks up and throws a ball, indicating gameplay activities.", "related_timespan": [12.31, 16.35], "reply_timespan": [12.31, 17.35]}, {"role": "assistant", "content": "An athlete is seen running and interacting on the court, with the presence of many players and spectators.", "related_timespan": [16.38, 19.39], "reply_timespan": [16.38, 19.4194]}], "conversation": [{"role": "user", "content": "What is happening in the sports event?", "time": 0}], "duration": 19.4194}, {"video": "dUAPtHIz8Rg.5.mp4", "question_id": "dUAPtHIz8Rg.5.mp4", "answer": [{"role": "assistant", "content": "A person is cooking chopped butternut squash.", "related_timespan": [7.34, 12.6], "reply_timespan": [7.34, 13.6]}, {"role": "assistant", "content": "The man is cooking pumpkin.", "related_timespan": [12.64, 21.86], "reply_timespan": [12.64, 22.86]}], "conversation": [{"role": "user", "content": "What is being cooked in the kitchen?", "time": 0}], "duration": 25.233541666666664}, {"video": "ecilxRa6m5g.4.mp4", "question_id": "ecilxRa6m5g.4.mp4", "answer": [{"role": "assistant", "content": "The woman is swinging to show off her trench coat.", "related_timespan": [0.0, 9.23], "reply_timespan": [0.0, 10.23]}, {"role": "assistant", "content": "The woman is standing in front of the camera showing off her clothing.", "related_timespan": [9.27, 12.07], "reply_timespan": [9.27, 12.1]}], "conversation": [{"role": "user", "content": "What is the woman doing in front of the camera?", "time": 0}], "duration": 12.1}, {"video": "fRqJsYel7V0.15.mp4", "question_id": "fRqJsYel7V0.15.mp4", "answer": [{"role": "assistant", "content": "The person is wrapping an onigiri in plastic wrap and putting the nori seam side down.", "related_timespan": [0.0, 4.4], "reply_timespan": [0.0, 5.4]}, {"role": "assistant", "content": "The person is putting down two onigiris on the table.", "related_timespan": [4.44, 8.94], "reply_timespan": [4.44, 9.94]}, {"role": "assistant", "content": "The person is putting an onigiri on a cutting board.", "related_timespan": [8.98, 10.94], "reply_timespan": [8.98, 11.94]}, {"role": "assistant", "content": "The person is cutting an onigiri on a cutting board.", "related_timespan": [10.98, 13.01], "reply_timespan": [10.98, 13.046366666666668]}], "conversation": [{"role": "user", "content": "What is being done to the onigiri?", "time": 0}], "duration": 13.046366666666668}, {"video": "_8UAR6y9vtM.1.mp4", "question_id": "_8UAR6y9vtM.1.mp4", "answer": [{"role": "assistant", "content": "A woman is explaining \"What is Antarctica?\"", "related_timespan": [0.0, 3.07], "reply_timespan": [0.0, 4.07]}, {"role": "assistant", "content": "Antarctica is shown as a white continent with \"South Pole\" written on it.", "related_timespan": [3.13, 13.27], "reply_timespan": [3.13, 14.27]}, {"role": "assistant", "content": "A snowy scene with wind is depicted, indicating the environmental conditions in Antarctica.", "related_timespan": [13.3, 16.83], "reply_timespan": [13.3, 17.83]}, {"role": "assistant", "content": "The video shows a vast white land, Antarctica, covered with snow and surrounded by ocean.", "related_timespan": [16.9, 23.37], "reply_timespan": [16.9, 23.4]}], "conversation": [{"role": "user", "content": "What is featured about Antarctica in the video?", "time": 0}], "duration": 23.4}, {"video": "rwbhbyWCR2U.8.mp4", "question_id": "rwbhbyWCR2U.8.mp4", "answer": [{"role": "assistant", "content": "Candles are present in the background and within the lamp the woman is holding.", "related_timespan": [0.0, 4.84], "reply_timespan": [0.0, 5.84]}, {"role": "assistant", "content": "The person lying on the ground is surrounded by a lot of lighted candles.", "related_timespan": [4.88, 11.01], "reply_timespan": [4.88, 12.01]}, {"role": "assistant", "content": "Candles are lit on the table and around the room for dim lighting.", "related_timespan": [11.05, 15.64], "reply_timespan": [11.05, 15.682333333333332]}], "conversation": [{"role": "user", "content": "What role do candles play in the scenes?", "time": 0}], "duration": 15.682333333333332}, {"video": "O7aiG0D0qo0.25.mp4", "question_id": "O7aiG0D0qo0.25.mp4", "answer": [{"role": "assistant", "content": "Lemons help flush out toxins throughout the body.", "related_timespan": [0.0, 1.37], "reply_timespan": [0.0, 2.37]}, {"role": "assistant", "content": "Lemons help reduce the risk of heart disease and stroke.", "related_timespan": [1.4, 6.43], "reply_timespan": [1.4, 7.43]}], "conversation": [{"role": "user", "content": "What are the potential health benefits of lemons mentioned in the video?", "time": 0}], "duration": 11.8}, {"video": "PXMngeRSdsE.25.mp4", "question_id": "PXMngeRSdsE.25.mp4", "answer": [{"role": "assistant", "content": "A cartoon character hangs in the air and rotates.", "related_timespan": [0.0, 2.87], "reply_timespan": [0.0, 3.87]}, {"role": "assistant", "content": "A game character stands on rocky terrain holding two lightsabers.", "related_timespan": [2.9, 6.14], "reply_timespan": [2.9, 7.14]}, {"role": "assistant", "content": "A person is preparing to open a car door.", "related_timespan": [7.31, 9.51], "reply_timespan": [7.31, 10.51]}, {"role": "assistant", "content": "A person opens the car door and pulls another person out.", "related_timespan": [9.54, 14.08], "reply_timespan": [9.54, 15.08]}, {"role": "assistant", "content": "A man stands on the street holding something, then falls down.", "related_timespan": [14.11, 16.75], "reply_timespan": [14.11, 16.783433333333335]}], "conversation": [{"role": "user", "content": "What actions are the characters performing?", "time": 0}], "duration": 16.783433333333335}, {"video": "0YSLASX3Iq8.3.mp4", "question_id": "0YSLASX3Iq8.3.mp4", "answer": [{"role": "assistant", "content": "Pasta.", "related_timespan": [0.0, 1.63], "reply_timespan": [0.0, 2.63]}, {"role": "assistant", "content": "Capsicum.", "related_timespan": [1.67, 4.5], "reply_timespan": [1.67, 5.5]}, {"role": "assistant", "content": "Tomatoes.", "related_timespan": [4.57, 7.57], "reply_timespan": [4.57, 8.57]}, {"role": "assistant", "content": "Onions and garlic.", "related_timespan": [7.61, 12.78], "reply_timespan": [7.61, 13.78]}, {"role": "assistant", "content": "Milk.", "related_timespan": [12.81, 16.05], "reply_timespan": [12.81, 17.05]}, {"role": "assistant", "content": "Flour and butter.", "related_timespan": [16.12, 20.45], "reply_timespan": [16.12, 21.45]}, {"role": "assistant", "content": "Cheese, salt, and black pepper.", "related_timespan": [20.49, 29.06], "reply_timespan": [20.49, 30.06]}, {"role": "assistant", "content": "Eggs.", "related_timespan": [29.13, 33.97], "reply_timespan": [29.13, 34.00063333333333]}], "conversation": [{"role": "user", "content": "What ingredients are shown in the video?", "time": 0}], "duration": 34.00063333333333}, {"video": "ClksIvHsYcc.24.mp4", "question_id": "ClksIvHsYcc.24.mp4", "answer": [{"role": "assistant", "content": "The person is dancing in front of a yellow background with many yellow and orange balloons.", "related_timespan": [0.0, 3.13], "reply_timespan": [0.0, 4.13]}, {"role": "assistant", "content": "The person is sitting on a white chair and playing the white piano.", "related_timespan": [3.17, 4.63], "reply_timespan": [3.17, 5.63]}, {"role": "assistant", "content": "The person is smiling and making gestures to the camera.", "related_timespan": [4.67, 7.34], "reply_timespan": [4.67, 8.34]}, {"role": "assistant", "content": "The person is talking and shaking his head.", "related_timespan": [7.38, 9.18], "reply_timespan": [7.38, 10.18]}, {"role": "assistant", "content": "The person is sitting by a black piano.", "related_timespan": [10.72, 13.72], "reply_timespan": [10.72, 13.763749999999998]}], "conversation": [{"role": "user", "content": "What is the person with a head that looks like the moon doing?", "time": 0}], "duration": 13.763749999999998}, {"video": "0lbseqh9fsg.35.mp4", "question_id": "0lbseqh9fsg.35.mp4", "answer": [{"role": "assistant", "content": "The man and the woman are embracing each other and seemed to enjoy each other's company.", "related_timespan": [0.0, 3.94], "reply_timespan": [0.0, 4.9399999999999995]}, {"role": "assistant", "content": "They have been friends for years according to the caption.", "related_timespan": [3.97, 8.71], "reply_timespan": [3.97, 9.71]}, {"role": "assistant", "content": "Fans are wondering when the two would cross the line of friendship.", "related_timespan": [8.74, 12.78], "reply_timespan": [8.74, 12.812800000000001]}], "conversation": [{"role": "user", "content": "What is the relationship between the man and the woman?", "time": 0}], "duration": 12.812800000000001}, {"video": "KqUZIzkIWaY.7.mp4", "question_id": "KqUZIzkIWaY.7.mp4", "answer": [{"role": "assistant", "content": "Oil is being poured into a pan on a stove.", "related_timespan": [0.0, 3.96], "reply_timespan": [0.0, 4.96]}, {"role": "assistant", "content": "The oil is being used to coat food and starts to sizzle.", "related_timespan": [6.59, 12.76], "reply_timespan": [6.59, 12.804458333333333]}], "conversation": [{"role": "user", "content": "What is being done with the oil?", "time": 0}], "duration": 12.804458333333333}, {"video": "2uthP1KSZMc.11.mp4", "question_id": "2uthP1KSZMc.11.mp4", "answer": [{"role": "assistant", "content": "A little girl with red hair holding a torch and wearing clothes made of leaves is depicted in the painting.", "related_timespan": [0.0, 5.21], "reply_timespan": [0.0, 6.21]}, {"role": "assistant", "content": "A large tree with red leaves and a bright light, with a man standing on a rock, is depicted in the painting.", "related_timespan": [5.26, 12.55], "reply_timespan": [5.26, 12.595916666666666]}], "conversation": [{"role": "user", "content": "What is depicted in the paintings?", "time": 0}], "duration": 12.595916666666666}, {"video": "crFioD1EelM.13.mp4", "question_id": "crFioD1EelM.13.mp4", "answer": [{"role": "assistant", "content": "A person is standing on a paddle board, paddling water with a paddle.", "related_timespan": [0.0, 4.63], "reply_timespan": [0.0, 5.63]}, {"role": "assistant", "content": "Three men are standing on a paddle board, gliding on the water.", "related_timespan": [10.39, 12.68], "reply_timespan": [10.39, 12.721041666666666]}], "conversation": [{"role": "user", "content": "What is happening on the paddle board?", "time": 0}], "duration": 12.721041666666666}, {"video": "6qTEzEcD3No.5.mp4", "question_id": "6qTEzEcD3No.5.mp4", "answer": [{"role": "assistant", "content": "The man in a black suit appears in front of a blue background, looks at the camera with a ferocious expression, and speaks.", "related_timespan": [0.0, 12.71], "reply_timespan": [0.0, 12.746066666666668]}], "conversation": [{"role": "user", "content": "What is the man in the suit doing?", "time": 0}], "duration": 12.746066666666668}, {"video": "dR9jfG9Zr5A.2.mp4", "question_id": "dR9jfG9Zr5A.2.mp4", "answer": [{"role": "assistant", "content": "The woman is doing sign language.", "related_timespan": [11.04, 13.65], "reply_timespan": [11.04, 14.65]}, {"role": "assistant", "content": "The woman is talking to two other ladies in sign language.", "related_timespan": [13.68, 19.29], "reply_timespan": [13.68, 20.29]}, {"role": "assistant", "content": "The woman is doing sign language with a sad expression on her face.", "related_timespan": [22.12, 24.52], "reply_timespan": [22.12, 24.557866666666666]}], "conversation": [{"role": "user", "content": "What is the woman in the dark camisole doing?", "time": 0}], "duration": 24.557866666666666}, {"video": "0tdkFPQri14.15.mp4", "question_id": "0tdkFPQri14.15.mp4", "answer": [{"role": "assistant", "content": "A man is sitting on a chair in a dimly lit room.", "related_timespan": [1.84, 7.41], "reply_timespan": [1.84, 8.41]}, {"role": "assistant", "content": "A man is sitting on a chair and speaks to the camera in a dimly lit room.", "related_timespan": [7.44, 9.41], "reply_timespan": [7.44, 10.41]}, {"role": "assistant", "content": "Special effects occur while a man sits in a dimly lit room, and something causes a man to rush out.", "related_timespan": [9.44, 11.91], "reply_timespan": [9.44, 12.91]}, {"role": "assistant", "content": "A man is sitting in a dimly lit room, talking to the camera.", "related_timespan": [11.95, 13.65], "reply_timespan": [11.95, 14.65]}, {"role": "assistant", "content": "A man is sitting in a dimly lit room, and the light slowly turns on revealing a monster.", "related_timespan": [13.68, 14.68], "reply_timespan": [13.68, 15.68]}, {"role": "assistant", "content": "Special effects occur as a man sits with someone passed out in a dimly lit room.", "related_timespan": [14.71, 17.58], "reply_timespan": [14.71, 17.6176]}], "conversation": [{"role": "user", "content": "What is happening in the dimly lit room?", "time": 0}], "duration": 17.6176}, {"video": "2iOaljmFfb0.4.mp4", "question_id": "2iOaljmFfb0.4.mp4", "answer": [{"role": "assistant", "content": "The video shows a woman speaking in front of a wall of light bulbs.", "related_timespan": [0.0, 10.94], "reply_timespan": [0.0, 11.94]}, {"role": "assistant", "content": "The video shows a close-up shot of a spiral-lit bulb.", "related_timespan": [10.98, 13.91], "reply_timespan": [10.98, 14.91]}], "conversation": [{"role": "user", "content": "What does the video primarily focus on?", "time": 0}], "duration": 15.3153}, {"video": "PvvMz5FBofo.6.mp4", "question_id": "PvvMz5FBofo.6.mp4", "answer": [{"role": "assistant", "content": "Players in red and white jerseys are in the field, and one player is walking forward.", "related_timespan": [0.0, 3.56], "reply_timespan": [0.0, 4.5600000000000005]}, {"role": "assistant", "content": "Players in red and white jerseys are passing the ball.", "related_timespan": [3.6, 8.12], "reply_timespan": [3.6, 9.12]}], "conversation": [{"role": "user", "content": "What are the players in red and white jerseys doing?", "time": 0}], "duration": 11.68}, {"video": "Itob3CQDzR4.18.mp4", "question_id": "Itob3CQDzR4.18.mp4", "answer": [{"role": "assistant", "content": "Colorful bubbles appear covering the whole screen after the yellow cow is shown.", "related_timespan": [0.0, 1.6], "reply_timespan": [0.0, 2.6]}, {"role": "assistant", "content": "Colorful bubbles appear covering the whole screen after the bear is formed.", "related_timespan": [1.64, 15.32], "reply_timespan": [1.64, 15.36]}], "conversation": [{"role": "user", "content": "What happens when the colorful bubbles appear?", "time": 0}], "duration": 15.36}, {"video": "e3aLYhXDbz8.12.mp4", "question_id": "e3aLYhXDbz8.12.mp4", "answer": [{"role": "assistant", "content": "Red.", "related_timespan": [0.0, 3.24], "reply_timespan": [0.0, 4.24]}, {"role": "assistant", "content": "Red and white.", "related_timespan": [3.32, 11.36], "reply_timespan": [3.32, 11.4]}], "conversation": [{"role": "user", "content": "What is the color of the vehicles mentioned?", "time": 0}], "duration": 11.4}, {"video": "_Jh8mvvoXsM.1.mp4", "question_id": "_Jh8mvvoXsM.1.mp4", "answer": [{"role": "assistant", "content": "A man in a space suit is smiling at the camera, implying involvement in space activities.", "related_timespan": [5.67, 8.04], "reply_timespan": [5.67, 9.04]}, {"role": "assistant", "content": "A space shuttle is launching into space.", "related_timespan": [8.07, 10.04], "reply_timespan": [8.07, 10.076733333333333]}], "conversation": [{"role": "user", "content": "What happens in space in the video?", "time": 0}], "duration": 10.076733333333333}, {"video": "GFhJFhBUc-Y.7.mp4", "question_id": "GFhJFhBUc-Y.7.mp4", "answer": [{"role": "assistant", "content": "A woman is speaking into a microphone.", "related_timespan": [0.0, 1.6], "reply_timespan": [0.0, 2.6]}, {"role": "assistant", "content": "Several people stand next to and behind the car.", "related_timespan": [1.63, 4.44], "reply_timespan": [1.63, 5.44]}, {"role": "assistant", "content": "A group of people stand by the side of the road watching a car drive.", "related_timespan": [4.47, 11.08], "reply_timespan": [4.47, 12.08]}, {"role": "assistant", "content": "A few people are still standing near the road.", "related_timespan": [11.14, 15.02], "reply_timespan": [11.14, 15.048366666666666]}], "conversation": [{"role": "user", "content": "What activities are people doing around the road?", "time": 0}], "duration": 15.048366666666666}, {"video": "iz6G1svj7Ng.4.mp4", "question_id": "iz6G1svj7Ng.4.mp4", "answer": [{"role": "assistant", "content": "The device is described as being a gaming console.", "related_timespan": [0.0, 1.84], "reply_timespan": [0.0, 2.84]}, {"role": "assistant", "content": "The device has several ports on the side, and a person is removing the lids from the ports.", "related_timespan": [1.87, 12.61], "reply_timespan": [1.87, 12.645966666666666]}], "conversation": [{"role": "user", "content": "What is shown about the device?", "time": 0}], "duration": 12.645966666666666}, {"video": "84GzJD_ARp0.4.mp4", "question_id": "84GzJD_ARp0.4.mp4", "answer": [{"role": "assistant", "content": "The woman is speaking to the camera with an onion in her hand.", "related_timespan": [0.0, 7.88], "reply_timespan": [0.0, 8.879999999999999]}, {"role": "assistant", "content": "The woman is pulling things out of a green shopping bag and talking to the camera.", "related_timespan": [7.92, 11.56], "reply_timespan": [7.92, 11.6]}], "conversation": [{"role": "user", "content": "What is the woman doing in her kitchen?", "time": 0}], "duration": 11.6}, {"video": "1rtBD13QbXE.16.mp4", "question_id": "1rtBD13QbXE.16.mp4", "answer": [{"role": "assistant", "content": "A black leather jacket and its details are being shown.", "related_timespan": [0.0, 9.18], "reply_timespan": [0.0, 10.18]}, {"role": "assistant", "content": "A black leather jacket and its details are being shown.", "related_timespan": [9.21, 11.01], "reply_timespan": [9.21, 11.044366666666667]}], "conversation": [{"role": "user", "content": "What is being shown or described in the video?", "time": 0}], "duration": 11.044366666666667}]
paper_experiments/run.py ADDED
@@ -0,0 +1,157 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python
2
+ # paper_experiments/run.py — reproduce every table in the report with one command each.
3
+ #
4
+ # python paper_experiments/run.py table1 # Table 1: main progression (score AND cost)
5
+ # python paper_experiments/run.py table2 # Table 2: ensemble ablation (calls per frame)
6
+ # python paper_experiments/run.py table3 # Table 3: dedup ablation
7
+ # python paper_experiments/run.py gate # Table 4: perception-gate cost/score frontier
8
+ # python paper_experiments/run.py sensitivity # FS example-sensitivity check (Sec. 4.x)
9
+ # python paper_experiments/run.py all
10
+ #
11
+ # Self-contained and DETERMINISTIC: replays the shipped per-frame VLM decision caches
12
+ # (caches/web_*.jsonl, holdout videos only) through the system's merge + entailment dedup, and scores
13
+ # with the official-protocol harness using the shipped LLM-judge cache (caches/judge_cache.json) —
14
+ # so a rerun makes ZERO API calls and reproduces the reported numbers exactly. The perception gate is
15
+ # replayed from the shipped per-frame novelty cache (caches/novelty_holdout.json). Requires only
16
+ # `pip install -e .` (first run downloads the MiniLM sentence embedder, ~90 MB).
17
+ import argparse
18
+ import json
19
+ import os
20
+ import shutil
21
+ import sys
22
+ from concurrent.futures import ThreadPoolExecutor
23
+
24
+ HERE = os.path.dirname(os.path.abspath(__file__))
25
+ ROOT = os.path.dirname(HERE)
26
+ os.chdir(ROOT) # humomni modules use repo-root-relative cache paths
27
+ sys.path.insert(0, os.path.join(ROOT, "src"))
28
+
29
+ # The scoring harness reads its judge cache from cache/judge_cache.json (repo-root relative).
30
+ # Ship-in the reproduction copy if the local one is absent so reruns are exact and free.
31
+ os.makedirs("cache", exist_ok=True)
32
+ if not os.path.exists("cache/judge_cache.json"):
33
+ shutil.copyfile(os.path.join(HERE, "caches", "judge_cache.json"), "cache/judge_cache.json")
34
+
35
+ import humomni.core.emb_cache as emb_cache
36
+ from humomni.phase1.dedup import EntailmentGuard
37
+ from humomni.tuning.cache import VLMCache
38
+ from humomni.tuning.faithful_eval import faithful_score
39
+
40
+ THETA = 0.6 # NEW_ANSWER confidence threshold (config.json "theta")
41
+ GOLD = json.load(open(os.path.join(HERE, "data", "gold_holdout.json"), encoding="utf-8"))
42
+ CACHES = {n: VLMCache(os.path.join(HERE, "caches", f"web_{n}.jsonl"))
43
+ for n in ("base", "comp", "exh", "fs", "fs_alt")}
44
+ NOVELTY = json.load(open(os.path.join(HERE, "caches", "novelty_holdout.json"), encoding="utf-8"))
45
+ _TIMES = {} # qid -> ascending frame timestamps (from the cache itself)
46
+ for c in CACHES.values():
47
+ for (qid, t) in c.mem:
48
+ _TIMES.setdefault(qid, set()).add(t)
49
+ _TIMES = {q: sorted(ts) for q, ts in _TIMES.items()}
50
+
51
+ emb_cache.load_disk()
52
+ emb_cache.embed_many([d["draft"].strip() for c in CACHES.values()
53
+ for d in c.mem.values() if d.get("draft", "").strip()])
54
+
55
+
56
+ def replay(names, k, use_llm=True, sim_high=0.97, sim_low=0.55, gate=None):
57
+ """Replay the cached decisions of ensemble `names` through the merge + dedup (identical to the
58
+ live policy: prompts queried in order per frame, said=[]). `gate` = None (call the VLM on every
59
+ frame) or a dict {novelty, max_silence_s, warmup_s}: the CPU perception gate is replayed from the
60
+ per-frame novelty cache, and the VLM is consulted ONLY on frames where the gate fires.
61
+ Returns (predictions, vlm_calls_per_video)."""
62
+ cs = [CACHES[n] for n in names]
63
+ def one(item):
64
+ qid = item["question_id"]
65
+ g = EntailmentGuard(sim_high=sim_high, sim_low=sim_low, recent_k=k, use_llm=use_llm)
66
+ said, res, calls, last_fire = [], [], 0, -1e9
67
+ for t in _TIMES[qid]:
68
+ if gate is not None:
69
+ # Bit-identical replay of phase1.perception_gate.PerceptionGate.should_decide: the
70
+ # cached novelty (tuning.cache.build_novelty, keys f"{t:.1f}") uses the SAME grayscale
71
+ # 64x36 mean-abs-diff feature the live gate computes, and the live policy runs this
72
+ # check BEFORE the VLM request (policy_api.step) — skip means no API call.
73
+ nov = NOVELTY[qid][f"{t:.1f}"]
74
+ fire = (t <= gate["warmup_s"]) or (nov >= gate["novelty"]) \
75
+ or (t - last_fire >= gate["max_silence_s"])
76
+ if not fire:
77
+ continue
78
+ last_fire = t
79
+ calls += len(cs)
80
+ for c in cs:
81
+ d = c.get(qid, t)
82
+ if d and d["decision"] == "NEW_ANSWER" and d["confidence"] >= THETA:
83
+ dr = d["draft"].strip()
84
+ if dr and not g.is_duplicate(dr, said):
85
+ said.append(dr)
86
+ res.append({"time": round(t, 3), "content": dr})
87
+ return qid, (res, calls)
88
+ with ThreadPoolExecutor(max_workers=3) as ex:
89
+ out = dict(ex.map(one, GOLD))
90
+ preds = {q: v[0] for q, v in out.items()}
91
+ calls = sum(v[1] for v in out.values()) / len(out)
92
+ return preds, calls
93
+
94
+
95
+ def row(label, **kw):
96
+ pred, calls = replay(**kw)
97
+ r = faithful_score(GOLD, pred, workers=3)
98
+ emit = sum(len(v) for v in pred.values()) / len(pred)
99
+ print(f" {label:46} score={r['score']:.4f} PAUC={r['PAUC']:.4f} dup={r['duplicate']:.4f} "
100
+ f"emits/video={emit:4.1f} VLM-calls/video={calls:5.1f}", flush=True)
101
+
102
+
103
+ def table1():
104
+ print("Table 1 — main progression: score AND cost (holdout, official protocol)", flush=True)
105
+ row("BASE (focused prompt), k=16", names=["base"], k=16)
106
+ row("COMP (comprehensive), k=16", names=["comp"], k=16)
107
+ row("EXH (exhaustive+OCR), k=16", names=["exh"], k=16)
108
+ row("EXH+COMP ensemble, k=32", names=["exh", "comp"], k=32)
109
+ row("EXH+COMP+FS ensemble, k=40 [max-score]", names=["exh", "comp", "fs"], k=40)
110
+
111
+
112
+ def table2():
113
+ print("Table 2 — ensemble ablation (calls per frame)", flush=True)
114
+ row("1x EXH alone, k=16", names=["exh"], k=16)
115
+ row("1x FS alone (EXH rules + few-shot), k=16", names=["fs"], k=16)
116
+ row("2x EXH+COMP, k=32", names=["exh", "comp"], k=32)
117
+ row("3x EXH+COMP+FS, k=40", names=["exh", "comp", "fs"], k=40)
118
+
119
+
120
+ def table3():
121
+ print("Table 3 — dedup ablation (on EXH+COMP, k=32)", flush=True)
122
+ row("two-stage entailment (ours)", names=["exh", "comp"], k=32, use_llm=True)
123
+ row("cosine-only", names=["exh", "comp"], k=32, use_llm=False)
124
+ row("none (emit everything)", names=["exh", "comp"], k=32, use_llm=False, sim_high=2.0)
125
+
126
+
127
+ # Gate operating points: "deployed" = the configuration our initial system shipped with.
128
+ GATES = [("gate off (max coverage)", None),
129
+ ("gate: nov>=8, silence 1.5s (deployed)", {"novelty": 8, "max_silence_s": 1.5, "warmup_s": 1.0}),
130
+ ("gate: nov>=16, silence 3s (aggressive)", {"novelty": 16, "max_silence_s": 3.0, "warmup_s": 1.0})]
131
+
132
+
133
+ def gate():
134
+ print("Table 4 — perception-gate cost/score frontier", flush=True)
135
+ print(" on EXH alone (k=16):", flush=True)
136
+ for lab, gcfg in GATES:
137
+ row(" " + lab, names=["exh"], k=16, gate=gcfg)
138
+ print(" on the full ensemble EXH+COMP+FS (k=40):", flush=True)
139
+ for lab, gcfg in GATES:
140
+ row(" " + lab, names=["exh", "comp", "fs"], k=40, gate=gcfg)
141
+
142
+
143
+ def sensitivity():
144
+ print("FS example-sensitivity (same shape, different examples)", flush=True)
145
+ row("EXH+COMP+FS (paper examples), k=40", names=["exh", "comp", "fs"], k=40)
146
+ row("EXH+COMP+FS-alt (swapped examples), k=40", names=["exh", "comp", "fs_alt"], k=40)
147
+
148
+
149
+ if __name__ == "__main__":
150
+ ap = argparse.ArgumentParser(description="Reproduce the report's experiment tables.")
151
+ ap.add_argument("what", choices=["table1", "table2", "table3", "gate", "sensitivity", "all"])
152
+ a = ap.parse_args()
153
+ todo = [table1, table2, table3, gate, sensitivity] if a.what == "all" else [globals()[a.what]]
154
+ for fn in todo:
155
+ fn()
156
+ print(flush=True)
157
+ emb_cache.save_disk()
pyproject.toml ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [build-system]
2
+ requires = ["setuptools>=61"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "humomni-track2"
7
+ version = "0.1.0"
8
+ description = "HumOmni 2026 Track 2 (ProactivEval) — causal proactive streaming video QA (Phase-1 API orchestrator)"
9
+ requires-python = ">=3.10"
10
+ dependencies = [
11
+ "openai", "pillow", "numpy", "scikit-learn", "sentence-transformers", "tqdm",
12
+ ]
13
+ # Note: the Gemini judge talks to Vertex AI express mode over plain REST (stdlib urllib) — no SDK dep.
14
+
15
+ [tool.setuptools.packages.find]
16
+ where = ["src"]
17
+
18
+ [tool.pytest.ini_options]
19
+ testpaths = ["tests"]
scripts/build_submission.py ADDED
@@ -0,0 +1,178 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python
2
+ # build_submission.py — ONE command from the test set to a validated submission.
3
+ #
4
+ # python scripts/build_submission.py --out submissions/tryanderror2.jsonl
5
+ #
6
+ # Everything is config-driven: the METHOD lives in config.json's "ensemble" block (v5+v3+v8 @ recent_k=40,
7
+ # the highest-score config = holdout 0.8018) + "dedup"/"gate"/"theta"; secrets (OPENROUTER_API_KEY for the
8
+ # VLM, VERTEX_KEY for the LLM dedup) live in .env. The script:
9
+ # 1. for EACH ensemble prompt, ensures a per-prompt decision cache exists — BUILDING it from the test
10
+ # frames (data/phase1/data) if missing/incomplete (resumable, skips cached frames);
11
+ # 2. REPLAYS the caches through the SAME merge + entailment-dedup as the live policy (APIPolicy.step) —
12
+ # identical output to `run_inference.py --policy api` (live ≡ replay: each prompt per frame, said=[],
13
+ # gate-off, then dedup);
14
+ # 3. writes the JSONL and VALIDATES it (500 ids, times ×0.5).
15
+ # First run builds the caches (PAID, ~1× VLM call per prompt per frame); subsequent runs are ~free.
16
+ # The only manual step is naming the upload file exactly your Team ID.
17
+ import argparse
18
+ import glob
19
+ import json
20
+ import os
21
+ import sys
22
+ import threading
23
+ from concurrent.futures import ThreadPoolExecutor
24
+
25
+ import humomni.core.emb_cache as emb_cache
26
+ import humomni.core.judges as judges
27
+ from humomni.tuning.cache import VLMCache, frames_of
28
+ from humomni.phase1.dedup import EntailmentGuard
29
+ from humomni.core.validate_submission import validate, gold_ids_from_dir
30
+
31
+ _save_lock = threading.Lock()
32
+ _progress = [0]
33
+
34
+
35
+ def _checkpoint(every=20):
36
+ """Persist the (LLM-dedup) judge cache every `every` samples so a Vertex 429 mid-build never
37
+ re-pays for finished work — a resubmit resumes from the disk cache. The original one-off never
38
+ saved these, which is why a fresh build floods Vertex with uncached entailment calls."""
39
+ with _save_lock:
40
+ _progress[0] += 1
41
+ if _progress[0] % every == 0:
42
+ judges.jc_save()
43
+
44
+
45
+ def build(data_dir, out, cfg, cache_tpl, workers, limit=0, build_workers=8):
46
+ ens = cfg.get("ensemble")
47
+ if not ens:
48
+ sys.exit("config.json has no 'ensemble' block — the best method is the v5+v3 ensemble; add it.")
49
+ prompts = ens["prompts"]
50
+ recent_k = ens.get("recent_k", cfg.get("dedup", {}).get("recent_k", 4))
51
+ theta = cfg.get("theta", 0.6)
52
+ d = cfg.get("dedup", {})
53
+
54
+ # Optional perception gate (config.json "gate", use_gate=true): replayed BIT-IDENTICALLY to the live
55
+ # phase1.perception_gate (same grayscale-novelty feature, same fire rule, runs BEFORE the VLM lookup).
56
+ # Novelty per frame is computed once on CPU (free) and cached; gated-off frames emit nothing.
57
+ g = cfg.get("gate", {})
58
+ novelty = None
59
+ if g.get("use_gate"):
60
+ from humomni.tuning.cache import build_novelty
61
+ nov_path = "cache/phase1_novelty.json"
62
+ if not os.path.exists(nov_path):
63
+ print(f"[gate] computing per-frame novelty for {data_dir} -> {nov_path} (CPU, one-off) …", flush=True)
64
+ build_novelty(data_dir=data_dir, out=nov_path) # ALWAYS full — a --limit-partial cache would poison later runs
65
+ novelty = json.load(open(nov_path, encoding="utf-8"))
66
+ print(f"[gate] ON — novelty>={g['novelty']}, max_silence={g['max_silence_s']}s, warmup={g['warmup_s']}s", flush=True)
67
+
68
+ # Ensure a per-prompt decision cache for EACH ensemble prompt, BUILDING it from the test frames if
69
+ # missing/incomplete (resumable — skips already-cached frames). This makes the whole pipeline a SINGLE
70
+ # command from the test set to the submission; the config lives in config.json + secrets in .env.
71
+ # Building calls the VLM (PAID, ~1× per prompt per frame); a complete cache is a no-op replay.
72
+ from humomni.tuning.cache import build_web_cache
73
+ from humomni.phase1.vlm_client import prompt_for
74
+ caches = {}
75
+ for p in prompts:
76
+ path = cache_tpl.format(p)
77
+ print(f"[cache] ensuring {path} (prompt {p}) from {data_dir} …", flush=True)
78
+ build_web_cache(data_dir=data_dir, cache_path=path, limit=limit, workers=build_workers, prompt=prompt_for(p))
79
+ caches[p] = VLMCache(path)
80
+
81
+ emb_cache.load_disk() # prewarm draft embeddings (cosine dedup prefilter)
82
+ judges.jc_load() # reuse any saved LLM-dedup results (resume / re-run = free)
83
+ warm = [v["draft"].strip() for c in caches.values() for v in c.mem.values() if v.get("draft", "").strip()]
84
+ if warm:
85
+ emb_cache.embed_many(warm)
86
+ dirs = sorted(x for x in glob.glob(os.path.join(data_dir, "*")) if os.path.isdir(x))
87
+ if limit:
88
+ dirs = dirs[:limit] # quick correctness check (first N samples, same sort order)
89
+
90
+ # Sample-level RESUME: completed records are appended to a `<out>.partial` sidecar as they finish,
91
+ # so a killed build resumes instead of restarting (the dedup judge cache is already persisted
92
+ # incrementally). The final `out` is written — and the sidecar dropped — only once all ids are done.
93
+ partial = out + ".partial"
94
+ done = {}
95
+ if os.path.exists(partial):
96
+ for line in open(partial, encoding="utf-8"):
97
+ try:
98
+ done[json.loads(line)["question_id"]] = line.rstrip("\n")
99
+ except Exception:
100
+ pass
101
+ if done:
102
+ print(f"[resume] {len(done)} samples already done in {partial}", flush=True)
103
+ pw = open(partial, "a", encoding="utf-8")
104
+ pwlock = threading.Lock()
105
+
106
+ def one(sd):
107
+ qid = json.load(open(os.path.join(sd, "question.json"), encoding="utf-8"))["question_id"]
108
+ if qid in done: # resumed — skip, keep its earlier result
109
+ return
110
+ # one shared guard per sample == APIPolicy.step's dedup (sim band + LLM entailment, recent_k).
111
+ guard = EntailmentGuard(sim_high=d.get("sim_high", 0.97), sim_low=d.get("sim_low", 0.55),
112
+ recent_k=recent_k, use_llm=d.get("use_llm", True))
113
+ said, res, last_fire = [], [], -1e9
114
+ for t, _ in frames_of(qid, data_dir): # ASCENDING time — causal (frames ≤ t only)
115
+ if novelty is not None: # gate replay == PerceptionGate.should_decide
116
+ nov = novelty[qid][f"{t:.1f}"]
117
+ fire = (t <= g["warmup_s"]) or (nov >= g["novelty"]) or (t - last_fire >= g["max_silence_s"])
118
+ if not fire:
119
+ continue
120
+ last_fire = t
121
+ for p in prompts: # merge order = prompt order (v5 first, then v3)
122
+ dec = caches[p].get(qid, t)
123
+ if dec and dec["decision"] == "NEW_ANSWER" and dec["confidence"] >= theta:
124
+ draft = dec["draft"].strip()
125
+ if draft and not guard.is_duplicate(draft, said):
126
+ said.append(draft)
127
+ res.append({"time": round(t, 3), "content": draft})
128
+ line = json.dumps({"question_id": qid, "model_response_list": res}, ensure_ascii=False)
129
+ with pwlock: # append + fsync the completed sample immediately
130
+ pw.write(line + "\n"); pw.flush(); os.fsync(pw.fileno())
131
+ done[qid] = line
132
+ _checkpoint()
133
+
134
+ try:
135
+ with ThreadPoolExecutor(max_workers=workers) as ex:
136
+ list(ex.map(one, dirs))
137
+ finally:
138
+ pw.close()
139
+ judges.jc_save() # persist dedup results even on a mid-build 429
140
+ emb_cache.save_disk()
141
+ order = [json.load(open(os.path.join(sd, "question.json"), encoding="utf-8"))["question_id"] for sd in dirs]
142
+ records = [json.loads(done[q]) for q in order if q in done] # final output in dir order
143
+ with open(out, "w", encoding="utf-8") as w:
144
+ for r in records:
145
+ w.write(json.dumps(r, ensure_ascii=False) + "\n")
146
+ if len(records) == len(dirs) and os.path.exists(partial):
147
+ os.remove(partial) # complete -> drop the sidecar
148
+ return records
149
+
150
+
151
+ if __name__ == "__main__":
152
+ ap = argparse.ArgumentParser(description="Build + validate the best Phase-1 submission from per-prompt caches.")
153
+ ap.add_argument("--data_dir", default="data/phase1/data")
154
+ ap.add_argument("--out", default="submissions/submission.jsonl")
155
+ ap.add_argument("--config", default="config.json")
156
+ ap.add_argument("--cache_tpl", default="cache/phase1_{}.jsonl", help="per-prompt cache path ({} = prompt name)")
157
+ ap.add_argument("--workers", type=int, default=1,
158
+ help="parallel samples. Keep at 1 on a COLD judge cache (the LLM dedup floods Vertex → 429); "
159
+ "raise to 3-4 once cached (re-run is then free).")
160
+ ap.add_argument("--limit", type=int, default=0, help="only the first N samples (quick correctness check)")
161
+ ap.add_argument("--build_workers", type=int, default=8, help="parallelism for BUILDING missing per-prompt VLM caches")
162
+ a = ap.parse_args()
163
+ cfg = json.load(open(a.config, encoding="utf-8"))
164
+ os.makedirs(os.path.dirname(a.out) or ".", exist_ok=True)
165
+
166
+ records = build(a.data_dir, a.out, cfg, a.cache_tpl, a.workers, a.limit, a.build_workers)
167
+ avg = sum(len(r["model_response_list"]) for r in records) / max(1, len(records))
168
+ ens = cfg["ensemble"]
169
+ print(f"wrote {a.out}: {len(records)} samples, avg {avg:.1f} resp/sample "
170
+ f"(ensemble {ens['prompts']} @ recent_k={ens.get('recent_k')})")
171
+
172
+ errs = validate(a.out, gold_ids_from_dir(a.data_dir)) # same checks as validate_submission.py
173
+ if errs:
174
+ print(f"INVALID — {len(errs)} problem(s):")
175
+ for e in errs[:20]:
176
+ print(" -", e)
177
+ sys.exit(1)
178
+ print(f"VALID — all ids present, times are multiples of 0.5. Rename {a.out} to your Team ID to upload.")
scripts/infer_simple.py ADDED
@@ -0,0 +1,98 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python
2
+ # infer_simple.py — the minimal causal inference loop, for reading and reference.
3
+ #
4
+ # Pick a policy (api or rl), stream each sample's frames one at a time in time order, and write
5
+ # one JSONL line per sample. A failing sample is skipped (empty record, run continues) so the
6
+ # output always has every id. The production runner (run_inference.py) adds the rest for a real
7
+ # paid 500-sample run — 3x retry, --resume, parallel workers, client warm-up — which this file
8
+ # deliberately omits.
9
+ import argparse
10
+ import glob
11
+ import json
12
+ import os
13
+
14
+ from humomni.core.streaming_driver import _emissions # SAME normalization as run_inference -> identical output
15
+
16
+
17
+ def make_policy(name):
18
+ """A fresh policy per sample (no state leak). Imported lazily — each pulls heavy deps."""
19
+ if name == "api":
20
+ from humomni.phase1.policy_api import APIPolicy
21
+ config = json.load(open("config.json", encoding="utf-8"))
22
+ return lambda: APIPolicy(config=config)
23
+ raise SystemExit(f"unknown policy: {name!r} (use 'api')")
24
+
25
+
26
+ def _video_length(sample_dir):
27
+ """The latest frame timestamp present — the 'video_length' the loop steps up to (frames sit on a
28
+ 0.5 s grid: 0.5.jpg, 1.0.jpg, …)."""
29
+ ts = []
30
+ for p in glob.glob(os.path.join(sample_dir, "*.jpg")):
31
+ try:
32
+ ts.append(float(os.path.basename(p)[:-4]))
33
+ except ValueError:
34
+ pass # skip non-frame files
35
+ return max(ts) if ts else 0.0
36
+
37
+
38
+ def infer_one(sample_dir, policy):
39
+ """One sample, matching the competition inference pseudocode:
40
+
41
+ input(question)
42
+ for current_time in range(0.5, video_length, 0.5):
43
+ input(f"{current_time}.png") # frames at t > current_time are PROHIBITED
44
+ if respond_now: output(... @ current_time)
45
+
46
+ The policy gets one frame per tick, in ascending time, and never sees t > current_time.
47
+ policy.step() may return None / a str / a list of strs — one frame can emit several answers
48
+ (the v5+v3 ensemble); we append each AT current_time, exactly like streaming_driver.drive().
49
+ A transient failure returns an empty-but-present record (the run never aborts and every id
50
+ stays); a causality AssertionError is not caught. No retry/resume/parallelism — that's
51
+ run_inference.py.
52
+ """
53
+ with open(os.path.join(sample_dir, "question.json"), encoding="utf-8") as f:
54
+ q = json.load(f)
55
+ question, qid = q["question"], q["question_id"]
56
+ try:
57
+ policy.reset(question=question)
58
+ responses, last_t = [], -1.0
59
+ video_length = _video_length(sample_dir)
60
+ for step in range(1, int(round(video_length / 0.5)) + 1): # current_time = 0.5 .. video_length
61
+ current_time = step * 0.5
62
+ frame_path = os.path.join(sample_dir, f"{current_time:.1f}.jpg")
63
+ if not os.path.exists(frame_path): # missing tick on the grid — skip
64
+ continue
65
+ assert current_time > last_t, "frames must arrive in order (no future frames)"
66
+ last_t = current_time
67
+ reply = policy.step(current_time, frame_path)
68
+ for content in _emissions(reply): # 0..N answers, all at current_time
69
+ responses.append({"time": current_time, "content": content})
70
+ return {"question_id": qid, "model_response_list": responses}
71
+ except AssertionError:
72
+ raise
73
+ except Exception as e:
74
+ print(f" ! {qid} failed ({type(e).__name__}: {str(e)[:80]}) — empty record")
75
+ return {"question_id": qid, "model_response_list": []}
76
+
77
+
78
+ def main():
79
+ ap = argparse.ArgumentParser()
80
+ ap.add_argument("--policy", choices=["api"], default="api")
81
+ ap.add_argument("--data_dir", default="data/phase1/data")
82
+ ap.add_argument("--out", default="submission.jsonl")
83
+ a = ap.parse_args()
84
+
85
+ new_policy = make_policy(a.policy)
86
+ dirs = [d for d in sorted(glob.glob(os.path.join(a.data_dir, "*"))) if os.path.isdir(d)]
87
+ total = len(dirs)
88
+ with open(a.out, "w", encoding="utf-8") as w:
89
+ for i, sample_dir in enumerate(dirs, 1):
90
+ rec = infer_one(sample_dir, new_policy()) # fresh policy per sample
91
+ w.write(json.dumps(rec, ensure_ascii=False) + "\n")
92
+ print(f"[{i}/{total}] {rec['question_id']} -> {len(rec['model_response_list'])} responses",
93
+ flush=True) # flush so progress shows live
94
+ print(f"done {total} samples -> {a.out}")
95
+
96
+
97
+ if __name__ == "__main__":
98
+ main()
scripts/run_inference.py ADDED
@@ -0,0 +1,136 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # run_inference.py — drive every sample through a policy -> one JSONL line per sample.
2
+ # Works for any policy (SilentPolicy, APIPolicy, RLPolicy later). EVERY question_id must
3
+ # appear, even with an empty model_response_list. Robust for paid runs:
4
+ # - per-sample retry (transient Windows Errno 22 / API blips) with empty-record fallback,
5
+ # so ONE bad sample never aborts the whole run;
6
+ # - incremental flushed append, so a crash never loses (or re-pays for) finished samples;
7
+ # - --resume skips question_ids already present in the output.
8
+ import glob
9
+ import json
10
+ import os
11
+ import time
12
+ import argparse
13
+ import threading
14
+ from concurrent.futures import ThreadPoolExecutor
15
+
16
+ from humomni.core.streaming_driver import run_sample
17
+
18
+
19
+ def make_policy_factory(name, config):
20
+ """Return a zero-arg factory that builds a FRESH policy per sample (no state leak)."""
21
+ if name == "silent":
22
+ from humomni.phase1.policies import SilentPolicy
23
+ return lambda: SilentPolicy()
24
+ if name == "api":
25
+ from humomni.phase1.policy_api import APIPolicy # imported lazily (needs deps + API key)
26
+ return lambda: APIPolicy(config=config)
27
+ raise ValueError(f"unknown policy {name!r}")
28
+
29
+
30
+ def _qid(d):
31
+ with open(os.path.join(d, "question.json"), encoding="utf-8") as f:
32
+ return json.load(f)["question_id"]
33
+
34
+
35
+ def _done_qids(out):
36
+ done = set()
37
+ if os.path.exists(out):
38
+ with open(out, encoding="utf-8") as f:
39
+ for ln in f:
40
+ ln = ln.strip()
41
+ if ln:
42
+ try:
43
+ done.add(json.loads(ln)["question_id"])
44
+ except Exception:
45
+ pass
46
+ return done
47
+
48
+
49
+ def main(data_dir, out, policy_factory, limit=0, workers=1, resume=False):
50
+ dirs = sorted(d for d in glob.glob(os.path.join(data_dir, "*")) if os.path.isdir(d))
51
+ if limit:
52
+ dirs = dirs[:limit] # used by experiment.py for fast partial Phase-2 evals
53
+ done = _done_qids(out) if resume else set()
54
+ if not resume:
55
+ open(out, "w", encoding="utf-8").close() # truncate (fresh run)
56
+ todo = [d for d in dirs if _qid(d) not in done]
57
+ print(f"{len(dirs)} samples | {len(done)} already done | {len(todo)} to run", flush=True)
58
+
59
+ lock = threading.Lock()
60
+ fails = []
61
+ wfile = open(out, "a", encoding="utf-8")
62
+
63
+ def process(d):
64
+ for attempt in range(3):
65
+ try:
66
+ return run_sample(d, policy_factory()) # fresh policy state per sample
67
+ except Exception as e:
68
+ if attempt == 2:
69
+ fails.append((_qid(d), str(e)[:120]))
70
+ return {"question_id": _qid(d), "model_response_list": []} # keep id present
71
+ time.sleep(0.5)
72
+
73
+ def handle(d):
74
+ rec = process(d)
75
+ with lock:
76
+ wfile.write(json.dumps(rec, ensure_ascii=False) + "\n")
77
+ wfile.flush()
78
+ return rec
79
+
80
+ try:
81
+ if workers > 1:
82
+ with ThreadPoolExecutor(max_workers=workers) as ex:
83
+ for n, _ in enumerate(ex.map(handle, todo), 1):
84
+ if n % 50 == 0:
85
+ print(f" ...{n}/{len(todo)}", flush=True)
86
+ else:
87
+ for n, d in enumerate(todo, 1):
88
+ handle(d)
89
+ if n % 50 == 0:
90
+ print(f" ...{n}/{len(todo)}", flush=True)
91
+ finally:
92
+ wfile.close()
93
+ print(f"wrote -> {out} (failed samples: {len(fails)})")
94
+ for q, e in fails[:10]:
95
+ print(" FAIL", q, e)
96
+
97
+
98
+ if __name__ == "__main__":
99
+ ap = argparse.ArgumentParser()
100
+ ap.add_argument("--data_dir", default="data/phase1/data")
101
+ ap.add_argument("--out", default="submission.jsonl")
102
+ ap.add_argument("--policy", default="api", choices=["silent", "api"])
103
+ ap.add_argument("--config", default="config.json", help="orchestrator config (gate/theta/dedup)")
104
+ ap.add_argument("--limit", type=int, default=0, help="process only the first N samples (0=all; experiment.py uses it for fast Phase-2 evals)")
105
+ ap.add_argument("--workers", type=int, default=1, help="parallel samples (independent, still causal)")
106
+ ap.add_argument("--resume", action="store_true", help="skip question_ids already in --out")
107
+ a = ap.parse_args()
108
+ cfg = None
109
+ if a.policy == "api" and os.path.exists(a.config):
110
+ cfg = json.load(open(a.config, encoding="utf-8"))
111
+
112
+ if a.policy == "api" and a.workers > 1:
113
+ # Warm BOTH API clients + their lazy pydantic imports in the MAIN thread first;
114
+ # otherwise concurrent first-use across worker threads races and throws
115
+ # "No module named 'pydantic._migration'".
116
+ import humomni.phase1.vlm_client as vlm_client
117
+ try:
118
+ vlm_client._client_().chat.completions.create(
119
+ model=vlm_client.MODEL, max_tokens=1,
120
+ messages=[{"role": "user", "content": "ping"}])
121
+ except Exception:
122
+ pass
123
+ if (cfg or {}).get("dedup", {}).get("use_llm"):
124
+ import humomni.core.judges as judges
125
+ try:
126
+ judges.gemini("ping")
127
+ except Exception:
128
+ pass
129
+ try:
130
+ import humomni.core.emb_cache as emb_cache # warm MiniLM (cosine dedup prefilter) single-threaded
131
+ emb_cache.embed("ping")
132
+ except Exception:
133
+ pass
134
+
135
+ main(a.data_dir, a.out, make_policy_factory(a.policy, cfg),
136
+ limit=a.limit, workers=a.workers, resume=a.resume)
src/humomni/__init__.py ADDED
File without changes
src/humomni/core/__init__.py ADDED
File without changes
src/humomni/core/context.py ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # context.py — build the VLM input under a token/cost budget (§4).
2
+ # Recent frames at full detail, older history as sparse downscaled keyframes, plus an
3
+ # explicit memory of what we already said (structural anti-repeat). Causal by construction:
4
+ # `history` only ever contains frames with timestamp <= now.
5
+ # Frame budget is env-tunable (CTX_RECENT_S / CTX_HIST_STRIDE_S) so the perception lever can be
6
+ # swept without code edits; defaults reproduce the shipped behavior.
7
+ import os
8
+
9
+ RECENT_S = float(os.environ.get("CTX_RECENT_S", "6.0")) # last N s at full res (~2N frames @ 2 fps)
10
+ HIST_STRIDE_S = float(os.environ.get("CTX_HIST_STRIDE_S", "2.0")) # older history: 1 keyframe / N s
11
+ STEP = 0.5
12
+
13
+
14
+ def build_context(question, history, said):
15
+ """history: ascending list of (t, frame_path); said: list of emitted strings."""
16
+ now = history[-1][0]
17
+ recent = [(t, p) for (t, p) in history if now - t <= RECENT_S]
18
+ older = [(t, p) for (t, p) in history if now - t > RECENT_S]
19
+ keyframes = older[::max(1, int(HIST_STRIDE_S / STEP))] # subsample older frames
20
+ return {
21
+ "question": question,
22
+ "now": now,
23
+ "recent_frames": recent, # sent at native 480x270
24
+ "history_frames": keyframes, # downscaled in vlm_client
25
+ "already_said": said, # anti-repeat memory
26
+ }
src/humomni/core/emb_cache.py ADDED
@@ -0,0 +1,71 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # emb_cache.py — one shared MiniLM instance + a text->vector cache (in-memory + disk).
2
+ # The fast metric (emb_judge) and the dedup guard both embed the same gold refs / drafts
3
+ # many times; caching by text turns repeated encodes into dict lookups, which is what makes
4
+ # offline replay and tuning run in seconds instead of minutes.
5
+ import os
6
+ import json
7
+ import threading
8
+
9
+ import numpy as np
10
+
11
+ from humomni.core.env_util import load_env
12
+
13
+ load_env() # set HF_TOKEN / HF_HUB_OFFLINE from .env BEFORE sentence_transformers/huggingface_hub load
14
+
15
+ # Persist as a plain .npy matrix + a .json key list (NO pickle / allow_pickle) so loading
16
+ # never executes arbitrary code, even though this cache is only ever produced locally.
17
+ PATH = "cache/emb_cache" # -> emb_cache.npy + emb_cache.keys.json
18
+ _model = None
19
+ _cache = {}
20
+ _lock = threading.Lock()
21
+
22
+
23
+ def _model_():
24
+ global _model
25
+ if _model is None:
26
+ from sentence_transformers import SentenceTransformer
27
+ _model = SentenceTransformer("all-MiniLM-L6-v2")
28
+ return _model
29
+
30
+
31
+ def load_disk(path=PATH):
32
+ npy, kj = path + ".npy", path + ".keys.json"
33
+ if os.path.exists(npy) and os.path.exists(kj):
34
+ vecs = np.load(npy) # plain float matrix, no pickle
35
+ keys = json.load(open(kj, encoding="utf-8"))
36
+ for k, v in zip(keys, vecs):
37
+ _cache.setdefault(k, v)
38
+
39
+
40
+ def save_disk(path=PATH):
41
+ if not _cache:
42
+ return
43
+ os.makedirs(os.path.dirname(path) or ".", exist_ok=True)
44
+ keys = list(_cache.keys())
45
+ vecs = np.stack([_cache[k] for k in keys]).astype(np.float32)
46
+ np.save(path + ".npy", vecs)
47
+ json.dump(keys, open(path + ".keys.json", "w", encoding="utf-8"))
48
+
49
+
50
+ def embed_many(texts):
51
+ """Return unit-norm vectors for texts (order preserved), encoding only cache misses."""
52
+ miss = [t for t in texts if t not in _cache]
53
+ if miss:
54
+ uniq = list(dict.fromkeys(miss))
55
+ with _lock:
56
+ still = [t for t in uniq if t not in _cache]
57
+ if still:
58
+ vecs = _model_().encode(still, normalize_embeddings=True,
59
+ batch_size=256, show_progress_bar=False)
60
+ for t, v in zip(still, np.asarray(vecs, dtype=np.float32)):
61
+ _cache[t] = v
62
+ return [_cache[t] for t in texts]
63
+
64
+
65
+ def embed(text):
66
+ return embed_many([text])[0]
67
+
68
+
69
+ def cos(a, b):
70
+ va, vb = embed_many([a, b])
71
+ return float(np.dot(va, vb))
src/humomni/core/env_util.py ADDED
@@ -0,0 +1,46 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # env_util.py — minimal, dependency-free .env loader.
2
+ # Secrets (OPENROUTER_API_KEY) and optional model-id overrides live in .env (gitignored).
3
+ # Real environment variables always win over .env so CI / shell overrides keep working.
4
+ import os
5
+ import site
6
+ import sys
7
+
8
+
9
+ def load_env(path=".env"):
10
+ """Populate os.environ from a simple KEY=VALUE .env file (no-op if absent).
11
+
12
+ Existing environment variables are never overwritten. Lines that are blank,
13
+ comments (#...), or lack '=' are skipped. Surrounding quotes are stripped.
14
+ """
15
+ if not os.path.exists(path):
16
+ return
17
+ with open(path, encoding="utf-8") as f:
18
+ for line in f:
19
+ line = line.strip()
20
+ if not line or line.startswith("#") or "=" not in line:
21
+ continue
22
+ key, _, val = line.partition("=")
23
+ key = key.strip()
24
+ val = val.strip().strip('"').strip("'")
25
+ if key and key not in os.environ:
26
+ os.environ[key] = val
27
+
28
+
29
+ def strip_user_site_paths():
30
+ """Remove user-local Python package paths from sys.path.
31
+
32
+ Some shared HPC environments inject ~/.local packages into otherwise isolated
33
+ conda envs. Those user-site packages can pull in binary wheels compiled against
34
+ a different NumPy and break imports deep inside transformers/peft.
35
+ """
36
+ user_site = os.path.realpath(site.getusersitepackages())
37
+ user_base = os.path.realpath(os.path.expanduser("~/.local"))
38
+ keep = []
39
+ for path in sys.path:
40
+ real = os.path.realpath(path or os.getcwd())
41
+ if real == user_site or real.startswith(user_site + os.sep):
42
+ continue
43
+ if real == user_base or real.startswith(user_base + os.sep):
44
+ continue
45
+ keep.append(path)
46
+ sys.path[:] = keep
src/humomni/core/judges.py ADDED
@@ -0,0 +1,266 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # judges.py — scoring/dedup judges for the PAUC harness (§9.2).
2
+ # llm_judge : faithful Gemini-family grader (matches the hidden leaderboard judge family)
3
+ # emb_judge : fast, API-free embedding approximation (never report as final)
4
+ # llm_entails : duplicate = "does NEW add info beyond PRIOR?" (for the dedup metric)
5
+ # The judge is Gemini 3.1 Flash-Lite via VERTEX AI in EXPRESS MODE — a plain REST generateContent call
6
+ # with an API key (no SDK / service account needed; Gemini is not served on OpenRouter). Set VERTEX_KEY
7
+ # (or VERTEX_API_KEY / GOOGLE_API_KEY) + JUDGE_MODEL in .env. Vertex can be slow, so a generous per-call
8
+ # timeout + retry/backoff are built into gemini() below. (The VLM backbone uses OpenRouter; only the
9
+ # judge goes through Vertex.)
10
+ import os
11
+ import re
12
+ import json
13
+ import time
14
+ import hashlib
15
+ import threading
16
+ import urllib.request
17
+ import urllib.error
18
+
19
+ from humomni.core.env_util import load_env
20
+
21
+ load_env()
22
+ # Vertex Gemini model id. Normalize an OpenRouter-style value (`google/gemini-3.1-flash-lite-20260507`):
23
+ # drop the `google/` prefix and a trailing `-YYYYMMDD` date — Vertex publisher ids have neither.
24
+ JUDGE_MODEL = re.sub(r"-\d{8}$", "", os.environ.get("JUDGE_MODEL", "gemini-3.1-flash-lite").split("/", 1)[-1])
25
+ if not re.fullmatch(r"[A-Za-z0-9.\-]+", JUDGE_MODEL): # catch a malformed .env value early (clear error vs a later 404)
26
+ raise ValueError(f"JUDGE_MODEL={JUDGE_MODEL!r} is not a valid Vertex model id (expected e.g. 'gemini-3.1-flash-lite')")
27
+ VERTEX_TIMEOUT_S = float(os.environ.get("VERTEX_TIMEOUT_S", "120")) # per-call request timeout (Vertex is slow)
28
+ # Express-mode REST host (global). Override for a regional endpoint, e.g. https://us-central1-aiplatform.googleapis.com
29
+ VERTEX_BASE_URL = os.environ.get("VERTEX_BASE_URL", "https://aiplatform.googleapis.com/v1")
30
+
31
+ # Disk-backed cache of Gemini judge/entail results so faithful evals are reusable + warmable
32
+ # in parallel. Keyed by a hash of the exact inputs; values are small ints/bools.
33
+ JUDGE_CACHE_PATH = "cache/judge_cache.json"
34
+ _jcache = {}
35
+ _jc_lock = threading.Lock()
36
+ _jc_loaded = False
37
+
38
+
39
+ def jc_load():
40
+ # Under _jc_lock: many warm threads call this concurrently, and jc_save() rewrites the file
41
+ # under the same lock — without it a thread could json.load a half-written file or two threads
42
+ # could double-load. If the file is unreadable we refuse to proceed rather than silently starting
43
+ # from an empty cache (a later jc_save would then overwrite the on-disk cache with the empty one —
44
+ # exactly how a 480k-entry cache once got clobbered to a few thousand).
45
+ global _jc_loaded
46
+ with _jc_lock:
47
+ if not _jc_loaded:
48
+ if os.path.exists(JUDGE_CACHE_PATH):
49
+ _jcache.update(json.load(open(JUDGE_CACHE_PATH, encoding="utf-8")))
50
+ _jc_loaded = True
51
+
52
+
53
+ def jc_save():
54
+ # ATOMIC: write to a temp file in the same dir, then os.replace() (atomic on Windows + POSIX).
55
+ # A process killed mid-save leaves the previous cache fully intact — never a truncated file.
56
+ os.makedirs(os.path.dirname(JUDGE_CACHE_PATH) or ".", exist_ok=True)
57
+ with _jc_lock:
58
+ tmp = JUDGE_CACHE_PATH + f".tmp{os.getpid()}"
59
+ with open(tmp, "w", encoding="utf-8") as f:
60
+ json.dump(_jcache, f)
61
+ os.replace(tmp, JUDGE_CACHE_PATH)
62
+
63
+
64
+ def jc_key(*parts):
65
+ return hashlib.sha1("\x1f".join(parts).encode()).hexdigest()
66
+
67
+
68
+ def _vertex_key():
69
+ """The Vertex express-mode API key from .env (accepts a few common names)."""
70
+ return (os.environ.get("VERTEX_KEY") or os.environ.get("VERTEX_API_KEY")
71
+ or os.environ.get("GOOGLE_API_KEY"))
72
+
73
+
74
+ # Throttle the AGGREGATE judge call rate (regardless of worker count) to stay under the Vertex
75
+ # per-minute quota, and back off on timeouts/429/5xx so parallel warming can't blow it.
76
+ _RATE_LOCK = threading.Lock()
77
+ _LAST_CALL = [0.0]
78
+ _COOLDOWN_UNTIL = [0.0]
79
+ _MIN_INTERVAL = 60.0 / float(os.environ.get("JUDGE_RPM", "280")) # tune to your Vertex quota (default <300 rpm)
80
+
81
+
82
+ def _retry_after_s(header_value):
83
+ """Best-effort parse of Retry-After seconds from a Vertex 429/5xx response."""
84
+ if not header_value:
85
+ return None
86
+ try:
87
+ secs = int(float(header_value.strip()))
88
+ except Exception:
89
+ return None
90
+ return max(0, secs)
91
+
92
+
93
+ def _throttle():
94
+ with _RATE_LOCK:
95
+ now = time.monotonic()
96
+ wait = max(_LAST_CALL[0] + _MIN_INTERVAL, _COOLDOWN_UNTIL[0]) - now
97
+ if wait > 0:
98
+ time.sleep(wait)
99
+ _LAST_CALL[0] = time.monotonic()
100
+
101
+
102
+ def _cooldown(seconds):
103
+ """Pause all subsequent judge calls for a shared cooldown window after quota/network spikes."""
104
+ if seconds <= 0:
105
+ return
106
+ with _RATE_LOCK:
107
+ _COOLDOWN_UNTIL[0] = max(_COOLDOWN_UNTIL[0], time.monotonic() + seconds)
108
+
109
+
110
+ # --- HARD spend ceiling. Cap the number of LIVE Gemini calls across the WHOLE job (all stages and
111
+ # subprocesses share a counter file), so an unattended run cannot accumulate unbounded spend.
112
+ # 0 = unlimited; set via the JUDGE_MAX_CALLS environment variable.
113
+ _JUDGE_MAX = int(os.environ.get("JUDGE_MAX_CALLS", "0"))
114
+ _CALLS_FILE = os.environ.get("JUDGE_CALLS_FILE", "cache/.judge_calls")
115
+ _SINCE_SAVE = [0]
116
+
117
+
118
+ class JudgeBudgetExceeded(RuntimeError):
119
+ """Raised when the JUDGE_MAX_CALLS live-call ceiling is hit — stop, don't keep spending."""
120
+
121
+
122
+ def _bump_calls():
123
+ if not _JUDGE_MAX:
124
+ return
125
+ with _RATE_LOCK: # serialize the read-modify-write (approx across processes is fine)
126
+ try:
127
+ n = int((open(_CALLS_FILE, encoding="utf-8").read().strip() or "0"))
128
+ except Exception:
129
+ n = 0
130
+ if n >= _JUDGE_MAX:
131
+ raise JudgeBudgetExceeded(
132
+ f"JUDGE_MAX_CALLS={_JUDGE_MAX} reached — stopping to cap Vertex AI / Gemini spend. "
133
+ f"Best ckpt + metrics are saved; raise JUDGE_MAX_CALLS to continue.")
134
+ try:
135
+ os.makedirs(os.path.dirname(_CALLS_FILE) or ".", exist_ok=True)
136
+ open(_CALLS_FILE, "w", encoding="utf-8").write(str(n + 1))
137
+ except Exception:
138
+ pass
139
+
140
+
141
+ def _extract_text(data):
142
+ """Pull the answer text out of a Vertex generateContent response (or '' if blocked/empty)."""
143
+ try:
144
+ parts = data["candidates"][0]["content"]["parts"]
145
+ return "".join(p.get("text", "") for p in parts).strip()
146
+ except Exception:
147
+ return "" # blocked/empty candidate -> empty grade (llm_judge clamps to 0)
148
+
149
+
150
+ def gemini(prompt, temperature=0, retries=None):
151
+ """Grade one prompt with Gemini via Vertex AI express-mode REST. Generous timeout + retry on
152
+ transient timeouts/429/5xx; fail fast on 4xx config/auth errors. stdlib only (no SDK)."""
153
+ retries = int(os.environ.get("VERTEX_RETRIES", str(retries or 12)))
154
+ max_wait_s = float(os.environ.get("VERTEX_MAX_WAIT_S", "1800"))
155
+ quota_backoff_cap_s = float(os.environ.get("VERTEX_429_BACKOFF_MAX_S", "600"))
156
+ transient_backoff_cap_s = float(os.environ.get("VERTEX_TRANSIENT_BACKOFF_MAX_S", "120"))
157
+ _bump_calls() # raises JudgeBudgetExceeded at the ceiling (before spending)
158
+ key = _vertex_key()
159
+ if not key:
160
+ raise RuntimeError("no Vertex API key — set VERTEX_KEY (or VERTEX_API_KEY) in .env for the Gemini judge")
161
+ url = f"{VERTEX_BASE_URL}/publishers/google/models/{JUDGE_MODEL}:generateContent"
162
+ body = json.dumps({
163
+ "contents": [{"role": "user", "parts": [{"text": prompt}]}],
164
+ "generationConfig": {"temperature": temperature, "maxOutputTokens": 8},
165
+ }).encode("utf-8")
166
+ last = None
167
+ started = time.monotonic()
168
+ for attempt in range(retries):
169
+ _throttle()
170
+ try:
171
+ req = urllib.request.Request(url, data=body, method="POST", headers={
172
+ "Content-Type": "application/json", "x-goog-api-key": key})
173
+ with urllib.request.urlopen(req, timeout=VERTEX_TIMEOUT_S) as resp:
174
+ data = json.loads(resp.read().decode("utf-8"))
175
+ _SINCE_SAVE[0] += 1
176
+ if _SINCE_SAVE[0] % 100 == 0: # persist the judge cache so a crash/cap-stop resumes cheap
177
+ jc_save()
178
+ return _extract_text(data)
179
+ except JudgeBudgetExceeded:
180
+ raise
181
+ except urllib.error.HTTPError as e: # has a status code
182
+ detail = ""
183
+ try:
184
+ detail = e.read().decode("utf-8")[:300]
185
+ except Exception:
186
+ pass
187
+ last = RuntimeError(f"Vertex HTTP {e.code}: {detail}")
188
+ if e.code in (400, 401, 403, 404): # bad key / model / project / request — NOT transient
189
+ raise last from e
190
+ retry_after = _retry_after_s(getattr(e, "headers", {}).get("Retry-After"))
191
+ if e.code == 429:
192
+ wait_s = max(retry_after or 0, min(2 ** attempt, quota_backoff_cap_s), 15)
193
+ _cooldown(wait_s)
194
+ else:
195
+ wait_s = max(retry_after or 0, min(2 ** attempt, transient_backoff_cap_s))
196
+ if attempt == retries - 1 or time.monotonic() + wait_s - started > max_wait_s:
197
+ raise last from e
198
+ time.sleep(wait_s)
199
+ except Exception as e: # timeout / connection reset -> transient, retry
200
+ last = e
201
+ wait_s = min(2 ** attempt, transient_backoff_cap_s)
202
+ _cooldown(wait_s)
203
+ if attempt == retries - 1 or time.monotonic() + wait_s - started > max_wait_s:
204
+ raise
205
+ time.sleep(wait_s) # generous backoff for Vertex transient transport failures
206
+ raise last if last is not None else RuntimeError("gemini: no attempts made (retries < 1)")
207
+
208
+
209
+ # Rubric mirrors the official ProactiveVideoQA judge (scale 3/2/1) so the Gemini grade
210
+ # tracks the leaderboard; we remap 3/2/1 -> 2/1/0 to match the PAUC y-axis (S=2).
211
+ JUDGE_INSTRUCTION = (
212
+ "You are an evaluator for a video question answering system. Rate whether the "
213
+ "predicted answer covers the key points of the ground truth answer. Use this scale:\n"
214
+ "- 3: Mostly covered; the predicted answer covers all key information in the ground "
215
+ "truth answer, though it may have minor inaccuracies or rephrases.\n"
216
+ "- 2: Partially covered; the predicted answer has some correct information, but also "
217
+ "contains significant inaccuracies or missing key points.\n"
218
+ "- 1: Incorrect; the predicted answer may be related but most information is missing, "
219
+ "or it is irrelevant / very poor quality.\n"
220
+ "Output the score only, do not add more explanations."
221
+ )
222
+
223
+
224
+ def judge_key(ref, ans, question=None):
225
+ """Cache key for a judge call. question=None preserves the old (question-blind) key so legacy
226
+ cache entries still hit; with a question it is a distinct key (faithful, official format)."""
227
+ return jc_key("judge", ref if question is None else f"{question}\x00{ref}", ans)
228
+
229
+
230
+ def llm_judge(ans, ref, question=None): # faithful: Gemini-family API, temp=0
231
+ jc_load()
232
+ k = judge_key(ref, ans, question)
233
+ if k in _jcache:
234
+ return _jcache[k]
235
+ if question is None:
236
+ prompt = (f"{JUDGE_INSTRUCTION}\n\nQuestion-moment ground truth: {ref}\n"
237
+ f"Predicted answer: {ans}")
238
+ else: # official format: Question + GT + Predicted
239
+ prompt = (f"{JUDGE_INSTRUCTION}\n\nQuestion: {question}\n"
240
+ f"Ground Truth Answer: {ref}\nPredicted Answer: {ans}")
241
+ out = gemini(prompt, temperature=0).strip()
242
+ # official maps the leading digit of {1,2,3} -> {0,1,2}; clamp anything else to 0.
243
+ v = {"1": 0, "2": 1, "3": 2}.get(out[:1], 0)
244
+ with _jc_lock:
245
+ _jcache[k] = v
246
+ return v
247
+
248
+
249
+ def emb_judge(ans, ref, question=None): # fast fallback (approximate); ignores question
250
+ from humomni.core.emb_cache import cos
251
+ c = cos(ans, ref)
252
+ return 2 if c >= 0.62 else 1 if c >= 0.42 else 0
253
+
254
+
255
+ def llm_entails(prior, hyp): # duplicate = "adds nothing new?"
256
+ jc_load()
257
+ k = jc_key("entail", " | ".join(prior), hyp)
258
+ if k in _jcache:
259
+ return _jcache[k]
260
+ p = ("Does the NEW statement add information beyond the PRIOR statements about the same "
261
+ "moment? Answer yes (adds new) or no (fully entailed/duplicate).\nPRIOR: "
262
+ + " | ".join(prior) + "\nNEW: " + hyp)
263
+ v = gemini(p, temperature=0).strip().lower().startswith("no")
264
+ with _jc_lock:
265
+ _jcache[k] = v
266
+ return v
src/humomni/core/pauc_eval.py ADDED
@@ -0,0 +1,158 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # pauc_eval.py — fast offline harness for the ranking score 0.9*PAUC + 0.1*(1-duplicate).
2
+ #
3
+ # The PAUC math here is a faithful port of the OFFICIAL ProactiveVideoQA scorer
4
+ # (external/ProactiveVideoQA/pauc/judge_llm_scores_openai.py): area_under_line_ratio +
5
+ # stat_metric, plus its exact-string accumulation dedup and second-turn "bleed" removal.
6
+ # Verified equal to the official area function in scorer_crosscheck.py (M2).
7
+ #
8
+ # NOTE: the official repo computes PAUC only; the duplicate term (0.1 weight) is the
9
+ # HumOmni Track-2 addition and lives in duplicate_in_span() below.
10
+ import json
11
+ import os
12
+ import argparse
13
+ from typing import Callable
14
+
15
+ S = 2 # max judge score on the y-axis (official: max_scores-1 = 2)
16
+ START_SCORE = 0.5 # silence/pre-first-reply height (official start_score)
17
+ OMEGA = 0.5 # timeliness knob (paper default, ω=0.5)
18
+ SPAN = "reply_timespan" # the scored window (verified field; ~5.51 s on WEB)
19
+ RELATED = "related_timespan"
20
+
21
+
22
+ def _turn_auc(points_rel, span_len: float, omega: float = OMEGA) -> float:
23
+ """Area-under-step-curve ratio for ONE turn. Mirrors the official area_under_line_ratio.
24
+
25
+ points_rel: list of (time_relative_to_span_start, score) — UNWARPED times, score in [0, S].
26
+ Warp shifts only the reply times toward the start; the curve then holds the last score
27
+ out to the UNWARPED span_len, and the denominator is span_len * S.
28
+ """
29
+ if not points_rel:
30
+ return START_SCORE / S # 0.25 — the silence floor
31
+ pts = sorted(points_rel, key=lambda p: p[0])
32
+ pts = [(x * (1 - omega), y) for x, y in pts] # warp reply times only
33
+ pts.append((span_len, pts[-1][1])) # extend last score to the TRUE end
34
+ prev_x, prev_y, area = 0.0, START_SCORE, 0.0
35
+ for x, y in pts:
36
+ area += (x - prev_x) * prev_y
37
+ prev_x, prev_y = x, y
38
+ return area / (span_len * S)
39
+
40
+
41
+ def _question_of(it) -> str:
42
+ """The question text for a gold item (official judge is given this; pass it to the judge)."""
43
+ conv = it.get("conversation")
44
+ return (conv[0].get("content") if conv else it.get("question")) or None
45
+
46
+
47
+ def turn_judge_scores(turn, preds, judge: Callable, question=None) -> dict:
48
+ """Build {pred_time: score} for one turn, mirroring the official construction:
49
+ accumulated judging, exact-string dedup of the accumulation, restricted to the reply
50
+ span, then drop post-related-span replies that score below the last in-related score.
51
+ `question` (when given) is forwarded to the judge to match the official prompt format.
52
+ """
53
+ a, b = turn[SPAN]
54
+ # FAITHFUL ORDER: accumulate in RESPONSE-LIST order, NOT sorted by time — matches the official
55
+ # create_openai_batch_input (it filters pred_data to the span without re-sorting). Identical to a
56
+ # time-sort for a time-ordered submission (ours always are), but exact for any input + overlapping spans.
57
+ ins = [p for p in preds if a <= p["time"] <= b]
58
+ acc, scores = [], {}
59
+ for p in ins:
60
+ c = p["content"]
61
+ if c in acc: # official: skip exact-duplicate accumulated text
62
+ continue
63
+ acc.append(c)
64
+ scores[p["time"]] = judge(" ".join(acc), turn["content"], question)
65
+ # second-turn "bleed" removal: a low-scoring reply after the related span is likely
66
+ # from the next turn — drop it (official process_openai_batch_output, lines ~111-121).
67
+ if len(scores) > 1 and RELATED in turn:
68
+ related_end = turn[RELATED][1]
69
+ last_related = 0
70
+ for t, sc in sorted(scores.items()):
71
+ if t <= related_end:
72
+ last_related = sc
73
+ elif sc < last_related:
74
+ del scores[t]
75
+ return scores
76
+
77
+
78
+ def pauc_turn(turn, preds, judge: Callable, omega: float = OMEGA, question=None):
79
+ """PAUC for one gold turn given a prediction list. Returns None for a degenerate span."""
80
+ a, b = turn[SPAN]
81
+ span_len = b - a
82
+ if span_len <= 0:
83
+ return None
84
+ scores = turn_judge_scores(turn, preds, judge, question)
85
+ points = [(t - a, sc) for t, sc in scores.items()]
86
+ return _turn_auc(points, span_len, omega)
87
+
88
+
89
+ def duplicate_in_span(turn, preds, entail: Callable[[list, str], bool]):
90
+ """HumOmni duplicate term: (#in-span reply pairs, #entailed-by-earlier). Not in the
91
+ official PAUC repo. entail(prior_contents, new_content) -> True if new is a duplicate."""
92
+ a, b = turn[SPAN]
93
+ # response-list order ("starting from the second response … entailed by all prior") — see turn_judge_scores.
94
+ ins = [p for p in preds if a <= p["time"] <= b]
95
+ if len(ins) < 2:
96
+ return 0, 0
97
+ e = sum(entail([ins[j]["content"] for j in range(i)], ins[i]["content"])
98
+ for i in range(1, len(ins)))
99
+ return len(ins) - 1, e
100
+
101
+
102
+ def score(gold, pred_by_id, judge, entail, omega: float = OMEGA) -> dict:
103
+ paucs, dnum, dden = [], 0, 0
104
+ for it in gold:
105
+ preds = pred_by_id.get(it["question_id"], [])
106
+ q = _question_of(it)
107
+ for turn in it["answer"]:
108
+ v = pauc_turn(turn, preds, judge, omega, question=q)
109
+ if v is not None:
110
+ paucs.append(v)
111
+ n, e = duplicate_in_span(turn, preds, entail)
112
+ dden += n
113
+ dnum += e
114
+ pauc = sum(paucs) / len(paucs) if paucs else 0.0
115
+ dup = dnum / dden if dden else 0.0
116
+ return {"PAUC": pauc, "duplicate": dup, "score": 0.9 * pauc + 0.1 * (1 - dup)}
117
+
118
+
119
+ def _load_pred(path) -> dict:
120
+ """jsonl -> {question_id: model_response_list}. None/missing => all-silent."""
121
+ if not path or not os.path.exists(path):
122
+ return {}
123
+ out = {}
124
+ with open(path, encoding="utf-8") as f:
125
+ for ln in f:
126
+ ln = ln.strip()
127
+ if ln:
128
+ r = json.loads(ln)
129
+ out[r["question_id"]] = r.get("model_response_list", [])
130
+ return out
131
+
132
+
133
+ def make_judges(kind: str):
134
+ """Return (judge_fn, entail_fn) for kind in {'emb','llm'}.
135
+ ('local' = an on-GPU judge from a separate training package that is not included in this repository.)"""
136
+ if kind == "emb":
137
+ from humomni.core.judges import emb_judge
138
+ return emb_judge, (lambda P, h: emb_judge(h, " ".join(P)) >= 2)
139
+ if kind == "local":
140
+ raise ValueError("judge kind 'local' requires a separate training package not included in this repository; use 'llm' or 'emb'")
141
+ from humomni.core.judges import llm_judge, llm_entails
142
+ return llm_judge, llm_entails
143
+
144
+
145
+ if __name__ == "__main__":
146
+ ap = argparse.ArgumentParser()
147
+ ap.add_argument("--gold", default="data/ProactiveVideoQA/WEB/anno.json")
148
+ ap.add_argument("--pred", default=None, help="submission jsonl; omit for all-silent baseline")
149
+ ap.add_argument("--judge", default="emb", choices=["emb", "llm", "local"])
150
+ ap.add_argument("--limit", type=int, default=0, help="score only the first N gold items (0=all)")
151
+ a = ap.parse_args()
152
+ gold = json.load(open(a.gold, encoding="utf-8"))
153
+ if a.limit:
154
+ gold = gold[:a.limit]
155
+ judge_fn, entail_fn = make_judges(a.judge)
156
+ r = score(gold, _load_pred(a.pred), judge=judge_fn, entail=entail_fn)
157
+ print(json.dumps({**r, "judge": a.judge, "n_items": len(gold),
158
+ "pred": a.pred or "(all-silent)"}, indent=2))
src/humomni/core/streaming_driver.py ADDED
@@ -0,0 +1,70 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # streaming_driver.py — the shared causal streaming loop (Phase 1 API + Phase 2 RL).
2
+ #
3
+ # THE ONE HARD RULE (causality): when the policy acts at time t it may only ever have
4
+ # seen frames with timestamp <= t. We enforce this STRUCTURALLY: frames are handed to
5
+ # the policy one at a time in strictly ascending t, and the policy keeps its own history
6
+ # (no handle to future frames exists). The `assert t > last_t` is a tripwire; the
7
+ # shuffled-frame unit test (test_causality.py) proves it fires.
8
+ import json
9
+ import glob
10
+ import os
11
+
12
+
13
+ def iter_frames(sample_dir):
14
+ """Yield (t, path) in ascending time — the ONLY way frames enter the system.
15
+
16
+ Non-frame files (e.g. question.json) and any unparsable stems are skipped.
17
+ """
18
+ items = []
19
+ for f in glob.glob(os.path.join(sample_dir, "*.jpg")):
20
+ stem = os.path.basename(f)[:-4]
21
+ try:
22
+ items.append((float(stem), f))
23
+ except ValueError:
24
+ pass # skip non-frame files
25
+ items.sort(key=lambda x: x[0])
26
+ out, last = [], None
27
+ for t, f in items: # drop a malformed duplicate-timestamp frame (would trip drive()'s assert)
28
+ if t != last:
29
+ out.append((t, f))
30
+ last = t
31
+ return out
32
+
33
+
34
+ def _emissions(out):
35
+ """Normalize a policy.step() return into a list of reply strings.
36
+ None -> []; a str -> [str]; a list/tuple -> itself. Lets one frame emit several answers
37
+ (the v5+v3 ensemble) without changing single-reply policies (SilentPolicy/RLPolicy)."""
38
+ if out is None:
39
+ return []
40
+ return [out] if isinstance(out, str) else list(out)
41
+
42
+
43
+ def drive(frames, policy, question_id, question):
44
+ """Run an ascending (t, frame_path) stream through a policy and collect emissions.
45
+
46
+ `frames` must be strictly time-ordered; the assert below makes a causality
47
+ violation structurally impossible to ignore. The policy sees one frame at a time.
48
+ """
49
+ policy.reset(question=question)
50
+ responses, last_t = [], -1.0
51
+ for t, frame_path in frames:
52
+ assert t > last_t, "frames must arrive strictly in order" # causality guard
53
+ last_t = t
54
+ for content in _emissions(policy.step(t, frame_path)): # sees only frames up to t
55
+ responses.append({"time": round(t, 3), "content": content})
56
+ return {"question_id": question_id, "model_response_list": responses}
57
+
58
+
59
+ def run_sample(sample_dir, policy):
60
+ """Drive one sample directory (frames + question.json) through a fresh policy."""
61
+ with open(os.path.join(sample_dir, "question.json"), encoding="utf-8") as f:
62
+ q = json.load(f)
63
+ return drive(iter_frames(sample_dir), policy, q["question_id"], q["question"])
64
+
65
+
66
+ # Policy contract (both phases implement this):
67
+ # policy.reset(question: str) -> None
68
+ # policy.step(t: float, frame_path: str) -> str | list[str] | None
69
+ # None / [] == stay silent; a str or a 1+ list == emit that/those answer(s) AT TIME t
70
+ # (the ensemble emits several per frame). See _emissions().
src/humomni/core/validate_submission.py ADDED
@@ -0,0 +1,67 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # validate_submission.py — run before EVERY upload (a malformed line = an invalid entry).
2
+ # Checks: valid JSONL; model_response_list present & a list; times >= 0 and multiples of
3
+ # 0.5; non-empty string content; ALL gold ids present (emit [] for silent samples).
4
+ import json
5
+ import glob
6
+ import os
7
+ import argparse
8
+ import sys
9
+
10
+
11
+ def validate(path, gold_ids):
12
+ seen, errs = set(), []
13
+ with open(path, encoding="utf-8") as f:
14
+ for i, ln in enumerate(f, 1):
15
+ ln = ln.strip()
16
+ if not ln:
17
+ continue
18
+ try:
19
+ r = json.loads(ln)
20
+ except Exception:
21
+ errs.append(f"L{i}: invalid JSON")
22
+ continue
23
+ qid = r.get("question_id")
24
+ seen.add(qid)
25
+ if not isinstance(r.get("model_response_list"), list):
26
+ errs.append(f"{qid}: missing/!list model_response_list")
27
+ continue
28
+ for resp in r["model_response_list"]:
29
+ t, c = resp.get("time"), resp.get("content")
30
+ # bool is a subclass of int — reject it explicitly.
31
+ if isinstance(t, bool) or not isinstance(t, (int, float)) or t < 0 or round(t * 2) != t * 2:
32
+ errs.append(f"{qid}: time {t!r} not a non-negative multiple of 0.5")
33
+ if not isinstance(c, str) or not c.strip():
34
+ errs.append(f"{qid}: empty/invalid content")
35
+ missing = set(gold_ids) - seen
36
+ if missing:
37
+ errs.append(f"{len(missing)} question_ids missing — emit every id, even with []")
38
+ extra = seen - set(gold_ids)
39
+ if extra:
40
+ errs.append(f"{len(extra)} unexpected question_ids not in the gold set")
41
+ return errs
42
+
43
+
44
+ def gold_ids_from_dir(data_dir):
45
+ """The authoritative id list = question.json['question_id'] for every sample dir."""
46
+ ids = []
47
+ for d in sorted(glob.glob(os.path.join(data_dir, "*"))):
48
+ qj = os.path.join(d, "question.json")
49
+ if os.path.isdir(d) and os.path.exists(qj):
50
+ with open(qj, encoding="utf-8") as f:
51
+ ids.append(json.load(f)["question_id"])
52
+ return ids
53
+
54
+
55
+ if __name__ == "__main__":
56
+ ap = argparse.ArgumentParser()
57
+ ap.add_argument("submission")
58
+ ap.add_argument("--data_dir", default="data/phase1/data")
59
+ a = ap.parse_args()
60
+ gold = gold_ids_from_dir(a.data_dir)
61
+ errs = validate(a.submission, gold)
62
+ if errs:
63
+ print(f"INVALID — {len(errs)} problem(s):")
64
+ for e in errs[:50]:
65
+ print(" -", e)
66
+ sys.exit(1)
67
+ print(f"VALID — {len(gold)} ids checked, all present, times are multiples of 0.5.")
src/humomni/phase1/__init__.py ADDED
File without changes
src/humomni/phase1/dedup.py ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # dedup.py — duplicate guard (§6 + §19). At inference we don't know gold spans, so we dedup a
2
+ # draft against the most recent K emitted answers (a safe superset of "same span"): suppress if
3
+ # max cosine to an individual recent >= sim_high, and escalate the borderline band
4
+ # [sim_low, sim_high) to the LLM entailment judge (the proven, metric-aligned setting).
5
+ #
6
+ # (A speculative "concat"/n-gram mode was removed — no config or experiment ever used it; the
7
+ # §19 bidirectional-entailment idea it sketched lives on as the use_llm escalation above.)
8
+ import numpy as np
9
+
10
+ from humomni.core.judges import llm_entails
11
+
12
+
13
+ class EntailmentGuard:
14
+ def __init__(self, sim_high=0.86, sim_low=0.70, recent_k=4, use_llm=True):
15
+ self.sim_high = sim_high
16
+ self.sim_low = sim_low
17
+ self.k = recent_k
18
+ self.use_llm = use_llm
19
+
20
+ def is_duplicate(self, draft, said):
21
+ recents = said[-self.k:]
22
+ if not recents:
23
+ return False
24
+ from humomni.core.emb_cache import embed_many
25
+ vecs = embed_many([draft] + recents)
26
+ sims = [float(np.dot(vecs[0], v)) for v in vecs[1:]]
27
+ if max(sims) >= self.sim_high: # clearly a repeat
28
+ return True
29
+ if self.use_llm and max(sims) >= self.sim_low: # borderline -> ask the NLI judge
30
+ return llm_entails(prior=recents, hyp=draft)
31
+ return False
src/humomni/phase1/perception_gate.py ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # perception_gate.py — Tier-1 CPU prefilter: decide whether a frame is worth a VLM call (§5.1).
2
+ # Baseline = grayscale frame-diff novelty + warmup + max-silence timer. No GPU, no model.
3
+ # The §19 upgrade (CLIP/DINO event-onset gating + hysteresis + refractory) lands at M5.
4
+ import numpy as np
5
+ from PIL import Image
6
+
7
+
8
+ class PerceptionGate:
9
+ def __init__(self, novelty=12.0, max_silence_s=2.0, warmup_s=1.0):
10
+ self.prev = None
11
+ self.last_fire = -1e9
12
+ self.novelty = novelty
13
+ self.max_silence = max_silence_s
14
+ self.warmup = warmup_s
15
+
16
+ def _feat(self, path):
17
+ return np.asarray(Image.open(path).convert("L").resize((64, 36)), dtype=np.float32)
18
+
19
+ def should_decide(self, t, path):
20
+ """Return (fire: bool, novelty: float). Fires at warmup, on visual novelty, or on timeout."""
21
+ f = self._feat(path)
22
+ nov = 255.0 if self.prev is None else float(np.abs(f - self.prev).mean())
23
+ self.prev = f
24
+ fire = (t <= self.warmup) or (nov >= self.novelty) or (t - self.last_fire >= self.max_silence)
25
+ if fire:
26
+ self.last_fire = t
27
+ return fire, nov
src/humomni/phase1/policies.py ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # policies.py — simple policies implementing the reset/step contract (§3).
2
+ # The real decision policy (APIPolicy, §5) arrives at M3 in policy_api.py.
3
+
4
+
5
+ class SilentPolicy:
6
+ """Dummy always-silent baseline (M1).
7
+
8
+ Never speaks, so it scores 0.25 PAUC (pure silence) but proves the causal loop,
9
+ the submission format, and the validator end-to-end before any API cost.
10
+ """
11
+
12
+ def reset(self, question):
13
+ self.question = question
14
+
15
+ def step(self, t, frame_path):
16
+ return None # stay silent
src/humomni/phase1/policy_api.py ADDED
@@ -0,0 +1,68 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # policy_api.py — Phase 1 decision module (implements the reset/step policy contract, §5.2).
2
+ # gate -> VLM decision(s) -> theta threshold -> entailment guard -> emit.
3
+ # use_gate=False makes it call the VLM on EVERY frame (the M4 cache-building pass).
4
+ #
5
+ # BEST METHOD (config.json "ensemble"): query SEVERAL prompts per frame (v5 then v3) and merge
6
+ # their drafts through ONE shared dedup guard — the validated v5+v3 @ recent_k=32 ensemble (holdout
7
+ # 0.7909). With no "ensemble" block it is the single-prompt policy (the env VLM_PROMPT, default v5).
8
+ # step() returns a LIST of drafts (0..N) so one frame can emit more than one answer (the ensemble).
9
+ from humomni.core.context import build_context
10
+ from humomni.phase1.vlm_client import call_vlm, prompt_for
11
+ from humomni.phase1.dedup import EntailmentGuard
12
+ from humomni.phase1.perception_gate import PerceptionGate
13
+
14
+
15
+ class APIPolicy:
16
+ def __init__(self, config=None, use_gate=True, vlm_fn=None):
17
+ cfg = config or {}
18
+ self.theta = cfg.get("theta", 0.6)
19
+ self.gate_cfg = cfg.get("gate", {"use_gate": True})
20
+ self.dedup_cfg = cfg.get("dedup", {})
21
+ self.ensemble_cfg = cfg.get("ensemble") # {"prompts": [...], "recent_k": N} or None
22
+ self.use_gate = use_gate and self.gate_cfg.get("use_gate", True)
23
+ self.vlm_fn = vlm_fn or call_vlm
24
+
25
+ def reset(self, question):
26
+ self.question = question
27
+ self.history = []
28
+ self.said = []
29
+ g = self.gate_cfg
30
+ self.gate = PerceptionGate(novelty=g.get("novelty", 12.0),
31
+ max_silence_s=g.get("max_silence_s", 2.0),
32
+ warmup_s=g.get("warmup_s", 1.0))
33
+ d = self.dedup_cfg
34
+ # Ensemble: a list of system-prompt texts queried per frame (None == the default SYSTEM_PROMPT);
35
+ # it uses its own (deeper) recent_k since merging prompts raises emit density. Single-prompt
36
+ # policy = one None entry, so behavior is byte-identical to the pre-ensemble policy.
37
+ if self.ensemble_cfg:
38
+ self.prompts = [prompt_for(p) for p in self.ensemble_cfg["prompts"]]
39
+ recent_k = self.ensemble_cfg.get("recent_k", d.get("recent_k", 4))
40
+ else:
41
+ self.prompts = [None]
42
+ recent_k = d.get("recent_k", 4)
43
+ self.guard = EntailmentGuard(sim_high=d.get("sim_high", 0.86), sim_low=d.get("sim_low", 0.70),
44
+ recent_k=recent_k, use_llm=d.get("use_llm", False))
45
+
46
+ def step(self, t, frame_path):
47
+ self.history.append((t, frame_path))
48
+ if self.use_gate:
49
+ fire, _ = self.gate.should_decide(t, frame_path)
50
+ if not fire:
51
+ return [] # cheap skip, no API call
52
+ # said=[] (NOT self.said): the VLM describes each frame fully; the entailment dedup below
53
+ # handles repeats. This MATCHES the offline cache (cache.py builds with said=[]) so the live
54
+ # submission reproduces the validated replay score — and scores HIGHER than feeding self.said,
55
+ # which makes a comprehensive (v5) VLM over-suppress ("SAME_ANSWER") and emit ~half as much.
56
+ ctx = build_context(self.question, self.history, [])
57
+ emitted = []
58
+ for prompt in self.prompts: # merge order = prompt order (v5 first, then v3)
59
+ d = self.vlm_fn(ctx, prompt=prompt) if prompt is not None else self.vlm_fn(ctx)
60
+ # §19 (M5): insert confidence calibration on d["confidence"] here before thresholding.
61
+ if d["decision"] != "NEW_ANSWER" or d["confidence"] < self.theta:
62
+ continue
63
+ draft = d["draft"].strip()
64
+ if not draft or self.guard.is_duplicate(draft, self.said):
65
+ continue
66
+ self.said.append(draft)
67
+ emitted.append(draft)
68
+ return emitted
src/humomni/phase1/vlm_client.py ADDED
@@ -0,0 +1,425 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # vlm_client.py — VLM per-frame decision call (§7). TWO backends, selected by VLM_PROVIDER:
2
+ # openrouter (default) — OpenAI-style chat, the shipped qwen/qwen3.7-plus VLM.
3
+ # vertex — Gemini via Vertex AI express REST (e.g. gemini-3.5-flash), images inline,
4
+ # NO SDK / NO OpenRouter (same transport as the judge). Auth = VERTEX_KEY in .env.
5
+ # Deterministic (temperature=0), images as base64, robust JSON parse (fail-safe = silent),
6
+ # exponential backoff on transient errors. Model id + key come from env / .env.
7
+ import base64
8
+ import io
9
+ import json
10
+ import os
11
+ import re
12
+ import time
13
+ import threading
14
+ import urllib.request
15
+ import urllib.error
16
+
17
+ from PIL import Image
18
+
19
+ from humomni.core.env_util import load_env
20
+
21
+ load_env()
22
+ VLM_PROVIDER = os.environ.get("VLM_PROVIDER", "openrouter").lower() # 'openrouter' | 'vertex'
23
+ MODEL = os.environ.get("VLM_MODEL", "qwen/qwen3.7-plus") # OpenRouter id; verify it is live
24
+ # qwen3.x "plus" is a reasoning model; a per-frame JSON decision needs no chain-of-thought,
25
+ # so disable thinking by default (huge latency + cost win). Override with VLM_REASONING=1.
26
+ DISABLE_REASONING = os.environ.get("VLM_REASONING", "0") != "1"
27
+ # --- Vertex backend (only used when VLM_PROVIDER=vertex) ---
28
+ _VLM_MODEL_VX = re.sub(r"-\d{8}$", "", MODEL.split("/", 1)[-1]) # strip google/ prefix + -YYYYMMDD date
29
+ VERTEX_BASE_URL = os.environ.get("VERTEX_BASE_URL", "https://aiplatform.googleapis.com/v1")
30
+ VLM_TIMEOUT_S = float(os.environ.get("VLM_TIMEOUT_S", "60")) # Gemini+images is slow
31
+ VLM_MAX_TOKENS = int(os.environ.get("VLM_MAX_TOKENS", "512")) # room for thinking + the JSON
32
+ _VLM_THINKING = os.environ.get("VLM_THINKING_BUDGET") # set "0" to try disabling thinking
33
+ _RECENT_PX = int(os.environ.get("CTX_RECENT_PX", "480")) # recent-frame max px (native 480)
34
+ _HIST_PX = int(os.environ.get("CTX_HIST_PX", "256")) # history-keyframe max px (perception lever)
35
+ _client = None
36
+
37
+ _SYSTEM_PROMPTS = {
38
+ # v1 — the shipped prompt (current submission). Default; unchanged behavior.
39
+ "v1": """You are a proactive visual assistant watching a LIVE video stream to answer ONE
40
+ question, given at the start. You only ever receive frames up to the current moment.
41
+ After each update you decide whether to speak.
42
+
43
+ RULES
44
+ 1. Speak ONLY when the current frames give clear visual evidence answering some part
45
+ of the question. If unsure, stay SILENT — silence beats a wrong or vague statement.
46
+ 2. Be timely: the instant the evidence is visible, say it. Do not wait for the action
47
+ to finish or for more certainty than you need.
48
+ 3. One NEW fact per message. Declarative, present tense, describe only what is visible.
49
+ No speculation, no hedging words ("maybe", "appears", "seems"), no timestamps in text.
50
+ 4. NEVER repeat or rephrase anything you have already told the user (it is listed for
51
+ you). Speak only if you have genuinely new information.
52
+ 5. Output STRICT JSON, nothing else:
53
+ {"decision":"NO_ANSWER"|"SAME_ANSWER"|"NEW_ANSWER","confidence":0.0-1.0,"draft":"..."}
54
+ - NEW_ANSWER: genuinely new, evidence-backed information (put it in "draft").
55
+ - SAME_ANSWER: the scene only repeats what you already said.
56
+ - NO_ANSWER: nothing answerable is visible yet.
57
+ "confidence" = your probability that "draft" is correct AND new.""",
58
+ # v2 — content-alignment experiment: answer the QUESTION's action/change tersely (caption
59
+ # style), stop cataloguing appearance/text/brands. Targets the dominant "scene-dump / wrong
60
+ # granularity" failure (inspect_wrong.py: ~20% of in-span turns). Select with VLM_PROMPT=v2.
61
+ "v2": """You are a proactive visual assistant watching a LIVE video stream to answer ONE
62
+ question, given at the start. You only ever receive frames up to the current moment.
63
+ After each update you decide whether to speak.
64
+
65
+ RULES
66
+ 1. Speak ONLY when the current frames give clear visual evidence answering some part
67
+ of the question. If unsure, stay SILENT — silence beats a wrong or vague statement.
68
+ 2. Be timely: the instant the evidence is visible, say it. Do not wait for the action
69
+ to finish or for more certainty than you need.
70
+ 3. ANSWER THE QUESTION DIRECTLY and briefly, like a one-line caption (aim for ≤12 words).
71
+ Report the single most relevant ACTION or CHANGE — what someone is DOING, or what just
72
+ happened — using a clear verb. Focus on events and interactions, NOT static scenery. Do
73
+ NOT list appearance, clothing, colors, background objects, on-screen text, brands, or
74
+ names unless that detail is exactly what the question asks for.
75
+ 4. One NEW fact per message. Declarative, present tense. No speculation, no hedging words
76
+ ("maybe", "appears", "seems"), no timestamps in text.
77
+ 5. NEVER repeat or rephrase anything you have already told the user (it is listed for
78
+ you). Speak only if you have genuinely new information.
79
+ 6. Output STRICT JSON, nothing else:
80
+ {"decision":"NO_ANSWER"|"SAME_ANSWER"|"NEW_ANSWER","confidence":0.0-1.0,"draft":"..."}
81
+ - NEW_ANSWER: genuinely new, evidence-backed information (put it in "draft").
82
+ - SAME_ANSWER: the scene only repeats what you already said.
83
+ - NO_ANSWER: nothing answerable is visible yet.
84
+ "confidence" = your probability that "draft" is correct AND new.""",
85
+ # v3 — content-alignment experiment, OPPOSITE of v2: maximize COVERAGE. The judge rewards partial
86
+ # credit for matching any aspect of the gold, so hedge multiple entities/actions instead of
87
+ # committing to one (targets the "wrong-focus" failures). Select with VLM_PROMPT=v3.
88
+ "v3": """You are a proactive visual assistant watching a LIVE video stream to answer ONE
89
+ question, given at the start. You only ever receive frames up to the current moment.
90
+ After each update you decide whether to speak.
91
+
92
+ RULES
93
+ 1. Speak ONLY when the current frames give clear visual evidence answering some part
94
+ of the question. If unsure, stay SILENT — silence beats a wrong or vague statement.
95
+ 2. Be timely: the instant the evidence is visible, say it. Do not wait for the action
96
+ to finish or for more certainty than you need.
97
+ 3. Answer the question as COMPLETELY as the current frames allow. State the main action or
98
+ event, AND also mention every other person, object, or action in view that is relevant to
99
+ the question. If several things could plausibly answer it, include them ALL rather than
100
+ committing to just one. Prefer concrete actions (verbs) and the specific subjects asked about.
101
+ 4. Declarative, present tense. No speculation, no hedging words ("maybe", "appears", "seems"),
102
+ no timestamps in text. NEVER repeat or rephrase anything you have already told the user (it
103
+ is listed for you) — add only genuinely new information.
104
+ 5. Output STRICT JSON, nothing else:
105
+ {"decision":"NO_ANSWER"|"SAME_ANSWER"|"NEW_ANSWER","confidence":0.0-1.0,"draft":"..."}
106
+ - NEW_ANSWER: genuinely new, evidence-backed information (put it in "draft").
107
+ - SAME_ANSWER: the scene only repeats what you already said.
108
+ - NO_ANSWER: nothing answerable is visible yet.
109
+ "confidence" = your probability that "draft" is correct AND new.""",
110
+ # v4 — v3's coverage PLUS v1's crisp lead. Two-part answer: direct answer FIRST, then enumerate
111
+ # everything relevant. Aims to capture the v3+v1 ensemble's +0.008 at 1x cost. Select VLM_PROMPT=v4.
112
+ "v4": """You are a proactive visual assistant watching a LIVE video stream to answer ONE
113
+ question, given at the start. You only ever receive frames up to the current moment.
114
+ After each update you decide whether to speak.
115
+
116
+ RULES
117
+ 1. Speak ONLY when the current frames give clear visual evidence answering some part
118
+ of the question. If unsure, stay SILENT — silence beats a wrong or vague statement.
119
+ 2. Be timely: the instant the evidence is visible, say it. Do not wait for the action
120
+ to finish or for more certainty than you need.
121
+ 3. Structure each answer in TWO parts. FIRST: state the single most DIRECT answer to the
122
+ question — the specific action or object asked about — in one clear opening clause. THEN:
123
+ add every other relevant person, object, action, or detail in view. If several things could
124
+ answer the question, include them ALL. Use concrete verbs and name the specific subjects asked about.
125
+ 4. Declarative, present tense. No speculation, no hedging words ("maybe", "appears", "seems"),
126
+ no timestamps in text. NEVER repeat or rephrase anything you have already told the user (it
127
+ is listed for you) — add only genuinely new information.
128
+ 5. Output STRICT JSON, nothing else:
129
+ {"decision":"NO_ANSWER"|"SAME_ANSWER"|"NEW_ANSWER","confidence":0.0-1.0,"draft":"..."}
130
+ - NEW_ANSWER: genuinely new, evidence-backed information (put it in "draft").
131
+ - SAME_ANSWER: the scene only repeats what you already said.
132
+ - NO_ANSWER: nothing answerable is visible yet.
133
+ "confidence" = your probability that "draft" is correct AND new.""",
134
+ # v5 — even MORE exhaustive than v3: explicitly read on-screen text/captions/numbers + spatial/context.
135
+ # Tests whether v3 leaves coverage on the table (esp. text-based questions). Select VLM_PROMPT=v5.
136
+ "v5": """You are a proactive visual assistant watching a LIVE video stream to answer ONE
137
+ question, given at the start. You only ever receive frames up to the current moment.
138
+ After each update you decide whether to speak.
139
+
140
+ RULES
141
+ 1. Speak ONLY when the current frames give clear visual evidence answering some part
142
+ of the question. If unsure, stay SILENT — silence beats a wrong or vague statement.
143
+ 2. Be timely: the instant the evidence is visible, say it. Do not wait for the action
144
+ to finish or for more certainty than you need.
145
+ 3. Describe the scene as EXHAUSTIVELY as the frames allow, to answer the question. Include:
146
+ every person and what each is DOING (concrete verbs); every relevant object; ALL on-screen
147
+ text, captions, labels, names, and numbers, quoted verbatim; and the setting/context. If
148
+ several things could answer the question, include them ALL — leave nothing relevant out.
149
+ 4. Declarative, present tense. No speculation, no hedging words ("maybe", "appears", "seems"),
150
+ no timestamps in text. NEVER repeat or rephrase anything you have already told the user (it
151
+ is listed for you) — add only genuinely new information.
152
+ 5. Output STRICT JSON, nothing else:
153
+ {"decision":"NO_ANSWER"|"SAME_ANSWER"|"NEW_ANSWER","confidence":0.0-1.0,"draft":"..."}
154
+ - NEW_ANSWER: genuinely new, evidence-backed information (put it in "draft").
155
+ - SAME_ANSWER: the scene only repeats what you already said.
156
+ - NO_ANSWER: nothing answerable is visible yet.
157
+ "confidence" = your probability that "draft" is correct AND new.""",
158
+ # v6 — v5's exhaustive breadth PLUS temporal coverage: describe the SEQUENCE of actions / what just
159
+ # changed across recent frames, to catch fleeting actions v5 misses (chew/throw/swirl). VLM_PROMPT=v6.
160
+ "v6": """You are a proactive visual assistant watching a LIVE video stream to answer ONE
161
+ question, given at the start. You only ever receive frames up to the current moment.
162
+ After each update you decide whether to speak.
163
+
164
+ RULES
165
+ 1. Speak ONLY when the current frames give clear visual evidence answering some part
166
+ of the question. If unsure, stay SILENT — silence beats a wrong or vague statement.
167
+ 2. Be timely: the instant the evidence is visible, say it. Do not wait for the action
168
+ to finish or for more certainty than you need.
169
+ 3. Describe EXHAUSTIVELY to answer the question, covering BOTH the static scene AND its motion:
170
+ - every person and the specific action each is performing RIGHT NOW (concrete verbs);
171
+ - what just CHANGED across the recent frames — the sequence of actions/events, not a snapshot
172
+ (e.g. picked up, threw, turned, poured, swirled, stepped forward);
173
+ - every relevant object; ALL on-screen text, captions, labels, names, and numbers, verbatim;
174
+ - the setting/context. If several things could answer the question, include them ALL.
175
+ 4. Declarative, present tense. No speculation, no hedging words ("maybe", "appears", "seems"),
176
+ no timestamps in text. NEVER repeat or rephrase anything you have already told the user (it
177
+ is listed for you) — add only genuinely new information.
178
+ 5. Output STRICT JSON, nothing else:
179
+ {"decision":"NO_ANSWER"|"SAME_ANSWER"|"NEW_ANSWER","confidence":0.0-1.0,"draft":"..."}
180
+ - NEW_ANSWER: genuinely new, evidence-backed information (put it in "draft").
181
+ - SAME_ANSWER: the scene only repeats what you already said.
182
+ - NO_ANSWER: nothing answerable is visible yet.
183
+ "confidence" = your probability that "draft" is correct AND new.""",
184
+ # v8 — FEW-SHOT (prompt-USAGE experiment): v5-style exhaustive rules + two GENERIC invented worked
185
+ # examples (no eval leakage) teaching "name the asked action, then cover everything, hedge if ambiguous".
186
+ # Probe: recovers v5-misses (+0.33) with far less good-turn poisoning than v7's instruction-anchoring.
187
+ "v8": """You are a proactive visual assistant watching a LIVE video stream to answer ONE
188
+ question, given at the start. You only ever receive frames up to the current moment.
189
+ After each update you decide whether to speak.
190
+
191
+ RULES
192
+ 1. Speak ONLY when the current frames give clear visual evidence answering some part
193
+ of the question. If unsure, stay SILENT.
194
+ 2. Be timely: the instant the evidence is visible, say it.
195
+ 3. Describe the scene as EXHAUSTIVELY as the frames allow, to answer the question. Make sure you state
196
+ what the specific subject the question asks about is DOING (the action/event), AND include every other
197
+ person and what each is doing, every relevant object, ALL on-screen text/captions/numbers verbatim, and
198
+ the setting. If the action could be read multiple ways, mention ALL plausible readings rather than guessing one.
199
+ 4. Declarative, present tense. No hedging words, no timestamps. NEVER repeat what you already said.
200
+ 5. Output STRICT JSON only: {"decision":"NO_ANSWER"|"SAME_ANSWER"|"NEW_ANSWER","confidence":0.0-1.0,"draft":"..."}
201
+
202
+ Two GENERIC examples of an ideal "draft" (invented — study how each NAMES the asked action up front, then covers everything else):
203
+
204
+ Q: "What is the person doing?" -> draft: "The person is whisking eggs in a glass mixing bowl. She wears
205
+ a blue apron and stands at a marble kitchen counter; beside the bowl are a carton of milk, a bag of flour,
206
+ and a wooden spoon. On-screen text in the corner reads 'Breakfast Recipes'."
207
+
208
+ Q: "What is happening in the game?" -> draft: "A soccer player in a red jersey kicks the ball toward the
209
+ goal while a defender in white slides in to block it. Other players run across the green pitch; a packed
210
+ stadium crowd is behind them, and the scoreboard shows '2 - 1'."
211
+
212
+ Now answer for the current frames. Output only the JSON.""",
213
+ # v9 — few-shot iteration of v8: 3 GENERIC examples covering the failure modes (#32): a clear action,
214
+ # a HEDGED ambiguous action (teach hedge-don't-commit), and a SCREEN/text+numbers case.
215
+ "v9": """You are a proactive visual assistant watching a LIVE video stream to answer ONE
216
+ question, given at the start. You only ever receive frames up to the current moment.
217
+ After each update you decide whether to speak.
218
+
219
+ RULES
220
+ 1. Speak ONLY when the current frames give clear visual evidence answering some part
221
+ of the question. If unsure, stay SILENT.
222
+ 2. Be timely: the instant the evidence is visible, say it.
223
+ 3. Describe the scene as EXHAUSTIVELY as the frames allow, to answer the question. Make sure you state
224
+ what the specific subject the question asks about is DOING (the action/event), AND include every other
225
+ person and what each is doing, every relevant object, ALL on-screen text/captions/numbers verbatim, and
226
+ the setting. If the action could be read multiple ways, mention ALL plausible readings rather than guessing one.
227
+ 4. Declarative, present tense. No hedging words, no timestamps. NEVER repeat what you already said.
228
+ 5. Output STRICT JSON only: {"decision":"NO_ANSWER"|"SAME_ANSWER"|"NEW_ANSWER","confidence":0.0-1.0,"draft":"..."}
229
+
230
+ Three GENERIC examples of an ideal "draft" (invented — study how each NAMES the asked action, COVERS everything, and HEDGES when unsure):
231
+
232
+ Q: "What is the person doing?" -> draft: "The person is whisking eggs in a glass mixing bowl. She wears
233
+ a blue apron and stands at a marble kitchen counter; beside the bowl are a carton of milk, a bag of flour,
234
+ and a wooden spoon. On-screen text in the corner reads 'Breakfast Recipes'."
235
+
236
+ Q: "What is the man doing with the paper?" -> draft: "The man is either folding or creasing a sheet of
237
+ paper — the exact motion is unclear, so both readings are possible. He holds it with both hands over a
238
+ wooden table scattered with scissors, a glue stick, and sheets of coloured card."
239
+
240
+ Q: "What is shown on the screen?" -> draft: "The screen shows a video-game menu titled 'SQUAD SELECT'
241
+ listing four teams, each with a star rating (three to five stars) and a percentage. A scoreboard in the
242
+ corner reads '1,250 pts'. A voice-over explains how the ratings are calculated."
243
+
244
+ Now answer for the current frames. Output only the JSON.""",
245
+ # v10 — ROBUSTNESS test of v8: SAME few-shot shape (2 clean action examples) but DIFFERENT generic
246
+ # content (gardening + car-repair). If v5+v3+v10 ≈ v5+v3+v8 (~0.80), the few-shot lever is robust to
247
+ # the example choice (not example-luck overfit). Generic/invented — no eval leakage.
248
+ "v10": """You are a proactive visual assistant watching a LIVE video stream to answer ONE
249
+ question, given at the start. You only ever receive frames up to the current moment.
250
+ After each update you decide whether to speak.
251
+
252
+ RULES
253
+ 1. Speak ONLY when the current frames give clear visual evidence answering some part
254
+ of the question. If unsure, stay SILENT.
255
+ 2. Be timely: the instant the evidence is visible, say it.
256
+ 3. Describe the scene as EXHAUSTIVELY as the frames allow, to answer the question. Make sure you state
257
+ what the specific subject the question asks about is DOING (the action/event), AND include every other
258
+ person and what each is doing, every relevant object, ALL on-screen text/captions/numbers verbatim, and
259
+ the setting. If the action could be read multiple ways, mention ALL plausible readings rather than guessing one.
260
+ 4. Declarative, present tense. No hedging words, no timestamps. NEVER repeat what you already said.
261
+ 5. Output STRICT JSON only: {"decision":"NO_ANSWER"|"SAME_ANSWER"|"NEW_ANSWER","confidence":0.0-1.0,"draft":"..."}
262
+
263
+ Two GENERIC examples of an ideal "draft" (invented — study how each NAMES the asked action up front, then covers everything else):
264
+
265
+ Q: "What is the person doing?" -> draft: "The person is repotting a seedling — she lifts a small green
266
+ plant from a plastic tray and lowers it into a terracotta pot of dark soil. She works at a wooden potting
267
+ bench; a watering can and a bag of compost stand beside her, and a label on the pot reads 'BASIL'."
268
+
269
+ Q: "What is the man doing?" -> draft: "The man is changing a car tyre. He kneels by the front wheel of a
270
+ silver sedan and loosens the bolts with a wrench; a jack holds the car up and the removed hubcap lies on the
271
+ tarmac. He wears a grey hoodie and blue jeans."
272
+
273
+ Now answer for the current frames. Output only the JSON.""",
274
+ }
275
+ # v5 (exhaustive + OCR/text) is the validated default: +0.0408 on the WEB holdout vs v3 (0.7328->0.7737),
276
+ # which was itself +0.133 vs v1. Override with VLM_PROMPT=v1|v2|v3|v4 for ablation.
277
+ SYSTEM_PROMPT = _SYSTEM_PROMPTS[os.environ.get("VLM_PROMPT", "v5").lower()]
278
+
279
+
280
+ def prompt_for(name):
281
+ """Resolve a prompt name (v1..v6) to its system-prompt TEXT; None -> the env default SYSTEM_PROMPT.
282
+ Lets the ensemble policy call the VLM with several prompts per frame (e.g. v5 then v3)."""
283
+ return SYSTEM_PROMPT if name is None else _SYSTEM_PROMPTS[name.lower()]
284
+
285
+
286
+ _client_lock = threading.Lock()
287
+
288
+
289
+ def _client_():
290
+ global _client
291
+ if _client is None: # double-checked locking for thread pools
292
+ with _client_lock:
293
+ if _client is None:
294
+ from openai import OpenAI
295
+ _client = OpenAI(api_key=os.environ["OPENROUTER_API_KEY"],
296
+ base_url="https://openrouter.ai/api/v1")
297
+ return _client
298
+
299
+
300
+ def _b64(path, max_px):
301
+ for attempt in range(4): # Windows throws transient Errno 22 under load
302
+ try:
303
+ with Image.open(path) as im0: # context-manager => file handle is CLOSED
304
+ im0.load() # (leaking handles -> WinError 1450 under load)
305
+ im = im0.convert("RGB")
306
+ break # reached only on success -> `im` is bound
307
+ except OSError:
308
+ if attempt == 3:
309
+ raise
310
+ time.sleep(0.25)
311
+ # any non-OSError (MemoryError, bad mode, ...) propagates with its real traceback
312
+ if max(im.size) > max_px:
313
+ im.thumbnail((max_px, max_px))
314
+ buf = io.BytesIO()
315
+ im.save(buf, "JPEG", quality=85)
316
+ return base64.b64encode(buf.getvalue()).decode()
317
+
318
+
319
+ def _img(path, max_px):
320
+ return {"type": "image_url",
321
+ "image_url": {"url": f"data:image/jpeg;base64,{_b64(path, max_px)}"}}
322
+
323
+
324
+ def _user_text(ctx):
325
+ """The user-turn text for ONE streaming decision (question + already-said memory). Shared by both
326
+ backends + the cost probe so the prompt can't drift."""
327
+ said = "\n".join(f"- {s}" for s in ctx["already_said"]) or "(nothing yet)"
328
+ return (f'Question (asked at t=0): {ctx["question"]}\n'
329
+ f'Current time: {ctx["now"]:.1f}s\n\nYou have already told the user:\n{said}\n\n'
330
+ "Below are the most recent frames (newest last), then older keyframes. "
331
+ "Decide for THIS moment. Output only the JSON.")
332
+
333
+
334
+ def build_messages(ctx, prompt=None):
335
+ """OpenAI-style system+user chat messages (recent frames 480px, then older keyframes 256px).
336
+ `prompt` (system-prompt text) overrides the default SYSTEM_PROMPT for one call (ensemble)."""
337
+ content = [{"type": "text", "text": _user_text(ctx)}]
338
+ content += [_img(p, _RECENT_PX) for _, p in ctx["recent_frames"]]
339
+ content += [_img(p, _HIST_PX) for _, p in ctx["history_frames"]]
340
+ return [{"role": "system", "content": prompt or SYSTEM_PROMPT}, {"role": "user", "content": content}]
341
+
342
+
343
+ def _vertex_key():
344
+ return os.environ.get("VERTEX_KEY") or os.environ.get("VERTEX_API_KEY") or os.environ.get("GOOGLE_API_KEY")
345
+
346
+
347
+ def _vx_text(data):
348
+ try:
349
+ return "".join(p.get("text", "") for p in data["candidates"][0]["content"]["parts"])
350
+ except Exception:
351
+ return ""
352
+
353
+
354
+ def _call_vertex(ctx, retries=4, prompt=None):
355
+ """Gemini VLM decision via Vertex AI express REST (images inline). Same JSON contract as OpenRouter.
356
+ `prompt` (system-prompt text) overrides the default SYSTEM_PROMPT for one call (ensemble)."""
357
+ key = _vertex_key()
358
+ if not key:
359
+ raise RuntimeError("no VERTEX_KEY — set it in .env to use VLM_PROVIDER=vertex")
360
+ parts = [{"text": _user_text(ctx)}]
361
+ parts += [{"inlineData": {"mimeType": "image/jpeg", "data": _b64(p, _RECENT_PX)}} for _, p in ctx["recent_frames"]]
362
+ parts += [{"inlineData": {"mimeType": "image/jpeg", "data": _b64(p, _HIST_PX)}} for _, p in ctx["history_frames"]]
363
+ gen = {"temperature": 0, "maxOutputTokens": VLM_MAX_TOKENS}
364
+ if _VLM_THINKING is not None: # e.g. "0" to try disabling thinking
365
+ gen["thinkingConfig"] = {"thinkingBudget": int(_VLM_THINKING)}
366
+ body = json.dumps({
367
+ "systemInstruction": {"parts": [{"text": prompt or SYSTEM_PROMPT}]},
368
+ "contents": [{"role": "user", "parts": parts}],
369
+ "generationConfig": gen,
370
+ }).encode("utf-8")
371
+ url = f"{VERTEX_BASE_URL}/publishers/google/models/{_VLM_MODEL_VX}:generateContent"
372
+ last = None
373
+ for attempt in range(retries):
374
+ try:
375
+ req = urllib.request.Request(url, data=body, method="POST",
376
+ headers={"Content-Type": "application/json", "x-goog-api-key": key})
377
+ with urllib.request.urlopen(req, timeout=VLM_TIMEOUT_S) as resp:
378
+ return _parse(_vx_text(json.loads(resp.read().decode("utf-8"))))
379
+ except urllib.error.HTTPError as e:
380
+ detail = ""
381
+ try:
382
+ detail = e.read().decode("utf-8")[:300]
383
+ except Exception:
384
+ pass
385
+ last = RuntimeError(f"Vertex VLM HTTP {e.code}: {detail}")
386
+ if e.code in (400, 401, 403, 404): # bad key / model / request — not transient
387
+ raise last from e
388
+ if attempt == retries - 1:
389
+ raise last from e
390
+ time.sleep(min(2 ** attempt, 20))
391
+ except Exception as e: # timeout / connection -> retry
392
+ last = e
393
+ if attempt == retries - 1:
394
+ raise
395
+ time.sleep(min(2 ** attempt, 20))
396
+ raise last
397
+
398
+
399
+ def call_vlm(ctx, max_tokens=200, retries=4, prompt=None):
400
+ """One per-frame VLM decision. `prompt` (system-prompt text from prompt_for()) overrides the
401
+ default for one call so the ensemble policy can query v5 then v3 on the same frame."""
402
+ if VLM_PROVIDER == "vertex":
403
+ return _call_vertex(ctx, retries=retries, prompt=prompt)
404
+ messages = build_messages(ctx, prompt)
405
+ extra = {"extra_body": {"reasoning": {"enabled": False}}} if DISABLE_REASONING else {}
406
+ for attempt in range(retries):
407
+ try:
408
+ r = _client_().chat.completions.create(
409
+ model=MODEL, temperature=0, max_tokens=max_tokens, messages=messages, **extra)
410
+ return _parse(r.choices[0].message.content)
411
+ except Exception:
412
+ if attempt == retries - 1:
413
+ raise
414
+ time.sleep(2 ** attempt) # 1, 2, 4 s backoff on 429/5xx/transient
415
+
416
+
417
+ def _parse(s):
418
+ """Last flat JSON object in the text (robust to prose). Fail-safe = stay silent."""
419
+ try:
420
+ d = json.loads(re.findall(r"\{[^{}]*\}", s, re.S)[-1])
421
+ return {"decision": d.get("decision", "NO_ANSWER"),
422
+ "confidence": float(d.get("confidence", 0.0)),
423
+ "draft": str(d.get("draft", ""))}
424
+ except Exception:
425
+ return {"decision": "NO_ANSWER", "confidence": 0.0, "draft": ""}
src/humomni/tuning/__init__.py ADDED
File without changes
src/humomni/tuning/cache.py ADDED
@@ -0,0 +1,137 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # cache.py — disk-backed VLM-decision cache for the M4 "one paid pass -> free tuning" loop.
2
+ # Decisions are cached per (question_id, t) with a FIXED empty already_said, so each call is
3
+ # independent of theta/gate/dedup and can be parallelised + replayed offline (tune.py).
4
+ # Also precomputes per-frame visual novelty so the perception gate can be replayed offline.
5
+ import os
6
+ import glob
7
+ import json
8
+ import time
9
+ import threading
10
+ from concurrent.futures import ThreadPoolExecutor
11
+
12
+ # VLM_CACHE_PATH lets a different VLM (e.g. a Vertex gemini probe) write a SEPARATE cache so it never
13
+ # collides with the shipped qwen cache — the (qid,t) key is VLM-agnostic, so the file must differ.
14
+ CACHE_PATH = os.environ.get("VLM_CACHE_PATH", "cache/web_vlm_cache.jsonl")
15
+ NOVELTY_PATH = "cache/web_novelty.json"
16
+
17
+
18
+ class VLMCache:
19
+ """Append-only jsonl cache keyed by (question_id, round(t,1)). Thread-safe, resumable."""
20
+
21
+ def __init__(self, path=CACHE_PATH):
22
+ self.path = path
23
+ self.mem = {}
24
+ self.lock = threading.Lock()
25
+ os.makedirs(os.path.dirname(path) or ".", exist_ok=True)
26
+ if os.path.exists(path):
27
+ with open(path, encoding="utf-8") as f:
28
+ for ln in f:
29
+ ln = ln.strip()
30
+ if ln:
31
+ r = json.loads(ln)
32
+ self.mem[(r["qid"], r["t"])] = r["v"]
33
+
34
+ def get(self, qid, t):
35
+ return self.mem.get((qid, round(float(t), 1)))
36
+
37
+ def set(self, qid, t, v):
38
+ k = (qid, round(float(t), 1))
39
+ line = json.dumps({"qid": k[0], "t": k[1], "v": v}, ensure_ascii=False) + "\n"
40
+ with self.lock:
41
+ self.mem[k] = v
42
+ for attempt in range(5): # ponytail: Windows throws transient EINVAL(22) on append under load
43
+ try:
44
+ with open(self.path, "a", encoding="utf-8") as w:
45
+ w.write(line)
46
+ return
47
+ except OSError:
48
+ if attempt == 4:
49
+ raise
50
+ time.sleep(0.1 * (attempt + 1))
51
+
52
+ def __len__(self):
53
+ return len(self.mem)
54
+
55
+
56
+ def frames_of(qid, data_dir="data/web_frames"):
57
+ """Ascending (t, path) for a question_id (dir name drops the .mp4 suffix)."""
58
+ name = qid[:-4] if qid.endswith(".mp4") else qid
59
+ d = os.path.join(data_dir, name)
60
+ return sorted([(float(os.path.basename(p)[:-4]), p) for p in glob.glob(d + "/*.jpg")],
61
+ key=lambda x: x[0])
62
+
63
+
64
+ def _sample_dirs(data_dir, limit):
65
+ dirs = sorted(d for d in glob.glob(os.path.join(data_dir, "*")) if os.path.isdir(d))
66
+ return dirs[:limit] if limit else dirs
67
+
68
+
69
+ def build_web_cache(data_dir="data/web_frames", cache_path=CACHE_PATH, limit=0, workers=12, prompt=None):
70
+ """Call the VLM on EVERY frame (gate OFF, already_said=[]) and cache the decision. Resumable (skips
71
+ already-cached frames). `prompt` = a system-prompt TEXT override (e.g. prompt_for('v8')) so one process
72
+ can build several prompt caches; None uses the env default SYSTEM_PROMPT."""
73
+ from humomni.core.context import build_context
74
+ from humomni.phase1.vlm_client import call_vlm
75
+ cache = VLMCache(cache_path)
76
+ tasks = []
77
+ for d in _sample_dirs(data_dir, limit):
78
+ q = json.load(open(os.path.join(d, "question.json"), encoding="utf-8"))
79
+ qid, question = q["question_id"], q["question"]
80
+ fr = sorted([(float(os.path.basename(p)[:-4]), p) for p in glob.glob(d + "/*.jpg")],
81
+ key=lambda x: x[0])
82
+ for i, (t, _) in enumerate(fr):
83
+ if cache.get(qid, t) is None:
84
+ tasks.append((qid, question, fr[:i + 1], t))
85
+ print(f"to compute: {len(tasks)} frame-decisions ({len(cache)} already cached)")
86
+
87
+ def work(task):
88
+ qid, question, hist, t = task
89
+ cache.set(qid, t, call_vlm(build_context(question, hist, said=[]), prompt=prompt))
90
+ return 1
91
+
92
+ if tasks:
93
+ try:
94
+ from tqdm import tqdm
95
+ with ThreadPoolExecutor(max_workers=workers) as ex:
96
+ for _ in tqdm(ex.map(work, tasks), total=len(tasks), desc="vlm-cache"):
97
+ pass
98
+ except ImportError:
99
+ with ThreadPoolExecutor(max_workers=workers) as ex:
100
+ list(ex.map(work, tasks))
101
+ print(f"cache size: {len(cache)} decisions -> {cache_path}")
102
+ return cache
103
+
104
+
105
+ def build_novelty(data_dir="data/web_frames", out=NOVELTY_PATH, limit=0):
106
+ """Per-frame grayscale novelty (consecutive-frame diff) so the gate is replayable offline."""
107
+ import numpy as np
108
+ from PIL import Image
109
+ res = {}
110
+ for d in _sample_dirs(data_dir, limit):
111
+ qid = json.load(open(os.path.join(d, "question.json"), encoding="utf-8"))["question_id"]
112
+ fr = sorted([(float(os.path.basename(p)[:-4]), p) for p in glob.glob(d + "/*.jpg")],
113
+ key=lambda x: x[0])
114
+ prev, nov = None, {}
115
+ for t, p in fr:
116
+ f = np.asarray(Image.open(p).convert("L").resize((64, 36)), dtype=np.float32)
117
+ nov[f"{t:.1f}"] = 255.0 if prev is None else float(np.abs(f - prev).mean())
118
+ prev = f
119
+ res[qid] = nov
120
+ os.makedirs(os.path.dirname(out) or ".", exist_ok=True)
121
+ json.dump(res, open(out, "w"))
122
+ print(f"novelty for {len(res)} samples -> {out}")
123
+ return res
124
+
125
+
126
+ if __name__ == "__main__":
127
+ import argparse
128
+ ap = argparse.ArgumentParser()
129
+ ap.add_argument("--func", choices=["vlm", "novelty", "both"], default="both")
130
+ ap.add_argument("--data_dir", default="data/web_frames")
131
+ ap.add_argument("--limit", type=int, default=0)
132
+ ap.add_argument("--workers", type=int, default=12)
133
+ a = ap.parse_args()
134
+ if a.func in ("novelty", "both"):
135
+ build_novelty(a.data_dir, limit=a.limit)
136
+ if a.func in ("vlm", "both"):
137
+ build_web_cache(a.data_dir, limit=a.limit, workers=a.workers)
src/humomni/tuning/faithful_eval.py ADDED
@@ -0,0 +1,67 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # faithful_eval.py — score a config under the REAL Gemini judge, fast.
2
+ # Trick: a recording pass enumerates every (judge/entail) call the metric will make, we warm
3
+ # those concurrently through a thread pool (Gemini is I/O-bound, results cached to disk), then
4
+ # the real score() runs sequentially on cache hits. Subsequent configs reuse overlapping calls.
5
+ import concurrent.futures as cf
6
+
7
+ import humomni.core.judges as judges
8
+ from humomni.core.pauc_eval import score, make_judges
9
+
10
+
11
+ def _collect(gold, pred):
12
+ """Enumerate (superset of) the judge/entail string-pairs the metric needs. Dummy returns
13
+ (0 / False) make turn bleed-removal a no-op, so we capture ALL prefix pairs -> never miss."""
14
+ jpairs, epairs = [], []
15
+
16
+ def rec_judge(ans, ref, question=None):
17
+ jpairs.append((ans, ref, question))
18
+ return 0
19
+
20
+ def rec_entail(prior, hyp):
21
+ epairs.append((list(prior), hyp))
22
+ return False
23
+
24
+ score(gold, pred, judge=rec_judge, entail=rec_entail)
25
+ return jpairs, epairs
26
+
27
+
28
+ def warm(gold, pred, workers=8):
29
+ judges.jc_load()
30
+ jpairs, epairs = _collect(gold, pred)
31
+ jobs, seen = [], set()
32
+ for ans, ref, question in jpairs:
33
+ k = judges.judge_key(ref, ans, question)
34
+ if k not in judges._jcache and k not in seen:
35
+ seen.add(k)
36
+ jobs.append(("judge", ans, ref, question))
37
+ for prior, hyp in epairs:
38
+ k = judges.jc_key("entail", " | ".join(prior), hyp)
39
+ if k not in judges._jcache and k not in seen:
40
+ seen.add(k)
41
+ jobs.append(("entail", prior, hyp))
42
+
43
+ def do(job):
44
+ if job[0] == "judge":
45
+ judges.llm_judge(job[1], job[2], job[3])
46
+ else:
47
+ judges.llm_entails(job[1], job[2])
48
+
49
+ if jobs:
50
+ # incremental jc_save every CHUNK so a mid-run credit drain doesn't lose warm progress
51
+ CHUNK = 100
52
+ with cf.ThreadPoolExecutor(max_workers=workers) as ex:
53
+ done = 0
54
+ for _ in ex.map(do, jobs):
55
+ done += 1
56
+ if done % CHUNK == 0:
57
+ judges.jc_save()
58
+ judges.jc_save()
59
+ return len(jobs)
60
+
61
+
62
+ def faithful_score(gold, pred, workers=8):
63
+ n_new = warm(gold, pred, workers)
64
+ jf, ef = make_judges("llm") # cached Gemini judge + entailment
65
+ r = score(gold, pred, judge=jf, entail=ef)
66
+ r["new_judge_calls"] = n_new
67
+ return r
tests/test_causality.py ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # test_causality.py — the causality regression test (keep green at all times).
2
+ # Feeds shuffled frames to the driver and requires an AssertionError; also checks that
3
+ # a correctly-ordered stream runs and that the silent policy emits nothing.
4
+ # Dependency-free: run with `python test_causality.py` (exits non-zero on failure).
5
+ from humomni.core.streaming_driver import drive
6
+ from humomni.phase1.policies import SilentPolicy
7
+
8
+
9
+ def test_shuffled_raises():
10
+ frames = [(0.5, "a"), (1.0, "b"), (2.0, "c"), (1.5, "d")] # 1.5 after 2.0 -> violation
11
+ try:
12
+ drive(frames, SilentPolicy(), "qid.mp4", "q")
13
+ except AssertionError:
14
+ return
15
+ raise SystemExit("FAIL: shuffled frames did NOT raise AssertionError (causality unguarded)")
16
+
17
+
18
+ def test_ascending_ok():
19
+ frames = [(0.5, "a"), (1.0, "b"), (1.5, "c")]
20
+ rec = drive(frames, SilentPolicy(), "qid.mp4", "q")
21
+ assert rec["question_id"] == "qid.mp4", rec
22
+ assert rec["model_response_list"] == [], rec # silent policy never speaks
23
+
24
+
25
+ def test_equal_timestamp_raises():
26
+ frames = [(0.5, "a"), (0.5, "b")] # not strictly increasing
27
+ try:
28
+ drive(frames, SilentPolicy(), "qid.mp4", "q")
29
+ except AssertionError:
30
+ return
31
+ raise SystemExit("FAIL: duplicate timestamp did NOT raise (assert must be strict >)")
32
+
33
+
34
+ if __name__ == "__main__":
35
+ test_shuffled_raises()
36
+ test_ascending_ok()
37
+ test_equal_timestamp_raises()
38
+ print("PASS: causality assert raises on shuffled/duplicate frames; ascending order OK.")
tests/test_inference_parity.py ADDED
@@ -0,0 +1,60 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python
2
+ # test_inference_parity.py — run_inference and infer_simple MUST produce identical output for the
3
+ # same policy (both drive the shared reset/step contract). Also checks the multi-emit contract:
4
+ # a frame whose step() returns a list emits several answers at that timestamp (the v5+v3 ensemble).
5
+ import importlib.util
6
+ import json
7
+ import os
8
+ import sys
9
+ import tempfile
10
+
11
+ from humomni.core.streaming_driver import run_sample, _emissions
12
+
13
+ # infer_simple lives in scripts/ (not a package) — load it by path so the test can call infer_one.
14
+ _p = os.path.join(os.path.dirname(__file__), "..", "scripts", "infer_simple.py")
15
+ _spec = importlib.util.spec_from_file_location("infer_simple", _p)
16
+ infer_simple = importlib.util.module_from_spec(_spec)
17
+ _spec.loader.exec_module(infer_simple)
18
+ infer_one = infer_simple.infer_one
19
+
20
+
21
+ class MockPolicy:
22
+ """Deterministic, content-free: exercises None / str / list / [] returns from step()."""
23
+ def reset(self, question):
24
+ self.n = 0
25
+
26
+ def step(self, t, frame_path):
27
+ self.n += 1
28
+ if self.n == 2:
29
+ return "single" # one answer (str)
30
+ if self.n == 3:
31
+ return ["a", "b"] # TWO answers at one timestamp (the ensemble case)
32
+ if self.n == 4:
33
+ return [] # explicit silence
34
+ return None # silence
35
+
36
+
37
+ def _make_sample(d, n_frames=5):
38
+ for i in range(1, n_frames + 1):
39
+ open(os.path.join(d, f"{i * 0.5:.1f}.jpg"), "wb").close() # empty frames on a 0.5 grid
40
+ json.dump({"question_id": "vid.mp4", "question": "what happens?"},
41
+ open(os.path.join(d, "question.json"), "w", encoding="utf-8"))
42
+
43
+
44
+ def test_parity_and_multiemit():
45
+ with tempfile.TemporaryDirectory() as d:
46
+ _make_sample(d)
47
+ a = run_sample(d, MockPolicy()) # run_inference path (streaming_driver.drive)
48
+ b = infer_one(d, MockPolicy()) # infer_simple path
49
+ assert a == b, f"run_inference != infer_simple:\n {a}\n {b}"
50
+ resp = a["model_response_list"]
51
+ # frame 2 -> "single" @1.0; frame 3 -> "a","b" @1.5; frames 4,5 -> silent
52
+ assert [r["content"] for r in resp] == ["single", "a", "b"], resp
53
+ assert [r["time"] for r in resp] == [1.0, 1.5, 1.5], resp # two emits share t=1.5
54
+ # _emissions normalization
55
+ assert _emissions(None) == [] and _emissions("x") == ["x"] and _emissions(["a", "b"]) == ["a", "b"]
56
+ print("PASS: run_inference == infer_simple; multi-emit (ensemble) lands N answers at one timestamp.")
57
+
58
+
59
+ if __name__ == "__main__":
60
+ sys.exit(test_parity_and_multiemit())