lit-acquisition 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- lit_acquisition-0.1.0/.gitignore +24 -0
- lit_acquisition-0.1.0/PKG-INFO +219 -0
- lit_acquisition-0.1.0/README.md +187 -0
- lit_acquisition-0.1.0/pyproject.toml +60 -0
- lit_acquisition-0.1.0/src/lit_acquisition/__init__.py +150 -0
- lit_acquisition-0.1.0/src/lit_acquisition/_parse_service.py +29 -0
- lit_acquisition-0.1.0/src/lit_acquisition/_rust_io.py +26 -0
- lit_acquisition-0.1.0/src/lit_acquisition/config.py +301 -0
- lit_acquisition-0.1.0/src/lit_acquisition/contracts.py +200 -0
- lit_acquisition-0.1.0/src/lit_acquisition/gateway.py +465 -0
- lit_acquisition-0.1.0/src/lit_acquisition/literature_type_classifier.py +291 -0
- lit_acquisition-0.1.0/src/lit_acquisition/normalizers.py +692 -0
- lit_acquisition-0.1.0/src/lit_acquisition/provider_health.py +83 -0
- lit_acquisition-0.1.0/src/lit_acquisition/pubmed_service.py +186 -0
- lit_acquisition-0.1.0/src/lit_acquisition/query_translator.py +189 -0
- lit_acquisition-0.1.0/src/lit_acquisition/relevance_gate.py +364 -0
- lit_acquisition-0.1.0/src/lit_acquisition/search_service.py +308 -0
- lit_acquisition-0.1.0/src/lit_acquisition/utils/__init__.py +12 -0
- lit_acquisition-0.1.0/src/lit_acquisition/utils/llm_params.py +25 -0
- lit_acquisition-0.1.0/src/lit_acquisition/utils/ssrf.py +36 -0
- lit_acquisition-0.1.0/src/lit_acquisition/utils/text.py +33 -0
- lit_acquisition-0.1.0/src/lit_acquisition/web_search/__init__.py +7 -0
- lit_acquisition-0.1.0/src/lit_acquisition/web_search/adapter.py +65 -0
- lit_acquisition-0.1.0/src/lit_acquisition/web_search/firecrawl_adapter.py +242 -0
- lit_acquisition-0.1.0/src/lit_acquisition/web_search/serpapi_adapter.py +137 -0
- lit_acquisition-0.1.0/src/lit_acquisition/web_search/tavily_adapter.py +132 -0
- lit_acquisition-0.1.0/src/lit_acquisition/workflow.py +1066 -0
- lit_acquisition-0.1.0/tests/test_config.py +105 -0
- lit_acquisition-0.1.0/tests/test_contracts.py +133 -0
- lit_acquisition-0.1.0/uv.lock +1507 -0
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
*.so
|
|
6
|
+
.Python
|
|
7
|
+
build/
|
|
8
|
+
dist/
|
|
9
|
+
*.egg-info/
|
|
10
|
+
*.egg
|
|
11
|
+
|
|
12
|
+
# Virtual environments
|
|
13
|
+
.venv/
|
|
14
|
+
venv/
|
|
15
|
+
|
|
16
|
+
# Testing
|
|
17
|
+
.pytest_cache/
|
|
18
|
+
.coverage
|
|
19
|
+
htmlcov/
|
|
20
|
+
|
|
21
|
+
# IDE
|
|
22
|
+
.idea/
|
|
23
|
+
.vscode/
|
|
24
|
+
*.swp
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: lit-acquisition
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Multilingual biomedical literature acquisition toolkit - search, download, and classify academic papers from 15+ providers
|
|
5
|
+
Author: Lingua Seeker Maintainers
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Keywords: acquisition,biomedical,crossref,literature,multilingual,openalex,pubmed
|
|
8
|
+
Classifier: Development Status :: 4 - Beta
|
|
9
|
+
Classifier: Intended Audience :: Science/Research
|
|
10
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
13
|
+
Classifier: Topic :: Scientific/Engineering :: Bio-Informatics
|
|
14
|
+
Requires-Python: >=3.12
|
|
15
|
+
Requires-Dist: httpx[socks]>=0.27.0
|
|
16
|
+
Requires-Dist: loguru>=0.7.0
|
|
17
|
+
Requires-Dist: openai>=1.0.0
|
|
18
|
+
Requires-Dist: pydantic>=2.7.0
|
|
19
|
+
Requires-Dist: pyjstage2>=0.1.2
|
|
20
|
+
Requires-Dist: pymupdf>=1.27.2
|
|
21
|
+
Provides-Extra: dev
|
|
22
|
+
Requires-Dist: pytest-asyncio>=0.23.0; extra == 'dev'
|
|
23
|
+
Requires-Dist: pytest>=8.2.0; extra == 'dev'
|
|
24
|
+
Requires-Dist: ruff>=0.5.0; extra == 'dev'
|
|
25
|
+
Provides-Extra: rust-io
|
|
26
|
+
Requires-Dist: rust-io; extra == 'rust-io'
|
|
27
|
+
Provides-Extra: web-search
|
|
28
|
+
Requires-Dist: firecrawl-py>=4.28.2; extra == 'web-search'
|
|
29
|
+
Requires-Dist: google-search-results>=2.4.2; extra == 'web-search'
|
|
30
|
+
Requires-Dist: tavily-python>=0.5.0; extra == 'web-search'
|
|
31
|
+
Description-Content-Type: text/markdown
|
|
32
|
+
|
|
33
|
+
# lit-acquisition
|
|
34
|
+
|
|
35
|
+
Multilingual biomedical literature acquisition toolkit — search, download, and classify academic papers from 15+ providers.
|
|
36
|
+
|
|
37
|
+
## Features
|
|
38
|
+
|
|
39
|
+
- **15+ provider integrations**: Crossref, PubMed, OpenAlex, EuropePMC, DOAJ, J-STAGE, arXiv, bioRxiv, medRxiv, SciELO, BASE, CORE, OpenAIRE, CiNii, Unpaywall
|
|
40
|
+
- **Multilingual search**: Query translation into 6 languages (en, zh, ja, de, fr, ru) with language-aware provider routing
|
|
41
|
+
- **PDF download**: DOI → Unpaywall OA resolution, PMCID → EuropePMC render, direct URL with HTML→PDF redirect handling
|
|
42
|
+
- **Relevance gate**: LLM-based classification to filter irrelevant downloads
|
|
43
|
+
- **Literature type classification**: Keyword-based classification (case report, sequencing, functional study) across 10+ languages
|
|
44
|
+
- **Web search fallback**: Firecrawl, Tavily, and SerpApi adapters for discovering papers beyond academic APIs
|
|
45
|
+
- **Provider health tracking**: Automatic health monitoring with sliding-window stats and unhealthy provider deprioritization
|
|
46
|
+
|
|
47
|
+
## Installation
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
pip install lit-acquisition
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
With web search support:
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
pip install "lit-acquisition[web-search]"
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
With Rust native extensions (faster HTTP I/O):
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
pip install "lit-acquisition[rust-io]"
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## Quick Start
|
|
66
|
+
|
|
67
|
+
### Configure
|
|
68
|
+
|
|
69
|
+
```python
|
|
70
|
+
from lit_acquisition import configure
|
|
71
|
+
|
|
72
|
+
configure(
|
|
73
|
+
# LLM for relevance gate and query translation
|
|
74
|
+
llm_base_url="https://api.openai.com/v1",
|
|
75
|
+
llm_api_key="sk-...",
|
|
76
|
+
llm_model="gpt-4o",
|
|
77
|
+
|
|
78
|
+
# Optional: dedicated translation model
|
|
79
|
+
translation_base_url="https://api.openai.com/v1",
|
|
80
|
+
translation_api_key="sk-...",
|
|
81
|
+
translation_model="gpt-4o-mini",
|
|
82
|
+
|
|
83
|
+
# Optional: web search providers
|
|
84
|
+
firecrawl_api_key="fc-...",
|
|
85
|
+
tavily_api_key="tvly-...",
|
|
86
|
+
|
|
87
|
+
# Optional: network proxy
|
|
88
|
+
proxy="http://127.0.0.1:7890",
|
|
89
|
+
|
|
90
|
+
# Optional: PubMed API key (higher rate limits)
|
|
91
|
+
pubmed_api_key="...",
|
|
92
|
+
)
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Or via environment variables:
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
export LIT_LLM_BASE_URL=https://api.openai.com/v1
|
|
99
|
+
export LIT_LLM_API_KEY=sk-...
|
|
100
|
+
export LIT_LLM_MODEL=gpt-4o
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### Search a Single Provider
|
|
104
|
+
|
|
105
|
+
```python
|
|
106
|
+
import asyncio
|
|
107
|
+
from lit_acquisition import search_provider
|
|
108
|
+
|
|
109
|
+
async def main():
|
|
110
|
+
result = await search_provider(
|
|
111
|
+
provider="crossref",
|
|
112
|
+
query="MECP2 Rett syndrome case report",
|
|
113
|
+
limit=20,
|
|
114
|
+
)
|
|
115
|
+
print(f"Found {len(result.items)} items")
|
|
116
|
+
for item in result.items:
|
|
117
|
+
print(f" - {item.get('title', 'untitled')}")
|
|
118
|
+
|
|
119
|
+
asyncio.run(main())
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### Run the Full Multilingual Pipeline
|
|
123
|
+
|
|
124
|
+
```python
|
|
125
|
+
import asyncio
|
|
126
|
+
from lit_acquisition import multilingual_acquisition_workflow
|
|
127
|
+
|
|
128
|
+
async def main():
|
|
129
|
+
result = await multilingual_acquisition_workflow({
|
|
130
|
+
"query": "MECP2 Rett syndrome case report",
|
|
131
|
+
"action": "search", # or "download" to also fetch PDFs
|
|
132
|
+
"limit": 30,
|
|
133
|
+
"language": "auto",
|
|
134
|
+
"relevance_gate": True, # LLM-based relevance filtering
|
|
135
|
+
"literature_types": ["case_report"],
|
|
136
|
+
})
|
|
137
|
+
print(f"Success: {result['success']}")
|
|
138
|
+
print(f"Items: {len(result['items'])}")
|
|
139
|
+
print(f"Downloads: {len(result['downloads'])}")
|
|
140
|
+
|
|
141
|
+
asyncio.run(main())
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### Download PDFs
|
|
145
|
+
|
|
146
|
+
```python
|
|
147
|
+
import asyncio
|
|
148
|
+
from lit_acquisition import download_file_from_url
|
|
149
|
+
|
|
150
|
+
async def main():
|
|
151
|
+
file_path, final_url, warnings = await download_file_from_url(
|
|
152
|
+
url="https://example.com/paper.pdf",
|
|
153
|
+
download_path="./downloads",
|
|
154
|
+
filename_stem="my_paper",
|
|
155
|
+
)
|
|
156
|
+
print(f"Downloaded to: {file_path}")
|
|
157
|
+
|
|
158
|
+
asyncio.run(main())
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
### Use the PubMed Service
|
|
162
|
+
|
|
163
|
+
```python
|
|
164
|
+
import asyncio
|
|
165
|
+
from lit_acquisition import get_pubmed_service
|
|
166
|
+
|
|
167
|
+
async def main():
|
|
168
|
+
svc = get_pubmed_service()
|
|
169
|
+
candidates = await svc.search_candidates("BRCA1 breast cancer", candidate_limit=10)
|
|
170
|
+
for c in candidates:
|
|
171
|
+
print(f" PMID: {c.pmid}, Title: {c.title}")
|
|
172
|
+
|
|
173
|
+
asyncio.run(main())
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
## Supported Providers
|
|
177
|
+
|
|
178
|
+
| Provider | Search | Download | Notes |
|
|
179
|
+
|----------|--------|----------|-------|
|
|
180
|
+
| Crossref | ✓ | — | Metadata only |
|
|
181
|
+
| Unpaywall | ✓ | ✓ | OA PDF resolution via DOI |
|
|
182
|
+
| OpenAlex | ✓ | — | Metadata only |
|
|
183
|
+
| EuropePMC | ✓ | ✓ | Full text via PMCID render |
|
|
184
|
+
| PMC | ✓ | ✓ | esearch + esummary |
|
|
185
|
+
| DOAJ | ✓ | — | Metadata only |
|
|
186
|
+
| J-STAGE | ✓ | — | Japanese literature |
|
|
187
|
+
| CiNii | ✓ | — | Japanese research |
|
|
188
|
+
| arXiv | ✓ | ✓ | Preprint server |
|
|
189
|
+
| bioRxiv | ✓ | ✓ | Preprint server |
|
|
190
|
+
| medRxiv | ✓ | ✓ | Preprint server |
|
|
191
|
+
| SciELO | ✓ | — | Latin American literature |
|
|
192
|
+
| BASE | ✓ | — | Multidisciplinary |
|
|
193
|
+
| CORE | ✓ | — | Open access |
|
|
194
|
+
| OpenAIRE | ✓ | — | European research |
|
|
195
|
+
|
|
196
|
+
## Configuration Reference
|
|
197
|
+
|
|
198
|
+
### Environment Variables
|
|
199
|
+
|
|
200
|
+
| Variable | Description | Default |
|
|
201
|
+
|----------|-------------|---------|
|
|
202
|
+
| `LIT_LLM_BASE_URL` | LLM API base URL | — |
|
|
203
|
+
| `LIT_LLM_API_KEY` | LLM API key | — |
|
|
204
|
+
| `LIT_LLM_MODEL` | LLM model name | — |
|
|
205
|
+
| `LIT_LLM_API_KEYS` | Comma-separated API key pool | — |
|
|
206
|
+
| `LIT_LLM_MAX_TOKENS` | Max tokens for LLM | `8192` |
|
|
207
|
+
| `LIT_TRANSLATION_BASE_URL` | Translation LLM base URL | Falls back to LLM config |
|
|
208
|
+
| `LIT_TRANSLATION_API_KEY` | Translation LLM API key | Falls back to LLM config |
|
|
209
|
+
| `LIT_TRANSLATION_MODEL` | Translation LLM model | Falls back to LLM config |
|
|
210
|
+
| `LIT_FIRECRAWL_API_KEY` | Firecrawl API key | — |
|
|
211
|
+
| `LIT_TAVILY_API_KEY` | Tavily API key | — |
|
|
212
|
+
| `LIT_SERPAPI_API_KEY` | SerpApi API key | — |
|
|
213
|
+
| `LIT_PROXY` | HTTP/HTTPS/SOCKS proxy URL | — |
|
|
214
|
+
| `LIT_NO_PROXY` | Comma-separated proxy bypass domains | `cn,ncbi.nlm.nih.gov,...` |
|
|
215
|
+
| `LIT_PUBMED_API_KEY` | PubMed eutils API key | — |
|
|
216
|
+
|
|
217
|
+
## License
|
|
218
|
+
|
|
219
|
+
MIT
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
# lit-acquisition
|
|
2
|
+
|
|
3
|
+
Multilingual biomedical literature acquisition toolkit — search, download, and classify academic papers from 15+ providers.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **15+ provider integrations**: Crossref, PubMed, OpenAlex, EuropePMC, DOAJ, J-STAGE, arXiv, bioRxiv, medRxiv, SciELO, BASE, CORE, OpenAIRE, CiNii, Unpaywall
|
|
8
|
+
- **Multilingual search**: Query translation into 6 languages (en, zh, ja, de, fr, ru) with language-aware provider routing
|
|
9
|
+
- **PDF download**: DOI → Unpaywall OA resolution, PMCID → EuropePMC render, direct URL with HTML→PDF redirect handling
|
|
10
|
+
- **Relevance gate**: LLM-based classification to filter irrelevant downloads
|
|
11
|
+
- **Literature type classification**: Keyword-based classification (case report, sequencing, functional study) across 10+ languages
|
|
12
|
+
- **Web search fallback**: Firecrawl, Tavily, and SerpApi adapters for discovering papers beyond academic APIs
|
|
13
|
+
- **Provider health tracking**: Automatic health monitoring with sliding-window stats and unhealthy provider deprioritization
|
|
14
|
+
|
|
15
|
+
## Installation
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
pip install lit-acquisition
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
With web search support:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
pip install "lit-acquisition[web-search]"
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
With Rust native extensions (faster HTTP I/O):
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
pip install "lit-acquisition[rust-io]"
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Quick Start
|
|
34
|
+
|
|
35
|
+
### Configure
|
|
36
|
+
|
|
37
|
+
```python
|
|
38
|
+
from lit_acquisition import configure
|
|
39
|
+
|
|
40
|
+
configure(
|
|
41
|
+
# LLM for relevance gate and query translation
|
|
42
|
+
llm_base_url="https://api.openai.com/v1",
|
|
43
|
+
llm_api_key="sk-...",
|
|
44
|
+
llm_model="gpt-4o",
|
|
45
|
+
|
|
46
|
+
# Optional: dedicated translation model
|
|
47
|
+
translation_base_url="https://api.openai.com/v1",
|
|
48
|
+
translation_api_key="sk-...",
|
|
49
|
+
translation_model="gpt-4o-mini",
|
|
50
|
+
|
|
51
|
+
# Optional: web search providers
|
|
52
|
+
firecrawl_api_key="fc-...",
|
|
53
|
+
tavily_api_key="tvly-...",
|
|
54
|
+
|
|
55
|
+
# Optional: network proxy
|
|
56
|
+
proxy="http://127.0.0.1:7890",
|
|
57
|
+
|
|
58
|
+
# Optional: PubMed API key (higher rate limits)
|
|
59
|
+
pubmed_api_key="...",
|
|
60
|
+
)
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Or via environment variables:
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
export LIT_LLM_BASE_URL=https://api.openai.com/v1
|
|
67
|
+
export LIT_LLM_API_KEY=sk-...
|
|
68
|
+
export LIT_LLM_MODEL=gpt-4o
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### Search a Single Provider
|
|
72
|
+
|
|
73
|
+
```python
|
|
74
|
+
import asyncio
|
|
75
|
+
from lit_acquisition import search_provider
|
|
76
|
+
|
|
77
|
+
async def main():
|
|
78
|
+
result = await search_provider(
|
|
79
|
+
provider="crossref",
|
|
80
|
+
query="MECP2 Rett syndrome case report",
|
|
81
|
+
limit=20,
|
|
82
|
+
)
|
|
83
|
+
print(f"Found {len(result.items)} items")
|
|
84
|
+
for item in result.items:
|
|
85
|
+
print(f" - {item.get('title', 'untitled')}")
|
|
86
|
+
|
|
87
|
+
asyncio.run(main())
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### Run the Full Multilingual Pipeline
|
|
91
|
+
|
|
92
|
+
```python
|
|
93
|
+
import asyncio
|
|
94
|
+
from lit_acquisition import multilingual_acquisition_workflow
|
|
95
|
+
|
|
96
|
+
async def main():
|
|
97
|
+
result = await multilingual_acquisition_workflow({
|
|
98
|
+
"query": "MECP2 Rett syndrome case report",
|
|
99
|
+
"action": "search", # or "download" to also fetch PDFs
|
|
100
|
+
"limit": 30,
|
|
101
|
+
"language": "auto",
|
|
102
|
+
"relevance_gate": True, # LLM-based relevance filtering
|
|
103
|
+
"literature_types": ["case_report"],
|
|
104
|
+
})
|
|
105
|
+
print(f"Success: {result['success']}")
|
|
106
|
+
print(f"Items: {len(result['items'])}")
|
|
107
|
+
print(f"Downloads: {len(result['downloads'])}")
|
|
108
|
+
|
|
109
|
+
asyncio.run(main())
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### Download PDFs
|
|
113
|
+
|
|
114
|
+
```python
|
|
115
|
+
import asyncio
|
|
116
|
+
from lit_acquisition import download_file_from_url
|
|
117
|
+
|
|
118
|
+
async def main():
|
|
119
|
+
file_path, final_url, warnings = await download_file_from_url(
|
|
120
|
+
url="https://example.com/paper.pdf",
|
|
121
|
+
download_path="./downloads",
|
|
122
|
+
filename_stem="my_paper",
|
|
123
|
+
)
|
|
124
|
+
print(f"Downloaded to: {file_path}")
|
|
125
|
+
|
|
126
|
+
asyncio.run(main())
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
### Use the PubMed Service
|
|
130
|
+
|
|
131
|
+
```python
|
|
132
|
+
import asyncio
|
|
133
|
+
from lit_acquisition import get_pubmed_service
|
|
134
|
+
|
|
135
|
+
async def main():
|
|
136
|
+
svc = get_pubmed_service()
|
|
137
|
+
candidates = await svc.search_candidates("BRCA1 breast cancer", candidate_limit=10)
|
|
138
|
+
for c in candidates:
|
|
139
|
+
print(f" PMID: {c.pmid}, Title: {c.title}")
|
|
140
|
+
|
|
141
|
+
asyncio.run(main())
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
## Supported Providers
|
|
145
|
+
|
|
146
|
+
| Provider | Search | Download | Notes |
|
|
147
|
+
|----------|--------|----------|-------|
|
|
148
|
+
| Crossref | ✓ | — | Metadata only |
|
|
149
|
+
| Unpaywall | ✓ | ✓ | OA PDF resolution via DOI |
|
|
150
|
+
| OpenAlex | ✓ | — | Metadata only |
|
|
151
|
+
| EuropePMC | ✓ | ✓ | Full text via PMCID render |
|
|
152
|
+
| PMC | ✓ | ✓ | esearch + esummary |
|
|
153
|
+
| DOAJ | ✓ | — | Metadata only |
|
|
154
|
+
| J-STAGE | ✓ | — | Japanese literature |
|
|
155
|
+
| CiNii | ✓ | — | Japanese research |
|
|
156
|
+
| arXiv | ✓ | ✓ | Preprint server |
|
|
157
|
+
| bioRxiv | ✓ | ✓ | Preprint server |
|
|
158
|
+
| medRxiv | ✓ | ✓ | Preprint server |
|
|
159
|
+
| SciELO | ✓ | — | Latin American literature |
|
|
160
|
+
| BASE | ✓ | — | Multidisciplinary |
|
|
161
|
+
| CORE | ✓ | — | Open access |
|
|
162
|
+
| OpenAIRE | ✓ | — | European research |
|
|
163
|
+
|
|
164
|
+
## Configuration Reference
|
|
165
|
+
|
|
166
|
+
### Environment Variables
|
|
167
|
+
|
|
168
|
+
| Variable | Description | Default |
|
|
169
|
+
|----------|-------------|---------|
|
|
170
|
+
| `LIT_LLM_BASE_URL` | LLM API base URL | — |
|
|
171
|
+
| `LIT_LLM_API_KEY` | LLM API key | — |
|
|
172
|
+
| `LIT_LLM_MODEL` | LLM model name | — |
|
|
173
|
+
| `LIT_LLM_API_KEYS` | Comma-separated API key pool | — |
|
|
174
|
+
| `LIT_LLM_MAX_TOKENS` | Max tokens for LLM | `8192` |
|
|
175
|
+
| `LIT_TRANSLATION_BASE_URL` | Translation LLM base URL | Falls back to LLM config |
|
|
176
|
+
| `LIT_TRANSLATION_API_KEY` | Translation LLM API key | Falls back to LLM config |
|
|
177
|
+
| `LIT_TRANSLATION_MODEL` | Translation LLM model | Falls back to LLM config |
|
|
178
|
+
| `LIT_FIRECRAWL_API_KEY` | Firecrawl API key | — |
|
|
179
|
+
| `LIT_TAVILY_API_KEY` | Tavily API key | — |
|
|
180
|
+
| `LIT_SERPAPI_API_KEY` | SerpApi API key | — |
|
|
181
|
+
| `LIT_PROXY` | HTTP/HTTPS/SOCKS proxy URL | — |
|
|
182
|
+
| `LIT_NO_PROXY` | Comma-separated proxy bypass domains | `cn,ncbi.nlm.nih.gov,...` |
|
|
183
|
+
| `LIT_PUBMED_API_KEY` | PubMed eutils API key | — |
|
|
184
|
+
|
|
185
|
+
## License
|
|
186
|
+
|
|
187
|
+
MIT
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "lit-acquisition"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "Multilingual biomedical literature acquisition toolkit - search, download, and classify academic papers from 15+ providers"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.12"
|
|
7
|
+
license = "MIT"
|
|
8
|
+
authors = [
|
|
9
|
+
{ name = "Lingua Seeker Maintainers" },
|
|
10
|
+
]
|
|
11
|
+
keywords = ["literature", "biomedical", "acquisition", "pubmed", "crossref", "openalex", "multilingual"]
|
|
12
|
+
classifiers = [
|
|
13
|
+
"Development Status :: 4 - Beta",
|
|
14
|
+
"Intended Audience :: Science/Research",
|
|
15
|
+
"License :: OSI Approved :: MIT License",
|
|
16
|
+
"Programming Language :: Python :: 3.12",
|
|
17
|
+
"Programming Language :: Python :: 3.13",
|
|
18
|
+
"Topic :: Scientific/Engineering :: Bio-Informatics",
|
|
19
|
+
]
|
|
20
|
+
dependencies = [
|
|
21
|
+
"httpx[socks]>=0.27.0",
|
|
22
|
+
"pydantic>=2.7.0",
|
|
23
|
+
"loguru>=0.7.0",
|
|
24
|
+
"pymupdf>=1.27.2",
|
|
25
|
+
"openai>=1.0.0",
|
|
26
|
+
"pyjstage2>=0.1.2",
|
|
27
|
+
]
|
|
28
|
+
|
|
29
|
+
[project.optional-dependencies]
|
|
30
|
+
web-search = [
|
|
31
|
+
"firecrawl-py>=4.28.2",
|
|
32
|
+
"tavily-python>=0.5.0",
|
|
33
|
+
"google-search-results>=2.4.2",
|
|
34
|
+
]
|
|
35
|
+
rust-io = [
|
|
36
|
+
"rust-io",
|
|
37
|
+
]
|
|
38
|
+
dev = [
|
|
39
|
+
"pytest>=8.2.0",
|
|
40
|
+
"pytest-asyncio>=0.23.0",
|
|
41
|
+
"ruff>=0.5.0",
|
|
42
|
+
]
|
|
43
|
+
|
|
44
|
+
[build-system]
|
|
45
|
+
requires = ["hatchling"]
|
|
46
|
+
build-backend = "hatchling.build"
|
|
47
|
+
|
|
48
|
+
[tool.hatch.build.targets.wheel]
|
|
49
|
+
packages = ["src/lit_acquisition"]
|
|
50
|
+
|
|
51
|
+
[tool.ruff]
|
|
52
|
+
line-length = 120
|
|
53
|
+
target-version = "py312"
|
|
54
|
+
|
|
55
|
+
[tool.pytest.ini_options]
|
|
56
|
+
testpaths = ["tests"]
|
|
57
|
+
asyncio_mode = "auto"
|
|
58
|
+
|
|
59
|
+
[tool.ruff.lint]
|
|
60
|
+
ignore = ["BLE001", "S110", "PLC0206"]
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
"""lit-acquisition: Multilingual biomedical literature acquisition toolkit.
|
|
2
|
+
|
|
3
|
+
Search, download, and classify academic papers from 15+ providers including
|
|
4
|
+
Crossref, PubMed, OpenAlex, EuropePMC, DOAJ, J-STAGE, arXiv, and more.
|
|
5
|
+
|
|
6
|
+
Quick start::
|
|
7
|
+
|
|
8
|
+
from lit_acquisition import configure, search_provider, download_file_from_url
|
|
9
|
+
|
|
10
|
+
# Configure your LLM provider (for query translation and relevance gate)
|
|
11
|
+
configure(
|
|
12
|
+
llm_base_url="https://api.openai.com/v1",
|
|
13
|
+
llm_api_key="sk-...",
|
|
14
|
+
llm_model="gpt-4o",
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
# Search a single provider
|
|
18
|
+
result = await search_provider(provider="crossref", query="MECP2 Rett syndrome")
|
|
19
|
+
|
|
20
|
+
# Or run the full multilingual pipeline
|
|
21
|
+
from lit_acquisition import multilingual_acquisition_workflow
|
|
22
|
+
result = await multilingual_acquisition_workflow({"query": "MECP2 Rett syndrome", "action": "search"})
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
from .config import (
|
|
26
|
+
LitAcquisitionConfig,
|
|
27
|
+
LLMConfig,
|
|
28
|
+
NetworkConfig,
|
|
29
|
+
PubMedConfig,
|
|
30
|
+
TranslationLLMConfig,
|
|
31
|
+
WebSearchConfig,
|
|
32
|
+
configure,
|
|
33
|
+
get_config,
|
|
34
|
+
reload_config,
|
|
35
|
+
)
|
|
36
|
+
from .contracts import (
|
|
37
|
+
DownloadResult,
|
|
38
|
+
OnlineAcquisitionGatewayRequest,
|
|
39
|
+
OnlineAcquisitionGatewayResult,
|
|
40
|
+
OnlineAcquisitionItem,
|
|
41
|
+
OnlineAcquisitionRequest,
|
|
42
|
+
OnlineAcquisitionResponse,
|
|
43
|
+
OnlineAcquisitionRouteInfo,
|
|
44
|
+
OnlineAcquisitionSourceTraceEntry,
|
|
45
|
+
)
|
|
46
|
+
from .gateway import (
|
|
47
|
+
call_provider,
|
|
48
|
+
call_provider_with_retry,
|
|
49
|
+
download_file_from_url,
|
|
50
|
+
resolve_oa_url,
|
|
51
|
+
search_provider,
|
|
52
|
+
)
|
|
53
|
+
from .literature_type_classifier import (
|
|
54
|
+
LiteratureType,
|
|
55
|
+
classify_item,
|
|
56
|
+
classify_items,
|
|
57
|
+
filter_by_type,
|
|
58
|
+
)
|
|
59
|
+
from .normalizers import normalize_items
|
|
60
|
+
from .provider_health import ProviderHealthTracker, ProviderStats, get_health_tracker
|
|
61
|
+
from .pubmed_service import (
|
|
62
|
+
OnlineAcquisitionPubMedArticle,
|
|
63
|
+
OnlineAcquisitionPubMedCandidate,
|
|
64
|
+
OnlineAcquisitionPubMedService,
|
|
65
|
+
get_pubmed_service,
|
|
66
|
+
)
|
|
67
|
+
from .query_translator import (
|
|
68
|
+
TARGET_LANGUAGES,
|
|
69
|
+
TranslatedQueries,
|
|
70
|
+
translate_query,
|
|
71
|
+
)
|
|
72
|
+
from .relevance_gate import (
|
|
73
|
+
RelevanceGateResult,
|
|
74
|
+
RelevanceJudgment,
|
|
75
|
+
run_relevance_gate,
|
|
76
|
+
)
|
|
77
|
+
from .search_service import (
|
|
78
|
+
build_provider_plan,
|
|
79
|
+
dedupe_candidates,
|
|
80
|
+
rank_candidates,
|
|
81
|
+
search_multilingual,
|
|
82
|
+
search_parallel,
|
|
83
|
+
)
|
|
84
|
+
from .workflow import (
|
|
85
|
+
multilingual_acquisition_workflow,
|
|
86
|
+
online_acquisition_workflow,
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
__all__ = [
|
|
90
|
+
"TARGET_LANGUAGES",
|
|
91
|
+
"DownloadResult",
|
|
92
|
+
"LLMConfig",
|
|
93
|
+
# Config
|
|
94
|
+
"LitAcquisitionConfig",
|
|
95
|
+
# Literature type classifier
|
|
96
|
+
"LiteratureType",
|
|
97
|
+
"NetworkConfig",
|
|
98
|
+
"OnlineAcquisitionGatewayRequest",
|
|
99
|
+
"OnlineAcquisitionGatewayResult",
|
|
100
|
+
# Contracts
|
|
101
|
+
"OnlineAcquisitionItem",
|
|
102
|
+
"OnlineAcquisitionPubMedArticle",
|
|
103
|
+
"OnlineAcquisitionPubMedCandidate",
|
|
104
|
+
# PubMed
|
|
105
|
+
"OnlineAcquisitionPubMedService",
|
|
106
|
+
"OnlineAcquisitionRequest",
|
|
107
|
+
"OnlineAcquisitionResponse",
|
|
108
|
+
"OnlineAcquisitionRouteInfo",
|
|
109
|
+
"OnlineAcquisitionSourceTraceEntry",
|
|
110
|
+
"ProviderHealthTracker",
|
|
111
|
+
"ProviderStats",
|
|
112
|
+
"PubMedConfig",
|
|
113
|
+
# Relevance gate
|
|
114
|
+
"RelevanceGateResult",
|
|
115
|
+
"RelevanceJudgment",
|
|
116
|
+
# Query translator
|
|
117
|
+
"TranslatedQueries",
|
|
118
|
+
"TranslationLLMConfig",
|
|
119
|
+
"WebSearchConfig",
|
|
120
|
+
# Search
|
|
121
|
+
"build_provider_plan",
|
|
122
|
+
# Gateway
|
|
123
|
+
"call_provider",
|
|
124
|
+
"call_provider_with_retry",
|
|
125
|
+
"classify_item",
|
|
126
|
+
"classify_items",
|
|
127
|
+
"configure",
|
|
128
|
+
"dedupe_candidates",
|
|
129
|
+
"download_file_from_url",
|
|
130
|
+
"filter_by_type",
|
|
131
|
+
"get_config",
|
|
132
|
+
# Provider health
|
|
133
|
+
"get_health_tracker",
|
|
134
|
+
"get_pubmed_service",
|
|
135
|
+
"multilingual_acquisition_workflow",
|
|
136
|
+
# Normalizers
|
|
137
|
+
"normalize_items",
|
|
138
|
+
# Workflow
|
|
139
|
+
"online_acquisition_workflow",
|
|
140
|
+
"rank_candidates",
|
|
141
|
+
"reload_config",
|
|
142
|
+
"resolve_oa_url",
|
|
143
|
+
"run_relevance_gate",
|
|
144
|
+
"search_multilingual",
|
|
145
|
+
"search_parallel",
|
|
146
|
+
"search_provider",
|
|
147
|
+
"translate_query",
|
|
148
|
+
]
|
|
149
|
+
|
|
150
|
+
__version__ = "0.1.0"
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"""Optional MinerU parse service integration.
|
|
2
|
+
|
|
3
|
+
The multilingual workflow uses MinerU for early PDF parsing before the
|
|
4
|
+
relevance gate. When the ``parse_document`` module is not available
|
|
5
|
+
(e.g. when lit-acquisition is used standalone), batch parsing is skipped
|
|
6
|
+
and the relevance gate falls back to PyMuPDF text extraction.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def create_parse_service():
|
|
13
|
+
"""Create a parse service instance.
|
|
14
|
+
|
|
15
|
+
Returns:
|
|
16
|
+
A parse service object with ``parse_local_files`` method.
|
|
17
|
+
|
|
18
|
+
Raises:
|
|
19
|
+
ImportError: When the parse_document module is not available.
|
|
20
|
+
"""
|
|
21
|
+
try:
|
|
22
|
+
from lit_acquisition_parse_document import create_parse_service as _create
|
|
23
|
+
except ImportError as exc:
|
|
24
|
+
raise ImportError(
|
|
25
|
+
"MinerU parse service is not available. "
|
|
26
|
+
"Install the 'lit-acquisition-parse' extra or use the workflow "
|
|
27
|
+
"without early batch parsing."
|
|
28
|
+
) from exc
|
|
29
|
+
return _create()
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"""Optional Rust native extension loader.
|
|
2
|
+
|
|
3
|
+
When the ``rust-io`` package is installed, HTTP I/O is handled by the
|
|
4
|
+
fast Rust extension. When it is not available, the module falls back
|
|
5
|
+
to ``None``, and the gateway uses ``httpx`` as a pure-Python fallback.
|
|
6
|
+
|
|
7
|
+
Install the Rust extension for better performance::
|
|
8
|
+
|
|
9
|
+
pip install rust-io
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
from loguru import logger
|
|
15
|
+
|
|
16
|
+
NET_AVAILABLE: bool = False
|
|
17
|
+
net_io = None
|
|
18
|
+
|
|
19
|
+
_NATIVE_IMPORT_ERRORS = (ImportError, RuntimeError, SystemError, OSError)
|
|
20
|
+
|
|
21
|
+
try:
|
|
22
|
+
import rust_io.net as net_io # noqa: F401
|
|
23
|
+
|
|
24
|
+
NET_AVAILABLE = True
|
|
25
|
+
except _NATIVE_IMPORT_ERRORS:
|
|
26
|
+
logger.debug("rust_io.net not available - using httpx fallback for HTTP I/O")
|