Skip to content

Add OpenAIRE as a daily-discovery source - #614

Open
lazizbekravshanov wants to merge 3 commits into
ktwu01:mainfrom
lazizbekravshanov:feat/issue-545-openaire-source
Open

lazizbekravshanov wants to merge 3 commits into
ktwu01:mainfrom
lazizbekravshanov:feat/issue-545-openaire-source

Conversation

@lazizbekravshanov

Copy link
Copy Markdown

Closes #545

What this gives the radar

  • Research products the radar reaches through no other source. OpenAIRE aggregates publications, datasets, software and other products from repositories across Europe and beyond, including institutional repositories that neither Crossref nor arXiv covers.
  • No credentials, no secrets, no workflow change. The Graph API is anonymous for reads. daily-radar.yml is untouched, so there is nothing to coordinate on credentials. A registered token raises the budget from 60 to 7,200 requests per hour if it is ever wanted; the connector does not need it.
  • Six requests per daily run (one page per search phrase) against the anonymous budget of 60 per hour.
  • One commit on top of main at 5d9e378, 10 files, 925 additions. Clean-worktree CI sequence passes; details under Verification.

Two things to decide

  1. The README source count moves from 37 to 38. That is the figure count_ingest_sources in scripts/export_report_figure_data.py derives, and test_readmes_state_the_live_ingest_source_count now holds both READMEs to it. The repository description on GitHub also says "37 public sources"; that is a setting rather than a file, so it is left for you.
  2. Merge order with the DataCite PR ([3 points] add DataCite info source #544). Both start from the same main commit and both write 38. Whichever lands second conflicts on the two README lines and on adjacent registration lines in sources.py, config.yml, pipeline.py, rubric.py, corpus.py, app.js and the two test files. Every one is an adjacent-insertion conflict, resolved by keeping both sides and writing 39. I will rebase the second one the same day the first merges.

Acceptance criteria from #545

Criterion Where
Official API; authentication, rate limits, date semantics, supported product types and pagination documented GET https://api.openaire.eu/graph/v3/research-products, the Graph API v3 that superseded v1 and v2 in July 2026 (the older /search/* API was announced for phase-out on 2026-05-31); all five documented in the openaire block of config.yml and in the connector docstring
Identifiers, titles, dates, authors, organizations, descriptions and artifact links preserved, no generated prose fetch_openaire carries the DOI and other PIDs, main title, publicationDate and dateOfCollection, authors as deposited, organisations by legal name, short name or acronym, description, instance URLs and code repository, plus the citation, download and view counters; no summary is synthesized
Both ends of the window enforced; malformed and future-dated records rejected; failures reach source health fromPublicationDate / toPublicationDate in the query and again on the parsed record; future-dated rejected; unreadable shapes raise ConnectorPayloadError, HTTP failures RequestError, both surfaced as an optional, non-fatal source
Registered in configuration, scoring/provenance, backfill, dashboard source-name map config.yml, SOURCE_FETCHERS / _PARSER_VERSION_METHODS / SOURCE_DEFAULT_METHODS, EVIDENCE_PRIMARY_SOURCES, PRIMARY_SOURCE_RANK, BACKFILL_SOURCES, both maps in site/assets/app.js
Tests for valid, empty, malformed, undated and failed responses; clean-checkout CI passes tests/test_sources.py and tests/test_pipeline.py, listed under Tests; CI under Verification

Records merge only on the exact DOI, the key dedupe_keys already uses, so nothing here does fuzzy matching. Nothing is scraped behind authentication.

How the connector behaves

  • Filter values are quoted. A mainTitle value holding whitespace, parentheses or a bare logical operator has to reach v3 double-quoted or the request is answered with HTTP 400 rather than a search. Every phrase this project ships contains a space, so the connector quotes them, escapes an embedded ", and leaves a value that is already quoted or parenthesised intact. Without this the source would return nothing at all in production while looking configured.
  • Window and record date are the same field. publicationDate bounds the query on both ends and dates the record. dateOfCollection says when OpenAIRE indexed a product rather than when it appeared, and is routinely months later; dating by it would hide a product on its publication day and surface it on a day the connector's own window guard rejects.
  • The DOI is reduced to its bare name. Repositories deposit it bare, as a link and as a doi: URI. A link passed through would build https://doi.org/https://doi.org/..., which resolves nowhere and shares no identity with the Crossref or Zenodo copy of the same artifact, so the record would be counted twice.
  • A record needs an openable location. A product whose only openable location is an instance URL or an http(s) code repository keeps that as its address; one that names no openable location at all is dropped rather than published pointing at nothing.
  • Pagination. pageSize is capped at 100 by the API and the connector clamps a larger configured value; sortBy=publicationDate DESC so a page holds the newest matches. One page per phrase, so a run costs max_requests calls. A page that matched nothing arrives as results: null, which is treated as an ordinary empty result rather than a broken payload.
  • Names are preserved as deposited, with no reordering; a non-string title raises rather than publishing its repr.
  • Failures raise rather than skip. A non-object payload, a missing results array, or a malformed authors, pids, instances or organizations shape is a parsing gap the dashboard should show, not a quietly thinner record.

Wiring

  • config.yml: optional openaire block (6 phrases, page_size: 50, max_requests: 6), with the semantics above documented in place.
  • src/benchmark_radar/sources.py: fetch_openaire and its registrations.
  • src/benchmark_radar/pipeline.py: BACKFILL_SOURCES; the publication date range makes a past window honestly re-derivable.
  • src/benchmark_radar/rubric.py, src/benchmark_radar/corpus.py: primary evidence credit and provenance rank 2, beside Crossref.
  • site/assets/app.js: both source-name maps.
  • README.md, README.zh-CN.md: acknowledgement list and source count.

Tests

tests/test_sources.py: a valid record (every preserved field and the exact bounded, quoted query), undated, out-of-window, future-dated, no openable location, instance and repository fallbacks, a non-openable repository, author and organisation shapes, malformed row shapes, cross-search DOI dedupe with newest-first ordering, limit truncation, the pageSize cap, a minimal product, the request budget, quoting of plain, parenthesised, already-quoted and embedded-quote phrases, and the DOI deposited bare, as a link and as a doi: URI. Plus OpenAIRE entries in the shared empty / malformed / HTTP-failure / no-synthesized-summary parametrizations and the collection-method default.

tests/test_pipeline.py: test_simulate_backfill_places_an_openaire_product_on_its_publication_day runs the real connector through simulate_backfill, so the connector's window key and the backfill placement key are checked against each other rather than separately.

Each behaviour above that a test protects was checked by reverting it and confirming the test goes red, so the tests carry their regression rather than restating current output.

Verification

Clean-worktree run of the CI sequence (git worktree add --detach, submodule initialized) on this exact head: ruff check ., ruff format --check ., normalize-catalog, classify and build-data-release pass; pytest -q reports 1384 passed, 1 failed.

The one failure is tests/test_briefing.py::test_zh_output_budget_covers_worst_case, which downloads tiktoken's o200k_base table from openaipublic.blob.core.windows.net, a host the build sandbox's egress policy blocks. It fails identically on an untouched main checkout in the same sandbox and passes on GitHub's runner.

One honest caveat. The live OpenAIRE API was not reachable from the sandbox this was built in, so the request and response shapes were verified against OpenAIRE's own API contract tests plus two independent client libraries that agreed on the response shape, rather than a live call. The first daily workflow run after merge is the first live exercise; source health will show any surprise, and I will watch it.

Not in this PR

Related identifiers and relations stay in raw; carrying them as artifact URLs needs its own review, since any cited DOI would merge a record with everything it cites. The connector does not paginate past the first page per phrase, which the config block states.

🤖 Generated with Claude Code

https://claude.ai/code/session_01UaUf4LgR9HogJ1DhXwjESy

Copilot AI lite review requested due to automatic review settings September 12, 2026 15:42

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Unresolved moderate connector findings and a README count inconsistency must be addressed before approval.

Get a fresh assessment by requesting another Copilot review.

Pull request overview

Adds OpenAIRE Graph v3 as an optional daily-discovery source, integrating metadata parsing, backfill, scoring, provenance, dashboard labels, documentation, and tests.

Changes:

  • Adds bounded OpenAIRE fetching, normalization, deduplication, and validation.
  • Registers OpenAIRE across configuration and processing pipelines.
  • Adds connector and backfill regression tests and updates source documentation.
File summaries
File Summary and review status
tests/test_sources.py Tests OpenAIRE parsing, validation, failure handling, deduplication, and request behavior.
tests/test_pipeline.py Tests publication-date backfill placement.
src/benchmark_radar/sources.py Adds the connector and registrations. Six unresolved moderate findings concern missing results handling (3 votes), absent counter defaults (1), date-only future validation (1), metadata stored only in raw (1), and malformed author (1) and organization (1) name coercion.
src/benchmark_radar/rubric.py Adds primary evidence-source credit.
src/benchmark_radar/pipeline.py Enables OpenAIRE backfill.
src/benchmark_radar/corpus.py Adds provenance ranking.
site/assets/app.js Adds OpenAIRE source display mappings.
README.zh-CN.md Updates Chinese source documentation and counts.
README.md Updates English source documentation. Nit (1 vote): line 149 still reports 37 sources and 13 direct connectors, conflicting with the updated counts.
config.yml Configures the optional OpenAIRE source and documents API behavior.
Review details

Suppressed comments (6)

README.md:26

  • Please update the detailed count below as well: README.md:149 still says 37 sources (13 direct connectors), while this changed claim is 38 and the registry now derives 14 connectors plus 24 feeds. Leaving both values makes the README self-contradictory.
web. It pulls evidence from 38 public sources every day, and keeps updating.

src/benchmark_radar/sources.py:1268

  • These or 0 defaults turn an absent counter into an observed zero. The sparse-record test exercises exactly this path, but principle.md:104-105 and principle.md:146-156 require unknown measurements to remain unknown; otherwise snapshots and adoption scores falsely report zero citations/downloads/views. Include a metric only when its upstream key is present, while retaining an explicit numeric zero.
                metrics={
                    "citations": float(citations.get("citationCount") or 0),
                    "downloads": float(usage.get("downloads") or 0),
                    "views": float(usage.get("views") or 0),
                },

src/benchmark_radar/sources.py:1203

  • publicationDate is date-only, but this upper-bound check relies only on _reject_future, which allows a five-minute clock-skew tolerance. Near midnight, a row dated tomorrow is therefore accepted (and window_end already includes tomorrow), violating the connector's future-date rejection contract. Compare the publication date to the collection date separately; keep the tolerance only for genuinely timestamped fields.
            if published.date() < window_start or _reject_future(config, source_id, published):

src/benchmark_radar/sources.py:1269

  • Assigning the upstream row to raw does not preserve its extra metadata in published data: RadarItem.to_dict() removes raw and snapshots explicitly reject raw payloads, leaving only a hash. As a result, the non-DOI PIDs and dateOfCollection asserted in item.raw never reach snapshots/catalog output, which contradicts the issue/PR requirement to preserve upstream identifiers and dates; project the required fields into the public item contract instead of relying on raw.
                raw=row,

src/benchmark_radar/sources.py:1070

  • str(...) converts malformed non-string name fields (for example fullName: ["Ada"]) into Python-repr text, so the connector publishes ['Ada'] instead of surfacing the malformed author payload. Validate fullName, name, and surname as strings before joining to preserve deposited names and the documented malformed-payload failure behavior.
    full_name = str(author.get("fullName") or "").strip()
    if full_name:
        return full_name
    parts = (str(author.get("name") or "").strip(), str(author.get("surname") or "").strip())
    return " ".join(part for part in parts if part)

src/benchmark_radar/sources.py:1087

  • The same coercion issue exists for organizations: a malformed legalName or acronym object/list is stringified into a fabricated label rather than rejected. Validate these fields as strings before calling .strip() so source health exposes an incompatible row instead of publishing Python-repr organization names.
    for entry in _openaire_rows(value, "organizations"):
        name = str(entry.get("legalName") or entry.get("acronym") or "").strip()
        if name:
            names.append(name)
  • Files reviewed: 10/10 changed files
  • Comments generated: 1
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread src/benchmark_radar/sources.py Outdated
Comment on lines +1186 to +1187
rows = _payload_dict(payload, "OpenAIRE").get("results")
if rows is None:

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 2e230dae58

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

"OpenReview",
"Semantic Scholar",
"Crossref",
"OpenAIRE",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Remove the source-only evidence boost

Every OpenAIRE result now receives the 40-point primary-source evidence credit solely because it came through this connector, so an otherwise identical record from an unlisted source is ranked lower despite carrying the same attached evidence. Keep OpenAIRE as provenance, but do not use source membership as a trust tier in the discovery score.

AGENTS.md reference: AGENTS.md:L237-L241

Useful? React with 👍 / 👎.

"openreview",
"semantic_scholar",
"crossref",
"openaire",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Bound the OpenAIRE query to the simulated date

When simulate-history runs for dates before today, adding this source causes simulate_backfill to call fetch_openaire without _collection_now; that fetcher therefore sets toPublicationDate to the real current date and asks only for the first descending page. Once newer products exceed page_size, the requested historical products never enter the pool and the run reports OpenAIRE healthy while producing incomplete historical snapshots. Pass the last simulated date as the collection bound (or omit this connector from simulated backfill).

AGENTS.md reference: AGENTS.md:L93-L97

Useful? React with 👍 / 👎.

Comment thread README.md
I kept running into new benchmarks while doing benchmark research, so I built a
crawler that continuously collects benchmark-related signals from across the
web. It pulls evidence from 37 public sources every day, and keeps updating.
web. It pulls evidence from 38 public sources every day, and keeps updating.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Update the remaining README source total

This new headline says 38 sources, but the Abstract later in README.md still says “37 sources: 13 direct connectors and 24 first-party” (and README.zh-CN.md has the same stale claim). With OpenAIRE enabled, the direct-connector count is 14, so readers encounter contradictory public coverage figures in the same document.

AGENTS.md reference: AGENTS.md:L12-L17

Useful? React with 👍 / 👎.

Copilot AI review requested due to automatic review settings September 12, 2026 16:08
@lazizbekravshanov
lazizbekravshanov force-pushed the feat/issue-545-openaire-source branch from 2e230da to f2a2e6c Compare September 12, 2026 16:08

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@lazizbekravshanov

Copy link
Copy Markdown
Author

Thanks. One of the three findings was real and is fixed in the pushed commit.

The README source total was genuinely inconsistent: the opening line said 38 while the Abstract still said "37 sources: 13 direct connectors and 24 first-party". Both mentions now read 38 and 14, and tests/test_readme.py derives all three figures from count_ingest_sources and asserts both mentions, so a future connector cannot update one and leave the other. The same fix is on the DataCite PR, which had the same defect.

The other two I do not think hold, and I would rather explain than change the code quietly.

On bounding the query to the simulated date: simulate_backfill calls every fetcher as fetcher(source_config, earliest_since, limit) without _collection_now, and it has always done so for all of BACKFILL_SOURCES, Crossref included. _latest_allowed then falls back to the real now, which is the correct upper bound for a fetch, and the simulated date is applied afterwards in the per-date loop that keeps only items where since <= (updated_at or published_at) <= simulated_now. Adding OpenAIRE to that set inherits the existing behaviour rather than introducing a new gap; changing it would be a change to the shared backfill contract and belongs in its own PR.

On the evidence credit: EVIDENCE_PRIMARY_SOURCES already lists arXiv, GitHub, Hugging Face, OpenReview, Semantic Scholar and Crossref, and every one of them receives the same 40-point credit for the same reason. OpenAIRE is a primary research-product source of exactly that kind, so listing it follows the existing rule. If the rule itself should be evidence-quality-weighted rather than source-membership-based, that is a worthwhile change but it would apply to all seven sources, not to this connector alone.

Copilot AI review requested due to automatic review settings September 12, 2026 18:02
@lazizbekravshanov
lazizbekravshanov force-pushed the feat/issue-545-openaire-source branch from f2a2e6c to 2c52539 Compare September 12, 2026 18:02

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@lazizbekravshanov
lazizbekravshanov force-pushed the feat/issue-545-openaire-source branch from 2c52539 to 5266222 Compare September 12, 2026 19:30

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 5266222fba

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

"Semantic Scholar": 2,
"OpenAlex": 2,
"Crossref": 2,
"OpenAIRE": 2,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Remove the OpenAIRE source-priority tier

When an OpenAIRE observation and a rank-1 connector resolve to the same artifact, build_corpus uses this new source-only rank at lines 312–318 to prevent the OpenAIRE title and URL from becoming the public entity representation. That creates a preferred trust tier based solely on connector membership; retain the provenance records but choose the entity representation without source-based priority.

AGENTS.md reference: AGENTS.md:L237-L240

Useful? React with 👍 / 👎.

OpenAIRE aggregates publications, datasets, software and other research
products from repositories across Europe and beyond, including
institutional repositories this project reaches through no other source
(issue ktwu01#545).

This uses the Graph API v3, the version that superseded v1 and v2 in
July 2026; the older `/search/*` API was announced for phase-out on
2026-05-31. Reads need no credential. An anonymous caller gets 60
requests per hour against 7,200 for a registered token, so the connector
issues one request per search phrase and stays well inside the anonymous
budget.

A filter value holding whitespace, parentheses or a bare logical
operator has to reach v3 double-quoted or the request is answered with
HTTP 400 rather than a search. Every phrase this project ships contains
a space, so the connector quotes them and a value that is already quoted
or parenthesised is left intact. A page that matched nothing arrives as
`results: null`, which is an ordinary empty result rather than the
broken payload it would otherwise be reported as.

The window is `publicationDate` on both ends of the query and again on
the parsed record, and the record is dated by that same field.
`dateOfCollection` says when OpenAIRE indexed a product rather than when
it appeared, and is routinely months later; dating by it would hide a
product on its publication day and surface it on a day the connector's
own window guard rejects.

Records duplicate the Crossref and arXiv copies of the same
artifact by design. The DOI travels with every record that has one and
is reduced to its bare name first, because repositories deposit it as a
link and as a `doi:` URI as well, and a link passed through would build
an address resolving nowhere that shares no identity with the other
copies. A product whose only openable location is an instance URL or a
code repository keeps that as its address; one that names no openable
location at all is dropped rather than published pointing at nothing.

Registered in configuration (optional, non-fatal), the evidence rubric
and provenance rank beside Crossref, simulated backfill, and the
dashboard source-name maps.

Simulated backfill now asks each connector for the span it is rebuilding.
A connector takes its upper bound from `_collection_now` and returns one
page of its newest matches; `simulate_backfill` passed no such value, so
every backfill source queried up to real now and handed back rows
published this week. The per-date filter then discarded all of them and
the rows belonging inside the requested windows were never fetched, so a
rebuilt day came out empty rather than visibly wrong. Passing the newest
simulated date fixes the bound for every source in `BACKFILL_SOURCES`,
not only this one. The single-page cap still stands, so a long span is
sampled rather than exhaustive; what changes is that the sample comes
from inside the span. Both README source counts move from 37 to
38, in the opening line and in the Abstract, which also splits the
figure into 14 connectors and 24 feeds; `count_ingest_sources` derives
all three from the fetchers and the first-party feed list, and the
README test now holds both mentions to it rather than only the first.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UaUf4LgR9HogJ1DhXwjESy
Copilot AI review requested due to automatic review settings September 12, 2026 19:46
@lazizbekravshanov
lazizbekravshanov force-pushed the feat/issue-545-openaire-source branch from 5266222 to c4acfad Compare September 12, 2026 19:46

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c4acfad0a6

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

"openreview",
"semantic_scholar",
"crossref",
"openaire",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Keep capped OpenAIRE results out of simulated backfill

When a simulated span has more than page_size matches for an OpenAIRE phrase, adding this source here makes simulate-history fetch only its first, newest-sorted page once and then distribute that incomplete pool across all days. The new _collection_now bound fixes the prior current-date case, but it does not paginate this capped source; older days can therefore be written without their OpenAIRE observations while the copied source health still reports the fetch as healthy. Paginate for backfill or exclude OpenAIRE from BACKFILL_SOURCES.

AGENTS.md reference: AGENTS.md:L93-L97

Useful? React with 👍 / 👎.

Graph v3 returns the abstract as `descriptions`, a list, and carries no
singular `description` member, so every published record reached the site
with an empty summary. The suite passed because its own fixture invented
the singular key. `score_item` matches the taxonomy over title plus
summary, so the loss degraded categorisation without failing anything.

Checked against the live endpoint rather than a fixture: 8 of 8 sampled
products expose `descriptions` and none expose `description`, and a
product routinely carries `descriptions: null` where it was deposited
without an abstract, which is an absent abstract rather than a payload the
connector cannot read.

Also corrects two claims the connector documented that the live API
contradicts: an anonymous caller is budgeted 7,199 requests per hour in
`x-ratelimit-limit` rather than 60, and an unquoted multi-word value is
accepted rather than answered with HTTP 400. Quoting is still right, for
precision: it narrows `LLM benchmark` from 3,021 matches to 213.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings September 13, 2026 21:37

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 13, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-09-13T21:42:51.059305Z c841a81 New commits
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@ktwu01

ktwu01 commented Sep 13, 2026

Copy link
Copy Markdown
Owner

Agent annotation: 330226 / Claude

Pushed c841a81 to this branch. The fix came out of calling the live Graph v3 endpoint, which the PR description correctly flagged as the one thing that had not been exercised.

The bug

fetch_openaire read row.get("description"). v3 returns the abstract as descriptions, a list, and carries no singular description member at all. Sampling 8 live products: 8/8 expose descriptions, 0/8 expose description. Every record this connector published would have reached the site with an empty summary.

The suite did not catch it because _openaire_row invented the singular key and test_openaire_preserves_upstream_metadata_and_bounds_the_query asserted against that invention. The failure was silent rather than loud: score_item matches the taxonomy over title + summary (pipeline.py:246), so records would have been categorised on title alone, and the title-only provenance structural signal would not fire because artifact_urls and metrics are populated.

The fix

Read descriptions, take the first entry that survives clean_card_text, treat null as an absent abstract, and raise on a non-list or on non-string entries.

Live check after the fix, for "benchmark dataset": of 6 products returned, the 2 that carry an abstract now have summaries of 1,940 and 1,695 characters. The other 4 carry descriptions: null upstream, so they are correctly empty rather than being missed.

Fixtures now match the real payload shape, plus two regression tests: a multi-entry descriptions with <jats:*> markup and a blank leading entry, and descriptions: null.

Two documented claims corrected

Both are contradicted by the live API:

  • The anonymous budget is 7,199 requests/hour (reported in x-ratelimit-limit), not 60.
  • An unquoted multi-word value returns 200, not 400. Quoting is still the right call, but for precision rather than acceptance: LLM benchmark returns 213 titles quoted against 3,021 unquoted.

Verification

Full ci.yml sequence on a clean worktree: ruff check, ruff format --check, normalize-catalog, classify, build-data-release, then pytest -q. Result: 1417 passed, 0 failed (an untouched main baseline in the same sandbox: 1362 passed).

Still needs a maintainer

CI has never run on this PR. Every run sits at action_required because this is a fork PR, so it needs workflow approval before any check can report.

Two items left for the author, not changed here:

  • The merge-order note references #544, which is the issue number. The DataCite PR is #613, and both touch the same 11 files.
  • Expected yield is low. Across 2026-08-01 to 2026-09-13 the six phrases matched 0, 1, 1, 5, 25 and 0 titles respectively, and lookback_hours is 48, so most daily runs will collect nothing. That is a value question rather than a defect.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c841a81e5b

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/benchmark_radar/sources.py Outdated
},
**_request_options(config),
)
rows = _payload_dict(payload, "OpenAIRE").get("results")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Reject responses that omit the results field

When OpenAIRE returns an object without results—for example, an error-shaped HTTP 200 response—.get("results") yields None, so this code treats the malformed payload like the documented empty results: null response. run_pipeline consequently records OpenAIRE as healthy with zero items, silently losing its evidence from the durable daily snapshot; distinguish an absent key from an explicitly null value and raise ConnectorPayloadError for the former.

AGENTS.md reference: AGENTS.md:L93-L97

Useful? React with 👍 / 👎.

`.get("results")` returned None for both, so an error-shaped HTTP 200 body
carrying no `results` at all was read as the documented empty page: the
connector returned nothing and source health reported the day healthy.
That is the silent failure `_payload_rows` raises on for every other
connector, and the reason this one could not use it -- v3 really does send
`results: null` for a page that matched nothing -- argues for separating
the two cases rather than merging them. An absent key now raises; the
documented null is still an ordinary empty page, which its own test holds.

The premise is the documented response shape rather than a live check,
since the endpoint is not reachable from this sandbox.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UaUf4LgR9HogJ1DhXwjESy
Copilot AI review requested due to automatic review settings September 14, 2026 00:17

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@ktwu01 ktwu01 left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the live API follow-up; the descriptions and missing-results fixes are sound. I still found a few data-contract blockers:

  • Missing counters are published as zero. Please omit absent citation, download, and view metrics while preserving a real upstream zero.
  • Non-DOI PIDs and dateOfCollection exist only in raw, which RadarItem.to_dict() removes, so the required identifiers and dates do not reach durable snapshots. Please project them into the public record contract and test serialization.
  • Historical backfill reads only one newest-first page per phrase, so longer spans silently lose older OpenAIRE records while reporting healthy. Please paginate backfill or exclude this capped connector from it.
  • Date-only rows can pass as not-future during the five minutes before midnight; nested non-string author or organization names are also converted to Python representation text. Please add direct guards and regression cases.

Both clean CI runs pass (1,418 tests on the head; 1,440 with current main) and the merge itself is conflict-free. Thank you.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[3 points] add OpenAIRE info source

4 participants