Edit on GitHub

agent_search_gateway.academic.enrichment

Post-deduplication open-access enrichment for paper records.

 1"""Post-deduplication open-access enrichment for paper records."""
 2
 3from __future__ import annotations
 4
 5import asyncio
 6import logging
 7from dataclasses import replace
 8
 9from ..models import OAResolution, PaperRecord
10from ..observability import log_event
11from ..providers.contracts import OAResolver
12
13_LOGGER = logging.getLogger(__name__)
14
15
16async def enrich_paper_records(
17    records: list[PaperRecord],
18    resolver: OAResolver | None,
19) -> list[PaperRecord]:
20    """Enrich DOI-bearing papers without allowing resolver failures to drop records."""
21
22    if resolver is None:
23        return list(records)
24    cache: dict[str, OAResolution | None] = {}
25    enriched: list[PaperRecord] = []
26    for record in records:
27        doi = record.identifiers.doi
28        if not doi:
29            enriched.append(record)
30            continue
31        if doi not in cache:
32            try:
33                cache[doi] = await resolver.resolve(doi)
34            except asyncio.CancelledError:
35                raise
36            except Exception as exc:
37                log_event(
38                    _LOGGER,
39                    logging.DEBUG,
40                    "paper_enrichment_failed",
41                    resolver=resolver.name,
42                    stage="oa_resolve",
43                    error_type=type(exc).__name__,
44                )
45                cache[doi] = None
46        resolution = cache[doi]
47        enriched.append(_apply_resolution(record, resolution))
48    return enriched
49
50
51def _apply_resolution(record: PaperRecord, resolution: OAResolution | None) -> PaperRecord:
52    if resolution is None:
53        return record
54    return replace(
55        record,
56        pdf_url=record.pdf_url or resolution.pdf_url,
57        is_open_access=(
58            record.is_open_access
59            if record.is_open_access is not None
60            else resolution.is_open_access
61        ),
62        oa_status=record.oa_status or resolution.oa_status,
63        license=record.license or resolution.license,
64    )
async def enrich_paper_records( records: list[agent_search_gateway.models.PaperRecord], resolver: agent_search_gateway.providers.contracts.OAResolver | None) -> list[agent_search_gateway.models.PaperRecord]:
17async def enrich_paper_records(
18    records: list[PaperRecord],
19    resolver: OAResolver | None,
20) -> list[PaperRecord]:
21    """Enrich DOI-bearing papers without allowing resolver failures to drop records."""
22
23    if resolver is None:
24        return list(records)
25    cache: dict[str, OAResolution | None] = {}
26    enriched: list[PaperRecord] = []
27    for record in records:
28        doi = record.identifiers.doi
29        if not doi:
30            enriched.append(record)
31            continue
32        if doi not in cache:
33            try:
34                cache[doi] = await resolver.resolve(doi)
35            except asyncio.CancelledError:
36                raise
37            except Exception as exc:
38                log_event(
39                    _LOGGER,
40                    logging.DEBUG,
41                    "paper_enrichment_failed",
42                    resolver=resolver.name,
43                    stage="oa_resolve",
44                    error_type=type(exc).__name__,
45                )
46                cache[doi] = None
47        resolution = cache[doi]
48        enriched.append(_apply_resolution(record, resolution))
49    return enriched

Enrich DOI-bearing papers without allowing resolver failures to drop records.