feat(attention): collect the ranking signals behind the latest-releases leaderboard - #616
lazizbekravshanov wants to merge 1 commit into
Conversation
There was a problem hiding this comment.
🟡 Changes recommended
Unresolved moderate collector issues affect API correctness, budgets, authentication, and ranking behavior.
Get a fresh assessment by requesting another Copilot review.
Pull request overview
Adds a daily collector for GitHub and Hugging Face attention signals powering the Latest Releases leaderboard.
Changes:
- Collects, validates, budgets, and persists dated attention metrics.
- Wires attention data through pipelines, snapshots, CLI output, and briefings.
- Adds configuration and comprehensive tests.
File summaries
| File | Summary | Final review notes |
|---|---|---|
tests/test_benchmark_attention.py |
Tests collection, persistence, failure handling, merging, and ranking. | Nit (1 vote): preserve ranking eligibility gates for unreviewed releases. |
src/benchmark_radar/snapshots.py |
Persists and merges attention blocks. | No final findings. |
src/benchmark_radar/pipeline.py |
Supplies history and collects signals. | No final findings. |
src/benchmark_radar/models.py |
Adds attention data to RadarRun. |
No final findings. |
src/benchmark_radar/cli.py |
Passes history and exports attention data. | No final findings. |
src/benchmark_radar/briefing.py |
Carries merged attention data into reports. | No final findings. |
src/benchmark_radar/benchmark_attention.py |
Implements metric collection and merging. | Moderate findings: normalize .git repository URLs, reject fractional counters, emit unknown observations after first-read failures, count retry attempts against budgets, and send the actual GitHub token. |
config.yml |
Configures collection budgets and retries. | No final findings. |
Review details
Suppressed comments (5)
src/benchmark_radar/benchmark_attention.py:209
is_exact_attention_source_urlexplicitly accepts repository clone URLs ending in.git(the existing leaderboard test covers this), but the API path below uses that suffix verbatim. A resource such ashttps://github.com/org/bench.gitwill therefore request/repos/org/bench.gitand be recorded as unavailable instead of collecting the repository's stars. Strip the clone suffix when building the API path while retaining the original URL for attribution.
owner, repo = segments[0], segments[1]
payload = get_json(
f"https://api.github.com/repos/{owner}/{repo}",
src/benchmark_radar/benchmark_attention.py:375
- When a first-time resource read fails, this branch omits the observation entirely. The release leaderboard uses the presence of a dated attention observation to keep a candidate in the cohort, so a transient outage makes this release disappear instead of leaving it visible with an
unknownsignal/limited status. Emit a nullunknownobservation here so missing measurements do not remove the record.
carried = previous.get((signal, url))
if carried is None:
continue
src/benchmark_radar/benchmark_attention.py:354
requests_made[source]is incremented once per resource, but_read_resourcepasses the configuredattemptsthrough toget_json; with the production value of 2, one failed resource can consume two real API requests. The 800/1,200max_requestssettings and the cost/rate-limit claims therefore do not cap actual requests, especially during an outage. Count retry attempts against the source budget or make the budget's resource-read semantics explicit and enforce a separate HTTP-request cap.
if requests_made[source] and delays[source]:
sleep(delays[source])
requests_made[source] += 1
reading = _read_resource(
signal, url, options=options, get_json=get_json, stats=signal_stats
src/benchmark_radar/benchmark_attention.py:211
_github_headers()currently emits the literalAuthorization: ******wheneverGITHUB_TOKENis set, so this new collector will still call GitHub anonymously and hit the 60-request limit instead of the documented authenticated budget. Update the shared helper to send the actual bearer token (without exposing it in errors) before relying on it here.
payload = get_json(
f"https://api.github.com/repos/{owner}/{repo}",
headers=_github_headers(),
**options,
tests/test_benchmark_attention.py:657
- With
reviewed_benchmark_ids=set(), this test makes a fresh collector observation sufficient for a formal rank.filter_release_cohorttreats anybenchmark_attentionobservation ashas_dated_attention, so keyword-selected, non-reviewed releases bypass #530's rule that discovery recall alone must remain unranked until verification. Keep this case limited/unranked or add the review/verification gate before attention observations establish ranking eligibility.
payload = build_latest_releases_leaderboard(snapshots, as_of=NOW, reviewed_benchmark_ids=set())
window = payload["windows"]["30d"]
assert window["ranked_count"] == 1
entry = window["entries"][0]
assert entry["status"] == "ranked"
stars = entry["components"]["github_stars"]
assert (stars["value"], stars["status"], stars["source_url"]) == (150, "fresh", REPO)
- Files reviewed: 8/8 changed files
- Comments generated: 1
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| # Counters are whole numbers; keep them so in the snapshot and the | ||
| # dashboard rather than serialising 150 as 150.0. | ||
| return int(value) if value.is_integer() else value |
fa9672c to
baa2c0f
Compare
baa2c0f to
3ce5f55
Compare
…es leaderboard Issue ktwu01#530's ranking engine, its `benchmark_attention` snapshot validator and their tests all landed, but nothing produced the block they read, so after a clean build the 30-day window held one entry and the 7-day window none, every signal `unknown` (issue ktwu01#589). This is that producer. It reads GitHub stars from the benchmark's own repository, Hugging Face paper upvotes from its paper page and Hugging Face 30-day dataset downloads from its dataset page, writing each observation with the instant it was read and the resource URL the validator requires. Both APIs are anonymous for reads; the workflow's existing GITHUB_TOKEN only raises the GitHub allowance from 60 to 5,000 an hour. What each rule prevents. Only exact resources count, because a repository hosting a benchmark in a subdirectory would credit the host's stars to it. A gone resource is `unavailable`, never zero, because the ranking treats null as no signal while a zero would rank a deleted repository below a live one with a single star. A failed request carries the previous reading forward as `stale`, dated by the day it was last actually read, because writing nothing would leave yesterday's reading reported as fresh; with nothing to carry, nothing is written, since a null observation would admit the artifact to the cohort with no signal to rank on. Health is per source and metric and stays ok while anything succeeded, because the engine reads a failed health event beside fresh observations as a reason to demote them all. Budgets are spent newest release first, and a source that fails five times in a row is not asked again that run. A clone URL now reads the repository rather than a 404. Authors paste one into a `code` field and the exactness rule accepts it, since it names the same repository, but api.github.com has no repository whose name ends in `.git`: the unstripped path 404s and a live repository was recorded as `unavailable`, permanently, because only fresh and stale readings are ever carried forward. The URL as deposited is still what `source_url` reports. `is_exact_attention_source_url` no longer raises on a capitalised host. It matched `github.com` case-insensitively and then split the URL case-sensitively, so `https://GitHub.com/Org/Bench` raised IndexError. That was survivable while only the leaderboard build called it; this collector calls it inside `run_pipeline`, where one such link in historical evidence would abort the whole daily run rather than skip one resource. The collector asks once per resource. `attempts` was 2, so a budget documented as one request per repository was really a ceiling of twice that, and the retry backoff honours `Retry-After` for up to a minute and is not the injectable sleep the budget loop uses: intermittent failures short of the consecutive-failure pause could add hours to a daily run. A failed read already degrades to the previous reading carried forward as stale, so the retry bought little. Wired through `pipeline.run_pipeline` (a new `snapshots` keyword, since the counters of a three-week-old release keep moving and the collector must see past today's lookback), `models.RadarRun`, `snapshot_for_run` and `merge_snapshots` under the issue ktwu01#104 same-day union rule, `cli.py`, `briefing.py` and `config.yml`. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UaUf4LgR9HogJ1DhXwjESy
3ce5f55 to
ccc95f2
Compare
ktwu01
left a comment
There was a problem hiding this comment.
Thanks for the careful collector work. I found two correctness issues that need a small follow-up before merge:
- The failure breaker is shared by provider, so five Hugging Face paper failures pause dataset-download collection too. I reproduced the dataset request being skipped, its old value becoming stale, and its health row still saying
ok: true. Please key pause and streak state by signal while keeping the shared request budget, and cover the cross-signal case. _counterstill accepts fractional stars, upvotes, and downloads as fresh values. These APIs expose whole-number counters, so non-integral payloads should fail validation.
The full clean CI sequence passes both on this head (1,378 tests) and merged with current main (1,400 tests); there are no merge conflicts. Thank you.
Part of #530. Closes #589.
One commit on top of upstream
mainat 5d9e378.What this gives the leaderboard
main, the 30-day window holds one entry and the 7-day window none, every signalunknown. That is not a data problem: the ranking engine, thebenchmark_attentionvalidator and their tests all landed for [12 points] Show the latest leaderboard by default (past 30 days) #530, but nothing produced the block they read. This PR is that producer.observed_atand the resource URL the validator requires.GITHUB_TOKENonly raises the GitHub budget from 60 to 5,000 requests an hour.The page half
/leaderboard/still opens on the model-card adoption view onmain. The engine's payload is populated by this PR, and the view that reads it, opening the page on Latest releases · 30d, is the latest-releases view PR. The two are independent and merge in either order: with this one first the page gains real numbers the moment the view lands; with the view first it shows its empty state until this collector runs.How the collector decides, and what each rule prevents
is_exact_attention_source_urlis the single rule, shared with the validator.unavailable, never zero. The ranking treats null as "no signal"; a zero would rank a deleted repository below a live one with a single star. HTTP 404, 410 and 451 are the only codes read as facts about the artifact.stale. A rate limit or outage is a fact about the request, not the benchmark._resolve_attention_metricreports whatever the newest observation says, so writing nothing would leave yesterday's reading reported as fresh; instead the previous snapshot's reading is written again withstatus: staleandlast_successful_dateset to the day it was actually read, and a chain of failures keeps that date rather than refreshing it. With nothing to carry forward, nothing is written: a null reading would admit the artifact to the cohort with no signal to rank on. No exception escapes a single resource read (an HTML error page where JSON was expected counts as a failed request), so one bad response cannot abort the hundreds after it.okwhile anything succeeded. The engine reads a failed health event at the same instant as fresh observations as a reason to demote all of them to stale, so one throttled repository must not flip the whole source; and a paper-page outage must not stale dataset downloads.max_consecutive_failures(5) times in a row is not asked again that run, because a rate limit answers every request alike and waiting out the timeout for each of a thousand resources would outlive the daily workflow. The health row says so:github paused after 5 consecutive failures; 312 resources not read, 298 carried forward as stale.releasedobservation, so a re-announcement cannot move a benchmark back into a window it has left, and the observation'scanonical_artifact_idis the idfilter_release_cohortgroups on. Resources are gathered from every occurrence, because the repository of a paper released without one often arrives later on the repository's ownupdatedrecord; only the date is restricted toreleasedrecords. A paper and the repository it names are one artifact and cost one request.Wiring
src/benchmark_radar/benchmark_attention.py:select_targets,collect_benchmark_attention,merge_benchmark_attention.pipeline.run_pipeline: new keywordsnapshots(the committed history), because the counters of a three-week-old release keep moving and the collector must see releases older than today's 48-hour lookback; the previous snapshot's block goes in asprevious_blockfor the stale carry-forward; the collector's health rows join the attention health list.models.RadarRun.benchmark_attention;snapshots.snapshot_for_runwrites the block only when the collector ran, so an absent block means "not collected" rather than "nothing had attention";merge_snapshotsunions same-day passes under the issue Second daily run overwrites the first, dropping items from the permanent corpus #104 rule (a pass that ran out of budget must not erase the first pass's readings, and a reading the later pass only carried forward as stale never replaces one the earlier pass actually made that day).cli.py: passes the history in and mirrors the block intoout/items.json;briefing.daily_report_runcarries the merged day's block.config.yml:benchmark_attentionblock (window_days, per-sourcemax_requestsandrequest_delay_seconds,max_consecutive_failures,attempts,timeout_seconds), with the rules above documented in place.Tests
tests/test_benchmark_attention.py, fourteen tests, each named for the failure it protects against: identity grouping and exact-resource selection (subdirectory URLs, out-of-window releases and never-released artifacts excluded, a repository learned from anupdatedrecord dated by the paper's release); a block the snapshot validator accepts with all three signals as whole numbers; a gone resource asunavailable; a transient failure carrying yesterday's reading forward asstalewith the original date, across a chain of days, and writing nothing when there is nothing to carry; the leaderboard reporting that carried reading as stale end to end; a source paused after consecutive failures while the other source is unaffected, and a streak reset by a success; every request failing marking the signal unhealthy; a malformed payload and a non-JSON response as failures that do not abort later reads; budget spent newest-first with the unread tail carried forward; the disabled collector; same-day merge union with the stale-precedence rule; the pipeline passing the history and the previous block through and reporting a merged day's block; and end-to-end,build_latest_releases_leaderboardranking a release from the collected block that is invisible without it (the #589 failure).Eleven behaviours were checked by reverting each one and watching its test go red: the carry-forward, the broad exception catch, the pause and its streak reset, resources from
updatedrecords, the release date fromreleasedrecords only, the merge precedence and health rules, the carried date, the pipeline passing the previous block, and the integer coercion.Verification
Clean-worktree run of the CI sequence (
git worktree add --detach, submodule initialized):ruff check .,ruff format --check .,normalize-catalog,classifyandbuild-data-releasepass;pytest -qreports 1343 passed, 1 failed.The one failure is
tests/test_briefing.py::test_zh_output_budget_covers_worst_case, which downloads tiktoken'so200k_basetable fromopenaipublic.blob.core.windows.net, a host the build sandbox's egress policy blocks. It fails identically on an untouchedmaincheckout in the same sandbox and passes on GitHub's runner.The live GitHub and Hugging Face endpoints were not reachable from the sandbox, so the response fields (
stargazers_count,upvotes,downloads) follow the documented payloads and the connectors insources.pythat already read them. The first daily run after merge is the first live exercise; the three new rows in source health will show any surprise.Not in this PR
The
/leaderboard/default view (the latest-releases view PR, see above). Paper upvotes are read only where an artifact already carries a Hugging Face paper URL (30 of the 1,283 targets in the 90-day cohort); deriving a paper page from every arXiv id would cost a request per paper for mostly 404s, and is a separate decision. Fallback counters from connectormetricskeep their existingunknownstatus.🤖 Generated with Claude Code
https://claude.ai/code/session_01UaUf4LgR9HogJ1DhXwjESy