Spaces:
Sleeping
Sleeping
Upload folder using huggingface_hub
Browse files- app/config.py +1 -1
- app/fulltext.py +14 -3
app/config.py
CHANGED
|
@@ -26,7 +26,7 @@ class Settings(BaseSettings):
|
|
| 26 |
classifier_model_path: str = ""
|
| 27 |
small_model: str = "gpt-4.1-mini"
|
| 28 |
narration_model: str = "gpt-4.1"
|
| 29 |
-
extraction_limit: int = Field(default=
|
| 30 |
llm_concurrency: int = Field(default=5, ge=1, le=20)
|
| 31 |
# USD per million tokens: configurable assumptions, never claimed as a bill.
|
| 32 |
small_input_price: float = 0.4
|
|
|
|
| 26 |
classifier_model_path: str = ""
|
| 27 |
small_model: str = "gpt-4.1-mini"
|
| 28 |
narration_model: str = "gpt-4.1"
|
| 29 |
+
extraction_limit: int = Field(default=100, ge=0, le=200)
|
| 30 |
llm_concurrency: int = Field(default=5, ge=1, le=20)
|
| 31 |
# USD per million tokens: configurable assumptions, never claimed as a bill.
|
| 32 |
small_input_price: float = 0.4
|
app/fulltext.py
CHANGED
|
@@ -9,6 +9,7 @@ its exact-quote validation work unchanged. Papers without a PMCID stay abstract-
|
|
| 9 |
|
| 10 |
from __future__ import annotations
|
| 11 |
|
|
|
|
| 12 |
import logging
|
| 13 |
import re
|
| 14 |
import xml.etree.ElementTree as ET
|
|
@@ -248,21 +249,31 @@ def flatten_jats(xml_text: str, abstract: str = "", *, max_lines: int = 160) ->
|
|
| 248 |
return result
|
| 249 |
|
| 250 |
|
|
|
|
|
|
|
|
|
|
| 251 |
class FullTextClient:
|
| 252 |
"""Fetch and flatten one paper's Europe PMC full text; never raises for a missing paper."""
|
| 253 |
|
| 254 |
def __init__(self, config: Settings = settings, transport: httpx.AsyncBaseTransport | None = None):
|
| 255 |
self.config = config
|
| 256 |
self.transport = transport
|
|
|
|
|
|
|
| 257 |
|
| 258 |
def pmcid(self, study: dict) -> str | None:
|
| 259 |
return normalize_pmcid(study.get("pmcid"))
|
| 260 |
|
| 261 |
async def fetch_xml(self, pmcid: str) -> str | None:
|
| 262 |
url = f"{self.config.europepmc_url.rstrip('/')}/{pmcid}/fullTextXML"
|
| 263 |
-
async with
|
| 264 |
-
|
| 265 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 266 |
response = await client.get(url, headers={"Accept": "application/xml"})
|
| 267 |
if response.status_code == 404:
|
| 268 |
return None
|
|
|
|
| 9 |
|
| 10 |
from __future__ import annotations
|
| 11 |
|
| 12 |
+
import asyncio
|
| 13 |
import logging
|
| 14 |
import re
|
| 15 |
import xml.etree.ElementTree as ET
|
|
|
|
| 249 |
return result
|
| 250 |
|
| 251 |
|
| 252 |
+
FETCH_CONCURRENCY = 8
|
| 253 |
+
|
| 254 |
+
|
| 255 |
class FullTextClient:
|
| 256 |
"""Fetch and flatten one paper's Europe PMC full text; never raises for a missing paper."""
|
| 257 |
|
| 258 |
def __init__(self, config: Settings = settings, transport: httpx.AsyncBaseTransport | None = None):
|
| 259 |
self.config = config
|
| 260 |
self.transport = transport
|
| 261 |
+
# One search can read a hundred papers; Europe PMC is a shared public service.
|
| 262 |
+
self.slots = asyncio.Semaphore(FETCH_CONCURRENCY)
|
| 263 |
|
| 264 |
def pmcid(self, study: dict) -> str | None:
|
| 265 |
return normalize_pmcid(study.get("pmcid"))
|
| 266 |
|
| 267 |
async def fetch_xml(self, pmcid: str) -> str | None:
|
| 268 |
url = f"{self.config.europepmc_url.rstrip('/')}/{pmcid}/fullTextXML"
|
| 269 |
+
async with (
|
| 270 |
+
self.slots,
|
| 271 |
+
httpx.AsyncClient(
|
| 272 |
+
timeout=self.config.fulltext_timeout,
|
| 273 |
+
transport=self.transport,
|
| 274 |
+
follow_redirects=True,
|
| 275 |
+
) as client,
|
| 276 |
+
):
|
| 277 |
response = await client.get(url, headers={"Accept": "application/xml"})
|
| 278 |
if response.status_code == 404:
|
| 279 |
return None
|