v0.3.0: scoring assembly, API serving, Docker, ownership data, factuality feasibility
- orient: party-mean speech embeddings -> axis sign + reliability flag (ADR-0003) - score/run: deterministic ratings.json + stories artifact; report: evidence pages - serve: stdlib read-only API (/health /outlets /ratings /stories /manifests /evidence), CORS, path-traversal-safe - Docker: CPU-torch image (1.23GB) w/ baked model for offline recompute; compose; CI builds every push, publishes GHCR from main - outlets.yaml: sourced ownership data per outlet (verified flags); outlets doc regenerated w/ ownership columns - docs/feasibility-factuality.md: anchor analysis, F1/F2/F3 architecture, gate - fix: torch as direct dep pinned to CPU index (image was pulling CUDA, 6.7GB)
This commit is contained in:
@@ -0,0 +1,12 @@
|
||||
.git
|
||||
.venv
|
||||
data
|
||||
releases
|
||||
__pycache__
|
||||
*.pyc
|
||||
.pytest_cache
|
||||
.ruff_cache
|
||||
.github
|
||||
docs
|
||||
tests
|
||||
scripts
|
||||
@@ -16,6 +16,32 @@ jobs:
|
||||
- name: tests (includes docs gates - readability, glossary, decision-block lint)
|
||||
run: uv run pytest -q
|
||||
|
||||
docker:
|
||||
# image must always build; publish to GHCR only from main
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: docker/setup-buildx-action@v3
|
||||
- name: log in to GHCR
|
||||
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: build (and push from main)
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: .
|
||||
push: ${{ github.ref == 'refs/heads/main' && github.event_name == 'push' }}
|
||||
tags: |
|
||||
ghcr.io/sudolulo/tiltmeter:latest
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
|
||||
docs-sync:
|
||||
# Code and config may not change without a docs change in the same range.
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
@@ -8,6 +8,45 @@ requires a version bump and, if it changes methodology, a decision record in
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [0.3.0] - 2026-07-10
|
||||
|
||||
### Added
|
||||
|
||||
- Axis orientation (`orient.py`): party-mean speech embeddings decide the
|
||||
axis sign (negative = left, positive = right); agreement strength is
|
||||
published and weak orientation is flagged `reliable: false` (ADR-0003).
|
||||
- Scoring assembly (`tiltmeter run`): manifest → deterministic ratings.json
|
||||
(schema_version 1, no timestamps — reruns are byte-identical) + per-outlet
|
||||
evidence pages with the axis-distinguishing stories, real headlines,
|
||||
covered/skipped marks, neighbors, and CIs.
|
||||
- Read-only HTTP API (`tiltmeter serve`, stdlib, CORS enabled): /health,
|
||||
/ratings, /ratings/latest, /ratings/{id}, /manifests/{id},
|
||||
/evidence/{id}/… — tiltmeter is a data layer; visualization is a separate
|
||||
consumer's job (ADR-0003).
|
||||
- Docker: reproducible image with the pinned embedding model baked in
|
||||
(offline recomputation), compose file serving the API on :8477; CI builds
|
||||
the image on every push and publishes ghcr.io/sudolulo/tiltmeter from main.
|
||||
- First dry run on a one-day snapshot: pipeline exercised end to end; output
|
||||
correctly self-flagged orientation as unreliable (ρ = +0.18, 63 stories).
|
||||
- Outlet ownership data in `config/outlets.yaml`: owner, structure type, and
|
||||
control notes per outlet, each entry carrying source URL, retrieval date,
|
||||
and verified flag (same integrity rules as reference ratings). Served at
|
||||
`/outlets`; shown in the generated outlets table. Context only — never a
|
||||
scoring input.
|
||||
- Story-cluster artifact (`stories-{snapshot}.json`) and `/stories/{id}`
|
||||
endpoint: the side-by-side coverage primitive for consumer apps — who
|
||||
covered each story and how each outlet headlined it.
|
||||
- Factuality feasibility study (`docs/feasibility-factuality.md`): the anchor
|
||||
problem, five candidate anchors evaluated, proposed F1/F2/F3 architecture
|
||||
(process reliability / corroboration / resolvable claims), naming rule, and
|
||||
validation gate. Planning only; no factuality signal is implemented.
|
||||
|
||||
### Fixed
|
||||
|
||||
- Docker image pulled CUDA torch via the transitive dependency (6.7GB image);
|
||||
torch is now a direct dependency pinned to the CPU wheel index — image is
|
||||
1.23GB and runs anywhere.
|
||||
|
||||
## [0.2.0] - 2026-07-10
|
||||
|
||||
### Added
|
||||
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
# tiltmeter: the whole pipeline in one reproducible image.
|
||||
# The pinned embedding model is baked in at build time, so a container can
|
||||
# recompute any release fully offline — part of the reproducibility story,
|
||||
# not just packaging convenience.
|
||||
#
|
||||
# Default command serves the read-only API over /app/releases; every other
|
||||
# pipeline stage is available as a one-shot command, e.g.:
|
||||
# docker compose run --rm tiltmeter ingest
|
||||
# docker compose run --rm tiltmeter run --manifest releases/manifest-<id>.json
|
||||
|
||||
FROM python:3.13-slim
|
||||
|
||||
COPY --from=ghcr.io/astral-sh/uv:0.7 /uv /uvx /bin/
|
||||
|
||||
WORKDIR /app
|
||||
ENV UV_LINK_MODE=copy UV_COMPILE_BYTECODE=1
|
||||
|
||||
# dependency layer first: rebuilds only when the lockfile changes
|
||||
COPY pyproject.toml uv.lock README.md ./
|
||||
RUN --mount=type=cache,target=/root/.cache/uv uv sync --frozen --no-dev --no-install-project
|
||||
|
||||
COPY src ./src
|
||||
COPY config ./config
|
||||
RUN --mount=type=cache,target=/root/.cache/uv uv sync --frozen --no-dev
|
||||
|
||||
# bake the pinned model so runs need no network and no HF account
|
||||
RUN /app/.venv/bin/python -c "from tiltmeter import embed; embed._load_model()"
|
||||
|
||||
ENV PATH="/app/.venv/bin:$PATH"
|
||||
VOLUME ["/app/data", "/app/releases"]
|
||||
EXPOSE 8477
|
||||
|
||||
ENTRYPOINT ["tiltmeter"]
|
||||
CMD ["serve", "--releases", "releases"]
|
||||
@@ -40,8 +40,26 @@ uv run tiltmeter snapshot --start 2026-07-10 --end 2026-07-24
|
||||
|
||||
# fetch the orientation anchor: congressional floor speeches + party records
|
||||
uv run tiltmeter reference --end 2026-07-09 --days 10
|
||||
|
||||
# compute ratings + evidence pages from a manifest
|
||||
uv run tiltmeter run --manifest releases/manifest-2026-07-10_2026-07-24.json
|
||||
|
||||
# serve the computed releases as a read-only JSON API on :8477
|
||||
uv run tiltmeter serve
|
||||
```
|
||||
|
||||
Or with Docker (the pinned embedding model is baked into the image, so
|
||||
recomputation works offline):
|
||||
|
||||
```sh
|
||||
docker compose up -d # API on :8477
|
||||
docker compose run --rm tiltmeter ingest # any pipeline command as one-shot
|
||||
```
|
||||
|
||||
tiltmeter is a **data layer**: it computes and serves ratings JSON plus evidence
|
||||
pages. Visualization is deliberately someone else's job — point your news reader
|
||||
or dashboard at the API.
|
||||
|
||||
## The auditability contract
|
||||
|
||||
- **Reproducible**: same snapshot + same version ⇒ byte-identical ratings. Anyone
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
# API serving + one-shot pipeline commands.
|
||||
# docker compose up -d # serve ratings API :8477
|
||||
# docker compose run --rm tiltmeter ingest # poll feeds once
|
||||
# docker compose run --rm tiltmeter snapshot --start ... --end ...
|
||||
# docker compose run --rm tiltmeter run --manifest releases/manifest-<id>.json
|
||||
services:
|
||||
tiltmeter:
|
||||
build: .
|
||||
image: tiltmeter:latest
|
||||
ports:
|
||||
- "8477:8477"
|
||||
volumes:
|
||||
- ./data:/app/data
|
||||
- ./releases:/app/releases
|
||||
restart: unless-stopped
|
||||
+144
-1
@@ -8,69 +8,212 @@
|
||||
# - feed must actually work; broken outlets get swapped for a
|
||||
# same-region-of-spectrum alternative, recorded in CHANGELOG.md
|
||||
#
|
||||
# Fields: name (canonical id), feed (RSS/Atom URL), homepage.
|
||||
# Ownership is factual context served alongside ratings (never a scoring
|
||||
# input). Integrity rules match reference_ratings.yaml: every entry carries
|
||||
# source + retrieval date; verified: false = transcribed from secondary
|
||||
# knowledge, confirm at source before relying on it downstream.
|
||||
#
|
||||
# Fields: name (canonical id), feed (RSS/Atom URL), homepage, ownership.
|
||||
|
||||
outlets:
|
||||
- name: cs-monitor
|
||||
homepage: https://www.csmonitor.com
|
||||
feed: https://rss.csmonitor.com/feeds/politics
|
||||
ownership:
|
||||
owner: The Christian Science Publishing Society (First Church of Christ, Scientist)
|
||||
type: nonprofit
|
||||
source: https://en.wikipedia.org/wiki/The_Christian_Science_Monitor
|
||||
retrieved: 2026-07-10
|
||||
verified: false
|
||||
- name: pbs-newshour
|
||||
homepage: https://www.pbs.org/newshour
|
||||
feed: https://www.pbs.org/newshour/feeds/rss/politics
|
||||
ownership:
|
||||
owner: NewsHour Productions LLC (WETA, PBS member station)
|
||||
type: nonprofit
|
||||
source: https://en.wikipedia.org/wiki/PBS_News_Hour
|
||||
retrieved: 2026-07-10
|
||||
verified: false
|
||||
- name: npr
|
||||
homepage: https://www.npr.org
|
||||
feed: https://feeds.npr.org/1014/rss.xml
|
||||
ownership:
|
||||
owner: National Public Radio, Inc.
|
||||
type: nonprofit
|
||||
source: https://en.wikipedia.org/wiki/NPR
|
||||
retrieved: 2026-07-10
|
||||
verified: false
|
||||
- name: axios
|
||||
homepage: https://www.axios.com
|
||||
feed: https://api.axios.com/feed/
|
||||
ownership:
|
||||
owner: Cox Enterprises
|
||||
type: private-company
|
||||
note: family-held conglomerate; acquired Axios Media 2022
|
||||
source: https://en.wikipedia.org/wiki/Axios_(website)
|
||||
retrieved: 2026-07-10
|
||||
verified: false
|
||||
- name: the-hill
|
||||
homepage: https://thehill.com
|
||||
feed: https://thehill.com/homenews/feed/
|
||||
ownership:
|
||||
owner: Nexstar Media Group
|
||||
type: public-company
|
||||
note: acquired 2021; NASDAQ NXST
|
||||
source: https://en.wikipedia.org/wiki/The_Hill_(newspaper)
|
||||
retrieved: 2026-07-10
|
||||
verified: false
|
||||
- name: nytimes
|
||||
homepage: https://www.nytimes.com
|
||||
feed: https://rss.nytimes.com/services/xml/rss/nyt/Politics.xml
|
||||
ownership:
|
||||
owner: The New York Times Company
|
||||
type: public-company
|
||||
note: NYSE NYT; Ochs-Sulzberger family voting control via Class B shares
|
||||
source: https://en.wikipedia.org/wiki/The_New_York_Times_Company
|
||||
retrieved: 2026-07-10
|
||||
verified: false
|
||||
- name: washington-post
|
||||
homepage: https://www.washingtonpost.com
|
||||
feed: https://feeds.washingtonpost.com/rss/politics
|
||||
ownership:
|
||||
owner: Nash Holdings (Jeff Bezos)
|
||||
type: private-company
|
||||
source: https://en.wikipedia.org/wiki/The_Washington_Post
|
||||
retrieved: 2026-07-10
|
||||
verified: false
|
||||
- name: cnn
|
||||
homepage: https://www.cnn.com
|
||||
feed: http://rss.cnn.com/rss/cnn_allpolitics.rss
|
||||
ownership:
|
||||
owner: Warner Bros. Discovery
|
||||
type: public-company
|
||||
note: in flux — WBD sale to Paramount Skydance DOJ-approved 2026-06-12;
|
||||
CNN slated for the Discovery Global linear-networks entity; re-verify
|
||||
on completion
|
||||
source: https://www.pbs.org/newshour/nation/cnn-and-fellow-cable-networks-are-in-limbo-amid-takeover-bids-of-warner-bros-discovery
|
||||
retrieved: 2026-07-10
|
||||
verified: true
|
||||
- name: nbc-news
|
||||
homepage: https://www.nbcnews.com
|
||||
feed: https://feeds.nbcnews.com/nbcnews/public/politics
|
||||
ownership:
|
||||
owner: NBCUniversal (Comcast)
|
||||
type: public-company
|
||||
note: NBC News remained with Comcast after the 2025 Versant cable spinoff
|
||||
(which took MSNBC/CNBC); confirm current structure
|
||||
source: https://en.wikipedia.org/wiki/NBC_News
|
||||
retrieved: 2026-07-10
|
||||
verified: false
|
||||
- name: huffpost
|
||||
homepage: https://www.huffpost.com
|
||||
feed: https://www.huffpost.com/section/politics/feed
|
||||
ownership:
|
||||
owner: BuzzFeed, Inc.
|
||||
type: public-company
|
||||
note: acquired 2021
|
||||
source: https://en.wikipedia.org/wiki/HuffPost
|
||||
retrieved: 2026-07-10
|
||||
verified: false
|
||||
- name: mother-jones
|
||||
homepage: https://www.motherjones.com
|
||||
feed: https://www.motherjones.com/politics/feed/
|
||||
ownership:
|
||||
owner: Center for Investigative Reporting
|
||||
type: nonprofit
|
||||
note: Foundation for National Progress merged into CIR 2024-02-01
|
||||
source: https://pressgazette.co.uk/the-wire/media-mergers-news-tracker/mother-jones-merges-with-center-for-investigative-reporting/
|
||||
retrieved: 2026-07-10
|
||||
verified: true
|
||||
- name: the-nation
|
||||
homepage: https://www.thenation.com
|
||||
feed: https://www.thenation.com/feed/?post_type=article
|
||||
ownership:
|
||||
owner: The Nation Company, L.P.
|
||||
type: private-company
|
||||
source: https://en.wikipedia.org/wiki/The_Nation
|
||||
retrieved: 2026-07-10
|
||||
verified: false
|
||||
# WSJ's politics feed is dead; the working feed is general news (not the
|
||||
# Opinion feed — news and opinion are rated separately by every incumbent
|
||||
# rater, and we collect news). Topic-mix tradeoff noted in METHODOLOGY D9.
|
||||
- name: wsj
|
||||
homepage: https://www.wsj.com
|
||||
feed: https://feeds.a.dj.com/rss/RSSWorldNews.xml
|
||||
ownership:
|
||||
owner: Dow Jones & Company (News Corp)
|
||||
type: public-company
|
||||
note: NASDAQ NWSA; Murdoch family trust voting control
|
||||
source: https://en.wikipedia.org/wiki/The_Wall_Street_Journal
|
||||
retrieved: 2026-07-10
|
||||
verified: false
|
||||
- name: national-review
|
||||
homepage: https://www.nationalreview.com
|
||||
feed: https://www.nationalreview.com/feed/
|
||||
ownership:
|
||||
owner: National Review Institute
|
||||
type: nonprofit
|
||||
note: wholly-owned subsidiary of the 501(c)(3) NRI since 2015
|
||||
source: https://ballotpedia.org/National_Review
|
||||
retrieved: 2026-07-10
|
||||
verified: true
|
||||
- name: washington-examiner
|
||||
homepage: https://www.washingtonexaminer.com
|
||||
feed: https://www.washingtonexaminer.com/feed
|
||||
ownership:
|
||||
owner: Clarity Media Group (Anschutz Corporation)
|
||||
type: private-company
|
||||
source: https://en.wikipedia.org/wiki/Washington_Examiner
|
||||
retrieved: 2026-07-10
|
||||
verified: false
|
||||
- name: ny-post
|
||||
homepage: https://nypost.com
|
||||
feed: https://nypost.com/politics/feed/
|
||||
ownership:
|
||||
owner: NYP Holdings (News Corp)
|
||||
type: public-company
|
||||
note: Murdoch family trust voting control
|
||||
source: https://en.wikipedia.org/wiki/New_York_Post
|
||||
retrieved: 2026-07-10
|
||||
verified: false
|
||||
- name: fox-news
|
||||
homepage: https://www.foxnews.com
|
||||
feed: https://moxie.foxnews.com/google-publisher/politics.xml
|
||||
ownership:
|
||||
owner: Fox News Media (Fox Corporation)
|
||||
type: public-company
|
||||
note: NASDAQ FOXA; Murdoch family trust voting control
|
||||
source: https://en.wikipedia.org/wiki/Fox_News
|
||||
retrieved: 2026-07-10
|
||||
verified: false
|
||||
- name: daily-wire
|
||||
homepage: https://www.dailywire.com
|
||||
feed: https://www.dailywire.com/feeds/rss.xml
|
||||
ownership:
|
||||
owner: The Daily Wire, LLC
|
||||
type: private-company
|
||||
note: co-founders Ben Shapiro and Jeremy Boreing; principal early funder
|
||||
Farris Wilks
|
||||
source: https://en.wikipedia.org/wiki/The_Daily_Wire
|
||||
retrieved: 2026-07-10
|
||||
verified: false
|
||||
- name: breitbart
|
||||
homepage: https://www.breitbart.com
|
||||
feed: https://feeds.feedburner.com/breitbart
|
||||
ownership:
|
||||
owner: Breitbart News Network, LLC
|
||||
type: private-company
|
||||
note: Mercer family reported major stakeholders; CEO Larry Solov
|
||||
source: https://en.wikipedia.org/wiki/Breitbart_News
|
||||
retrieved: 2026-07-10
|
||||
verified: false
|
||||
- name: newsmax
|
||||
homepage: https://www.newsmax.com
|
||||
feed: https://www.newsmax.com/rss/Politics/1/
|
||||
ownership:
|
||||
owner: Newsmax, Inc.
|
||||
type: public-company
|
||||
note: NYSE NMAX since 2025; founder Christopher Ruddy controlling
|
||||
source: https://finance.yahoo.com/quote/NMAX/
|
||||
retrieved: 2026-07-10
|
||||
verified: true
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
# ADR-0003: Orientation estimator; tiltmeter stays a data layer
|
||||
|
||||
- **Status**: accepted
|
||||
- **Date**: 2026-07-10
|
||||
- **Supersedes**: none (implements METHODOLOGY D5; scopes D10's delivery)
|
||||
|
||||
## Decision
|
||||
|
||||
1. **Orientation estimator**: each party's floor speeches (first 200 words each)
|
||||
are embedded with the same pinned model as articles and averaged into a D-mean
|
||||
and an R-mean vector. Each outlet's mean article embedding yields a proxy:
|
||||
cosine-to-R minus cosine-to-D. The Spearman correlation between the unoriented
|
||||
selection axis and this proxy decides the sign (negative correlation ⇒ flip),
|
||||
with the convention **negative = left, positive = right**. If |ρ| < 0.3 the
|
||||
orientation is marked `reliable: false` in ratings.json and the CLI prints
|
||||
"UNRELIABLE — do not interpret".
|
||||
2. **Product shape**: tiltmeter is a data layer — a pipeline plus a read-only HTTP
|
||||
API (`tiltmeter serve`, stdlib only, CORS `*`) over the releases directory.
|
||||
Visualization belongs to separate consumer software. tiltmeter never grows a UI;
|
||||
outputs stay machine-consumable with versioned JSON schemas
|
||||
(`schema_version` in ratings.json).
|
||||
|
||||
## Rationale (sources)
|
||||
|
||||
- Embedding-similarity to party language is the modern, cheap analog of
|
||||
Gentzkow & Shapiro (2010) phrase-frequency slant; used here only for a **single
|
||||
sign bit + a diagnostic**, not for scoring, which keeps the heavy lifting in the
|
||||
transparent coverage signal (ADR-0001).
|
||||
- Publishing the axis↔party-language correlation makes orientation failure visible
|
||||
instead of silent — on day-one dry-run data it correctly reported ρ = +0.18,
|
||||
UNRELIABLE (D6's honesty requirement working as intended).
|
||||
- Serving files the pipeline already wrote means the API adds zero audit surface:
|
||||
what you GET is byte-identical to what the pipeline produced and hash-pinned.
|
||||
|
||||
## Alternatives considered
|
||||
|
||||
- Full Gentzkow–Shapiro phrase-based slant as the orientation source — planned as
|
||||
signal S2 (v0.4+); requires the Gentzkow, Shapiro & Taddy (2019) estimator.
|
||||
- Orienting via declared anchor outlets — rejected in ADR-0001.
|
||||
- A web framework (FastAPI/Flask) for serving — more ergonomic, but a dependency
|
||||
and an attack surface for what is, by design, static-file delivery. Revisit only
|
||||
if the API grows beyond read-only.
|
||||
|
||||
## Failure modes / risks accepted
|
||||
|
||||
- Party-mean embeddings compress each party to one point; intra-party variation is
|
||||
ignored. Acceptable for one sign bit; unacceptable for scoring — which is why it
|
||||
doesn't score.
|
||||
- With thin reference or news corpora the proxy correlation will hover near zero
|
||||
and orientation will stay flagged unreliable; ratings remain publishable only as
|
||||
explicitly-unreliable dry runs.
|
||||
- An unoriented-but-strong axis with a weak proxy correlation likely means the
|
||||
axis captured something other than politics (topic mix); the D7 gate would catch
|
||||
this as low ρ against reference ratings.
|
||||
@@ -0,0 +1,134 @@
|
||||
# Feasibility: reproducible factuality measurement
|
||||
|
||||
Status: **planning document** — no factuality signal is implemented. This is the
|
||||
feasibility analysis for whether tiltmeter can ever rate reliability the way it
|
||||
rates lean: reproducibly, with no panel and no judge. Sources surveyed 2026-07-10.
|
||||
|
||||
## 1. The anchor problem
|
||||
|
||||
The lean pipeline works because ideology is **relational** and has a behavioral
|
||||
public record: politicians reveal their positions by voting, DW-NOMINATE turns
|
||||
roll calls into coordinates, and the Congressional Record ties party to language.
|
||||
The anchor is (a) a public record, (b) behavior rather than anyone's rating,
|
||||
(c) attributable to known actors, and (d) enormous.
|
||||
|
||||
Factuality is different in kind: it is **correspondence to world-states**, not
|
||||
position among positions. There is no roll call for truth. Any feasible design
|
||||
must either find data with the four anchor properties above, or honestly rename
|
||||
what it measures. This document evaluates every candidate anchor we could find.
|
||||
|
||||
## 2. Candidate anchors, evaluated
|
||||
|
||||
| Anchor | Public record? | Judgment-free? | Outlet-attributable? | Coverage | Verdict |
|
||||
|---|---|---|---|---|---|
|
||||
| A. Court-adjudicated falsehoods (defamation judgments) | yes | yes (adjudicated) | yes | ~zero per outlet-year | footnote, not a signal |
|
||||
| B. Resolvable claims vs. official statistics & event outcomes | yes | comparison is mechanical | yes | narrow slice of articles | **the structural twin — research-grade effort** |
|
||||
| C. Revision/correction behavior (outlet's own edit history) | self-generated, capturable | yes | yes | every article | **cheapest honest signal — measures process, not truth** |
|
||||
| D. Cross-outlet corroboration structure (our own clusters) | our corpus | mostly (alignment via embeddings) | yes | every story | **free by-product — measures isolation, not falsity** |
|
||||
| E. Fact-checker verdicts (ClaimReview corpus) | published | no — human judgments | weakly (checks target claims/politicians) | sparse per outlet | validation target only, like AllSides for lean |
|
||||
|
||||
### B. Resolvable claims — the Congress-method equivalent
|
||||
|
||||
Some published claims resolve against public records with no judgment involved:
|
||||
economic figures (BLS/BEA/FRED releases), election results (state returns, FEC),
|
||||
census numbers, court outcomes (PACER/CourtListener), weather/disaster tolls
|
||||
(official counts). "The unemployment rate fell to 3.9%" is checkable the way a
|
||||
roll-call vote is checkable. Prior art exists and is active: the QuanTemp
|
||||
benchmark for numerical claim verification (arXiv 2403.17169), Full Fact's
|
||||
prototype Stats Checker verifying claims against official statistics, and the
|
||||
CLEF CheckThat! lab series.
|
||||
|
||||
The costs, honestly: (1) **claim extraction requires an NLP model**, which
|
||||
collides with METHODOLOGY D8 — resolvable only via a documented carve-out where
|
||||
models may *extract and align* claims but never *evaluate* them, the mechanical
|
||||
record-comparison doing all evaluation, with published human-audited extraction
|
||||
samples; (2) only a minority of articles make resolvable claims, so per-outlet
|
||||
sample sizes build slowly; (3) connectors to each statistical source must be
|
||||
built and pinned. This is a research-grade pipeline — the strongest possible
|
||||
anchor, and the most expensive.
|
||||
|
||||
### C. Revision behavior — cheap, behavioral, already half-built
|
||||
|
||||
An outlet's own edit history is a behavioral public record it generates about
|
||||
itself. Track article revisions (we already poll every 6h; a revision mode
|
||||
re-fetches each article on a decaying schedule for ~72h and diffs by content
|
||||
hash) and measure: substantive-edit rate, **stealth-edit rate** (substantive
|
||||
changes without a correction notice), correction latency, and correction-notice
|
||||
practice. Strong prior art: NewsDiffs (2012–), the NewsEdits dataset (1.2M+
|
||||
revision histories, NAACL 2022), DiffEngine/NewsSniffer.
|
||||
|
||||
The honest limit: this measures **process transparency, not accuracy**. Many
|
||||
corrections can mean error-prone *or* conscientious. The literature (and every
|
||||
incumbent's rubric) treats correction practice as a credibility criterion, but
|
||||
it must be published under its real name: process behavior.
|
||||
|
||||
### D. Corroboration structure — free by-product of the lean pipeline
|
||||
|
||||
We already cluster same-event articles across 20 outlets. Within a cluster,
|
||||
an outlet's *distinctive* claims (present in its version, absent from all
|
||||
others) are measurable via embedding alignment — no truth judgment. Track the
|
||||
**time-lagged corroboration rate**: distinctive claims that other outlets later
|
||||
confirm (scoops) vs. those that never get picked up. Persistent epistemic
|
||||
isolation is a signal; it is not falsity, and scoop-heavy outlets need the time
|
||||
lag to avoid punishment for being first. Also measurable per cluster:
|
||||
**wire fidelity** — drift between an outlet's rendering and the wire original
|
||||
it credits.
|
||||
|
||||
### E. Fact-checker cross-reference — validation, not signal
|
||||
|
||||
The ClaimReview corpus (Google Fact Check Tools API, Data Commons feed;
|
||||
FactCheck.org, PolitiFact, WaPo judgments) is open and machine-readable, but it
|
||||
is human judgment with severe selection bias: checkers check what went viral,
|
||||
which correlates with audience size and topic, not with outlet accuracy.
|
||||
Feeding it into scoring would import the panel we exist to remove — the same
|
||||
circularity rule as lean (D7): **incumbent factuality ratings (MBFC factual
|
||||
reporting, Ad Fontes reliability) and ClaimReview hit-rates are validation
|
||||
targets only.**
|
||||
|
||||
## 3. Proposed architecture (v0.5+, strictly gated)
|
||||
|
||||
- **F1 — process reliability** (from C): revision tracking, stealth-edit rate,
|
||||
correction latency/notice practice. Ships first; infrastructure is a small
|
||||
delta on the collector.
|
||||
- **F2 — corroboration structure** (from D): unilateral-claim rate with time
|
||||
lag, wire fidelity. Ships from existing cluster data.
|
||||
- **F3 — resolvable-claim accuracy** (from B): pilot on one domain first
|
||||
(economic statistics — cleanest official sources), expand only if the pilot
|
||||
survives audit. Requires the D8 extraction carve-out as a new ADR before any
|
||||
code.
|
||||
- **Naming rule**: the published axis is called **process reliability**, never
|
||||
"factuality" or "truth", until/unless F3 matures enough to carry an accuracy
|
||||
component. A score is a claim; the name is part of the claim.
|
||||
- **Validation gate** (mirror of M3): composite F-signal must rank-correlate
|
||||
ρ ≥ 0.6 with MBFC factual-reporting and Ad Fontes reliability ratings over
|
||||
the outlet sample. Below gate ⇒ iterate or kill, published either way. The
|
||||
bar is lower than lean's 0.7 because factuality prediction is documented in
|
||||
the literature as the harder task, and the incumbent ratings we validate
|
||||
against are themselves noisier on this axis.
|
||||
|
||||
## 4. What this can never do
|
||||
|
||||
No reproducible system measures truth at scale; claiming otherwise would just
|
||||
hide the judgment somewhere. The incumbents' "factuality" grades are rubric
|
||||
judgments of process plus reputation. Ours would be: measured process behavior
|
||||
(F1), measured corroboration structure (F2), and — for one auditable slice of
|
||||
claims — mechanical comparison against official records (F3). Narrower than
|
||||
what a "truth score" implies, and honest about it. That honesty is the product.
|
||||
|
||||
## 5. Sequencing
|
||||
|
||||
Nothing here starts before the lean M3 gate reports. Then: F1 (revision
|
||||
tracking is cheap and its data, like the corpus, gains value with every day it
|
||||
runs — worth starting early), F2, F3 pilot. Each phase gets its own ADR with
|
||||
kill criteria before code.
|
||||
|
||||
## 6. Sources
|
||||
|
||||
- QuanTemp numerical-claim benchmark: <https://arxiv.org/abs/2403.17169>
|
||||
- Full Fact automated stats-checking: <https://fullfact.org/blog/2022/feb/claim-challenge-update/>
|
||||
- CLEF CheckThat! fact-checking labs: <https://ceur-ws.org/Vol-4038/paper_53.pdf>
|
||||
- NewsEdits revision dataset: <https://arxiv.org/abs/2206.07106>
|
||||
- NewsDiffs: <https://en.wikipedia.org/wiki/NewsDiffs>
|
||||
- Google Fact Check Tools API: <https://developers.google.com/fact-check/tools/api>
|
||||
- Data Commons fact-check corpus: <https://datacommons.org/factcheck/blog>
|
||||
- Baly et al., predicting factuality of sources (EMNLP 2018): <https://aclanthology.org/D18-1389/>
|
||||
+26
-22
@@ -10,27 +10,31 @@ swap policy for dead feeds are documented in
|
||||
[METHODOLOGY.md, decision D4](../METHODOLOGY.md#d4-twenty-outlets-deliberately-spread-chosen-once-and-openly);
|
||||
individual swaps are recorded in the [CHANGELOG](../CHANGELOG.md).
|
||||
|
||||
| Outlet | Homepage | Feed |
|
||||
|---|---|---|
|
||||
| axios | <https://www.axios.com> | <https://api.axios.com/feed/> |
|
||||
| breitbart | <https://www.breitbart.com> | <https://feeds.feedburner.com/breitbart> |
|
||||
| cnn | <https://www.cnn.com> | <http://rss.cnn.com/rss/cnn_allpolitics.rss> |
|
||||
| cs-monitor | <https://www.csmonitor.com> | <https://rss.csmonitor.com/feeds/politics> |
|
||||
| daily-wire | <https://www.dailywire.com> | <https://www.dailywire.com/feeds/rss.xml> |
|
||||
| fox-news | <https://www.foxnews.com> | <https://moxie.foxnews.com/google-publisher/politics.xml> |
|
||||
| huffpost | <https://www.huffpost.com> | <https://www.huffpost.com/section/politics/feed> |
|
||||
| mother-jones | <https://www.motherjones.com> | <https://www.motherjones.com/politics/feed/> |
|
||||
| national-review | <https://www.nationalreview.com> | <https://www.nationalreview.com/feed/> |
|
||||
| nbc-news | <https://www.nbcnews.com> | <https://feeds.nbcnews.com/nbcnews/public/politics> |
|
||||
| newsmax | <https://www.newsmax.com> | <https://www.newsmax.com/rss/Politics/1/> |
|
||||
| npr | <https://www.npr.org> | <https://feeds.npr.org/1014/rss.xml> |
|
||||
| ny-post | <https://nypost.com> | <https://nypost.com/politics/feed/> |
|
||||
| nytimes | <https://www.nytimes.com> | <https://rss.nytimes.com/services/xml/rss/nyt/Politics.xml> |
|
||||
| pbs-newshour | <https://www.pbs.org/newshour> | <https://www.pbs.org/newshour/feeds/rss/politics> |
|
||||
| the-hill | <https://thehill.com> | <https://thehill.com/homenews/feed/> |
|
||||
| the-nation | <https://www.thenation.com> | <https://www.thenation.com/feed/?post_type=article> |
|
||||
| washington-examiner | <https://www.washingtonexaminer.com> | <https://www.washingtonexaminer.com/feed> |
|
||||
| washington-post | <https://www.washingtonpost.com> | <https://feeds.washingtonpost.com/rss/politics> |
|
||||
| wsj | <https://www.wsj.com> | <https://feeds.a.dj.com/rss/RSSWorldNews.xml> |
|
||||
Ownership is sourced factual context (see the `ownership` blocks in the
|
||||
config for source URLs, retrieval dates, and verification status); it is
|
||||
never a scoring input.
|
||||
|
||||
| Outlet | Owner | Type | Feed |
|
||||
|---|---|---|---|
|
||||
| [axios](https://www.axios.com) | Cox Enterprises | private-company | <https://api.axios.com/feed/> |
|
||||
| [breitbart](https://www.breitbart.com) | Breitbart News Network, LLC | private-company | <https://feeds.feedburner.com/breitbart> |
|
||||
| [cnn](https://www.cnn.com) | Warner Bros. Discovery | public-company | <http://rss.cnn.com/rss/cnn_allpolitics.rss> |
|
||||
| [cs-monitor](https://www.csmonitor.com) | The Christian Science Publishing Society (First Church of Christ, Scientist) | nonprofit | <https://rss.csmonitor.com/feeds/politics> |
|
||||
| [daily-wire](https://www.dailywire.com) | The Daily Wire, LLC | private-company | <https://www.dailywire.com/feeds/rss.xml> |
|
||||
| [fox-news](https://www.foxnews.com) | Fox News Media (Fox Corporation) | public-company | <https://moxie.foxnews.com/google-publisher/politics.xml> |
|
||||
| [huffpost](https://www.huffpost.com) | BuzzFeed, Inc. | public-company | <https://www.huffpost.com/section/politics/feed> |
|
||||
| [mother-jones](https://www.motherjones.com) | Center for Investigative Reporting | nonprofit | <https://www.motherjones.com/politics/feed/> |
|
||||
| [national-review](https://www.nationalreview.com) | National Review Institute | nonprofit | <https://www.nationalreview.com/feed/> |
|
||||
| [nbc-news](https://www.nbcnews.com) | NBCUniversal (Comcast) | public-company | <https://feeds.nbcnews.com/nbcnews/public/politics> |
|
||||
| [newsmax](https://www.newsmax.com) | Newsmax, Inc. | public-company | <https://www.newsmax.com/rss/Politics/1/> |
|
||||
| [npr](https://www.npr.org) | National Public Radio, Inc. | nonprofit | <https://feeds.npr.org/1014/rss.xml> |
|
||||
| [ny-post](https://nypost.com) | NYP Holdings (News Corp) | public-company | <https://nypost.com/politics/feed/> |
|
||||
| [nytimes](https://www.nytimes.com) | The New York Times Company | public-company | <https://rss.nytimes.com/services/xml/rss/nyt/Politics.xml> |
|
||||
| [pbs-newshour](https://www.pbs.org/newshour) | NewsHour Productions LLC (WETA, PBS member station) | nonprofit | <https://www.pbs.org/newshour/feeds/rss/politics> |
|
||||
| [the-hill](https://thehill.com) | Nexstar Media Group | public-company | <https://thehill.com/homenews/feed/> |
|
||||
| [the-nation](https://www.thenation.com) | The Nation Company, L.P. | private-company | <https://www.thenation.com/feed/?post_type=article> |
|
||||
| [washington-examiner](https://www.washingtonexaminer.com) | Clarity Media Group (Anschutz Corporation) | private-company | <https://www.washingtonexaminer.com/feed> |
|
||||
| [washington-post](https://www.washingtonpost.com) | Nash Holdings (Jeff Bezos) | private-company | <https://feeds.washingtonpost.com/rss/politics> |
|
||||
| [wsj](https://www.wsj.com) | Dow Jones & Company (News Corp) | public-company | <https://feeds.a.dj.com/rss/RSSWorldNews.xml> |
|
||||
|
||||
Total: 20 outlets.
|
||||
|
||||
+6
-1
@@ -1,6 +1,6 @@
|
||||
[project]
|
||||
name = "tiltmeter"
|
||||
version = "0.2.0"
|
||||
version = "0.3.0"
|
||||
description = "Auditable, reproducible political-lean ratings for news outlets"
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.13"
|
||||
@@ -11,6 +11,11 @@ dependencies = [
|
||||
"trafilatura>=1.12",
|
||||
"pyyaml>=6.0",
|
||||
"sentence-transformers>=3.0",
|
||||
# torch is transitive via sentence-transformers, but must be a direct
|
||||
# dependency for the pytorch-cpu index pin below to apply (uv resolves
|
||||
# tool.uv.sources for direct dependencies only) — otherwise the lock
|
||||
# pulls CUDA torch and the Docker image balloons to ~7GB
|
||||
"torch>=2.6",
|
||||
"scikit-learn>=1.5",
|
||||
"numpy>=2.0",
|
||||
"requests>=2.32",
|
||||
|
||||
@@ -0,0 +1,157 @@
|
||||
{
|
||||
"axis_inertia_share": 0.119917,
|
||||
"corpus_hash": "626c32fd35709f7174672c22fa86b56227986e0d4560306bddd49b1a11b03b76",
|
||||
"n_articles": 583,
|
||||
"n_stories": 63,
|
||||
"orientation": {
|
||||
"correlation": 0.183459,
|
||||
"method": "party-mean speech embeddings (ADR-0003)",
|
||||
"reliable": false
|
||||
},
|
||||
"outlets": [
|
||||
{
|
||||
"ci_high": 1.0,
|
||||
"ci_low": -1.0,
|
||||
"outlet": "national-review",
|
||||
"score": -0.056998,
|
||||
"stories_covered": 4
|
||||
},
|
||||
{
|
||||
"ci_high": 0.370098,
|
||||
"ci_low": -0.531857,
|
||||
"outlet": "newsmax",
|
||||
"score": -0.052117,
|
||||
"stories_covered": 12
|
||||
},
|
||||
{
|
||||
"ci_high": 0.228278,
|
||||
"ci_low": -0.436618,
|
||||
"outlet": "ny-post",
|
||||
"score": -0.050542,
|
||||
"stories_covered": 6
|
||||
},
|
||||
{
|
||||
"ci_high": 0.228342,
|
||||
"ci_low": -0.495406,
|
||||
"outlet": "npr",
|
||||
"score": -0.049238,
|
||||
"stories_covered": 7
|
||||
},
|
||||
{
|
||||
"ci_high": 0.263456,
|
||||
"ci_low": -0.370786,
|
||||
"outlet": "the-hill",
|
||||
"score": -0.047202,
|
||||
"stories_covered": 12
|
||||
},
|
||||
{
|
||||
"ci_high": 0.218725,
|
||||
"ci_low": -0.516228,
|
||||
"outlet": "breitbart",
|
||||
"score": -0.044667,
|
||||
"stories_covered": 9
|
||||
},
|
||||
{
|
||||
"ci_high": 0.480408,
|
||||
"ci_low": -0.647752,
|
||||
"outlet": "the-nation",
|
||||
"score": -0.044493,
|
||||
"stories_covered": 11
|
||||
},
|
||||
{
|
||||
"ci_high": 0.141765,
|
||||
"ci_low": -0.240933,
|
||||
"outlet": "daily-wire",
|
||||
"score": -0.043512,
|
||||
"stories_covered": 14
|
||||
},
|
||||
{
|
||||
"ci_high": 0.785451,
|
||||
"ci_low": -0.898349,
|
||||
"outlet": "washington-post",
|
||||
"score": -0.036812,
|
||||
"stories_covered": 7
|
||||
},
|
||||
{
|
||||
"ci_high": 0.401493,
|
||||
"ci_low": -0.447497,
|
||||
"outlet": "pbs-newshour",
|
||||
"score": -0.035984,
|
||||
"stories_covered": 11
|
||||
},
|
||||
{
|
||||
"ci_high": 0.570331,
|
||||
"ci_low": -0.60672,
|
||||
"outlet": "fox-news",
|
||||
"score": -0.03245,
|
||||
"stories_covered": 9
|
||||
},
|
||||
{
|
||||
"ci_high": 0.813997,
|
||||
"ci_low": -0.869201,
|
||||
"outlet": "washington-examiner",
|
||||
"score": -0.030359,
|
||||
"stories_covered": 8
|
||||
},
|
||||
{
|
||||
"ci_high": 0.320726,
|
||||
"ci_low": -0.239156,
|
||||
"outlet": "huffpost",
|
||||
"score": -0.00761,
|
||||
"stories_covered": 25
|
||||
},
|
||||
{
|
||||
"ci_high": 0.0,
|
||||
"ci_low": 0.0,
|
||||
"outlet": "cnn",
|
||||
"score": -0.0,
|
||||
"stories_covered": 0
|
||||
},
|
||||
{
|
||||
"ci_high": 0.0,
|
||||
"ci_low": 0.0,
|
||||
"outlet": "wsj",
|
||||
"score": -0.0,
|
||||
"stories_covered": 0
|
||||
},
|
||||
{
|
||||
"ci_high": 0.557721,
|
||||
"ci_low": -0.434797,
|
||||
"outlet": "nytimes",
|
||||
"score": 0.01963,
|
||||
"stories_covered": 9
|
||||
},
|
||||
{
|
||||
"ci_high": 0.495591,
|
||||
"ci_low": -0.278963,
|
||||
"outlet": "nbc-news",
|
||||
"score": 0.019704,
|
||||
"stories_covered": 11
|
||||
},
|
||||
{
|
||||
"ci_high": 0.352731,
|
||||
"ci_low": -0.286694,
|
||||
"outlet": "axios",
|
||||
"score": 0.032479,
|
||||
"stories_covered": 23
|
||||
},
|
||||
{
|
||||
"ci_high": 1.0,
|
||||
"ci_low": -0.458178,
|
||||
"outlet": "mother-jones",
|
||||
"score": 0.451646,
|
||||
"stories_covered": 4
|
||||
},
|
||||
{
|
||||
"ci_high": 1.0,
|
||||
"ci_low": -0.0,
|
||||
"outlet": "cs-monitor",
|
||||
"score": 1.0,
|
||||
"stories_covered": 2
|
||||
}
|
||||
],
|
||||
"pipeline_version": "0.3.0",
|
||||
"reference_frame": "lean relative to contemporary US congressional party discourse; negative = left, positive = right",
|
||||
"schema_version": 1,
|
||||
"snapshot_id": "2026-07-10_2026-07-11"
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
# Evidence: axios
|
||||
|
||||
- **Score**: +0.032 (95% CI -0.287 … +0.353); negative = left, positive = right
|
||||
- **Snapshot**: 2026-07-10_2026-07-11 (corpus 626c32fd3570…), pipeline v0.3.0
|
||||
- **Coverage**: 23 of 63 cross-outlet stories
|
||||
- **Nearest neighbors**: nbc-news (+0.020), nytimes (+0.020), cnn (-0.000)
|
||||
|
||||
The stories below are the most axis-distinguishing in this snapshot —
|
||||
the ones outlets' choices disagree about most. What this outlet covered
|
||||
and skipped among them is what placed it where it is.
|
||||
|
||||
## Left-pole stories
|
||||
|
||||
- **skipped** — [national-review] Does the WNBA Not Like Caitlin Clark?
|
||||
- **skipped** — [the-nation] The Supreme Court Undermines Immigrant Rights
|
||||
- **skipped** — [npr] Campaign staffers keep trying to bet on races despite push to curb insider trading
|
||||
- **skipped** — [newsmax] RNC Midterm Convention Becomes Fundraiser
|
||||
- **skipped** — [npr] Sen. Jeanne Shaheen talks about Trump at NATO and the renewed strikes against Iran
|
||||
- **skipped** — [the-nation] Rahm Emanuel’s Speech in Tel Aviv Breaks With a “No Daylight” Approach
|
||||
- **skipped** — [daily-wire] Israel Warns U.S. Of New Iranian Plot To Assassinate Trump: Report
|
||||
- **skipped** — [fox-news] Jeb Bush praises Trump for crippling Iran’s military, but warns of ‘threat’ to US from reported drones in Cuba
|
||||
|
||||
## Right-pole stories
|
||||
|
||||
- **skipped** — [mother-jones] Trump Can’t Stop Talking About Communists
|
||||
- **covered** — [cs-monitor] The hands-off era of AI oversight is ending. What comes next?
|
||||
- **skipped** — [mother-jones] ICE Keeps Using The Same Justification For Killing Drivers
|
||||
- **skipped** — [nbc-news] U.S. strikes Iran in major escalation as fears grow of a return to full-scale conflict
|
||||
- **covered** — [nbc-news] Mallory McMorrow ends bid for Democratic Senate nomination in Michigan
|
||||
- **covered** — [axios] Ukraine proves it can hit Russia almost anywhere
|
||||
- **covered** — [huffpost] Kentucky Governor Asks Mitch McConnell To ‘Fully Update’ People On His Health
|
||||
- **covered** — [axios] UEFA says FIFA crossed a line on Balogun decision
|
||||
@@ -0,0 +1,32 @@
|
||||
# Evidence: breitbart
|
||||
|
||||
- **Score**: -0.045 (95% CI -0.516 … +0.219); negative = left, positive = right
|
||||
- **Snapshot**: 2026-07-10_2026-07-11 (corpus 626c32fd3570…), pipeline v0.3.0
|
||||
- **Coverage**: 9 of 63 cross-outlet stories
|
||||
- **Nearest neighbors**: the-nation (-0.044), daily-wire (-0.044), the-hill (-0.047)
|
||||
|
||||
The stories below are the most axis-distinguishing in this snapshot —
|
||||
the ones outlets' choices disagree about most. What this outlet covered
|
||||
and skipped among them is what placed it where it is.
|
||||
|
||||
## Left-pole stories
|
||||
|
||||
- **skipped** — [national-review] Does the WNBA Not Like Caitlin Clark?
|
||||
- **skipped** — [the-nation] The Supreme Court Undermines Immigrant Rights
|
||||
- **skipped** — [npr] Campaign staffers keep trying to bet on races despite push to curb insider trading
|
||||
- **covered** — [newsmax] RNC Midterm Convention Becomes Fundraiser
|
||||
- **covered** — [npr] Sen. Jeanne Shaheen talks about Trump at NATO and the renewed strikes against Iran
|
||||
- **skipped** — [the-nation] Rahm Emanuel’s Speech in Tel Aviv Breaks With a “No Daylight” Approach
|
||||
- **skipped** — [daily-wire] Israel Warns U.S. Of New Iranian Plot To Assassinate Trump: Report
|
||||
- **skipped** — [fox-news] Jeb Bush praises Trump for crippling Iran’s military, but warns of ‘threat’ to US from reported drones in Cuba
|
||||
|
||||
## Right-pole stories
|
||||
|
||||
- **skipped** — [mother-jones] Trump Can’t Stop Talking About Communists
|
||||
- **skipped** — [cs-monitor] The hands-off era of AI oversight is ending. What comes next?
|
||||
- **skipped** — [mother-jones] ICE Keeps Using The Same Justification For Killing Drivers
|
||||
- **skipped** — [nbc-news] U.S. strikes Iran in major escalation as fears grow of a return to full-scale conflict
|
||||
- **skipped** — [nbc-news] Mallory McMorrow ends bid for Democratic Senate nomination in Michigan
|
||||
- **skipped** — [axios] Ukraine proves it can hit Russia almost anywhere
|
||||
- **skipped** — [huffpost] Kentucky Governor Asks Mitch McConnell To ‘Fully Update’ People On His Health
|
||||
- **skipped** — [axios] UEFA says FIFA crossed a line on Balogun decision
|
||||
@@ -0,0 +1,32 @@
|
||||
# Evidence: cnn
|
||||
|
||||
- **Score**: -0.000 (95% CI +0.000 … +0.000); negative = left, positive = right
|
||||
- **Snapshot**: 2026-07-10_2026-07-11 (corpus 626c32fd3570…), pipeline v0.3.0
|
||||
- **Coverage**: 0 of 63 cross-outlet stories
|
||||
- **Nearest neighbors**: wsj (-0.000), huffpost (-0.008), nytimes (+0.020)
|
||||
|
||||
The stories below are the most axis-distinguishing in this snapshot —
|
||||
the ones outlets' choices disagree about most. What this outlet covered
|
||||
and skipped among them is what placed it where it is.
|
||||
|
||||
## Left-pole stories
|
||||
|
||||
- **skipped** — [national-review] Does the WNBA Not Like Caitlin Clark?
|
||||
- **skipped** — [the-nation] The Supreme Court Undermines Immigrant Rights
|
||||
- **skipped** — [npr] Campaign staffers keep trying to bet on races despite push to curb insider trading
|
||||
- **skipped** — [newsmax] RNC Midterm Convention Becomes Fundraiser
|
||||
- **skipped** — [npr] Sen. Jeanne Shaheen talks about Trump at NATO and the renewed strikes against Iran
|
||||
- **skipped** — [the-nation] Rahm Emanuel’s Speech in Tel Aviv Breaks With a “No Daylight” Approach
|
||||
- **skipped** — [daily-wire] Israel Warns U.S. Of New Iranian Plot To Assassinate Trump: Report
|
||||
- **skipped** — [fox-news] Jeb Bush praises Trump for crippling Iran’s military, but warns of ‘threat’ to US from reported drones in Cuba
|
||||
|
||||
## Right-pole stories
|
||||
|
||||
- **skipped** — [mother-jones] Trump Can’t Stop Talking About Communists
|
||||
- **skipped** — [cs-monitor] The hands-off era of AI oversight is ending. What comes next?
|
||||
- **skipped** — [mother-jones] ICE Keeps Using The Same Justification For Killing Drivers
|
||||
- **skipped** — [nbc-news] U.S. strikes Iran in major escalation as fears grow of a return to full-scale conflict
|
||||
- **skipped** — [nbc-news] Mallory McMorrow ends bid for Democratic Senate nomination in Michigan
|
||||
- **skipped** — [axios] Ukraine proves it can hit Russia almost anywhere
|
||||
- **skipped** — [huffpost] Kentucky Governor Asks Mitch McConnell To ‘Fully Update’ People On His Health
|
||||
- **skipped** — [axios] UEFA says FIFA crossed a line on Balogun decision
|
||||
@@ -0,0 +1,32 @@
|
||||
# Evidence: cs-monitor
|
||||
|
||||
- **Score**: +1.000 (95% CI -0.000 … +1.000); negative = left, positive = right
|
||||
- **Snapshot**: 2026-07-10_2026-07-11 (corpus 626c32fd3570…), pipeline v0.3.0
|
||||
- **Coverage**: 2 of 63 cross-outlet stories
|
||||
- **Nearest neighbors**: mother-jones (+0.452), axios (+0.032), nbc-news (+0.020)
|
||||
|
||||
The stories below are the most axis-distinguishing in this snapshot —
|
||||
the ones outlets' choices disagree about most. What this outlet covered
|
||||
and skipped among them is what placed it where it is.
|
||||
|
||||
## Left-pole stories
|
||||
|
||||
- **skipped** — [national-review] Does the WNBA Not Like Caitlin Clark?
|
||||
- **skipped** — [the-nation] The Supreme Court Undermines Immigrant Rights
|
||||
- **skipped** — [npr] Campaign staffers keep trying to bet on races despite push to curb insider trading
|
||||
- **skipped** — [newsmax] RNC Midterm Convention Becomes Fundraiser
|
||||
- **skipped** — [npr] Sen. Jeanne Shaheen talks about Trump at NATO and the renewed strikes against Iran
|
||||
- **skipped** — [the-nation] Rahm Emanuel’s Speech in Tel Aviv Breaks With a “No Daylight” Approach
|
||||
- **skipped** — [daily-wire] Israel Warns U.S. Of New Iranian Plot To Assassinate Trump: Report
|
||||
- **skipped** — [fox-news] Jeb Bush praises Trump for crippling Iran’s military, but warns of ‘threat’ to US from reported drones in Cuba
|
||||
|
||||
## Right-pole stories
|
||||
|
||||
- **covered** — [mother-jones] Trump Can’t Stop Talking About Communists
|
||||
- **covered** — [cs-monitor] The hands-off era of AI oversight is ending. What comes next?
|
||||
- **skipped** — [mother-jones] ICE Keeps Using The Same Justification For Killing Drivers
|
||||
- **skipped** — [nbc-news] U.S. strikes Iran in major escalation as fears grow of a return to full-scale conflict
|
||||
- **skipped** — [nbc-news] Mallory McMorrow ends bid for Democratic Senate nomination in Michigan
|
||||
- **skipped** — [axios] Ukraine proves it can hit Russia almost anywhere
|
||||
- **skipped** — [huffpost] Kentucky Governor Asks Mitch McConnell To ‘Fully Update’ People On His Health
|
||||
- **skipped** — [axios] UEFA says FIFA crossed a line on Balogun decision
|
||||
@@ -0,0 +1,32 @@
|
||||
# Evidence: daily-wire
|
||||
|
||||
- **Score**: -0.044 (95% CI -0.241 … +0.142); negative = left, positive = right
|
||||
- **Snapshot**: 2026-07-10_2026-07-11 (corpus 626c32fd3570…), pipeline v0.3.0
|
||||
- **Coverage**: 14 of 63 cross-outlet stories
|
||||
- **Nearest neighbors**: the-nation (-0.044), breitbart (-0.045), the-hill (-0.047)
|
||||
|
||||
The stories below are the most axis-distinguishing in this snapshot —
|
||||
the ones outlets' choices disagree about most. What this outlet covered
|
||||
and skipped among them is what placed it where it is.
|
||||
|
||||
## Left-pole stories
|
||||
|
||||
- **skipped** — [national-review] Does the WNBA Not Like Caitlin Clark?
|
||||
- **skipped** — [the-nation] The Supreme Court Undermines Immigrant Rights
|
||||
- **skipped** — [npr] Campaign staffers keep trying to bet on races despite push to curb insider trading
|
||||
- **skipped** — [newsmax] RNC Midterm Convention Becomes Fundraiser
|
||||
- **skipped** — [npr] Sen. Jeanne Shaheen talks about Trump at NATO and the renewed strikes against Iran
|
||||
- **skipped** — [the-nation] Rahm Emanuel’s Speech in Tel Aviv Breaks With a “No Daylight” Approach
|
||||
- **covered** — [daily-wire] Israel Warns U.S. Of New Iranian Plot To Assassinate Trump: Report
|
||||
- **skipped** — [fox-news] Jeb Bush praises Trump for crippling Iran’s military, but warns of ‘threat’ to US from reported drones in Cuba
|
||||
|
||||
## Right-pole stories
|
||||
|
||||
- **skipped** — [mother-jones] Trump Can’t Stop Talking About Communists
|
||||
- **skipped** — [cs-monitor] The hands-off era of AI oversight is ending. What comes next?
|
||||
- **skipped** — [mother-jones] ICE Keeps Using The Same Justification For Killing Drivers
|
||||
- **skipped** — [nbc-news] U.S. strikes Iran in major escalation as fears grow of a return to full-scale conflict
|
||||
- **skipped** — [nbc-news] Mallory McMorrow ends bid for Democratic Senate nomination in Michigan
|
||||
- **skipped** — [axios] Ukraine proves it can hit Russia almost anywhere
|
||||
- **skipped** — [huffpost] Kentucky Governor Asks Mitch McConnell To ‘Fully Update’ People On His Health
|
||||
- **skipped** — [axios] UEFA says FIFA crossed a line on Balogun decision
|
||||
@@ -0,0 +1,32 @@
|
||||
# Evidence: fox-news
|
||||
|
||||
- **Score**: -0.032 (95% CI -0.607 … +0.570); negative = left, positive = right
|
||||
- **Snapshot**: 2026-07-10_2026-07-11 (corpus 626c32fd3570…), pipeline v0.3.0
|
||||
- **Coverage**: 9 of 63 cross-outlet stories
|
||||
- **Nearest neighbors**: washington-examiner (-0.030), pbs-newshour (-0.036), washington-post (-0.037)
|
||||
|
||||
The stories below are the most axis-distinguishing in this snapshot —
|
||||
the ones outlets' choices disagree about most. What this outlet covered
|
||||
and skipped among them is what placed it where it is.
|
||||
|
||||
## Left-pole stories
|
||||
|
||||
- **skipped** — [national-review] Does the WNBA Not Like Caitlin Clark?
|
||||
- **skipped** — [the-nation] The Supreme Court Undermines Immigrant Rights
|
||||
- **skipped** — [npr] Campaign staffers keep trying to bet on races despite push to curb insider trading
|
||||
- **skipped** — [newsmax] RNC Midterm Convention Becomes Fundraiser
|
||||
- **skipped** — [npr] Sen. Jeanne Shaheen talks about Trump at NATO and the renewed strikes against Iran
|
||||
- **skipped** — [the-nation] Rahm Emanuel’s Speech in Tel Aviv Breaks With a “No Daylight” Approach
|
||||
- **covered** — [daily-wire] Israel Warns U.S. Of New Iranian Plot To Assassinate Trump: Report
|
||||
- **covered** — [fox-news] Jeb Bush praises Trump for crippling Iran’s military, but warns of ‘threat’ to US from reported drones in Cuba
|
||||
|
||||
## Right-pole stories
|
||||
|
||||
- **skipped** — [mother-jones] Trump Can’t Stop Talking About Communists
|
||||
- **skipped** — [cs-monitor] The hands-off era of AI oversight is ending. What comes next?
|
||||
- **skipped** — [mother-jones] ICE Keeps Using The Same Justification For Killing Drivers
|
||||
- **skipped** — [nbc-news] U.S. strikes Iran in major escalation as fears grow of a return to full-scale conflict
|
||||
- **skipped** — [nbc-news] Mallory McMorrow ends bid for Democratic Senate nomination in Michigan
|
||||
- **skipped** — [axios] Ukraine proves it can hit Russia almost anywhere
|
||||
- **skipped** — [huffpost] Kentucky Governor Asks Mitch McConnell To ‘Fully Update’ People On His Health
|
||||
- **skipped** — [axios] UEFA says FIFA crossed a line on Balogun decision
|
||||
@@ -0,0 +1,32 @@
|
||||
# Evidence: huffpost
|
||||
|
||||
- **Score**: -0.008 (95% CI -0.239 … +0.321); negative = left, positive = right
|
||||
- **Snapshot**: 2026-07-10_2026-07-11 (corpus 626c32fd3570…), pipeline v0.3.0
|
||||
- **Coverage**: 25 of 63 cross-outlet stories
|
||||
- **Nearest neighbors**: cnn (-0.000), wsj (-0.000), washington-examiner (-0.030)
|
||||
|
||||
The stories below are the most axis-distinguishing in this snapshot —
|
||||
the ones outlets' choices disagree about most. What this outlet covered
|
||||
and skipped among them is what placed it where it is.
|
||||
|
||||
## Left-pole stories
|
||||
|
||||
- **skipped** — [national-review] Does the WNBA Not Like Caitlin Clark?
|
||||
- **skipped** — [the-nation] The Supreme Court Undermines Immigrant Rights
|
||||
- **skipped** — [npr] Campaign staffers keep trying to bet on races despite push to curb insider trading
|
||||
- **skipped** — [newsmax] RNC Midterm Convention Becomes Fundraiser
|
||||
- **skipped** — [npr] Sen. Jeanne Shaheen talks about Trump at NATO and the renewed strikes against Iran
|
||||
- **skipped** — [the-nation] Rahm Emanuel’s Speech in Tel Aviv Breaks With a “No Daylight” Approach
|
||||
- **skipped** — [daily-wire] Israel Warns U.S. Of New Iranian Plot To Assassinate Trump: Report
|
||||
- **skipped** — [fox-news] Jeb Bush praises Trump for crippling Iran’s military, but warns of ‘threat’ to US from reported drones in Cuba
|
||||
|
||||
## Right-pole stories
|
||||
|
||||
- **skipped** — [mother-jones] Trump Can’t Stop Talking About Communists
|
||||
- **skipped** — [cs-monitor] The hands-off era of AI oversight is ending. What comes next?
|
||||
- **covered** — [mother-jones] ICE Keeps Using The Same Justification For Killing Drivers
|
||||
- **skipped** — [nbc-news] U.S. strikes Iran in major escalation as fears grow of a return to full-scale conflict
|
||||
- **skipped** — [nbc-news] Mallory McMorrow ends bid for Democratic Senate nomination in Michigan
|
||||
- **skipped** — [axios] Ukraine proves it can hit Russia almost anywhere
|
||||
- **covered** — [huffpost] Kentucky Governor Asks Mitch McConnell To ‘Fully Update’ People On His Health
|
||||
- **covered** — [axios] UEFA says FIFA crossed a line on Balogun decision
|
||||
@@ -0,0 +1,26 @@
|
||||
# Evidence pages — snapshot 2026-07-10_2026-07-11
|
||||
|
||||
Generated by the pipeline (never hand-edited). Reference frame: lean relative to contemporary US congressional party discourse; negative = left, positive = right.
|
||||
|
||||
| Outlet | Score | 95% CI |
|
||||
|---|---|---|
|
||||
| [national-review](national-review.md) | -0.057 | -1.000 … +1.000 |
|
||||
| [newsmax](newsmax.md) | -0.052 | -0.532 … +0.370 |
|
||||
| [ny-post](ny-post.md) | -0.051 | -0.437 … +0.228 |
|
||||
| [npr](npr.md) | -0.049 | -0.495 … +0.228 |
|
||||
| [the-hill](the-hill.md) | -0.047 | -0.371 … +0.263 |
|
||||
| [breitbart](breitbart.md) | -0.045 | -0.516 … +0.219 |
|
||||
| [the-nation](the-nation.md) | -0.044 | -0.648 … +0.480 |
|
||||
| [daily-wire](daily-wire.md) | -0.044 | -0.241 … +0.142 |
|
||||
| [washington-post](washington-post.md) | -0.037 | -0.898 … +0.785 |
|
||||
| [pbs-newshour](pbs-newshour.md) | -0.036 | -0.447 … +0.401 |
|
||||
| [fox-news](fox-news.md) | -0.032 | -0.607 … +0.570 |
|
||||
| [washington-examiner](washington-examiner.md) | -0.030 | -0.869 … +0.814 |
|
||||
| [huffpost](huffpost.md) | -0.008 | -0.239 … +0.321 |
|
||||
| [cnn](cnn.md) | -0.000 | +0.000 … +0.000 |
|
||||
| [wsj](wsj.md) | -0.000 | +0.000 … +0.000 |
|
||||
| [nytimes](nytimes.md) | +0.020 | -0.435 … +0.558 |
|
||||
| [nbc-news](nbc-news.md) | +0.020 | -0.279 … +0.496 |
|
||||
| [axios](axios.md) | +0.032 | -0.287 … +0.353 |
|
||||
| [mother-jones](mother-jones.md) | +0.452 | -0.458 … +1.000 |
|
||||
| [cs-monitor](cs-monitor.md) | +1.000 | -0.000 … +1.000 |
|
||||
@@ -0,0 +1,32 @@
|
||||
# Evidence: mother-jones
|
||||
|
||||
- **Score**: +0.452 (95% CI -0.458 … +1.000); negative = left, positive = right
|
||||
- **Snapshot**: 2026-07-10_2026-07-11 (corpus 626c32fd3570…), pipeline v0.3.0
|
||||
- **Coverage**: 4 of 63 cross-outlet stories
|
||||
- **Nearest neighbors**: axios (+0.032), nbc-news (+0.020), nytimes (+0.020)
|
||||
|
||||
The stories below are the most axis-distinguishing in this snapshot —
|
||||
the ones outlets' choices disagree about most. What this outlet covered
|
||||
and skipped among them is what placed it where it is.
|
||||
|
||||
## Left-pole stories
|
||||
|
||||
- **skipped** — [national-review] Does the WNBA Not Like Caitlin Clark?
|
||||
- **skipped** — [the-nation] The Supreme Court Undermines Immigrant Rights
|
||||
- **skipped** — [npr] Campaign staffers keep trying to bet on races despite push to curb insider trading
|
||||
- **skipped** — [newsmax] RNC Midterm Convention Becomes Fundraiser
|
||||
- **skipped** — [npr] Sen. Jeanne Shaheen talks about Trump at NATO and the renewed strikes against Iran
|
||||
- **skipped** — [the-nation] Rahm Emanuel’s Speech in Tel Aviv Breaks With a “No Daylight” Approach
|
||||
- **skipped** — [daily-wire] Israel Warns U.S. Of New Iranian Plot To Assassinate Trump: Report
|
||||
- **skipped** — [fox-news] Jeb Bush praises Trump for crippling Iran’s military, but warns of ‘threat’ to US from reported drones in Cuba
|
||||
|
||||
## Right-pole stories
|
||||
|
||||
- **covered** — [mother-jones] Trump Can’t Stop Talking About Communists
|
||||
- **skipped** — [cs-monitor] The hands-off era of AI oversight is ending. What comes next?
|
||||
- **covered** — [mother-jones] ICE Keeps Using The Same Justification For Killing Drivers
|
||||
- **covered** — [nbc-news] U.S. strikes Iran in major escalation as fears grow of a return to full-scale conflict
|
||||
- **skipped** — [nbc-news] Mallory McMorrow ends bid for Democratic Senate nomination in Michigan
|
||||
- **skipped** — [axios] Ukraine proves it can hit Russia almost anywhere
|
||||
- **skipped** — [huffpost] Kentucky Governor Asks Mitch McConnell To ‘Fully Update’ People On His Health
|
||||
- **skipped** — [axios] UEFA says FIFA crossed a line on Balogun decision
|
||||
@@ -0,0 +1,32 @@
|
||||
# Evidence: national-review
|
||||
|
||||
- **Score**: -0.057 (95% CI -1.000 … +1.000); negative = left, positive = right
|
||||
- **Snapshot**: 2026-07-10_2026-07-11 (corpus 626c32fd3570…), pipeline v0.3.0
|
||||
- **Coverage**: 4 of 63 cross-outlet stories
|
||||
- **Nearest neighbors**: newsmax (-0.052), ny-post (-0.051), npr (-0.049)
|
||||
|
||||
The stories below are the most axis-distinguishing in this snapshot —
|
||||
the ones outlets' choices disagree about most. What this outlet covered
|
||||
and skipped among them is what placed it where it is.
|
||||
|
||||
## Left-pole stories
|
||||
|
||||
- **covered** — [national-review] Does the WNBA Not Like Caitlin Clark?
|
||||
- **covered** — [the-nation] The Supreme Court Undermines Immigrant Rights
|
||||
- **skipped** — [npr] Campaign staffers keep trying to bet on races despite push to curb insider trading
|
||||
- **skipped** — [newsmax] RNC Midterm Convention Becomes Fundraiser
|
||||
- **skipped** — [npr] Sen. Jeanne Shaheen talks about Trump at NATO and the renewed strikes against Iran
|
||||
- **skipped** — [the-nation] Rahm Emanuel’s Speech in Tel Aviv Breaks With a “No Daylight” Approach
|
||||
- **skipped** — [daily-wire] Israel Warns U.S. Of New Iranian Plot To Assassinate Trump: Report
|
||||
- **skipped** — [fox-news] Jeb Bush praises Trump for crippling Iran’s military, but warns of ‘threat’ to US from reported drones in Cuba
|
||||
|
||||
## Right-pole stories
|
||||
|
||||
- **skipped** — [mother-jones] Trump Can’t Stop Talking About Communists
|
||||
- **skipped** — [cs-monitor] The hands-off era of AI oversight is ending. What comes next?
|
||||
- **skipped** — [mother-jones] ICE Keeps Using The Same Justification For Killing Drivers
|
||||
- **skipped** — [nbc-news] U.S. strikes Iran in major escalation as fears grow of a return to full-scale conflict
|
||||
- **skipped** — [nbc-news] Mallory McMorrow ends bid for Democratic Senate nomination in Michigan
|
||||
- **skipped** — [axios] Ukraine proves it can hit Russia almost anywhere
|
||||
- **skipped** — [huffpost] Kentucky Governor Asks Mitch McConnell To ‘Fully Update’ People On His Health
|
||||
- **skipped** — [axios] UEFA says FIFA crossed a line on Balogun decision
|
||||
@@ -0,0 +1,32 @@
|
||||
# Evidence: nbc-news
|
||||
|
||||
- **Score**: +0.020 (95% CI -0.279 … +0.496); negative = left, positive = right
|
||||
- **Snapshot**: 2026-07-10_2026-07-11 (corpus 626c32fd3570…), pipeline v0.3.0
|
||||
- **Coverage**: 11 of 63 cross-outlet stories
|
||||
- **Nearest neighbors**: nytimes (+0.020), axios (+0.032), cnn (-0.000)
|
||||
|
||||
The stories below are the most axis-distinguishing in this snapshot —
|
||||
the ones outlets' choices disagree about most. What this outlet covered
|
||||
and skipped among them is what placed it where it is.
|
||||
|
||||
## Left-pole stories
|
||||
|
||||
- **skipped** — [national-review] Does the WNBA Not Like Caitlin Clark?
|
||||
- **skipped** — [the-nation] The Supreme Court Undermines Immigrant Rights
|
||||
- **skipped** — [npr] Campaign staffers keep trying to bet on races despite push to curb insider trading
|
||||
- **skipped** — [newsmax] RNC Midterm Convention Becomes Fundraiser
|
||||
- **skipped** — [npr] Sen. Jeanne Shaheen talks about Trump at NATO and the renewed strikes against Iran
|
||||
- **skipped** — [the-nation] Rahm Emanuel’s Speech in Tel Aviv Breaks With a “No Daylight” Approach
|
||||
- **skipped** — [daily-wire] Israel Warns U.S. Of New Iranian Plot To Assassinate Trump: Report
|
||||
- **skipped** — [fox-news] Jeb Bush praises Trump for crippling Iran’s military, but warns of ‘threat’ to US from reported drones in Cuba
|
||||
|
||||
## Right-pole stories
|
||||
|
||||
- **skipped** — [mother-jones] Trump Can’t Stop Talking About Communists
|
||||
- **skipped** — [cs-monitor] The hands-off era of AI oversight is ending. What comes next?
|
||||
- **skipped** — [mother-jones] ICE Keeps Using The Same Justification For Killing Drivers
|
||||
- **covered** — [nbc-news] U.S. strikes Iran in major escalation as fears grow of a return to full-scale conflict
|
||||
- **covered** — [nbc-news] Mallory McMorrow ends bid for Democratic Senate nomination in Michigan
|
||||
- **skipped** — [axios] Ukraine proves it can hit Russia almost anywhere
|
||||
- **covered** — [huffpost] Kentucky Governor Asks Mitch McConnell To ‘Fully Update’ People On His Health
|
||||
- **covered** — [axios] UEFA says FIFA crossed a line on Balogun decision
|
||||
@@ -0,0 +1,32 @@
|
||||
# Evidence: newsmax
|
||||
|
||||
- **Score**: -0.052 (95% CI -0.532 … +0.370); negative = left, positive = right
|
||||
- **Snapshot**: 2026-07-10_2026-07-11 (corpus 626c32fd3570…), pipeline v0.3.0
|
||||
- **Coverage**: 12 of 63 cross-outlet stories
|
||||
- **Nearest neighbors**: ny-post (-0.051), npr (-0.049), national-review (-0.057)
|
||||
|
||||
The stories below are the most axis-distinguishing in this snapshot —
|
||||
the ones outlets' choices disagree about most. What this outlet covered
|
||||
and skipped among them is what placed it where it is.
|
||||
|
||||
## Left-pole stories
|
||||
|
||||
- **covered** — [national-review] Does the WNBA Not Like Caitlin Clark?
|
||||
- **skipped** — [the-nation] The Supreme Court Undermines Immigrant Rights
|
||||
- **covered** — [npr] Campaign staffers keep trying to bet on races despite push to curb insider trading
|
||||
- **covered** — [newsmax] RNC Midterm Convention Becomes Fundraiser
|
||||
- **skipped** — [npr] Sen. Jeanne Shaheen talks about Trump at NATO and the renewed strikes against Iran
|
||||
- **covered** — [the-nation] Rahm Emanuel’s Speech in Tel Aviv Breaks With a “No Daylight” Approach
|
||||
- **covered** — [daily-wire] Israel Warns U.S. Of New Iranian Plot To Assassinate Trump: Report
|
||||
- **covered** — [fox-news] Jeb Bush praises Trump for crippling Iran’s military, but warns of ‘threat’ to US from reported drones in Cuba
|
||||
|
||||
## Right-pole stories
|
||||
|
||||
- **skipped** — [mother-jones] Trump Can’t Stop Talking About Communists
|
||||
- **skipped** — [cs-monitor] The hands-off era of AI oversight is ending. What comes next?
|
||||
- **skipped** — [mother-jones] ICE Keeps Using The Same Justification For Killing Drivers
|
||||
- **skipped** — [nbc-news] U.S. strikes Iran in major escalation as fears grow of a return to full-scale conflict
|
||||
- **skipped** — [nbc-news] Mallory McMorrow ends bid for Democratic Senate nomination in Michigan
|
||||
- **skipped** — [axios] Ukraine proves it can hit Russia almost anywhere
|
||||
- **skipped** — [huffpost] Kentucky Governor Asks Mitch McConnell To ‘Fully Update’ People On His Health
|
||||
- **skipped** — [axios] UEFA says FIFA crossed a line on Balogun decision
|
||||
@@ -0,0 +1,32 @@
|
||||
# Evidence: npr
|
||||
|
||||
- **Score**: -0.049 (95% CI -0.495 … +0.228); negative = left, positive = right
|
||||
- **Snapshot**: 2026-07-10_2026-07-11 (corpus 626c32fd3570…), pipeline v0.3.0
|
||||
- **Coverage**: 7 of 63 cross-outlet stories
|
||||
- **Nearest neighbors**: ny-post (-0.051), the-hill (-0.047), newsmax (-0.052)
|
||||
|
||||
The stories below are the most axis-distinguishing in this snapshot —
|
||||
the ones outlets' choices disagree about most. What this outlet covered
|
||||
and skipped among them is what placed it where it is.
|
||||
|
||||
## Left-pole stories
|
||||
|
||||
- **skipped** — [national-review] Does the WNBA Not Like Caitlin Clark?
|
||||
- **skipped** — [the-nation] The Supreme Court Undermines Immigrant Rights
|
||||
- **covered** — [npr] Campaign staffers keep trying to bet on races despite push to curb insider trading
|
||||
- **skipped** — [newsmax] RNC Midterm Convention Becomes Fundraiser
|
||||
- **covered** — [npr] Sen. Jeanne Shaheen talks about Trump at NATO and the renewed strikes against Iran
|
||||
- **covered** — [the-nation] Rahm Emanuel’s Speech in Tel Aviv Breaks With a “No Daylight” Approach
|
||||
- **skipped** — [daily-wire] Israel Warns U.S. Of New Iranian Plot To Assassinate Trump: Report
|
||||
- **skipped** — [fox-news] Jeb Bush praises Trump for crippling Iran’s military, but warns of ‘threat’ to US from reported drones in Cuba
|
||||
|
||||
## Right-pole stories
|
||||
|
||||
- **skipped** — [mother-jones] Trump Can’t Stop Talking About Communists
|
||||
- **skipped** — [cs-monitor] The hands-off era of AI oversight is ending. What comes next?
|
||||
- **skipped** — [mother-jones] ICE Keeps Using The Same Justification For Killing Drivers
|
||||
- **skipped** — [nbc-news] U.S. strikes Iran in major escalation as fears grow of a return to full-scale conflict
|
||||
- **skipped** — [nbc-news] Mallory McMorrow ends bid for Democratic Senate nomination in Michigan
|
||||
- **skipped** — [axios] Ukraine proves it can hit Russia almost anywhere
|
||||
- **skipped** — [huffpost] Kentucky Governor Asks Mitch McConnell To ‘Fully Update’ People On His Health
|
||||
- **skipped** — [axios] UEFA says FIFA crossed a line on Balogun decision
|
||||
@@ -0,0 +1,32 @@
|
||||
# Evidence: ny-post
|
||||
|
||||
- **Score**: -0.051 (95% CI -0.437 … +0.228); negative = left, positive = right
|
||||
- **Snapshot**: 2026-07-10_2026-07-11 (corpus 626c32fd3570…), pipeline v0.3.0
|
||||
- **Coverage**: 6 of 63 cross-outlet stories
|
||||
- **Nearest neighbors**: npr (-0.049), newsmax (-0.052), the-hill (-0.047)
|
||||
|
||||
The stories below are the most axis-distinguishing in this snapshot —
|
||||
the ones outlets' choices disagree about most. What this outlet covered
|
||||
and skipped among them is what placed it where it is.
|
||||
|
||||
## Left-pole stories
|
||||
|
||||
- **skipped** — [national-review] Does the WNBA Not Like Caitlin Clark?
|
||||
- **skipped** — [the-nation] The Supreme Court Undermines Immigrant Rights
|
||||
- **skipped** — [npr] Campaign staffers keep trying to bet on races despite push to curb insider trading
|
||||
- **skipped** — [newsmax] RNC Midterm Convention Becomes Fundraiser
|
||||
- **skipped** — [npr] Sen. Jeanne Shaheen talks about Trump at NATO and the renewed strikes against Iran
|
||||
- **skipped** — [the-nation] Rahm Emanuel’s Speech in Tel Aviv Breaks With a “No Daylight” Approach
|
||||
- **covered** — [daily-wire] Israel Warns U.S. Of New Iranian Plot To Assassinate Trump: Report
|
||||
- **covered** — [fox-news] Jeb Bush praises Trump for crippling Iran’s military, but warns of ‘threat’ to US from reported drones in Cuba
|
||||
|
||||
## Right-pole stories
|
||||
|
||||
- **skipped** — [mother-jones] Trump Can’t Stop Talking About Communists
|
||||
- **skipped** — [cs-monitor] The hands-off era of AI oversight is ending. What comes next?
|
||||
- **skipped** — [mother-jones] ICE Keeps Using The Same Justification For Killing Drivers
|
||||
- **skipped** — [nbc-news] U.S. strikes Iran in major escalation as fears grow of a return to full-scale conflict
|
||||
- **skipped** — [nbc-news] Mallory McMorrow ends bid for Democratic Senate nomination in Michigan
|
||||
- **skipped** — [axios] Ukraine proves it can hit Russia almost anywhere
|
||||
- **skipped** — [huffpost] Kentucky Governor Asks Mitch McConnell To ‘Fully Update’ People On His Health
|
||||
- **skipped** — [axios] UEFA says FIFA crossed a line on Balogun decision
|
||||
@@ -0,0 +1,32 @@
|
||||
# Evidence: nytimes
|
||||
|
||||
- **Score**: +0.020 (95% CI -0.435 … +0.558); negative = left, positive = right
|
||||
- **Snapshot**: 2026-07-10_2026-07-11 (corpus 626c32fd3570…), pipeline v0.3.0
|
||||
- **Coverage**: 9 of 63 cross-outlet stories
|
||||
- **Nearest neighbors**: nbc-news (+0.020), axios (+0.032), cnn (-0.000)
|
||||
|
||||
The stories below are the most axis-distinguishing in this snapshot —
|
||||
the ones outlets' choices disagree about most. What this outlet covered
|
||||
and skipped among them is what placed it where it is.
|
||||
|
||||
## Left-pole stories
|
||||
|
||||
- **skipped** — [national-review] Does the WNBA Not Like Caitlin Clark?
|
||||
- **skipped** — [the-nation] The Supreme Court Undermines Immigrant Rights
|
||||
- **skipped** — [npr] Campaign staffers keep trying to bet on races despite push to curb insider trading
|
||||
- **skipped** — [newsmax] RNC Midterm Convention Becomes Fundraiser
|
||||
- **skipped** — [npr] Sen. Jeanne Shaheen talks about Trump at NATO and the renewed strikes against Iran
|
||||
- **skipped** — [the-nation] Rahm Emanuel’s Speech in Tel Aviv Breaks With a “No Daylight” Approach
|
||||
- **skipped** — [daily-wire] Israel Warns U.S. Of New Iranian Plot To Assassinate Trump: Report
|
||||
- **skipped** — [fox-news] Jeb Bush praises Trump for crippling Iran’s military, but warns of ‘threat’ to US from reported drones in Cuba
|
||||
|
||||
## Right-pole stories
|
||||
|
||||
- **skipped** — [mother-jones] Trump Can’t Stop Talking About Communists
|
||||
- **skipped** — [cs-monitor] The hands-off era of AI oversight is ending. What comes next?
|
||||
- **skipped** — [mother-jones] ICE Keeps Using The Same Justification For Killing Drivers
|
||||
- **covered** — [nbc-news] U.S. strikes Iran in major escalation as fears grow of a return to full-scale conflict
|
||||
- **skipped** — [nbc-news] Mallory McMorrow ends bid for Democratic Senate nomination in Michigan
|
||||
- **covered** — [axios] Ukraine proves it can hit Russia almost anywhere
|
||||
- **skipped** — [huffpost] Kentucky Governor Asks Mitch McConnell To ‘Fully Update’ People On His Health
|
||||
- **skipped** — [axios] UEFA says FIFA crossed a line on Balogun decision
|
||||
@@ -0,0 +1,32 @@
|
||||
# Evidence: pbs-newshour
|
||||
|
||||
- **Score**: -0.036 (95% CI -0.447 … +0.401); negative = left, positive = right
|
||||
- **Snapshot**: 2026-07-10_2026-07-11 (corpus 626c32fd3570…), pipeline v0.3.0
|
||||
- **Coverage**: 11 of 63 cross-outlet stories
|
||||
- **Nearest neighbors**: washington-post (-0.037), fox-news (-0.032), washington-examiner (-0.030)
|
||||
|
||||
The stories below are the most axis-distinguishing in this snapshot —
|
||||
the ones outlets' choices disagree about most. What this outlet covered
|
||||
and skipped among them is what placed it where it is.
|
||||
|
||||
## Left-pole stories
|
||||
|
||||
- **skipped** — [national-review] Does the WNBA Not Like Caitlin Clark?
|
||||
- **skipped** — [the-nation] The Supreme Court Undermines Immigrant Rights
|
||||
- **skipped** — [npr] Campaign staffers keep trying to bet on races despite push to curb insider trading
|
||||
- **skipped** — [newsmax] RNC Midterm Convention Becomes Fundraiser
|
||||
- **skipped** — [npr] Sen. Jeanne Shaheen talks about Trump at NATO and the renewed strikes against Iran
|
||||
- **covered** — [the-nation] Rahm Emanuel’s Speech in Tel Aviv Breaks With a “No Daylight” Approach
|
||||
- **skipped** — [daily-wire] Israel Warns U.S. Of New Iranian Plot To Assassinate Trump: Report
|
||||
- **skipped** — [fox-news] Jeb Bush praises Trump for crippling Iran’s military, but warns of ‘threat’ to US from reported drones in Cuba
|
||||
|
||||
## Right-pole stories
|
||||
|
||||
- **skipped** — [mother-jones] Trump Can’t Stop Talking About Communists
|
||||
- **skipped** — [cs-monitor] The hands-off era of AI oversight is ending. What comes next?
|
||||
- **skipped** — [mother-jones] ICE Keeps Using The Same Justification For Killing Drivers
|
||||
- **skipped** — [nbc-news] U.S. strikes Iran in major escalation as fears grow of a return to full-scale conflict
|
||||
- **skipped** — [nbc-news] Mallory McMorrow ends bid for Democratic Senate nomination in Michigan
|
||||
- **skipped** — [axios] Ukraine proves it can hit Russia almost anywhere
|
||||
- **skipped** — [huffpost] Kentucky Governor Asks Mitch McConnell To ‘Fully Update’ People On His Health
|
||||
- **skipped** — [axios] UEFA says FIFA crossed a line on Balogun decision
|
||||
@@ -0,0 +1,32 @@
|
||||
# Evidence: the-hill
|
||||
|
||||
- **Score**: -0.047 (95% CI -0.371 … +0.263); negative = left, positive = right
|
||||
- **Snapshot**: 2026-07-10_2026-07-11 (corpus 626c32fd3570…), pipeline v0.3.0
|
||||
- **Coverage**: 12 of 63 cross-outlet stories
|
||||
- **Nearest neighbors**: npr (-0.049), breitbart (-0.045), the-nation (-0.044)
|
||||
|
||||
The stories below are the most axis-distinguishing in this snapshot —
|
||||
the ones outlets' choices disagree about most. What this outlet covered
|
||||
and skipped among them is what placed it where it is.
|
||||
|
||||
## Left-pole stories
|
||||
|
||||
- **skipped** — [national-review] Does the WNBA Not Like Caitlin Clark?
|
||||
- **skipped** — [the-nation] The Supreme Court Undermines Immigrant Rights
|
||||
- **skipped** — [npr] Campaign staffers keep trying to bet on races despite push to curb insider trading
|
||||
- **skipped** — [newsmax] RNC Midterm Convention Becomes Fundraiser
|
||||
- **skipped** — [npr] Sen. Jeanne Shaheen talks about Trump at NATO and the renewed strikes against Iran
|
||||
- **skipped** — [the-nation] Rahm Emanuel’s Speech in Tel Aviv Breaks With a “No Daylight” Approach
|
||||
- **covered** — [daily-wire] Israel Warns U.S. Of New Iranian Plot To Assassinate Trump: Report
|
||||
- **skipped** — [fox-news] Jeb Bush praises Trump for crippling Iran’s military, but warns of ‘threat’ to US from reported drones in Cuba
|
||||
|
||||
## Right-pole stories
|
||||
|
||||
- **skipped** — [mother-jones] Trump Can’t Stop Talking About Communists
|
||||
- **skipped** — [cs-monitor] The hands-off era of AI oversight is ending. What comes next?
|
||||
- **skipped** — [mother-jones] ICE Keeps Using The Same Justification For Killing Drivers
|
||||
- **skipped** — [nbc-news] U.S. strikes Iran in major escalation as fears grow of a return to full-scale conflict
|
||||
- **skipped** — [nbc-news] Mallory McMorrow ends bid for Democratic Senate nomination in Michigan
|
||||
- **skipped** — [axios] Ukraine proves it can hit Russia almost anywhere
|
||||
- **skipped** — [huffpost] Kentucky Governor Asks Mitch McConnell To ‘Fully Update’ People On His Health
|
||||
- **skipped** — [axios] UEFA says FIFA crossed a line on Balogun decision
|
||||
@@ -0,0 +1,32 @@
|
||||
# Evidence: the-nation
|
||||
|
||||
- **Score**: -0.044 (95% CI -0.648 … +0.480); negative = left, positive = right
|
||||
- **Snapshot**: 2026-07-10_2026-07-11 (corpus 626c32fd3570…), pipeline v0.3.0
|
||||
- **Coverage**: 11 of 63 cross-outlet stories
|
||||
- **Nearest neighbors**: breitbart (-0.045), daily-wire (-0.044), the-hill (-0.047)
|
||||
|
||||
The stories below are the most axis-distinguishing in this snapshot —
|
||||
the ones outlets' choices disagree about most. What this outlet covered
|
||||
and skipped among them is what placed it where it is.
|
||||
|
||||
## Left-pole stories
|
||||
|
||||
- **skipped** — [national-review] Does the WNBA Not Like Caitlin Clark?
|
||||
- **covered** — [the-nation] The Supreme Court Undermines Immigrant Rights
|
||||
- **skipped** — [npr] Campaign staffers keep trying to bet on races despite push to curb insider trading
|
||||
- **skipped** — [newsmax] RNC Midterm Convention Becomes Fundraiser
|
||||
- **skipped** — [npr] Sen. Jeanne Shaheen talks about Trump at NATO and the renewed strikes against Iran
|
||||
- **covered** — [the-nation] Rahm Emanuel’s Speech in Tel Aviv Breaks With a “No Daylight” Approach
|
||||
- **skipped** — [daily-wire] Israel Warns U.S. Of New Iranian Plot To Assassinate Trump: Report
|
||||
- **skipped** — [fox-news] Jeb Bush praises Trump for crippling Iran’s military, but warns of ‘threat’ to US from reported drones in Cuba
|
||||
|
||||
## Right-pole stories
|
||||
|
||||
- **skipped** — [mother-jones] Trump Can’t Stop Talking About Communists
|
||||
- **skipped** — [cs-monitor] The hands-off era of AI oversight is ending. What comes next?
|
||||
- **skipped** — [mother-jones] ICE Keeps Using The Same Justification For Killing Drivers
|
||||
- **skipped** — [nbc-news] U.S. strikes Iran in major escalation as fears grow of a return to full-scale conflict
|
||||
- **skipped** — [nbc-news] Mallory McMorrow ends bid for Democratic Senate nomination in Michigan
|
||||
- **skipped** — [axios] Ukraine proves it can hit Russia almost anywhere
|
||||
- **skipped** — [huffpost] Kentucky Governor Asks Mitch McConnell To ‘Fully Update’ People On His Health
|
||||
- **skipped** — [axios] UEFA says FIFA crossed a line on Balogun decision
|
||||
@@ -0,0 +1,32 @@
|
||||
# Evidence: washington-examiner
|
||||
|
||||
- **Score**: -0.030 (95% CI -0.869 … +0.814); negative = left, positive = right
|
||||
- **Snapshot**: 2026-07-10_2026-07-11 (corpus 626c32fd3570…), pipeline v0.3.0
|
||||
- **Coverage**: 8 of 63 cross-outlet stories
|
||||
- **Nearest neighbors**: fox-news (-0.032), pbs-newshour (-0.036), washington-post (-0.037)
|
||||
|
||||
The stories below are the most axis-distinguishing in this snapshot —
|
||||
the ones outlets' choices disagree about most. What this outlet covered
|
||||
and skipped among them is what placed it where it is.
|
||||
|
||||
## Left-pole stories
|
||||
|
||||
- **skipped** — [national-review] Does the WNBA Not Like Caitlin Clark?
|
||||
- **skipped** — [the-nation] The Supreme Court Undermines Immigrant Rights
|
||||
- **skipped** — [npr] Campaign staffers keep trying to bet on races despite push to curb insider trading
|
||||
- **skipped** — [newsmax] RNC Midterm Convention Becomes Fundraiser
|
||||
- **skipped** — [npr] Sen. Jeanne Shaheen talks about Trump at NATO and the renewed strikes against Iran
|
||||
- **skipped** — [the-nation] Rahm Emanuel’s Speech in Tel Aviv Breaks With a “No Daylight” Approach
|
||||
- **skipped** — [daily-wire] Israel Warns U.S. Of New Iranian Plot To Assassinate Trump: Report
|
||||
- **skipped** — [fox-news] Jeb Bush praises Trump for crippling Iran’s military, but warns of ‘threat’ to US from reported drones in Cuba
|
||||
|
||||
## Right-pole stories
|
||||
|
||||
- **skipped** — [mother-jones] Trump Can’t Stop Talking About Communists
|
||||
- **skipped** — [cs-monitor] The hands-off era of AI oversight is ending. What comes next?
|
||||
- **skipped** — [mother-jones] ICE Keeps Using The Same Justification For Killing Drivers
|
||||
- **skipped** — [nbc-news] U.S. strikes Iran in major escalation as fears grow of a return to full-scale conflict
|
||||
- **skipped** — [nbc-news] Mallory McMorrow ends bid for Democratic Senate nomination in Michigan
|
||||
- **skipped** — [axios] Ukraine proves it can hit Russia almost anywhere
|
||||
- **skipped** — [huffpost] Kentucky Governor Asks Mitch McConnell To ‘Fully Update’ People On His Health
|
||||
- **skipped** — [axios] UEFA says FIFA crossed a line on Balogun decision
|
||||
@@ -0,0 +1,32 @@
|
||||
# Evidence: washington-post
|
||||
|
||||
- **Score**: -0.037 (95% CI -0.898 … +0.785); negative = left, positive = right
|
||||
- **Snapshot**: 2026-07-10_2026-07-11 (corpus 626c32fd3570…), pipeline v0.3.0
|
||||
- **Coverage**: 7 of 63 cross-outlet stories
|
||||
- **Nearest neighbors**: pbs-newshour (-0.036), fox-news (-0.032), washington-examiner (-0.030)
|
||||
|
||||
The stories below are the most axis-distinguishing in this snapshot —
|
||||
the ones outlets' choices disagree about most. What this outlet covered
|
||||
and skipped among them is what placed it where it is.
|
||||
|
||||
## Left-pole stories
|
||||
|
||||
- **skipped** — [national-review] Does the WNBA Not Like Caitlin Clark?
|
||||
- **skipped** — [the-nation] The Supreme Court Undermines Immigrant Rights
|
||||
- **skipped** — [npr] Campaign staffers keep trying to bet on races despite push to curb insider trading
|
||||
- **skipped** — [newsmax] RNC Midterm Convention Becomes Fundraiser
|
||||
- **skipped** — [npr] Sen. Jeanne Shaheen talks about Trump at NATO and the renewed strikes against Iran
|
||||
- **skipped** — [the-nation] Rahm Emanuel’s Speech in Tel Aviv Breaks With a “No Daylight” Approach
|
||||
- **skipped** — [daily-wire] Israel Warns U.S. Of New Iranian Plot To Assassinate Trump: Report
|
||||
- **skipped** — [fox-news] Jeb Bush praises Trump for crippling Iran’s military, but warns of ‘threat’ to US from reported drones in Cuba
|
||||
|
||||
## Right-pole stories
|
||||
|
||||
- **skipped** — [mother-jones] Trump Can’t Stop Talking About Communists
|
||||
- **skipped** — [cs-monitor] The hands-off era of AI oversight is ending. What comes next?
|
||||
- **skipped** — [mother-jones] ICE Keeps Using The Same Justification For Killing Drivers
|
||||
- **skipped** — [nbc-news] U.S. strikes Iran in major escalation as fears grow of a return to full-scale conflict
|
||||
- **skipped** — [nbc-news] Mallory McMorrow ends bid for Democratic Senate nomination in Michigan
|
||||
- **skipped** — [axios] Ukraine proves it can hit Russia almost anywhere
|
||||
- **skipped** — [huffpost] Kentucky Governor Asks Mitch McConnell To ‘Fully Update’ People On His Health
|
||||
- **skipped** — [axios] UEFA says FIFA crossed a line on Balogun decision
|
||||
@@ -0,0 +1,32 @@
|
||||
# Evidence: wsj
|
||||
|
||||
- **Score**: -0.000 (95% CI +0.000 … +0.000); negative = left, positive = right
|
||||
- **Snapshot**: 2026-07-10_2026-07-11 (corpus 626c32fd3570…), pipeline v0.3.0
|
||||
- **Coverage**: 0 of 63 cross-outlet stories
|
||||
- **Nearest neighbors**: cnn (-0.000), huffpost (-0.008), nytimes (+0.020)
|
||||
|
||||
The stories below are the most axis-distinguishing in this snapshot —
|
||||
the ones outlets' choices disagree about most. What this outlet covered
|
||||
and skipped among them is what placed it where it is.
|
||||
|
||||
## Left-pole stories
|
||||
|
||||
- **skipped** — [national-review] Does the WNBA Not Like Caitlin Clark?
|
||||
- **skipped** — [the-nation] The Supreme Court Undermines Immigrant Rights
|
||||
- **skipped** — [npr] Campaign staffers keep trying to bet on races despite push to curb insider trading
|
||||
- **skipped** — [newsmax] RNC Midterm Convention Becomes Fundraiser
|
||||
- **skipped** — [npr] Sen. Jeanne Shaheen talks about Trump at NATO and the renewed strikes against Iran
|
||||
- **skipped** — [the-nation] Rahm Emanuel’s Speech in Tel Aviv Breaks With a “No Daylight” Approach
|
||||
- **skipped** — [daily-wire] Israel Warns U.S. Of New Iranian Plot To Assassinate Trump: Report
|
||||
- **skipped** — [fox-news] Jeb Bush praises Trump for crippling Iran’s military, but warns of ‘threat’ to US from reported drones in Cuba
|
||||
|
||||
## Right-pole stories
|
||||
|
||||
- **skipped** — [mother-jones] Trump Can’t Stop Talking About Communists
|
||||
- **skipped** — [cs-monitor] The hands-off era of AI oversight is ending. What comes next?
|
||||
- **skipped** — [mother-jones] ICE Keeps Using The Same Justification For Killing Drivers
|
||||
- **skipped** — [nbc-news] U.S. strikes Iran in major escalation as fears grow of a return to full-scale conflict
|
||||
- **skipped** — [nbc-news] Mallory McMorrow ends bid for Democratic Senate nomination in Michigan
|
||||
- **skipped** — [axios] Ukraine proves it can hit Russia almost anywhere
|
||||
- **skipped** — [huffpost] Kentucky Governor Asks Mitch McConnell To ‘Fully Update’ People On His Health
|
||||
- **skipped** — [axios] UEFA says FIFA crossed a line on Balogun decision
|
||||
File diff suppressed because it is too large
Load Diff
@@ -28,8 +28,12 @@ swap policy for dead feeds are documented in
|
||||
[METHODOLOGY.md, decision D4](../METHODOLOGY.md#d4-twenty-outlets-deliberately-spread-chosen-once-and-openly);
|
||||
individual swaps are recorded in the [CHANGELOG](../CHANGELOG.md).
|
||||
|
||||
| Outlet | Homepage | Feed |
|
||||
|---|---|---|
|
||||
Ownership is sourced factual context (see the `ownership` blocks in the
|
||||
config for source URLs, retrieval dates, and verification status); it is
|
||||
never a scoring input.
|
||||
|
||||
| Outlet | Owner | Type | Feed |
|
||||
|---|---|---|---|
|
||||
"""
|
||||
|
||||
|
||||
@@ -37,7 +41,8 @@ def render() -> str:
|
||||
with open(CONFIG) as f:
|
||||
outlets = yaml.safe_load(f)["outlets"]
|
||||
rows = [
|
||||
f"| {o['name']} | <{o['homepage']}> | <{o['feed']}> |"
|
||||
f"| [{o['name']}]({o['homepage']}) | {o['ownership']['owner']} "
|
||||
f"| {o['ownership']['type']} | <{o['feed']}> |"
|
||||
for o in sorted(outlets, key=lambda o: o["name"])
|
||||
]
|
||||
return HEADER + "\n".join(rows) + f"\n\nTotal: {len(outlets)} outlets.\n"
|
||||
|
||||
@@ -4,4 +4,4 @@ Every module in this package opens with a plain-language docstring stating the
|
||||
question it answers. See docs/how-it-works.md for the full plain-language tour.
|
||||
"""
|
||||
|
||||
__version__ = "0.2.0"
|
||||
__version__ = "0.3.0"
|
||||
|
||||
+47
-1
@@ -6,8 +6,10 @@ The command-line interface:
|
||||
tiltmeter status — show how many articles we hold per outlet
|
||||
tiltmeter snapshot — freeze a window of the corpus into a manifest
|
||||
tiltmeter reference — fetch congressional floor speeches (the D5 anchor)
|
||||
tiltmeter run — manifest → ratings.json + evidence pages
|
||||
tiltmeter serve — read-only HTTP API over computed releases
|
||||
|
||||
Later milestones add: run, validate, report.
|
||||
Later milestones add: validate (the M3 gate).
|
||||
"""
|
||||
|
||||
import argparse
|
||||
@@ -61,6 +63,39 @@ def cmd_reference(args: argparse.Namespace) -> int:
|
||||
return 0
|
||||
|
||||
|
||||
def cmd_run(args: argparse.Namespace) -> int:
|
||||
from tiltmeter import __version__, report, score, snapshot
|
||||
|
||||
manifest = snapshot.load(args.manifest)
|
||||
conn = db.connect(args.db)
|
||||
ratings = score.compute(conn, manifest, __version__)
|
||||
ratings_path = score.write(ratings, args.out)
|
||||
stories, matrix, articles = score.story_details(conn, manifest)
|
||||
stories_path = score.write_stories(score.stories_json(stories, articles, manifest), args.out)
|
||||
report_dir = report.write(report.render(ratings, stories, matrix, articles), ratings, args.out)
|
||||
|
||||
print(f"ratings: {ratings_path}\nstories: {stories_path}\nevidence: {report_dir}/")
|
||||
o = ratings["orientation"]
|
||||
flag = "" if o["reliable"] else " [UNRELIABLE — do not interpret]"
|
||||
print(
|
||||
f"stories: {ratings['n_stories']}, axis inertia {ratings['axis_inertia_share']:.0%}, "
|
||||
f"orientation rho {o['correlation']:+.2f}{flag}"
|
||||
)
|
||||
for entry in ratings["outlets"]:
|
||||
print(
|
||||
f" {entry['score']:+.3f} [{entry['ci_low']:+.3f} {entry['ci_high']:+.3f}]"
|
||||
f" {entry['outlet']}"
|
||||
)
|
||||
return 0
|
||||
|
||||
|
||||
def cmd_serve(args: argparse.Namespace) -> int:
|
||||
from tiltmeter import serve
|
||||
|
||||
serve.run(args.releases, args.host, args.port)
|
||||
return 0
|
||||
|
||||
|
||||
def cmd_status(args: argparse.Namespace) -> int:
|
||||
conn = db.connect(args.db)
|
||||
rows = db.outlet_counts(conn)
|
||||
@@ -103,6 +138,17 @@ def main(argv: list[str] | None = None) -> int:
|
||||
p_reference.add_argument("--congress", type=int, default=119)
|
||||
p_reference.set_defaults(func=cmd_reference)
|
||||
|
||||
p_run = sub.add_parser("run", help="compute ratings + evidence pages from a manifest")
|
||||
p_run.add_argument("--manifest", required=True, help="path to a snapshot manifest")
|
||||
p_run.add_argument("--out", default="releases", help="output directory")
|
||||
p_run.set_defaults(func=cmd_run)
|
||||
|
||||
p_serve = sub.add_parser("serve", help="read-only HTTP API over computed releases")
|
||||
p_serve.add_argument("--releases", default="releases")
|
||||
p_serve.add_argument("--host", default="0.0.0.0")
|
||||
p_serve.add_argument("--port", type=int, default=8477)
|
||||
p_serve.set_defaults(func=cmd_serve)
|
||||
|
||||
args = parser.parse_args(argv)
|
||||
logging.basicConfig(level=logging.INFO, format="%(levelname)s %(name)s: %(message)s")
|
||||
return args.func(args)
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
"""Which end of the axis is politically left, and which is right?
|
||||
|
||||
The selection axis (signals/selection.py) has an arbitrary sign — the math
|
||||
doesn't know left from right. Orientation comes from public records
|
||||
(METHODOLOGY.md D5): we embed Democratic and Republican floor speeches from
|
||||
the Congressional Record, compute each outlet's average article embedding,
|
||||
and check which axis pole sits closer in language to which party. If the
|
||||
axis is backwards, we flip it. Convention: negative = left, positive = right
|
||||
(matches how AllSides and Ad Fontes draw their scales).
|
||||
|
||||
The strength of the axis↔party-language agreement is reported as a
|
||||
diagnostic: a weak agreement means orientation (and possibly the axis
|
||||
itself) shouldn't be trusted, and the output says so rather than hiding it.
|
||||
"""
|
||||
|
||||
import hashlib
|
||||
import sqlite3
|
||||
from dataclasses import dataclass
|
||||
|
||||
import numpy as np
|
||||
|
||||
SPEECH_EMBED_WORDS = 200 # MiniLM reads ~256 tokens; the opening covers the topic
|
||||
MIN_ABS_CORRELATION = 0.3 # below this, orientation is flagged unreliable
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class Orientation:
|
||||
sign: int # +1 keep, -1 flip
|
||||
correlation: float # spearman rho between axis and party-language proxy
|
||||
reliable: bool
|
||||
proxy_by_outlet: tuple[float, ...] # cos-to-R minus cos-to-D per outlet
|
||||
|
||||
|
||||
def _spearman(a: np.ndarray, b: np.ndarray) -> float:
|
||||
ra = np.argsort(np.argsort(a)).astype(np.float64)
|
||||
rb = np.argsort(np.argsort(b)).astype(np.float64)
|
||||
ra -= ra.mean()
|
||||
rb -= rb.mean()
|
||||
denom = np.sqrt((ra**2).sum() * (rb**2).sum())
|
||||
return float((ra * rb).sum() / denom) if denom > 0 else 0.0
|
||||
|
||||
|
||||
def _cached_embed_texts(conn: sqlite3.Connection, texts: list[str]) -> np.ndarray:
|
||||
"""Embed with the same fingerprint-keyed cache articles use."""
|
||||
from tiltmeter import embed
|
||||
|
||||
hashes = [hashlib.sha256(t.encode()).hexdigest() for t in texts]
|
||||
conn.executescript(embed.CACHE_SCHEMA)
|
||||
cached = {}
|
||||
for h in hashes:
|
||||
row = conn.execute(
|
||||
"SELECT vector FROM embeddings WHERE content_hash = ? AND model = ?",
|
||||
(h, embed.MODEL_NAME),
|
||||
).fetchone()
|
||||
if row:
|
||||
cached[h] = np.frombuffer(row[0], dtype=np.float32)
|
||||
missing = [(h, t) for h, t in zip(hashes, texts) if h not in cached]
|
||||
if missing:
|
||||
vectors = embed.embed_texts([t for _, t in missing])
|
||||
conn.executemany(
|
||||
"INSERT OR IGNORE INTO embeddings (content_hash, model, vector) VALUES (?, ?, ?)",
|
||||
[(h, embed.MODEL_NAME, v.tobytes()) for (h, _), v in zip(missing, vectors)],
|
||||
)
|
||||
conn.commit()
|
||||
cached.update({h: v for (h, _), v in zip(missing, vectors)})
|
||||
return np.stack([cached[h] for h in hashes])
|
||||
|
||||
|
||||
def party_means(conn: sqlite3.Connection) -> dict[str, np.ndarray]:
|
||||
"""Average embedding of each party's floor speeches (unit-normalized)."""
|
||||
rows = conn.execute("SELECT party, text FROM reference_speeches").fetchall()
|
||||
if not rows:
|
||||
raise ValueError("no reference speeches; run: tiltmeter reference")
|
||||
means = {}
|
||||
for party in ("D", "R"):
|
||||
texts = [
|
||||
" ".join(text.split()[:SPEECH_EMBED_WORDS])
|
||||
for p, text in rows
|
||||
if p == party
|
||||
]
|
||||
if len(texts) < 20:
|
||||
raise ValueError(f"only {len(texts)} {party} speeches; reference corpus too thin")
|
||||
mean = _cached_embed_texts(conn, texts).mean(axis=0)
|
||||
means[party] = mean / np.linalg.norm(mean)
|
||||
return means
|
||||
|
||||
|
||||
def outlet_proxy(
|
||||
outlet_vectors: dict[str, np.ndarray], means: dict[str, np.ndarray]
|
||||
) -> dict[str, float]:
|
||||
"""Per outlet: cosine-to-Republican minus cosine-to-Democratic language."""
|
||||
proxy = {}
|
||||
for outlet, vec in outlet_vectors.items():
|
||||
unit = vec / np.linalg.norm(vec)
|
||||
proxy[outlet] = float(unit @ means["R"] - unit @ means["D"])
|
||||
return proxy
|
||||
|
||||
|
||||
def orient_sign(axis_positions: list[float], proxy_values: list[float]) -> Orientation:
|
||||
"""Pure decision: flip the axis if it anti-correlates with party language."""
|
||||
rho = _spearman(np.asarray(axis_positions), np.asarray(proxy_values))
|
||||
return Orientation(
|
||||
sign=-1 if rho < 0 else 1,
|
||||
correlation=rho,
|
||||
reliable=abs(rho) >= MIN_ABS_CORRELATION,
|
||||
proxy_by_outlet=tuple(float(p) for p in proxy_values),
|
||||
)
|
||||
@@ -0,0 +1,105 @@
|
||||
"""Why is this outlet where it is? Show the reader actual headlines.
|
||||
|
||||
Evidence pages (METHODOLOGY.md D10): for every outlet, the stories that most
|
||||
distinguish the two ends of the axis — which of them this outlet covered and
|
||||
which it skipped, with real example headlines — plus its nearest neighbors
|
||||
and its confidence interval. Generated by the same deterministic pipeline as
|
||||
the scores; never hand-edited.
|
||||
"""
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
import numpy as np
|
||||
|
||||
from tiltmeter.signals.selection import _first_axis # same axis, story side
|
||||
|
||||
TOP_STORIES = 8
|
||||
|
||||
|
||||
def _story_axis_coords(matrix: np.ndarray) -> np.ndarray:
|
||||
"""Story (column) positions on the same axis the outlets were scaled on."""
|
||||
return _first_axis(matrix.T)
|
||||
|
||||
|
||||
def render(ratings: dict, stories: list, matrix: np.ndarray, articles: list) -> dict[str, str]:
|
||||
"""Markdown evidence page per outlet, plus an index. Returns {filename: content}."""
|
||||
outlet_order = [o["outlet"] for o in sorted(ratings["outlets"], key=lambda o: o["score"])]
|
||||
scores = {o["outlet"]: o for o in ratings["outlets"]}
|
||||
# matrix rows follow manifest outlet order, which is sorted outlet names
|
||||
row_names = sorted(scores)
|
||||
row_index = {name: i for i, name in enumerate(row_names)}
|
||||
coords = _story_axis_coords(matrix)
|
||||
# align story-axis sign with outlet orientation: a story mostly covered by
|
||||
# right-scored outlets should sit at the right pole
|
||||
row_scores = np.array([scores[n]["score"] for n in row_names])
|
||||
covered_lean = (matrix.T @ row_scores) / np.maximum(matrix.sum(axis=0), 1.0)
|
||||
if float(np.dot(coords, covered_lean)) < 0:
|
||||
coords = -coords
|
||||
|
||||
ranked_stories = np.argsort(coords)
|
||||
pages = {}
|
||||
headline = lambda i: f"[{articles[i]['outlet']}] {articles[i]['title']}" # noqa: E731
|
||||
|
||||
for name in outlet_order:
|
||||
row = row_index[name]
|
||||
entry = scores[name]
|
||||
neighbors = sorted(
|
||||
(o for o in ratings["outlets"] if o["outlet"] != name),
|
||||
key=lambda o: abs(o["score"] - entry["score"]),
|
||||
)[:3]
|
||||
lines = [
|
||||
f"# Evidence: {name}",
|
||||
"",
|
||||
f"- **Score**: {entry['score']:+.3f} "
|
||||
f"(95% CI {entry['ci_low']:+.3f} … {entry['ci_high']:+.3f}); "
|
||||
f"negative = left, positive = right",
|
||||
f"- **Snapshot**: {ratings['snapshot_id']} (corpus {ratings['corpus_hash'][:12]}…), "
|
||||
f"pipeline v{ratings['pipeline_version']}",
|
||||
f"- **Coverage**: {entry['stories_covered']} of {ratings['n_stories']} "
|
||||
f"cross-outlet stories",
|
||||
"- **Nearest neighbors**: "
|
||||
+ ", ".join(f"{n['outlet']} ({n['score']:+.3f})" for n in neighbors),
|
||||
"",
|
||||
"The stories below are the most axis-distinguishing in this snapshot —",
|
||||
"the ones outlets' choices disagree about most. What this outlet covered",
|
||||
"and skipped among them is what placed it where it is.",
|
||||
"",
|
||||
]
|
||||
for label, indices in (
|
||||
("Left-pole stories", ranked_stories[:TOP_STORIES]),
|
||||
("Right-pole stories", ranked_stories[::-1][:TOP_STORIES]),
|
||||
):
|
||||
lines.append(f"## {label}")
|
||||
lines.append("")
|
||||
for sid in indices:
|
||||
story = stories[int(sid)]
|
||||
mark = "covered" if matrix[row, int(sid)] else "skipped"
|
||||
lines.append(f"- **{mark}** — {headline(story.article_indices[0])}")
|
||||
lines.append("")
|
||||
pages[f"{name}.md"] = "\n".join(lines)
|
||||
|
||||
index = [
|
||||
f"# Evidence pages — snapshot {ratings['snapshot_id']}",
|
||||
"",
|
||||
"Generated by the pipeline (never hand-edited). Reference frame: "
|
||||
+ ratings["reference_frame"] + ".",
|
||||
"",
|
||||
"| Outlet | Score | 95% CI |",
|
||||
"|---|---|---|",
|
||||
]
|
||||
for name in outlet_order:
|
||||
e = scores[name]
|
||||
index.append(
|
||||
f"| [{name}]({name}.md) | {e['score']:+.3f} | "
|
||||
f"{e['ci_low']:+.3f} … {e['ci_high']:+.3f} |"
|
||||
)
|
||||
pages["index.md"] = "\n".join(index) + "\n"
|
||||
return pages
|
||||
|
||||
|
||||
def write(pages: dict[str, str], ratings: dict, out_dir: str | Path) -> Path:
|
||||
directory = Path(out_dir) / f"report-{ratings['snapshot_id']}"
|
||||
directory.mkdir(parents=True, exist_ok=True)
|
||||
for filename, content in pages.items():
|
||||
(directory / filename).write_text(content)
|
||||
return directory
|
||||
@@ -0,0 +1,128 @@
|
||||
"""How does a snapshot become ratings.json?
|
||||
|
||||
The assembly line, end to end: manifest → embeddings → story clusters →
|
||||
coverage matrix → selection axis with confidence intervals → orientation by
|
||||
congressional language → one deterministic JSON file. No timestamps, no
|
||||
randomness outside the fixed bootstrap seed: rerunning on the same snapshot
|
||||
must produce the same bytes (METHODOLOGY.md D1, D10).
|
||||
"""
|
||||
|
||||
import json
|
||||
import sqlite3
|
||||
from pathlib import Path
|
||||
|
||||
import numpy as np
|
||||
|
||||
from tiltmeter import embed, orient
|
||||
from tiltmeter.cluster import cluster_articles, coverage_matrix
|
||||
from tiltmeter.signals import selection
|
||||
|
||||
RATINGS_SCHEMA_VERSION = 1
|
||||
REFERENCE_FRAME = (
|
||||
"lean relative to contemporary US congressional party discourse; "
|
||||
"negative = left, positive = right"
|
||||
)
|
||||
|
||||
|
||||
def compute(conn: sqlite3.Connection, manifest: dict, pipeline_version: str) -> dict:
|
||||
"""Run the full pipeline on a loaded manifest; return the ratings dict."""
|
||||
articles = manifest["articles"]
|
||||
outlet_order = manifest["outlets"]
|
||||
|
||||
vectors = embed.embed_hashes(conn, [a["content_hash"] for a in articles])
|
||||
stories = cluster_articles(vectors, [a["outlet"] for a in articles])
|
||||
matrix = coverage_matrix(stories, outlet_order)
|
||||
axis = selection.compute(matrix, outlet_order)
|
||||
|
||||
outlet_vectors = {}
|
||||
for name in outlet_order:
|
||||
rows = [i for i, a in enumerate(articles) if a["outlet"] == name]
|
||||
outlet_vectors[name] = vectors[rows].mean(axis=0)
|
||||
party = orient.party_means(conn)
|
||||
proxy = orient.outlet_proxy(outlet_vectors, party)
|
||||
orientation = orient.orient_sign(
|
||||
list(axis.positions), [proxy[name] for name in axis.outlets]
|
||||
)
|
||||
s = orientation.sign
|
||||
|
||||
covered_counts = matrix.sum(axis=1)
|
||||
outlets_out = [
|
||||
{
|
||||
"outlet": name,
|
||||
"score": round(s * pos, 6),
|
||||
"ci_low": round(min(s * lo, s * hi), 6),
|
||||
"ci_high": round(max(s * lo, s * hi), 6),
|
||||
"stories_covered": int(covered_counts[i]),
|
||||
}
|
||||
for i, (name, pos, lo, hi) in enumerate(
|
||||
zip(axis.outlets, axis.positions, axis.ci_low, axis.ci_high)
|
||||
)
|
||||
]
|
||||
outlets_out.sort(key=lambda o: o["score"])
|
||||
|
||||
return {
|
||||
"schema_version": RATINGS_SCHEMA_VERSION,
|
||||
"pipeline_version": pipeline_version,
|
||||
"snapshot_id": manifest["snapshot_id"],
|
||||
"corpus_hash": manifest["corpus_hash"],
|
||||
"reference_frame": REFERENCE_FRAME,
|
||||
"n_articles": len(articles),
|
||||
"n_stories": len(stories),
|
||||
"axis_inertia_share": round(axis.inertia_share, 6),
|
||||
"orientation": {
|
||||
"method": "party-mean speech embeddings (ADR-0003)",
|
||||
"correlation": round(orientation.correlation, 6),
|
||||
"reliable": orientation.reliable,
|
||||
},
|
||||
"outlets": outlets_out,
|
||||
}
|
||||
|
||||
|
||||
def write(ratings: dict, out_dir: str | Path) -> Path:
|
||||
"""Deterministic serialization: same ratings dict, same bytes."""
|
||||
path = Path(out_dir) / f"ratings-{ratings['snapshot_id']}.json"
|
||||
path.parent.mkdir(parents=True, exist_ok=True)
|
||||
path.write_text(json.dumps(ratings, indent=1, sort_keys=True, ensure_ascii=False) + "\n")
|
||||
return path
|
||||
|
||||
|
||||
def story_details(conn: sqlite3.Connection, manifest: dict) -> tuple[list, np.ndarray, list]:
|
||||
"""Recompute stories + matrix + story axis coords for evidence pages."""
|
||||
articles = manifest["articles"]
|
||||
vectors = embed.embed_hashes(conn, [a["content_hash"] for a in articles])
|
||||
stories = cluster_articles(vectors, [a["outlet"] for a in articles])
|
||||
matrix = coverage_matrix(stories, manifest["outlets"])
|
||||
return stories, matrix, articles
|
||||
|
||||
|
||||
def stories_json(stories: list, articles: list, manifest: dict) -> dict:
|
||||
"""The side-by-side primitive for consumers: who covered each story, how
|
||||
each headlined it. Deterministic; same clusters the scores were built on."""
|
||||
return {
|
||||
"schema_version": RATINGS_SCHEMA_VERSION,
|
||||
"snapshot_id": manifest["snapshot_id"],
|
||||
"corpus_hash": manifest["corpus_hash"],
|
||||
"stories": [
|
||||
{
|
||||
"story_id": s.story_id,
|
||||
"n_outlets": len(s.outlets),
|
||||
"articles": [
|
||||
{
|
||||
"outlet": articles[i]["outlet"],
|
||||
"title": articles[i]["title"],
|
||||
"url": articles[i]["url"],
|
||||
"published": articles[i]["published"],
|
||||
}
|
||||
for i in s.article_indices
|
||||
],
|
||||
}
|
||||
for s in stories
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
def write_stories(payload: dict, out_dir: str | Path) -> Path:
|
||||
path = Path(out_dir) / f"stories-{payload['snapshot_id']}.json"
|
||||
path.parent.mkdir(parents=True, exist_ok=True)
|
||||
path.write_text(json.dumps(payload, indent=1, sort_keys=True, ensure_ascii=False) + "\n")
|
||||
return path
|
||||
@@ -0,0 +1,113 @@
|
||||
"""How does other software get the numbers?
|
||||
|
||||
tiltmeter is a data layer: it computes ratings; separate software visualizes
|
||||
them. This module is the delivery end — a small, read-only HTTP API over the
|
||||
releases directory. No framework, no state, no writes: every response is a
|
||||
file the pipeline already produced, so serving adds nothing to audit.
|
||||
|
||||
GET /health liveness + what's available
|
||||
GET /outlets outlet list incl. sourced ownership data
|
||||
GET /ratings list of snapshot ids with ratings
|
||||
GET /ratings/latest newest ratings.json
|
||||
GET /ratings/{snapshot_id} specific ratings.json
|
||||
GET /stories/{snapshot_id} story clusters: who covered what, headlines
|
||||
GET /manifests/{snapshot_id} corpus manifest (for verifiers)
|
||||
GET /evidence/{snapshot_id}/ evidence index + per-outlet pages
|
||||
|
||||
CORS is wide open: the data is public and consumers are other people's
|
||||
frontends. Snapshot ids sort lexicographically by date, so "latest" is just
|
||||
the maximum.
|
||||
"""
|
||||
|
||||
import json
|
||||
import logging
|
||||
import re
|
||||
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
|
||||
from pathlib import Path
|
||||
|
||||
log = logging.getLogger("tiltmeter.serve")
|
||||
|
||||
SNAPSHOT_ID_RE = re.compile(r"^\d{4}-\d{2}-\d{2}_\d{4}-\d{2}-\d{2}$")
|
||||
OUTLET_PAGE_RE = re.compile(r"^[a-z0-9-]+\.md$|^index\.md$")
|
||||
DEFAULT_PORT = 8477
|
||||
|
||||
|
||||
def _ratings_ids(releases: Path) -> list[str]:
|
||||
return sorted(
|
||||
p.stem.removeprefix("ratings-") for p in releases.glob("ratings-*.json")
|
||||
)
|
||||
|
||||
|
||||
def make_handler(releases: Path, outlets_config: Path | None = None):
|
||||
outlets_payload = None
|
||||
if outlets_config and outlets_config.is_file():
|
||||
import yaml
|
||||
|
||||
outlets_payload = {"outlets": yaml.safe_load(outlets_config.read_text())["outlets"]}
|
||||
|
||||
class Handler(BaseHTTPRequestHandler):
|
||||
server_version = "tiltmeter"
|
||||
|
||||
def _send(self, code: int, body: bytes, content_type: str) -> None:
|
||||
self.send_response(code)
|
||||
self.send_header("Content-Type", content_type)
|
||||
self.send_header("Content-Length", str(len(body)))
|
||||
self.send_header("Access-Control-Allow-Origin", "*")
|
||||
self.send_header("Cache-Control", "public, max-age=300")
|
||||
self.end_headers()
|
||||
self.wfile.write(body)
|
||||
|
||||
def _json(self, code: int, payload) -> None:
|
||||
self._send(code, json.dumps(payload).encode(), "application/json")
|
||||
|
||||
def _file(self, path: Path, content_type: str) -> None:
|
||||
if path.is_file():
|
||||
self._send(200, path.read_bytes(), content_type)
|
||||
else:
|
||||
self._json(404, {"error": "not found"})
|
||||
|
||||
def do_GET(self) -> None: # noqa: N802 - stdlib API
|
||||
parts = [p for p in self.path.split("?")[0].split("/") if p]
|
||||
ids = _ratings_ids(releases)
|
||||
match parts:
|
||||
case ["health"]:
|
||||
self._json(200, {"status": "ok", "ratings": ids})
|
||||
case ["ratings"]:
|
||||
self._json(200, {"snapshots": ids})
|
||||
case ["ratings", "latest"] if ids:
|
||||
self._file(releases / f"ratings-{ids[-1]}.json", "application/json")
|
||||
case ["ratings", sid] if SNAPSHOT_ID_RE.match(sid):
|
||||
self._file(releases / f"ratings-{sid}.json", "application/json")
|
||||
case ["outlets"] if outlets_payload:
|
||||
self._json(200, outlets_payload)
|
||||
case ["stories", sid] if SNAPSHOT_ID_RE.match(sid):
|
||||
self._file(releases / f"stories-{sid}.json", "application/json")
|
||||
case ["manifests", sid] if SNAPSHOT_ID_RE.match(sid):
|
||||
self._file(releases / f"manifest-{sid}.json", "application/json")
|
||||
case ["evidence", sid] if SNAPSHOT_ID_RE.match(sid):
|
||||
self._file(releases / f"report-{sid}" / "index.md", "text/markdown")
|
||||
case ["evidence", sid, page] if (
|
||||
SNAPSHOT_ID_RE.match(sid) and OUTLET_PAGE_RE.match(page)
|
||||
):
|
||||
self._file(releases / f"report-{sid}" / page, "text/markdown")
|
||||
case _:
|
||||
self._json(404, {"error": "not found"})
|
||||
|
||||
def log_message(self, fmt: str, *args) -> None:
|
||||
log.info("%s %s", self.address_string(), fmt % args)
|
||||
|
||||
return Handler
|
||||
|
||||
|
||||
def run(
|
||||
releases_dir: str | Path,
|
||||
host: str = "0.0.0.0",
|
||||
port: int = DEFAULT_PORT,
|
||||
outlets_config: str | Path = "config/outlets.yaml",
|
||||
) -> None:
|
||||
releases = Path(releases_dir)
|
||||
server = ThreadingHTTPServer(
|
||||
(host, port), make_handler(releases, Path(outlets_config))
|
||||
)
|
||||
log.info("serving %s on %s:%d", releases, host, port)
|
||||
server.serve_forever()
|
||||
@@ -0,0 +1,43 @@
|
||||
"""Does orientation flip the axis when — and only when — it should?
|
||||
|
||||
Pure-function tests: no model, no network. The embedding-dependent parts
|
||||
(party means from real speeches) are exercised by the live `tiltmeter run`;
|
||||
what must never regress silently is the flip decision and its honesty about
|
||||
weak agreement.
|
||||
"""
|
||||
|
||||
import numpy as np
|
||||
|
||||
from tiltmeter import orient
|
||||
|
||||
|
||||
def test_correctly_oriented_axis_is_kept():
|
||||
axis = [-0.9, -0.5, 0.0, 0.4, 0.8] # already: left negative
|
||||
proxy = [-0.10, -0.06, 0.01, 0.05, 0.09] # closer to R language as we go right
|
||||
result = orient.orient_sign(axis, proxy)
|
||||
assert result.sign == 1
|
||||
assert result.correlation > 0.9
|
||||
assert result.reliable
|
||||
|
||||
|
||||
def test_backwards_axis_is_flipped():
|
||||
axis = [0.9, 0.5, 0.0, -0.4, -0.8] # backwards: left ended up positive
|
||||
proxy = [-0.10, -0.06, 0.01, 0.05, 0.09]
|
||||
result = orient.orient_sign(axis, proxy)
|
||||
assert result.sign == -1
|
||||
assert result.correlation < -0.9
|
||||
|
||||
|
||||
def test_weak_agreement_is_flagged_unreliable():
|
||||
rng = np.random.default_rng(3)
|
||||
axis = list(rng.normal(size=12))
|
||||
proxy = list(rng.normal(size=12)) # unrelated
|
||||
result = orient.orient_sign(axis, proxy)
|
||||
assert not result.reliable
|
||||
|
||||
|
||||
def test_spearman_matches_known_value():
|
||||
# perfect monotone but nonlinear relation: rank correlation must be 1
|
||||
a = np.array([1.0, 2.0, 3.0, 4.0])
|
||||
assert orient._spearman(a, a**3) == 1.0
|
||||
assert orient._spearman(a, -(a**3)) == -1.0
|
||||
@@ -0,0 +1,89 @@
|
||||
"""Does the API serve exactly what the pipeline produced — and nothing else?
|
||||
|
||||
Spins the real server on an ephemeral port over a fixture releases dir.
|
||||
The contract: pipeline outputs are served verbatim, unknown paths 404,
|
||||
path traversal is impossible by construction (strict id/page regexes).
|
||||
"""
|
||||
|
||||
import http.client
|
||||
import json
|
||||
import threading
|
||||
from http.server import ThreadingHTTPServer
|
||||
|
||||
import pytest
|
||||
|
||||
from tiltmeter import serve
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def api(tmp_path):
|
||||
(tmp_path / "ratings-2026-07-01_2026-07-15.json").write_text('{"snapshot_id": "old"}')
|
||||
(tmp_path / "ratings-2026-07-10_2026-07-24.json").write_text('{"snapshot_id": "new"}')
|
||||
(tmp_path / "manifest-2026-07-10_2026-07-24.json").write_text('{"articles": []}')
|
||||
(tmp_path / "stories-2026-07-10_2026-07-24.json").write_text('{"stories": []}')
|
||||
report = tmp_path / "report-2026-07-10_2026-07-24"
|
||||
report.mkdir()
|
||||
(report / "index.md").write_text("# Evidence index")
|
||||
(report / "fox-news.md").write_text("# Evidence: fox-news")
|
||||
outlets = tmp_path / "outlets.yaml"
|
||||
outlets.write_text(
|
||||
"outlets:\n"
|
||||
" - name: fox-news\n"
|
||||
" homepage: https://www.foxnews.com\n"
|
||||
" feed: https://example.com/feed\n"
|
||||
" ownership: {owner: Fox Corporation, type: public-company}\n"
|
||||
)
|
||||
|
||||
server = ThreadingHTTPServer(
|
||||
("127.0.0.1", 0), serve.make_handler(tmp_path, outlets)
|
||||
)
|
||||
thread = threading.Thread(target=server.serve_forever, daemon=True)
|
||||
thread.start()
|
||||
yield server.server_address[1]
|
||||
server.shutdown()
|
||||
|
||||
|
||||
def get(port, path):
|
||||
conn = http.client.HTTPConnection("127.0.0.1", port, timeout=5)
|
||||
conn.request("GET", path)
|
||||
resp = conn.getresponse()
|
||||
return resp.status, resp.read(), dict(resp.getheaders())
|
||||
|
||||
|
||||
def test_health_and_listing(api):
|
||||
status, body, headers = get(api, "/health")
|
||||
assert status == 200
|
||||
assert json.loads(body)["ratings"] == [
|
||||
"2026-07-01_2026-07-15",
|
||||
"2026-07-10_2026-07-24",
|
||||
]
|
||||
assert headers["Access-Control-Allow-Origin"] == "*"
|
||||
|
||||
|
||||
def test_latest_is_newest_snapshot(api):
|
||||
status, body, _ = get(api, "/ratings/latest")
|
||||
assert status == 200
|
||||
assert json.loads(body)["snapshot_id"] == "new"
|
||||
|
||||
|
||||
def test_specific_ratings_manifest_and_evidence(api):
|
||||
assert json.loads(get(api, "/ratings/2026-07-01_2026-07-15")[1])["snapshot_id"] == "old"
|
||||
assert get(api, "/manifests/2026-07-10_2026-07-24")[0] == 200
|
||||
assert b"Evidence index" in get(api, "/evidence/2026-07-10_2026-07-24")[1]
|
||||
assert b"fox-news" in get(api, "/evidence/2026-07-10_2026-07-24/fox-news.md")[1]
|
||||
|
||||
|
||||
def test_outlets_and_stories_endpoints(api):
|
||||
status, body, _ = get(api, "/outlets")
|
||||
assert status == 200
|
||||
outlet = json.loads(body)["outlets"][0]
|
||||
assert outlet["ownership"]["owner"] == "Fox Corporation"
|
||||
assert json.loads(get(api, "/stories/2026-07-10_2026-07-24")[1]) == {"stories": []}
|
||||
assert get(api, "/stories/nonsense")[0] == 404
|
||||
|
||||
|
||||
def test_unknown_and_hostile_paths_404(api):
|
||||
assert get(api, "/ratings/nonsense")[0] == 404
|
||||
assert get(api, "/evidence/2026-07-10_2026-07-24/../../etc/passwd")[0] == 404
|
||||
assert get(api, "/evidence/2026-07-10_2026-07-24/%2e%2e%2fsecrets.md")[0] == 404
|
||||
assert get(api, "/")[0] == 404
|
||||
@@ -1,6 +1,10 @@
|
||||
version = 1
|
||||
revision = 3
|
||||
requires-python = ">=3.13"
|
||||
resolution-markers = [
|
||||
"sys_platform != 'darwin'",
|
||||
"sys_platform == 'darwin'",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "annotated-doc"
|
||||
@@ -124,79 +128,6 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/1f/38/ce65091ff20a16e06d17418c4353af5f56d3190821b1a06983c79ae79274/courlan-1.4.0-py3-none-any.whl", hash = "sha256:ad1dbdefd912ca7238d4607dc855df5df097f56bac175dd662c84eed3802f49e", size = 34193, upload-time = "2026-06-01T17:30:14.984Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cuda-bindings"
|
||||
version = "13.3.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "cuda-pathfinder" },
|
||||
]
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/cc/6e/2394f8163360f8391f8f1b7e72d300a82724edb81a7b7084c799fbd4c91f/cuda_bindings-13.3.1-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9efb21c1ee64981e184b9e0ba5eb3179e5ba3d4b51665a6cb52b8ef3d01a7cbf", size = 5920504, upload-time = "2026-05-29T23:11:56.883Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/34/c2/ef9b6a63f7dc432712a462c816662e662e00d38caa9b861c8c2588195d03/cuda_bindings-13.3.1-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2732904099e0a4d4db774a5fc6d91ee95fae065b4d2ecabb4968c5fe2406c9d7", size = 6476660, upload-time = "2026-05-29T23:11:59.188Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b1/81/bff68ce829999c1e4209c761bbf903b1c06ec570416ddb25020864ad5907/cuda_bindings-13.3.1-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1ab2f74ed65bfef4163ba07a8db16f1085e0729291db12a2423aff84ee8278b8", size = 6013639, upload-time = "2026-05-29T23:12:03.509Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d4/e0/c8a1f0c8f9ffdea4f5fe6dbab89b326cef4d85caf489dad39e209da89416/cuda_bindings-13.3.1-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:efd4c814d311ec08c981f6dded1dbe7d4b371067ee4f6c14cccec4bde9590f80", size = 6534419, upload-time = "2026-05-29T23:12:05.633Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/52/b8/83b1f563925b290f2d11a01a77a84013ba56052fe3653a5bef3ccfbb43d6/cuda_bindings-13.3.1-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c3c772dfff49681541d59630c90f858e173ac926b9c593a2b7123f2a1043cc76", size = 5809771, upload-time = "2026-05-29T23:12:10.422Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/12/20/e79b4bfe98f075195afb6343d41c498f9dbd2d161d7021d4d28bceb83581/cuda_bindings-13.3.1-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:36febb7c1079d68a981dbbd8d5a67235b399802b82075c9388624719607e52b9", size = 6358584, upload-time = "2026-05-29T23:12:12.767Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cuda-pathfinder"
|
||||
version = "1.5.6"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/d2/53/8fc9b0cdc5b7f62746e6a01b85b6461e5ae27f871010a5fcf8fa6950766d/cuda_pathfinder-1.5.6-py3-none-any.whl", hash = "sha256:7e4c07c117b78ba1fb35dac4c444d21f3677b1b1ff56175c53a8e3025c5b43c0", size = 52972, upload-time = "2026-06-30T00:58:04.34Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cuda-toolkit"
|
||||
version = "13.0.3.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/d1/c7/a79086a62c98befcdb8349656c6f114e2db3b8b2422f6e25c97a7f2a9a3c/cuda_toolkit-13.0.3.0-py2.py3-none-any.whl", hash = "sha256:d693caaa261214ddd7dbb60d68e71cbed884e68c2be7509778f3051da0b91c3f", size = 2512, upload-time = "2026-04-14T00:50:08.173Z" },
|
||||
]
|
||||
|
||||
[package.optional-dependencies]
|
||||
cublas = [
|
||||
{ name = "nvidia-cublas", marker = "(platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux') or (platform_machine == 'AMD64' and sys_platform == 'win32')" },
|
||||
{ name = "nvidia-cuda-nvrtc", marker = "(platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux') or (platform_machine == 'AMD64' and sys_platform == 'win32')" },
|
||||
]
|
||||
cudart = [
|
||||
{ name = "nvidia-cuda-runtime", marker = "(platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux') or (platform_machine == 'AMD64' and sys_platform == 'win32')" },
|
||||
]
|
||||
cufft = [
|
||||
{ name = "nvidia-cufft", marker = "(platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux') or (platform_machine == 'AMD64' and sys_platform == 'win32')" },
|
||||
{ name = "nvidia-nvjitlink", marker = "(platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux') or (platform_machine == 'AMD64' and sys_platform == 'win32')" },
|
||||
]
|
||||
cufile = [
|
||||
{ name = "nvidia-cufile", marker = "(platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" },
|
||||
]
|
||||
cupti = [
|
||||
{ name = "nvidia-cuda-cupti", marker = "(platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux') or (platform_machine == 'AMD64' and sys_platform == 'win32')" },
|
||||
]
|
||||
curand = [
|
||||
{ name = "nvidia-curand", marker = "(platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux') or (platform_machine == 'AMD64' and sys_platform == 'win32')" },
|
||||
]
|
||||
cusolver = [
|
||||
{ name = "nvidia-cublas", marker = "(platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux') or (platform_machine == 'AMD64' and sys_platform == 'win32')" },
|
||||
{ name = "nvidia-cusolver", marker = "(platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux') or (platform_machine == 'AMD64' and sys_platform == 'win32')" },
|
||||
{ name = "nvidia-cusparse", marker = "(platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux') or (platform_machine == 'AMD64' and sys_platform == 'win32')" },
|
||||
{ name = "nvidia-nvjitlink", marker = "(platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux') or (platform_machine == 'AMD64' and sys_platform == 'win32')" },
|
||||
]
|
||||
cusparse = [
|
||||
{ name = "nvidia-cusparse", marker = "(platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux') or (platform_machine == 'AMD64' and sys_platform == 'win32')" },
|
||||
{ name = "nvidia-nvjitlink", marker = "(platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux') or (platform_machine == 'AMD64' and sys_platform == 'win32')" },
|
||||
]
|
||||
nvjitlink = [
|
||||
{ name = "nvidia-nvjitlink", marker = "(platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux') or (platform_machine == 'AMD64' and sys_platform == 'win32')" },
|
||||
]
|
||||
nvrtc = [
|
||||
{ name = "nvidia-cuda-nvrtc", marker = "(platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux') or (platform_machine == 'AMD64' and sys_platform == 'win32')" },
|
||||
]
|
||||
nvtx = [
|
||||
{ name = "nvidia-nvtx", marker = "(platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux') or (platform_machine == 'AMD64' and sys_platform == 'win32')" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dateparser"
|
||||
version = "1.4.1"
|
||||
@@ -642,158 +573,6 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/a1/5a/4d2b1601df3602dba7a14f3348ba9bfe94a18adb428e693df6154c293831/numpy-2.5.1-cp314-cp314t-win_arm64.whl", hash = "sha256:5a6db61f9aaa57e369905c67d852045d3c4f7126405b29d09b19dec118e9c9cb", size = 10697674, upload-time = "2026-07-04T17:07:58.506Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nvidia-cublas"
|
||||
version = "13.1.1.3"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "nvidia-cuda-nvrtc" },
|
||||
]
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/a7/a1/0bd24ee8c8d03adac032fd2909426a00c88f8c57961b1277ded97f91119f/nvidia_cublas-13.1.1.3-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:b7a210458267ac818974c53038fbec2e969d5c99f305ab15c72522fa9f001dd5", size = 542848918, upload-time = "2026-04-08T18:46:22.985Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/3b/cd/154ca20c38269e05eff77c1464e6c1da89f50a6390b565e9d82e06bc11e1/nvidia_cublas-13.1.1.3-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:37936a16db8fe4ac1f065c2139360608a543a09275cb1a1af612e08cfa065436", size = 423138758, upload-time = "2026-04-08T18:46:58.655Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nvidia-cuda-cupti"
|
||||
version = "13.0.85"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/2a/2a/80353b103fc20ce05ef51e928daed4b6015db4aaa9162ed0997090fe2250/nvidia_cuda_cupti-13.0.85-py3-none-manylinux_2_25_aarch64.whl", hash = "sha256:796bd679890ee55fb14a94629b698b6db54bcfd833d391d5e94017dd9d7d3151", size = 10310827, upload-time = "2025-09-04T08:26:42.012Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/33/6d/737d164b4837a9bbd202f5ae3078975f0525a55730fe871d8ed4e3b952b0/nvidia_cuda_cupti-13.0.85-py3-none-manylinux_2_25_x86_64.whl", hash = "sha256:4eb01c08e859bf924d222250d2e8f8b8ff6d3db4721288cf35d14252a4d933c8", size = 10715597, upload-time = "2025-09-04T08:26:51.312Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nvidia-cuda-nvrtc"
|
||||
version = "13.0.88"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/c3/68/483a78f5e8f31b08fb1bb671559968c0ca3a065ac7acabfc7cee55214fd6/nvidia_cuda_nvrtc-13.0.88-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:ad9b6d2ead2435f11cbb6868809d2adeeee302e9bb94bcf0539c7a40d80e8575", size = 90215200, upload-time = "2025-09-04T08:28:44.204Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b7/dc/6bb80850e0b7edd6588d560758f17e0550893a1feaf436807d64d2da040f/nvidia_cuda_nvrtc-13.0.88-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d27f20a0ca67a4bb34268a5e951033496c5b74870b868bacd046b1b8e0c3267b", size = 43015449, upload-time = "2025-09-04T08:28:20.239Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nvidia-cuda-runtime"
|
||||
version = "13.0.96"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/87/4f/17d7b9b8e285199c58ce28e31b5c5bbaa4d8271af06a89b6405258245de2/nvidia_cuda_runtime-13.0.96-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ef9bcbe90493a2b9d810e43d249adb3d02e98dd30200d86607d8d02687c43f55", size = 2261060, upload-time = "2025-10-09T08:55:15.78Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2e/24/d1558f3b68b1d26e706813b1d10aa1d785e4698c425af8db8edc3dced472/nvidia_cuda_runtime-13.0.96-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7f82250d7782aa23b6cfe765ecc7db554bd3c2870c43f3d1821f1d18aebf0548", size = 2243632, upload-time = "2025-10-09T08:55:36.117Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nvidia-cudnn-cu13"
|
||||
version = "9.20.0.48"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "nvidia-cublas" },
|
||||
]
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/56/c5/83384d846b2fd17c44bd499b36c75a45ed4f095fbbb2252294e89cea5c5c/nvidia_cudnn_cu13-9.20.0.48-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:e31454ae00094b0c55319d9d15b6fa2fc50a9e1c0f5c8c80fb75258234e731e1", size = 444574296, upload-time = "2026-03-09T19:28:27.751Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6e/5e/edb9c0ae051602c3ccaffe424256463636d639e27d7f302dde9975ef9e7a/nvidia_cudnn_cu13-9.20.0.48-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:0c45dd8eeb50b603f07995b1b300c62ffe6a1980482b82b3bcf94a4ca9d49304", size = 366173588, upload-time = "2026-03-09T19:29:34.474Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nvidia-cufft"
|
||||
version = "12.0.0.61"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "nvidia-nvjitlink" },
|
||||
]
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/8b/ae/f417a75c0259e85c1d2f83ca4e960289a5f814ed0cea74d18c353d3e989d/nvidia_cufft-12.0.0.61-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:2708c852ef8cd89d1d2068bdbece0aa188813a0c934db3779b9b1faa8442e5f5", size = 214053554, upload-time = "2025-09-04T08:31:38.196Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a8/2f/7b57e29836ea8714f81e9898409196f47d772d5ddedddf1592eadb8ab743/nvidia_cufft-12.0.0.61-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:6c44f692dce8fd5ffd3e3df134b6cdb9c2f72d99cf40b62c32dde45eea9ddad3", size = 214085489, upload-time = "2025-09-04T08:31:56.044Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nvidia-cufile"
|
||||
version = "1.15.1.6"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/3f/70/4f193de89a48b71714e74602ee14d04e4019ad36a5a9f20c425776e72cd6/nvidia_cufile-1.15.1.6-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:08a3ecefae5a01c7f5117351c64f17c7c62efa5fffdbe24fc7d298da19cd0b44", size = 1223672, upload-time = "2025-09-04T08:32:22.779Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ab/73/cc4a14c9813a8a0d509417cf5f4bdaba76e924d58beb9864f5a7baceefbf/nvidia_cufile-1.15.1.6-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:bdc0deedc61f548bddf7733bdc216456c2fdb101d020e1ab4b88d232d5e2f6d1", size = 1136992, upload-time = "2025-09-04T08:32:14.119Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nvidia-curand"
|
||||
version = "10.4.0.35"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/1e/72/7c2ae24fb6b63a32e6ae5d241cc65263ea18d08802aaae087d9f013335a2/nvidia_curand-10.4.0.35-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:133df5a7509c3e292aaa2b477afd0194f06ce4ea24d714d616ff36439cee349a", size = 61962106, upload-time = "2025-08-04T10:21:41.128Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a5/9f/be0a41ca4a4917abf5cb9ae0daff1a6060cc5de950aec0396de9f3b52bc5/nvidia_curand-10.4.0.35-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:1aee33a5da6e1db083fe2b90082def8915f30f3248d5896bcec36a579d941bfc", size = 59544258, upload-time = "2025-08-04T10:22:03.992Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nvidia-cusolver"
|
||||
version = "12.0.4.66"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "nvidia-cublas" },
|
||||
{ name = "nvidia-cusparse" },
|
||||
{ name = "nvidia-nvjitlink" },
|
||||
]
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/c8/c3/b30c9e935fc01e3da443ec0116ed1b2a009bb867f5324d3f2d7e533e776b/nvidia_cusolver-12.0.4.66-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:02c2457eaa9e39de20f880f4bd8820e6a1cfb9f9a34f820eb12a155aa5bc92d2", size = 223467760, upload-time = "2025-09-04T08:33:04.222Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5f/67/cba3777620cdacb99102da4042883709c41c709f4b6323c10781a9c3aa34/nvidia_cusolver-12.0.4.66-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:0a759da5dea5c0ea10fd307de75cdeb59e7ea4fcb8add0924859b944babf1112", size = 200941980, upload-time = "2025-09-04T08:33:22.767Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nvidia-cusparse"
|
||||
version = "12.6.3.3"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "nvidia-nvjitlink" },
|
||||
]
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/f8/94/5c26f33738ae35276672f12615a64bd008ed5be6d1ebcb23579285d960a9/nvidia_cusparse-12.6.3.3-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:80bcc4662f23f1054ee334a15c72b8940402975e0eab63178fc7e670aa59472c", size = 162155568, upload-time = "2025-09-04T08:33:42.864Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/fa/18/623c77619c31d62efd55302939756966f3ecc8d724a14dab2b75f1508850/nvidia_cusparse-12.6.3.3-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2b3c89c88d01ee0e477cb7f82ef60a11a4bcd57b6b87c33f789350b59759360b", size = 145942937, upload-time = "2025-09-04T08:33:58.029Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nvidia-cusparselt-cu13"
|
||||
version = "0.8.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/46/e1/cdc1797eadf82d3a9a575a19b33fdc871a97edbec42c00b5b5e914f4aff4/nvidia_cusparselt_cu13-0.8.1-py3-none-manylinux2014_aarch64.whl", hash = "sha256:4dca476c50bf4780d46cd0bfbd82e2bc10a08e4fef7950917ce8d7578d22a23f", size = 221051344, upload-time = "2025-09-05T18:49:51.289Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/34/7d/2661f2fb3ac4302f3a246f5fc030213ac60c1fe0bce84f9783dbd831dbb7/nvidia_cusparselt_cu13-0.8.1-py3-none-manylinux2014_x86_64.whl", hash = "sha256:786ce87568c303fadb5afcc7102d454cd3040d75f6f8626f5db460d1871f4dd0", size = 170148586, upload-time = "2025-09-05T18:50:50.248Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nvidia-nccl-cu13"
|
||||
version = "2.29.7"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/72/0d/daf50d44177ee0cbc7ff0a0c91eb5ff676c82be42f9a970bc7597f440c3a/nvidia_nccl_cu13-2.29.7-py3-none-manylinux_2_18_aarch64.whl", hash = "sha256:674a12383e3c38a1bcccae7d4f3633b37852230b6047883cb2f4c2d1b36d9bf5", size = 206014712, upload-time = "2026-03-03T05:34:20.843Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/67/f4/58e4e91b6919367c7aafb8e36fce9aad1a3047e536bf7e2fd560927d3a4c/nvidia_nccl_cu13-2.29.7-py3-none-manylinux_2_18_x86_64.whl", hash = "sha256:edd81538446786ec3b73972543e53bb43bcaf0bfc8ef76cb679fcc390ffe136d", size = 205976000, upload-time = "2026-03-03T05:36:24.472Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nvidia-nvjitlink"
|
||||
version = "13.3.33"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/f0/ee/580ca6f29dcab0221db8706badca1bbbb084f1975c4d4e83329c3a7e31f0/nvidia_nvjitlink-13.3.33-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:26a6de7fb4c8fdaa7703d3dad720d6d427ddfea5c48a528fd97c11733ad830e5", size = 40742423, upload-time = "2026-05-26T16:54:51.613Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/69/30/45414e35ff2eee7db3da037e5707037ccf9d2b5218ffbdb055ea4d5aa98a/nvidia_nvjitlink-13.3.33-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ce48b37dfeb3cb1eae4cf85adacb47d7a6539ea2272870c9a3628ce275c2037e", size = 39168635, upload-time = "2026-05-26T16:54:13.906Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nvidia-nvshmem-cu13"
|
||||
version = "3.4.5"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/dc/0f/05cc9c720236dcd2db9c1ab97fff629e96821be2e63103569da0c9b72f19/nvidia_nvshmem_cu13-3.4.5-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:6dc2a197f38e5d0376ad52cd1a2a3617d3cdc150fd5966f4aee9bcebb1d68fe9", size = 60215947, upload-time = "2025-09-06T00:32:20.022Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/3c/35/a9bf80a609e74e3b000fef598933235c908fcefcef9026042b8e6dfde2a9/nvidia_nvshmem_cu13-3.4.5-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:290f0a2ee94c9f3687a02502f3b9299a9f9fe826e6d0287ee18482e78d495b80", size = 60412546, upload-time = "2025-09-06T00:32:41.564Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nvidia-nvtx"
|
||||
version = "13.0.85"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/c2/f3/d86c845465a2723ad7e1e5c36dcd75ddb82898b3f53be47ebd429fb2fa5d/nvidia_nvtx-13.0.85-py3-none-manylinux1_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:4936d1d6780fbe68db454f5e72a42ff64d1fd6397df9f363ae786930fd5c1cd4", size = 148047, upload-time = "2025-09-04T08:29:01.761Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a8/64/3708a90d1ebe202ffdeb7185f878a3c84d15c2b2c31858da2ce0583e2def/nvidia_nvtx-13.0.85-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:cb7780edb6b14107373c835bf8b72e7a178bac7367e23da7acb108f973f157a6", size = 148878, upload-time = "2025-09-04T08:28:53.627Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "packaging"
|
||||
version = "26.2"
|
||||
@@ -1135,7 +914,8 @@ dependencies = [
|
||||
{ name = "numpy" },
|
||||
{ name = "scikit-learn" },
|
||||
{ name = "scipy" },
|
||||
{ name = "torch" },
|
||||
{ name = "torch", version = "2.13.0", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "sys_platform == 'darwin'" },
|
||||
{ name = "torch", version = "2.13.0+cpu", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "sys_platform != 'darwin'" },
|
||||
{ name = "tqdm" },
|
||||
{ name = "transformers" },
|
||||
{ name = "typing-extensions" },
|
||||
@@ -1215,7 +995,7 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "tiltmeter"
|
||||
version = "0.2.0"
|
||||
version = "0.3.0"
|
||||
source = { editable = "." }
|
||||
dependencies = [
|
||||
{ name = "feedparser" },
|
||||
@@ -1224,6 +1004,8 @@ dependencies = [
|
||||
{ name = "requests" },
|
||||
{ name = "scikit-learn" },
|
||||
{ name = "sentence-transformers" },
|
||||
{ name = "torch", version = "2.13.0", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "sys_platform == 'darwin'" },
|
||||
{ name = "torch", version = "2.13.0+cpu", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "sys_platform != 'darwin'" },
|
||||
{ name = "trafilatura" },
|
||||
]
|
||||
|
||||
@@ -1242,6 +1024,7 @@ requires-dist = [
|
||||
{ name = "requests", specifier = ">=2.32" },
|
||||
{ name = "scikit-learn", specifier = ">=1.5" },
|
||||
{ name = "sentence-transformers", specifier = ">=3.0" },
|
||||
{ name = "torch", specifier = ">=2.6", index = "https://download.pytorch.org/whl/cpu" },
|
||||
{ name = "trafilatura", specifier = ">=1.12" },
|
||||
]
|
||||
|
||||
@@ -1290,36 +1073,59 @@ wheels = [
|
||||
[[package]]
|
||||
name = "torch"
|
||||
version = "2.13.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
source = { registry = "https://download.pytorch.org/whl/cpu" }
|
||||
resolution-markers = [
|
||||
"sys_platform == 'darwin'",
|
||||
]
|
||||
dependencies = [
|
||||
{ name = "cuda-bindings", marker = "python_full_version < '3.15' and sys_platform == 'linux'" },
|
||||
{ name = "cuda-toolkit", extra = ["cublas", "cudart", "cufft", "cufile", "cupti", "curand", "cusolver", "cusparse", "nvjitlink", "nvrtc", "nvtx"], marker = "sys_platform == 'linux'" },
|
||||
{ name = "filelock" },
|
||||
{ name = "fsspec" },
|
||||
{ name = "jinja2" },
|
||||
{ name = "networkx" },
|
||||
{ name = "nvidia-cudnn-cu13", marker = "sys_platform == 'linux'" },
|
||||
{ name = "nvidia-cusparselt-cu13", marker = "sys_platform == 'linux'" },
|
||||
{ name = "nvidia-nccl-cu13", marker = "sys_platform == 'linux'" },
|
||||
{ name = "nvidia-nvshmem-cu13", marker = "sys_platform == 'linux'" },
|
||||
{ name = "setuptools" },
|
||||
{ name = "sympy" },
|
||||
{ name = "triton", marker = "python_full_version < '3.15' and sys_platform == 'linux'" },
|
||||
{ name = "typing-extensions" },
|
||||
{ name = "filelock", marker = "sys_platform == 'darwin'" },
|
||||
{ name = "fsspec", marker = "sys_platform == 'darwin'" },
|
||||
{ name = "jinja2", marker = "sys_platform == 'darwin'" },
|
||||
{ name = "networkx", marker = "sys_platform == 'darwin'" },
|
||||
{ name = "setuptools", marker = "sys_platform == 'darwin'" },
|
||||
{ name = "sympy", marker = "sys_platform == 'darwin'" },
|
||||
{ name = "typing-extensions", marker = "sys_platform == 'darwin'" },
|
||||
]
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/0d/fa/c1c10b7aff4a9a3e8956d4f0a5f468fa6db7abc3208805719076772b4833/torch-2.13.0-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:33449899ce5496c1b84b4853179d94fd102028ae1407314d9fb956bb79e70d09", size = 111213743, upload-time = "2026-07-08T16:03:28.579Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/11/18/9ecb37b56293a0be8d80f810bf672a72fe7e02f8b475d5ef1b9bf8a0d748/torch-2.13.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:1e09d6a722504957c694faceca843acde562786df1144ebcc5a74075ec7f6005", size = 427213008, upload-time = "2026-07-08T16:03:44.106Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d4/5a/7c50ba1b7b713d71d34669c6d13dab0a11531a3eceb0307a5162dbfec0f7/torch-2.13.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:a3a9a21312872af8a26950b2c15680335a386a1f56ed03e780653d78b9607e9e", size = 526602329, upload-time = "2026-07-08T16:03:12.649Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/91/3d/e7adcc6aaf36961cd18f56cf8ad0f3058c3a5c84ccf391762176c94581b8/torch-2.13.0-cp313-cp313-win_amd64.whl", hash = "sha256:49b58f1e2c52440abb6f17c28f0335fe6c6d01ad1a7f55b0183b81e4b34d64e6", size = 122057920, upload-time = "2026-07-08T16:03:01.808Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/36/76/6dcc7f0c07052102dd36f83cbc5800842a909c8c3fbf1a7f8a5844954de9/torch-2.13.0-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:d849b390e07d8d333ce8ecaf91b273c656c598379a19c9acf1318a883f6b391c", size = 111227066, upload-time = "2026-07-08T16:03:33.6Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e9/09/2c10e8cd0e00fa5d23c052df6ce467eaa7182399f5e0f824f1e4ff42ccae/torch-2.13.0-cp314-cp314-manylinux_2_28_aarch64.whl", hash = "sha256:a3893dc2da0a972a8ca5d698c85a9f967559ac5f8ee1797b77408aa8734d073c", size = 427226309, upload-time = "2026-07-08T16:02:53.127Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/76/c6/22c2102bbef14ca6a6cb4c20e42f088e49c5f812be4e160ae57502e325f9/torch-2.13.0-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:49f1ea385c754e54919408a9bb3b5a72b0b755bbe2c916c1d6f70afbec4908a2", size = 526614507, upload-time = "2026-07-08T16:02:16.441Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2b/0c/7d1deb6bce5bc3e6042caf39100ac768eba3b9a098e1dddd16f75bd6489b/torch-2.13.0-cp314-cp314-win_amd64.whl", hash = "sha256:4f8573e3ce9ebcd53fe922f01077a6085ccdfbe5f12fd215883a9d87d7a744fd", size = 122051871, upload-time = "2026-07-08T16:03:23.521Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f4/ce/aa8b7f9949d32e0f2f624f342bc3b48112c1b8a130288465938bc83bcbf9/torch-2.13.0-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:c28def70706c2f9ecc752574766e8ae4da9b810ab6676b611166761a78a9f1e1", size = 111537025, upload-time = "2026-07-08T16:02:44.28Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/69/d1/491e3a0389430946145888b0203f2b6a759ce2a61481b96a85c2da4f2ced/torch-2.13.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:31061ff56ed8fbf26c749806905aeb749ebeb819810fd5d52508aa5afd90dddc", size = 427219769, upload-time = "2026-07-08T16:02:31.18Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9a/1d/38006e045bf0a1fc28ef01e757c554e59e59a8770c284bc4f47b14e60441/torch-2.13.0-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:cc26eead4cf51d0b544e31e364dcf000846549c273bd148936fe9d24d29acb92", size = 526571320, upload-time = "2026-07-08T16:01:59.348Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/56/94/655c91992a882bd5071aa0b5d22a07dbb130d801e872be97c0b627a7c693/torch-2.13.0-cp314-cp314t-win_amd64.whl", hash = "sha256:a7de8a313090dc5c7d7ba4bfe5c3be222528f9a4dba1acc83bddb1157360c4b8", size = 122306773, upload-time = "2026-07-08T16:02:39.832Z" },
|
||||
{ url = "https://download-r2.pytorch.org/whl/cpu/torch-2.13.0-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:33449899ce5496c1b84b4853179d94fd102028ae1407314d9fb956bb79e70d09", upload-time = "2026-07-08T12:26:23Z" },
|
||||
{ url = "https://download-r2.pytorch.org/whl/cpu/torch-2.13.0-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:d849b390e07d8d333ce8ecaf91b273c656c598379a19c9acf1318a883f6b391c", upload-time = "2026-07-08T12:26:28Z" },
|
||||
{ url = "https://download-r2.pytorch.org/whl/cpu/torch-2.13.0-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:c28def70706c2f9ecc752574766e8ae4da9b810ab6676b611166761a78a9f1e1", upload-time = "2026-07-08T12:26:33Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "torch"
|
||||
version = "2.13.0+cpu"
|
||||
source = { registry = "https://download.pytorch.org/whl/cpu" }
|
||||
resolution-markers = [
|
||||
"sys_platform != 'darwin'",
|
||||
]
|
||||
dependencies = [
|
||||
{ name = "filelock", marker = "sys_platform != 'darwin'" },
|
||||
{ name = "fsspec", marker = "sys_platform != 'darwin'" },
|
||||
{ name = "jinja2", marker = "sys_platform != 'darwin'" },
|
||||
{ name = "networkx", marker = "sys_platform != 'darwin'" },
|
||||
{ name = "setuptools", marker = "sys_platform != 'darwin'" },
|
||||
{ name = "sympy", marker = "sys_platform != 'darwin'" },
|
||||
{ name = "typing-extensions", marker = "sys_platform != 'darwin'" },
|
||||
]
|
||||
wheels = [
|
||||
{ url = "https://download-r2.pytorch.org/whl/cpu/torch-2.13.0%2Bcpu-cp313-cp313-linux_s390x.whl", hash = "sha256:966d020354f465672dc7dd10d3a5c6cd17d7eb48620aa1d265b48a1f78f06898", upload-time = "2026-07-08T19:29:30Z" },
|
||||
{ url = "https://download-r2.pytorch.org/whl/cpu/torch-2.13.0%2Bcpu-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:0b8f7d0423027ae8b90c7977c627f3379f325363a08224dffad9b4b2d684a83d", upload-time = "2026-07-08T19:29:40Z" },
|
||||
{ url = "https://download-r2.pytorch.org/whl/cpu/torch-2.13.0%2Bcpu-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:3fbf9c9d1f3c10c2d59d04aca426dee9ccc6ceb32d255c61e93acc3b4f75fae6", upload-time = "2026-07-08T19:29:54Z" },
|
||||
{ url = "https://download-r2.pytorch.org/whl/cpu/torch-2.13.0%2Bcpu-cp313-cp313-win_amd64.whl", hash = "sha256:a17ff48608634db245e17e8bb00a9558554a49aeb1e4f5fe6cd039af2a10515b", upload-time = "2026-07-08T19:30:05Z" },
|
||||
{ url = "https://download-r2.pytorch.org/whl/cpu/torch-2.13.0%2Bcpu-cp313-cp313-win_arm64.whl", hash = "sha256:ac7aaf322be4777765a53bed7264a214dd81b3a1d276b93150515a3c5f75e4b0", upload-time = "2026-07-08T19:30:12Z" },
|
||||
{ url = "https://download-r2.pytorch.org/whl/cpu/torch-2.13.0%2Bcpu-cp314-cp314-linux_s390x.whl", hash = "sha256:dec241fef3984c0d1edadd1f58708e218d4eae881ceef7bc10cf9964d41b68b9", upload-time = "2026-07-08T19:30:20Z" },
|
||||
{ url = "https://download-r2.pytorch.org/whl/cpu/torch-2.13.0%2Bcpu-cp314-cp314-manylinux_2_28_aarch64.whl", hash = "sha256:ca021f9eb2f8345c83fa03e3a04587308afb8df71bd472670b3ece00df58621c", upload-time = "2026-07-08T19:30:32Z" },
|
||||
{ url = "https://download-r2.pytorch.org/whl/cpu/torch-2.13.0%2Bcpu-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:d20fa53ee744502fa4c69818a720b05ca0d37abd055d4f6e66cae155114bc691", upload-time = "2026-07-08T19:30:45Z" },
|
||||
{ url = "https://download-r2.pytorch.org/whl/cpu/torch-2.13.0%2Bcpu-cp314-cp314-win_amd64.whl", hash = "sha256:e2e5134decf00e218da62318f3dc5df156231d367871918e91eba95ab0ad43ab", upload-time = "2026-07-08T19:30:58Z" },
|
||||
{ url = "https://download-r2.pytorch.org/whl/cpu/torch-2.13.0%2Bcpu-cp314-cp314t-linux_s390x.whl", hash = "sha256:991cc14b39e751122c01f017be6448533989868731cb5eecd1006893d26787c2", upload-time = "2026-07-08T19:31:09Z" },
|
||||
{ url = "https://download-r2.pytorch.org/whl/cpu/torch-2.13.0%2Bcpu-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:7b8d26e29bceafbdaa8d63bfe7612f23875b5af2cc07e13f809c3ed890bbe1d8", upload-time = "2026-07-08T19:31:21Z" },
|
||||
{ url = "https://download-r2.pytorch.org/whl/cpu/torch-2.13.0%2Bcpu-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:b222c15a0fc2ce207d1c1a59700b46c8fa6748df1f447ad11e5c870dde0933d9", upload-time = "2026-07-08T19:31:35Z" },
|
||||
{ url = "https://download-r2.pytorch.org/whl/cpu/torch-2.13.0%2Bcpu-cp314-cp314t-win_amd64.whl", hash = "sha256:a43376bd094124ef626bfdd3d4c2c62eacb0b5ddc99776f4a32d4fd16f1f3420", upload-time = "2026-07-08T19:31:48Z" },
|
||||
{ url = "https://download-r2.pytorch.org/whl/cpu/torch-2.13.0%2Bcpu-cp315-cp315-manylinux_2_28_aarch64.whl", hash = "sha256:8eb5002ca81af00ae69b57540f615b58b8ae922b6d4848176b366a52bd2196e6", upload-time = "2026-07-08T19:32:00Z" },
|
||||
{ url = "https://download-r2.pytorch.org/whl/cpu/torch-2.13.0%2Bcpu-cp315-cp315-manylinux_2_28_x86_64.whl", hash = "sha256:1a3a35229fdc13446b4eab50e7fcf9399ff941e89a3b761497786297a5d8dde5", upload-time = "2026-07-08T19:32:16Z" },
|
||||
{ url = "https://download-r2.pytorch.org/whl/cpu/torch-2.13.0%2Bcpu-cp315-cp315t-manylinux_2_28_aarch64.whl", hash = "sha256:8e109528e6bab044815daebaf71770fbaace3a66ef1c816cb55c875350f78a60", upload-time = "2026-07-08T19:32:30Z" },
|
||||
{ url = "https://download-r2.pytorch.org/whl/cpu/torch-2.13.0%2Bcpu-cp315-cp315t-manylinux_2_28_x86_64.whl", hash = "sha256:222a6681467cc7f6f05cd3068dfbc603def3a1e46d1d4620c1c8cdf6178bd563", upload-time = "2026-07-08T19:32:44Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1372,19 +1178,6 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/a5/3a/d99704c5effe10c6339c98cb236259161103e159bb99a78468b6729572ec/transformers-5.13.0-py3-none-any.whl", hash = "sha256:8adbc1d20bd5463cd6876b2eb7cb31971e1065788e7dc6bc12bab597a7c504b7", size = 11503730, upload-time = "2026-07-03T16:05:35.569Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "triton"
|
||||
version = "3.7.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/00/42/c5089d4d9327fcd1e862c599cc2927f39418f84dd11a84cb2ccff9d4787a/triton-3.7.1-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cdbfc09d9ec58bc5e68321525653220de7515c199e7a8097a97c85e62b52cd0a", size = 184694629, upload-time = "2026-06-17T20:03:53.444Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/07/42/2c3ac59253ae8892b6f307875263dd23dc875cdf732d3aea40d6d41fb7cb/triton-3.7.1-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:58c0e131da05134a2a4788ccbcc0c1105cf0f54c8e98f19e34cd465396dc15eb", size = 197729241, upload-time = "2026-06-17T19:53:27.801Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/40/71/e01aa7ad573883ed9456f130226babdec70b005e098c4d6226a6238e761b/triton-3.7.1-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fe4ea396a06171f1f1f58cbd39c70b09294398f7dd7c620939bab54ad6f934fa", size = 184705764, upload-time = "2026-06-17T20:03:59.064Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a4/09/5683146fda6a2b569deb78ccfd8fbfea8bfe55f726b081c0a6bb18dd6f28/triton-3.7.1-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2020153b08280415ec0da6607834e79166442147e78e144df06b508c75b186d2", size = 197729537, upload-time = "2026-06-17T19:53:35.516Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e9/f8/448220c3092019f9fdfab39ec47985968181d67da34b44f6a7f6280a5cbb/triton-3.7.1-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c58e4c61f0c73b5dba3b5d19b4a7093c32f90dc18b2a7f121a7c16ccd31107b7", size = 184814760, upload-time = "2026-06-17T20:04:04.984Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f0/ac/229b7d4589d2e5937310e72c6d46e89599d16a4a12b479ffa1499fee8eb8/triton-3.7.1-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:10ba85fa2cca4a2fbdeb36bf1cb082f2c252bda55bf9fccd74f65ec5bc647e68", size = 197824404, upload-time = "2026-06-17T19:53:42.772Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "typer"
|
||||
version = "0.26.8"
|
||||
|
||||
Reference in New Issue
Block a user