litresearch 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.
- litresearch-0.1.0/.env.example +7 -0
- litresearch-0.1.0/.github/workflows/ci.yml +13 -0
- litresearch-0.1.0/.github/workflows/release.yml +16 -0
- litresearch-0.1.0/.gitignore +39 -0
- litresearch-0.1.0/.python-version +1 -0
- litresearch-0.1.0/AGENTS.md +38 -0
- litresearch-0.1.0/CHANGELOG.md +19 -0
- litresearch-0.1.0/LICENSE +21 -0
- litresearch-0.1.0/PKG-INFO +45 -0
- litresearch-0.1.0/PROJECT.md +142 -0
- litresearch-0.1.0/README.md +25 -0
- litresearch-0.1.0/cliff.toml +49 -0
- litresearch-0.1.0/docs/decisions/.gitkeep +0 -0
- litresearch-0.1.0/noxfile.py +51 -0
- litresearch-0.1.0/pyproject.toml +59 -0
- litresearch-0.1.0/src/litresearch/__init__.py +5 -0
- litresearch-0.1.0/src/litresearch/__main__.py +6 -0
- litresearch-0.1.0/src/litresearch/cli.py +30 -0
- litresearch-0.1.0/src/litresearch/config.py +28 -0
- litresearch-0.1.0/src/litresearch/models.py +9 -0
- litresearch-0.1.0/tests/integration/__init__.py +0 -0
- litresearch-0.1.0/tests/integration/test_module_entrypoint.py +21 -0
- litresearch-0.1.0/tests/unit/__init__.py +0 -0
- litresearch-0.1.0/tests/unit/test_cli.py +12 -0
- litresearch-0.1.0/uv.lock +1936 -0
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
on:
|
|
3
|
+
push:
|
|
4
|
+
tags: ["v*"]
|
|
5
|
+
jobs:
|
|
6
|
+
publish:
|
|
7
|
+
runs-on: ubuntu-latest
|
|
8
|
+
permissions:
|
|
9
|
+
id-token: write
|
|
10
|
+
contents: write
|
|
11
|
+
steps:
|
|
12
|
+
- uses: actions/checkout@v4
|
|
13
|
+
- uses: astral-sh/setup-uv@v5
|
|
14
|
+
- run: uv run nox
|
|
15
|
+
- run: uv build
|
|
16
|
+
- uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# OS
|
|
2
|
+
.DS_Store
|
|
3
|
+
Thumbs.db
|
|
4
|
+
*.swp
|
|
5
|
+
*.swo
|
|
6
|
+
*~
|
|
7
|
+
|
|
8
|
+
# IDEs
|
|
9
|
+
.vscode/
|
|
10
|
+
.idea/
|
|
11
|
+
*.sublime-project
|
|
12
|
+
*.sublime-workspace
|
|
13
|
+
|
|
14
|
+
# Python
|
|
15
|
+
__pycache__/
|
|
16
|
+
*.py[cod]
|
|
17
|
+
*$py.class
|
|
18
|
+
*.so
|
|
19
|
+
.Python
|
|
20
|
+
build/
|
|
21
|
+
dist/
|
|
22
|
+
*.egg-info/
|
|
23
|
+
*.egg
|
|
24
|
+
.env
|
|
25
|
+
.venv/
|
|
26
|
+
|
|
27
|
+
# Tool caches
|
|
28
|
+
.ruff_cache/
|
|
29
|
+
.pyright/
|
|
30
|
+
.pytest_cache/
|
|
31
|
+
.coverage
|
|
32
|
+
htmlcov/
|
|
33
|
+
.nox/
|
|
34
|
+
.uv_cache/
|
|
35
|
+
|
|
36
|
+
# Agent runtime artifacts
|
|
37
|
+
.pi/plans/
|
|
38
|
+
.pi/reviews/
|
|
39
|
+
.pi/LESSONS.md
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.12
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# litresearch
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
CLI-first Python package for automated literature research workflows. The scaffold
|
|
5
|
+
supports a Typer CLI, importable package code, and standard OSS tooling.
|
|
6
|
+
|
|
7
|
+
## Tech Stack
|
|
8
|
+
- Language: Python 3.12
|
|
9
|
+
- Framework: Typer
|
|
10
|
+
- Core dependencies: LiteLLM, Semantic Scholar client, Pydantic, httpx, pypdf, Rich
|
|
11
|
+
- Tooling: uv, Ruff, Pyright, Pytest, Nox, GitHub Actions
|
|
12
|
+
|
|
13
|
+
## Directory Structure
|
|
14
|
+
src/
|
|
15
|
+
litresearch/ Python package and CLI entrypoints
|
|
16
|
+
|
|
17
|
+
tests/
|
|
18
|
+
unit/ Fast unit tests
|
|
19
|
+
integration/ Integration-level CLI checks
|
|
20
|
+
|
|
21
|
+
docs/
|
|
22
|
+
decisions/ ADRs and design notes
|
|
23
|
+
|
|
24
|
+
## Conventions
|
|
25
|
+
- Keep scaffold files minimal until /plan defines implementation work.
|
|
26
|
+
- Use `src/` layout for all package code.
|
|
27
|
+
- Prefer small, typed modules with fail-fast validation.
|
|
28
|
+
- Keep CLI wiring thin; business logic belongs in separate modules.
|
|
29
|
+
|
|
30
|
+
## Architecture Decisions
|
|
31
|
+
- Typer provides the public CLI surface.
|
|
32
|
+
- Pydantic settings handle environment-backed configuration.
|
|
33
|
+
- Nox is the single entrypoint for lint, typecheck, and test workflows.
|
|
34
|
+
- GitHub Actions should mirror local `uv run nox` behavior.
|
|
35
|
+
|
|
36
|
+
## Known Constraints
|
|
37
|
+
- This repo is scaffold-only at init time. Pipeline stages and prompts are not implemented here.
|
|
38
|
+
- Semantic Scholar and LLM provider details stay in configuration, not hard-coded in the scaffold.
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [0.1.0] - 2026-03-09
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- **ci:** Add oss release workflows
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
### Maintenance
|
|
17
|
+
|
|
18
|
+
- Initial project setup
|
|
19
|
+
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 SilasPignotti
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: litresearch
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: CLI tool for automated literature research workflows.
|
|
5
|
+
Project-URL: Homepage, https://github.com/spignotti/litresearch
|
|
6
|
+
Project-URL: Repository, https://github.com/spignotti/litresearch
|
|
7
|
+
Author-email: Silas Pignotti <pignottisilas@gmail.com>
|
|
8
|
+
License: MIT
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Requires-Python: >=3.12
|
|
11
|
+
Requires-Dist: httpx>=0.28.1
|
|
12
|
+
Requires-Dist: litellm>=1.82.0
|
|
13
|
+
Requires-Dist: pydantic-settings>=2.13.1
|
|
14
|
+
Requires-Dist: pydantic>=2.12.5
|
|
15
|
+
Requires-Dist: pypdf>=6.7.5
|
|
16
|
+
Requires-Dist: rich>=14.3.3
|
|
17
|
+
Requires-Dist: semanticscholar>=0.11.0
|
|
18
|
+
Requires-Dist: typer>=0.24.1
|
|
19
|
+
Description-Content-Type: text/markdown
|
|
20
|
+
|
|
21
|
+
# litresearch
|
|
22
|
+
[](https://github.com/spignotti/litresearch/actions/workflows/ci.yml) [](https://pypi.org/project/litresearch/)
|
|
23
|
+
|
|
24
|
+
CLI tool that automates literature research from research questions to curated,
|
|
25
|
+
ranked, and exported paper sets with structured reports.
|
|
26
|
+
|
|
27
|
+
## Overview
|
|
28
|
+
- Automates discovery, screening, analysis, ranking, and export steps.
|
|
29
|
+
- Targets a CLI-first workflow with an importable Python API.
|
|
30
|
+
- Uses Semantic Scholar for paper metadata and LiteLLM for provider-agnostic LLM access.
|
|
31
|
+
|
|
32
|
+
## Planned Architecture
|
|
33
|
+
- Framework: Typer CLI with package modules under `src/litresearch/`
|
|
34
|
+
- Core dependencies: LiteLLM, Semantic Scholar client, Pydantic, httpx, pypdf, Rich
|
|
35
|
+
- Outputs: Markdown reports, bibliographic exports, downloaded PDFs, and JSON pipeline state
|
|
36
|
+
|
|
37
|
+
## Development
|
|
38
|
+
```bash
|
|
39
|
+
uv sync
|
|
40
|
+
uv run nox
|
|
41
|
+
uv run litresearch --help
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Status
|
|
45
|
+
This repository currently contains project scaffolding only. Product logic will be added in later planning and task steps.
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
# litresearch
|
|
2
|
+
|
|
3
|
+
## Identity
|
|
4
|
+
- **What**: CLI tool that automates literature research — from research questions to a curated, ranked, and exported set of relevant papers with structured reports.
|
|
5
|
+
- **Why**: Manual literature research (Elicit → PDF download → LLM summarization → manual ranking) is tedious and repetitive. This pipeline automates the entire flow end-to-end.
|
|
6
|
+
- **Type**: cli
|
|
7
|
+
- **Python**: 3.12
|
|
8
|
+
|
|
9
|
+
## Architecture
|
|
10
|
+
- **Framework**: Typer (CLI) + importable Python API
|
|
11
|
+
- **Dependencies**:
|
|
12
|
+
- typer: CLI framework with type hints and auto-generated help
|
|
13
|
+
- litellm: Multi-provider LLM abstraction (100+ providers via unified interface)
|
|
14
|
+
- semanticscholar: Typed Python client for Semantic Scholar API (paper search, metadata, pagination)
|
|
15
|
+
- pypdf: PDF text extraction (pure Python, BSD license — MIT-compatible)
|
|
16
|
+
- pydantic / pydantic-settings: Config validation, typed models, TOML/ENV support
|
|
17
|
+
- httpx: Async HTTP client for PDF downloads
|
|
18
|
+
- rich: Terminal output formatting (progress bars, tables, status)
|
|
19
|
+
- **Secrets**:
|
|
20
|
+
- LLM API key (provider-dependent, e.g. OPENAI_API_KEY, ANTHROPIC_API_KEY) — required
|
|
21
|
+
- S2_API_KEY — optional (higher rate limits with key, works without)
|
|
22
|
+
|
|
23
|
+
### Data Flow
|
|
24
|
+
Research Questions → [Query Generation] → [Paper Discovery] → [Metadata Enrichment] → [LLM Analysis] → [Ranking] → [Export]
|
|
25
|
+
- **Trigger**: Manual (CLI command)
|
|
26
|
+
- **Volume**: ~50–200 candidate papers per run, ~20–50 analyzed in depth, Top-N exported
|
|
27
|
+
|
|
28
|
+
### External Services
|
|
29
|
+
| Service | Auth | Rate Limit | Purpose |
|
|
30
|
+
|---------|------|------------|---------|
|
|
31
|
+
| Semantic Scholar API | API Key (optional) | 1 RPS authenticated, shared pool unauthenticated | Paper search, metadata, OA-PDF links |
|
|
32
|
+
| LLM Provider (via LiteLLM) | API Key (required) | Provider-dependent | Query generation, abstract screening, paper analysis, synthesis |
|
|
33
|
+
|
|
34
|
+
### Data Model
|
|
35
|
+
- **Input**: List of research questions (strings)
|
|
36
|
+
- **Intermediate State** (JSON, resumable):
|
|
37
|
+
- Generated facets and search queries
|
|
38
|
+
- Candidate papers with metadata (title, abstract, authors, year, citations, venue, DOI, PDF URL)
|
|
39
|
+
- Screening scores (Stage A)
|
|
40
|
+
- Full analysis results (Stage B)
|
|
41
|
+
- **Output**:
|
|
42
|
+
- report.md — Main report (questions, search strategy, top-N papers with analysis, synthesis with research gaps)
|
|
43
|
+
- paper_analyses.md — Detailed analysis of all screened papers
|
|
44
|
+
- references.bib — BibTeX for top-N papers
|
|
45
|
+
- references.ris — RIS format (Zotero, Mendeley, Citavi import)
|
|
46
|
+
- papers/ — Downloaded PDFs of top-N open access papers
|
|
47
|
+
- data.json — Machine-readable export of all metadata + analyses
|
|
48
|
+
|
|
49
|
+
### Components
|
|
50
|
+
| Component | Purpose | Priority |
|
|
51
|
+
|-----------|---------|----------|
|
|
52
|
+
| cli.py | Typer CLI with `run`, `resume`, `config` commands | MVP |
|
|
53
|
+
| config.py | Pydantic Settings — all parameters configurable via TOML file + CLI flags + ENV | MVP |
|
|
54
|
+
| models.py | Pydantic models: Paper, Analysis, SearchQuery, Facet, PipelineState | MVP |
|
|
55
|
+
| pipeline.py | Orchestrator — runs stages sequentially, saves state after each stage | MVP |
|
|
56
|
+
| stages/query_gen.py | Stage 1: LLM generates facets + search queries from research questions | MVP |
|
|
57
|
+
| stages/discovery.py | Stage 2: Semantic Scholar API search, deduplication by DOI/Corpus-ID | MVP |
|
|
58
|
+
| stages/enrichment.py | Stage 3: Fetch full metadata for all candidates via S2 API | MVP |
|
|
59
|
+
| stages/analysis.py | Stage 4: Two-phase LLM analysis (A: abstract screening, B: extended with PDF text) | MVP |
|
|
60
|
+
| stages/ranking.py | Stage 5: Multi-criteria ranking (relevance score → citations → year) | MVP |
|
|
61
|
+
| stages/export.py | Stage 6: Generate report.md, paper_analyses.md, references.bib, references.ris, data.json, download PDFs | MVP |
|
|
62
|
+
| pdf.py | PDF text extraction — page heuristic: first N + last M pages | MVP |
|
|
63
|
+
| prompts/query_gen.md | Prompt template for facet + query generation | MVP |
|
|
64
|
+
| prompts/screening.md | Prompt template for abstract screening (Stage A) | MVP |
|
|
65
|
+
| prompts/analysis.md | Prompt template for extended analysis (Stage B) | MVP |
|
|
66
|
+
| prompts/synthesis.md | Prompt template for literature synthesis in report | MVP |
|
|
67
|
+
|
|
68
|
+
### Pipeline Stages Detail
|
|
69
|
+
|
|
70
|
+
**Stage 1 — Query Generation**
|
|
71
|
+
LLM receives research questions, derives thematic facets, generates 2–3 targeted search queries per facet. Output: list of Facet objects with queries.
|
|
72
|
+
|
|
73
|
+
**Stage 2 — Paper Discovery**
|
|
74
|
+
Each query hits Semantic Scholar relevance search endpoint. Results deduplicated by paperId/DOI. Output: candidate pool (typically 50–200 papers).
|
|
75
|
+
|
|
76
|
+
**Stage 3 — Metadata Enrichment**
|
|
77
|
+
Batch fetch via S2 API: title, abstract, authors, year, citationCount, venue, openAccessPdf, externalIds (DOI). Output: enriched Paper objects.
|
|
78
|
+
|
|
79
|
+
**Stage 4A — Abstract Screening**
|
|
80
|
+
Each paper's abstract + metadata sent to LLM. Returns relevance score (0–100). Papers below threshold (default: 40) are filtered out. Fast, cheap (short prompts).
|
|
81
|
+
|
|
82
|
+
**Stage 4B — Extended Analysis**
|
|
83
|
+
For papers passing screening: download PDF, extract first 4 + last 2 pages via pypdf, send to LLM with full analysis prompt. Returns: summary, key_findings, methodology, relevance_score (refined), relevance_rationale.
|
|
84
|
+
|
|
85
|
+
**Stage 5 — Ranking**
|
|
86
|
+
Sort by: relevance_score (desc) → citationCount (desc) → year (desc). Apply top_n cutoff.
|
|
87
|
+
|
|
88
|
+
**Stage 6 — Export**
|
|
89
|
+
Generate all output files. report.md includes a final LLM-generated synthesis section analyzing consensus, contradictions, and research gaps across top-N papers.
|
|
90
|
+
|
|
91
|
+
## Objectives
|
|
92
|
+
### MVP (v0.1)
|
|
93
|
+
- [ ] Full pipeline runs end-to-end: questions in → report + PDFs + BibTeX out
|
|
94
|
+
- [ ] All 6 stages implemented and working
|
|
95
|
+
- [ ] Configuration via TOML file + CLI flags
|
|
96
|
+
- [ ] Resume interrupted runs via --resume flag
|
|
97
|
+
- [ ] Works with at least OpenAI and Anthropic via LiteLLM
|
|
98
|
+
- [ ] Installable via pip install (proper pyproject.toml with entry point)
|
|
99
|
+
- [ ] README with quickstart, usage examples, configuration reference
|
|
100
|
+
- [ ] Tests for core logic (models, ranking, export formatting)
|
|
101
|
+
- [ ] CI via GitHub Actions (nox: lint + typecheck + test)
|
|
102
|
+
|
|
103
|
+
### Non-Goals
|
|
104
|
+
- GUI / Web interface
|
|
105
|
+
- NotebookLM integration
|
|
106
|
+
- Citation graph analysis
|
|
107
|
+
- Full-text PDF analysis (MVP uses page heuristic)
|
|
108
|
+
- Configurable analysis schema (MVP uses fixed schema)
|
|
109
|
+
- Section-aware PDF parsing (GROBID)
|
|
110
|
+
- Multi-source search (OpenAlex) — S2 only for MVP
|
|
111
|
+
|
|
112
|
+
## Constraints
|
|
113
|
+
- Semantic Scholar API: 1 RPS with key, respect rate limits. Use async batch where possible.
|
|
114
|
+
- LLM costs: Two-phase analysis minimizes tokens. Abstract screening is cheap (~200 tokens/paper). Extended analysis only for papers above threshold.
|
|
115
|
+
- PDF downloads: Only open access papers. Fail gracefully if PDF unavailable.
|
|
116
|
+
- License compatibility: All dependencies must be MIT/BSD/Apache-2.0 compatible. No AGPL.
|
|
117
|
+
|
|
118
|
+
## Setup
|
|
119
|
+
- **Category**: Open Source
|
|
120
|
+
- **Git Remote**: https://github.com/SilasPignotti/litresearch
|
|
121
|
+
- **License**: MIT
|
|
122
|
+
- **Release**: PyPI via GitHub Actions (Trusted Publisher, tag-triggered)
|
|
123
|
+
- **Versioning**: SemVer, start at 0.1.0, manual bumping + git-cliff for changelogs
|
|
124
|
+
- **Python**: 3.12
|
|
125
|
+
- **Package Manager**: uv
|
|
126
|
+
- **Quality**: nox (sessions: lint, typecheck, test) + ruff + pyright + pytest
|
|
127
|
+
- **CI**: GitHub Actions running nox on push/PR
|
|
128
|
+
|
|
129
|
+
## Context
|
|
130
|
+
- **Key Docs**:
|
|
131
|
+
- Semantic Scholar API: https://api.semanticscholar.org/api-docs/
|
|
132
|
+
- semanticscholar Python client: https://semanticscholar.readthedocs.io/
|
|
133
|
+
- LiteLLM: https://docs.litellm.ai/docs/
|
|
134
|
+
- Typer: https://typer.tiangolo.com/
|
|
135
|
+
- pypdf: https://pypdf.readthedocs.io/
|
|
136
|
+
- **Decisions**:
|
|
137
|
+
- LiteLLM over direct API calls — provider-agnostic is critical for open source adoption
|
|
138
|
+
- pypdf over PyMuPDF — AGPL license incompatible with MIT project
|
|
139
|
+
- JSON state over SQLite — simpler for MVP, SQLite as future enhancement
|
|
140
|
+
- Page heuristic (first 4 + last 2 pages) over section-aware parsing — no external services needed
|
|
141
|
+
- Prompts as markdown files — editable without code changes, version-controlled
|
|
142
|
+
- Dual interface (CLI + Python API) — Typer for CLI, underlying functions importable as library
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# litresearch
|
|
2
|
+
[](https://github.com/spignotti/litresearch/actions/workflows/ci.yml) [](https://pypi.org/project/litresearch/)
|
|
3
|
+
|
|
4
|
+
CLI tool that automates literature research from research questions to curated,
|
|
5
|
+
ranked, and exported paper sets with structured reports.
|
|
6
|
+
|
|
7
|
+
## Overview
|
|
8
|
+
- Automates discovery, screening, analysis, ranking, and export steps.
|
|
9
|
+
- Targets a CLI-first workflow with an importable Python API.
|
|
10
|
+
- Uses Semantic Scholar for paper metadata and LiteLLM for provider-agnostic LLM access.
|
|
11
|
+
|
|
12
|
+
## Planned Architecture
|
|
13
|
+
- Framework: Typer CLI with package modules under `src/litresearch/`
|
|
14
|
+
- Core dependencies: LiteLLM, Semantic Scholar client, Pydantic, httpx, pypdf, Rich
|
|
15
|
+
- Outputs: Markdown reports, bibliographic exports, downloaded PDFs, and JSON pipeline state
|
|
16
|
+
|
|
17
|
+
## Development
|
|
18
|
+
```bash
|
|
19
|
+
uv sync
|
|
20
|
+
uv run nox
|
|
21
|
+
uv run litresearch --help
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Status
|
|
25
|
+
This repository currently contains project scaffolding only. Product logic will be added in later planning and task steps.
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
[changelog]
|
|
2
|
+
header = """
|
|
3
|
+
# Changelog
|
|
4
|
+
|
|
5
|
+
All notable changes to this project will be documented in this file.
|
|
6
|
+
|
|
7
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
8
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
9
|
+
"""
|
|
10
|
+
body = """
|
|
11
|
+
{% if version %}
|
|
12
|
+
## [{{ version | trim_start_matches(pat="v") }}] - {{ timestamp | date(format="%Y-%m-%d") }}
|
|
13
|
+
{% else %}
|
|
14
|
+
## [Unreleased]
|
|
15
|
+
{% endif %}
|
|
16
|
+
{% for group, commits in commits | group_by(attribute="group") %}
|
|
17
|
+
### {{ group }}
|
|
18
|
+
{% for commit in commits %}
|
|
19
|
+
- {% if commit.scope %}**{{ commit.scope }}:** {% endif %}{{ commit.message | upper_first }}
|
|
20
|
+
{% endfor %}
|
|
21
|
+
{% endfor %}
|
|
22
|
+
"""
|
|
23
|
+
trim = true
|
|
24
|
+
|
|
25
|
+
[git]
|
|
26
|
+
conventional_commits = true
|
|
27
|
+
filter_unconventional = true
|
|
28
|
+
tag_pattern = "v[0-9]*"
|
|
29
|
+
split_commits = false
|
|
30
|
+
|
|
31
|
+
commit_preprocessors = [
|
|
32
|
+
{ pattern = "\\((#\\d+)\\)", replace = "" },
|
|
33
|
+
]
|
|
34
|
+
|
|
35
|
+
commit_parsers = [
|
|
36
|
+
{ message = "^feat", group = "Added" },
|
|
37
|
+
{ message = "^fix", group = "Fixed" },
|
|
38
|
+
{ message = "^refactor", group = "Changed" },
|
|
39
|
+
{ message = "^perf", group = "Changed" },
|
|
40
|
+
{ message = "^docs", group = "Documentation" },
|
|
41
|
+
{ message = "^test", group = "Tests" },
|
|
42
|
+
{ message = "^chore", group = "Maintenance" },
|
|
43
|
+
{ body = ".*security", group = "Security" },
|
|
44
|
+
]
|
|
45
|
+
|
|
46
|
+
protect_breaking_commits = false
|
|
47
|
+
filter_commits = false
|
|
48
|
+
toposort = false
|
|
49
|
+
sort_commits = "oldest"
|
|
File without changes
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import nox
|
|
2
|
+
|
|
3
|
+
nox.options.sessions = ["lint", "typecheck", "test"]
|
|
4
|
+
nox.options.stop_on_first_error = False
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
@nox.session
|
|
8
|
+
def lint(session: nox.Session) -> None:
|
|
9
|
+
"""Check code with ruff."""
|
|
10
|
+
session.run("uv", "run", "ruff", "check", "src", "tests", external=True)
|
|
11
|
+
session.run("uv", "run", "ruff", "format", "--check", "src", "tests", external=True)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@nox.session
|
|
15
|
+
def format(session: nox.Session) -> None:
|
|
16
|
+
"""Format code with ruff."""
|
|
17
|
+
session.run("uv", "run", "ruff", "format", "src", "tests", external=True)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@nox.session
|
|
21
|
+
def typecheck(session: nox.Session) -> None:
|
|
22
|
+
"""Type check with pyright."""
|
|
23
|
+
session.run("uv", "run", "pyright", external=True)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@nox.session
|
|
27
|
+
def fix(session: nox.Session) -> None:
|
|
28
|
+
"""Auto-fix lint issues and format code."""
|
|
29
|
+
session.run("uv", "run", "ruff", "check", "--fix", "src", "tests", external=True)
|
|
30
|
+
session.run("uv", "run", "ruff", "format", "src", "tests", external=True)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
@nox.session
|
|
34
|
+
def test(session: nox.Session) -> None:
|
|
35
|
+
"""Run tests."""
|
|
36
|
+
session.run("uv", "run", "pytest", "-v", external=True)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
@nox.session
|
|
40
|
+
def coverage(session: nox.Session) -> None:
|
|
41
|
+
"""Run tests with coverage."""
|
|
42
|
+
session.run("uv", "run", "pytest", "--cov=src", "--cov-report=term", "-v", external=True)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
@nox.session
|
|
46
|
+
def ci(session: nox.Session) -> None:
|
|
47
|
+
"""Run the full CI pipeline."""
|
|
48
|
+
session.run("uv", "run", "ruff", "check", "src", "tests", external=True)
|
|
49
|
+
session.run("uv", "run", "ruff", "format", "--check", "src", "tests", external=True)
|
|
50
|
+
session.run("uv", "run", "pyright", external=True)
|
|
51
|
+
session.run("uv", "run", "pytest", "--cov=src", "-v", external=True)
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "litresearch"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "CLI tool for automated literature research workflows."
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.12"
|
|
7
|
+
authors = [
|
|
8
|
+
{ name = "Silas Pignotti", email = "pignottisilas@gmail.com" },
|
|
9
|
+
]
|
|
10
|
+
license = { text = "MIT" }
|
|
11
|
+
dependencies = [
|
|
12
|
+
"httpx>=0.28.1",
|
|
13
|
+
"litellm>=1.82.0",
|
|
14
|
+
"pydantic>=2.12.5",
|
|
15
|
+
"pydantic-settings>=2.13.1",
|
|
16
|
+
"pypdf>=6.7.5",
|
|
17
|
+
"rich>=14.3.3",
|
|
18
|
+
"semanticscholar>=0.11.0",
|
|
19
|
+
"typer>=0.24.1",
|
|
20
|
+
]
|
|
21
|
+
|
|
22
|
+
[project.urls]
|
|
23
|
+
Homepage = "https://github.com/spignotti/litresearch"
|
|
24
|
+
Repository = "https://github.com/spignotti/litresearch"
|
|
25
|
+
|
|
26
|
+
[project.scripts]
|
|
27
|
+
litresearch = "litresearch.cli:main"
|
|
28
|
+
|
|
29
|
+
[build-system]
|
|
30
|
+
requires = ["hatchling"]
|
|
31
|
+
build-backend = "hatchling.build"
|
|
32
|
+
|
|
33
|
+
[dependency-groups]
|
|
34
|
+
dev = [
|
|
35
|
+
"nox>=2026.2.9",
|
|
36
|
+
"pyright>=1.1.408",
|
|
37
|
+
"pytest>=9.0.2",
|
|
38
|
+
"pytest-cov>=7.0.0",
|
|
39
|
+
"ruff>=0.15.5",
|
|
40
|
+
]
|
|
41
|
+
|
|
42
|
+
[tool.ruff]
|
|
43
|
+
line-length = 100
|
|
44
|
+
target-version = "py312"
|
|
45
|
+
|
|
46
|
+
[tool.ruff.lint]
|
|
47
|
+
select = ["E", "F", "I", "B"]
|
|
48
|
+
|
|
49
|
+
[tool.pyright]
|
|
50
|
+
include = ["src", "tests"]
|
|
51
|
+
pythonVersion = "3.12"
|
|
52
|
+
venvPath = "."
|
|
53
|
+
venv = ".venv"
|
|
54
|
+
typeCheckingMode = "standard"
|
|
55
|
+
|
|
56
|
+
[tool.pytest.ini_options]
|
|
57
|
+
addopts = "-q"
|
|
58
|
+
pythonpath = ["src"]
|
|
59
|
+
testpaths = ["tests"]
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"""CLI entrypoints for litresearch."""
|
|
2
|
+
|
|
3
|
+
import typer
|
|
4
|
+
from rich.console import Console
|
|
5
|
+
|
|
6
|
+
from litresearch import __version__
|
|
7
|
+
from litresearch.config import Settings
|
|
8
|
+
|
|
9
|
+
app = typer.Typer(help="Automated literature research workflow CLI.")
|
|
10
|
+
console = Console()
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@app.command()
|
|
14
|
+
def version() -> None:
|
|
15
|
+
"""Print the installed version."""
|
|
16
|
+
console.print(__version__)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@app.command()
|
|
20
|
+
def config() -> None:
|
|
21
|
+
"""Show basic configuration status without exposing secrets."""
|
|
22
|
+
settings = Settings()
|
|
23
|
+
console.print(f"default_model={settings.default_model}")
|
|
24
|
+
console.print(f"s2_api_key_configured={bool(settings.s2_api_key)}")
|
|
25
|
+
console.print(f"llm_api_key_configured={settings.has_llm_api_key}")
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def main() -> None:
|
|
29
|
+
"""Run the Typer application."""
|
|
30
|
+
app()
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"""Application settings for litresearch."""
|
|
2
|
+
|
|
3
|
+
from pydantic import computed_field
|
|
4
|
+
from pydantic_settings import BaseSettings, SettingsConfigDict
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class Settings(BaseSettings):
|
|
8
|
+
"""Environment-backed settings."""
|
|
9
|
+
|
|
10
|
+
model_config = SettingsConfigDict(env_file=".env", extra="ignore")
|
|
11
|
+
|
|
12
|
+
openai_api_key: str | None = None
|
|
13
|
+
anthropic_api_key: str | None = None
|
|
14
|
+
openrouter_api_key: str | None = None
|
|
15
|
+
s2_api_key: str | None = None
|
|
16
|
+
default_model: str = "gpt-4o-mini"
|
|
17
|
+
|
|
18
|
+
@computed_field
|
|
19
|
+
@property
|
|
20
|
+
def has_llm_api_key(self) -> bool:
|
|
21
|
+
"""Return whether any supported LLM provider key is configured."""
|
|
22
|
+
return any(
|
|
23
|
+
[
|
|
24
|
+
self.openai_api_key,
|
|
25
|
+
self.anthropic_api_key,
|
|
26
|
+
self.openrouter_api_key,
|
|
27
|
+
]
|
|
28
|
+
)
|
|
File without changes
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import subprocess
|
|
3
|
+
import sys
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def test_module_entrypoint_shows_help() -> None:
|
|
8
|
+
env = os.environ.copy()
|
|
9
|
+
src_path = str(Path(__file__).resolve().parents[2] / "src")
|
|
10
|
+
env["PYTHONPATH"] = src_path if not env.get("PYTHONPATH") else f"{src_path}:{env['PYTHONPATH']}"
|
|
11
|
+
|
|
12
|
+
result = subprocess.run(
|
|
13
|
+
[sys.executable, "-m", "litresearch", "--help"],
|
|
14
|
+
capture_output=True,
|
|
15
|
+
text=True,
|
|
16
|
+
check=False,
|
|
17
|
+
env=env,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
assert result.returncode == 0
|
|
21
|
+
assert "Automated literature research workflow CLI" in result.stdout
|
|
File without changes
|