pdf-reader-0jsfjf 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.
@@ -0,0 +1,202 @@
1
+ # Byte-compiled / optimized / DLL files
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+
6
+ # indexes
7
+ indexes/
8
+
9
+ # C extensions
10
+ *.so
11
+
12
+ # Distribution / packaging
13
+ .Python
14
+ build/
15
+ develop-eggs/
16
+ dist/
17
+ downloads/
18
+ eggs/
19
+ .eggs/
20
+ lib/
21
+ lib64/
22
+ parts/
23
+ sdist/
24
+ var/
25
+ wheels/
26
+ share/python-wheels/
27
+ *.egg-info/
28
+ .installed.cfg
29
+ *.egg
30
+ MANIFEST
31
+
32
+ # PyInstaller
33
+ # Usually these files are written by a python script from a template
34
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
35
+ *.manifest
36
+ *.spec
37
+
38
+ # Installer logs
39
+ pip-log.txt
40
+ pip-delete-this-directory.txt
41
+
42
+ # Unit test / coverage reports
43
+ htmlcov/
44
+ .tox/
45
+ .nox/
46
+ .coverage
47
+ .coverage.*
48
+ .cache
49
+ nosetests.xml
50
+ coverage.xml
51
+ *.cover
52
+ *.py,cover
53
+ .hypothesis/
54
+ .pytest_cache/
55
+ cover/
56
+
57
+ # Translations
58
+ *.mo
59
+ *.pot
60
+
61
+ # Django stuff:
62
+ *.log
63
+ local_settings.py
64
+ db.sqlite3
65
+ db.sqlite3-journal
66
+
67
+ # Flask stuff:
68
+ instance/
69
+ .webassets-cache
70
+
71
+ # Scrapy stuff:
72
+ .scrapy
73
+
74
+ # Sphinx documentation
75
+ docs/_build/
76
+
77
+ # PyBuilder
78
+ .pybuilder/
79
+ target/
80
+
81
+ # Jupyter Notebook
82
+ .ipynb_checkpoints
83
+
84
+ # IPython
85
+ profile_default/
86
+ ipython_config.py
87
+
88
+ # pyenv
89
+ # For a library or package, you might want to ignore these files since the code is
90
+ # intended to run in multiple environments; otherwise, check them in:
91
+ # .python-version
92
+
93
+ # pipenv
94
+ # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
95
+ # However, in case of collaboration, if having platform-specific dependencies or dependencies
96
+ # having no cross-platform support, pipenv may install dependencies that don't work, or not
97
+ # install all needed dependencies.
98
+ #Pipfile.lock
99
+
100
+ # UV
101
+ # Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
102
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
103
+ # commonly ignored for libraries.
104
+ #uv.lock
105
+
106
+ # poetry
107
+ # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
108
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
109
+ # commonly ignored for libraries.
110
+ # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
111
+ #poetry.lock
112
+
113
+ # pdm
114
+ # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
115
+ #pdm.lock
116
+ # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
117
+ # in version control.
118
+ # https://pdm.fming.dev/latest/usage/project/#working-with-version-control
119
+ .pdm.toml
120
+ .pdm-python
121
+ .pdm-build/
122
+
123
+ # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
124
+ __pypackages__/
125
+
126
+ # Celery stuff
127
+ celerybeat-schedule
128
+ celerybeat.pid
129
+
130
+ # SageMath parsed files
131
+ *.sage.py
132
+
133
+ # Environments
134
+ .env
135
+ .venv
136
+ env/
137
+ venv/
138
+ ENV/
139
+ env.bak/
140
+ venv.bak/
141
+
142
+ # Spyder project settings
143
+ .spyderproject
144
+ .spyproject
145
+
146
+ # Rope project settings
147
+ .ropeproject
148
+
149
+ # mkdocs documentation
150
+ /site
151
+
152
+ # mypy
153
+ .mypy_cache/
154
+ .dmypy.json
155
+ dmypy.json
156
+
157
+ # Pyre type checker
158
+ .pyre/
159
+
160
+ # pytype static type analyzer
161
+ .pytype/
162
+
163
+ # Cython debug symbols
164
+ cython_debug/
165
+
166
+ # PyCharm
167
+ # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
168
+ # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
169
+ # and can be added to the global gitignore or merged into this file. For a more nuclear
170
+ # option (not recommended) you can uncomment the following to ignore the entire idea folder.
171
+ #.idea/
172
+
173
+ # Abstra
174
+ # Abstra is an AI-powered process automation framework.
175
+ # Ignore directories containing user credentials, local state, and settings.
176
+ # Learn more at https://abstra.io/docs
177
+ .abstra/
178
+
179
+ # Visual Studio Code
180
+ # Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore
181
+ # that can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore
182
+ # and can be added to the global gitignore or merged into this file. However, if you prefer,
183
+ # you could uncomment the following to ignore the enitre vscode folder
184
+ # .vscode/
185
+
186
+ # Ruff stuff:
187
+ .ruff_cache/
188
+
189
+ # PyPI configuration file
190
+ .pypirc
191
+
192
+ # Cursor
193
+ # Cursor is an AI-powered code editor. `.cursorignore` specifies files/directories to
194
+ # exclude from AI features like autocomplete and code analysis. Recommended for sensitive data
195
+ # refer to https://docs.cursor.com/context/ignore-files
196
+ .cursorignore
197
+ .cursorindexingignore
198
+
199
+ # Local-only data: real plan OCR output and logs (never commit)
200
+ .DS_Store
201
+ samples/
202
+ log_sample.json
@@ -0,0 +1,67 @@
1
+ Metadata-Version: 2.4
2
+ Name: pdf-reader-0jsfjf
3
+ Version: 0.1.0
4
+ Summary: PDF OCR, indexing and search tools for AI assistants
5
+ License: Proprietary
6
+ Classifier: License :: Other/Proprietary License
7
+ Classifier: Programming Language :: Python :: 3
8
+ Requires-Python: >=3.12
9
+ Requires-Dist: aiohttp>=3.14.5
10
+ Requires-Dist: fastapi
11
+ Requires-Dist: fastmcp<4,>=3.4.11
12
+ Requires-Dist: lm-deluge
13
+ Requires-Dist: pdf2image
14
+ Requires-Dist: pillow>=13.0.0
15
+ Requires-Dist: pydantic
16
+ Requires-Dist: pypdf>=6.21.2
17
+ Requires-Dist: python-dotenv
18
+ Requires-Dist: requests
19
+ Requires-Dist: tantivy
20
+ Requires-Dist: tesserocr
21
+ Description-Content-Type: text/markdown
22
+
23
+ # PDF Reader
24
+
25
+ A hybrid PDF indexing and search system designed for AI assistant integration via MCP (Model Context Protocol). Rather than dumping entire PDF content into context windows, this tool provides intelligent document processing, full-text search, and optimized image handling.
26
+
27
+ ## Features
28
+
29
+ - **Hybrid Processing**: Local (tesserocr) and remote (Modal cloud) OCR processing
30
+ - **Smart Search**: Tantivy-based full-text search with BM25 ranking and query escaping
31
+ - **AI Enhancement**: LLM-generated document titles and page summaries
32
+ - **Provider Optimization**: Automatic image resizing for OpenAI/Anthropic API limits
33
+ - **MCP Integration**: RESTful API for seamless AI assistant integration
34
+
35
+ ## Quick Start
36
+
37
+ ```bash
38
+ # Activate virtual environment
39
+ source .venv/bin/activate
40
+
41
+ # Example usage (if you have an existing index)
42
+ python -c "
43
+ from pdf_reader.index import PDFIndex
44
+ index = PDFIndex.load('indexes/index2')
45
+ results = index.search_pages(['your search term'])
46
+ print(f'Found {len(results)} results')
47
+ "
48
+ ```
49
+
50
+ ## Architecture
51
+
52
+ The system uses a hybrid approach:
53
+ - **Tantivy**: Fast full-text search across OCR text and AI summaries
54
+ - **SQLite**: Blob storage for complete page data (text + images)
55
+ - **AI Enhancement**: Document titles and page summaries via LLM
56
+ - **Provider-Aware Images**: Optimized for different LLM API requirements
57
+
58
+ ## MCP Tools
59
+
60
+ - `list_pdfs`: List available PDF documents
61
+ - `search_pdfs`: Keyword search across all documents
62
+ - `read_pdf`: Read specific pages from a document
63
+ - `read_pdfs`: Bulk read across multiple documents
64
+
65
+ ## Development
66
+
67
+ See `CLAUDE.md` for detailed architecture documentation, development guidelines, and troubleshooting information.
@@ -0,0 +1,45 @@
1
+ # PDF Reader
2
+
3
+ A hybrid PDF indexing and search system designed for AI assistant integration via MCP (Model Context Protocol). Rather than dumping entire PDF content into context windows, this tool provides intelligent document processing, full-text search, and optimized image handling.
4
+
5
+ ## Features
6
+
7
+ - **Hybrid Processing**: Local (tesserocr) and remote (Modal cloud) OCR processing
8
+ - **Smart Search**: Tantivy-based full-text search with BM25 ranking and query escaping
9
+ - **AI Enhancement**: LLM-generated document titles and page summaries
10
+ - **Provider Optimization**: Automatic image resizing for OpenAI/Anthropic API limits
11
+ - **MCP Integration**: RESTful API for seamless AI assistant integration
12
+
13
+ ## Quick Start
14
+
15
+ ```bash
16
+ # Activate virtual environment
17
+ source .venv/bin/activate
18
+
19
+ # Example usage (if you have an existing index)
20
+ python -c "
21
+ from pdf_reader.index import PDFIndex
22
+ index = PDFIndex.load('indexes/index2')
23
+ results = index.search_pages(['your search term'])
24
+ print(f'Found {len(results)} results')
25
+ "
26
+ ```
27
+
28
+ ## Architecture
29
+
30
+ The system uses a hybrid approach:
31
+ - **Tantivy**: Fast full-text search across OCR text and AI summaries
32
+ - **SQLite**: Blob storage for complete page data (text + images)
33
+ - **AI Enhancement**: Document titles and page summaries via LLM
34
+ - **Provider-Aware Images**: Optimized for different LLM API requirements
35
+
36
+ ## MCP Tools
37
+
38
+ - `list_pdfs`: List available PDF documents
39
+ - `search_pdfs`: Keyword search across all documents
40
+ - `read_pdf`: Read specific pages from a document
41
+ - `read_pdfs`: Bulk read across multiple documents
42
+
43
+ ## Development
44
+
45
+ See `CLAUDE.md` for detailed architecture documentation, development guidelines, and troubleshooting information.
@@ -0,0 +1,77 @@
1
+ [build-system]
2
+ requires = ["hatchling==1.27.0"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "pdf-reader-0jsfjf"
7
+ version = "0.1.0"
8
+ description = "PDF OCR, indexing and search tools for AI assistants"
9
+ license = { text = "Proprietary" }
10
+ classifiers = [
11
+ "License :: Other/Proprietary License",
12
+ "Programming Language :: Python :: 3",
13
+ ]
14
+ readme = "README.md"
15
+ requires-python = ">=3.12"
16
+ dependencies = [
17
+ "tesserocr",
18
+ "fastmcp>=3.4.11,<4",
19
+ "tantivy",
20
+ "pdf2image",
21
+ "pillow>=13.0.0",
22
+ "pydantic",
23
+ "aiohttp>=3.14.5",
24
+ "lm-deluge",
25
+ "pypdf>=6.21.2",
26
+ "requests",
27
+ "python-dotenv",
28
+ "fastapi"
29
+ ]
30
+
31
+ [dependency-groups]
32
+ dev = [
33
+ "prek==0.4.14",
34
+ "ruff==0.17.2",
35
+ "ty==0.0.91",
36
+ "pytest==9.2.0",
37
+ "pip-audit==2.10.1",
38
+ "build==1.3.0",
39
+ "twine==6.2.0",
40
+ ]
41
+
42
+ [tool.ruff]
43
+ exclude = ["unused", "modal_endpoints", "samples", ".claude"]
44
+
45
+ [tool.ruff.lint]
46
+ select = ["E4", "E7", "E9", "F"]
47
+
48
+ [tool.ty.environment]
49
+ # Always check against this repo's venv, not whatever VIRTUAL_ENV is active.
50
+ python = ".venv"
51
+
52
+ [tool.ty.src]
53
+ exclude = ["unused", "modal_endpoints", "samples", ".claude"]
54
+
55
+ [tool.ty.analysis]
56
+ # Modal is optional and only imported by remote OCR backends.
57
+ allowed-unresolved-imports = ["modal"]
58
+
59
+ [tool.ty.rules]
60
+ unused-type-ignore-comment = "ignore"
61
+
62
+ [[tool.ty.overrides]]
63
+ include = ["tests/**"]
64
+
65
+ [tool.ty.overrides.rules]
66
+ # Validation tests intentionally omit required fields; mock tools and legacy
67
+ # integration tests use values broader than their static annotations.
68
+ missing-argument = "ignore"
69
+ invalid-argument-type = "ignore"
70
+
71
+ # Published to PyPI under an obscured name; the import name stays `pdf_reader`.
72
+ [tool.hatch.build.targets.wheel]
73
+ packages = ["src/pdf_reader"]
74
+
75
+ # Keep the sdist to the package itself (no tests, tooling or local data).
76
+ [tool.hatch.build.targets.sdist]
77
+ only-include = ["src/pdf_reader", "README.md", "pyproject.toml"]
@@ -0,0 +1,45 @@
1
+ """
2
+ PDF Reader - PDF indexing, search, and retrieval for AI applications.
3
+
4
+ This package provides:
5
+ - PDF/PDFPage: Data models for PDF documents and pages
6
+ - PDFIndex: Hybrid index combining Tantivy search with SQLite storage
7
+ - PDFService: Multi-index service layer for business logic
8
+ - Server implementations: REST API, MCP, and lm_deluge tools
9
+ - Formatting helpers: format_toc, format_ocr for LLM consumption
10
+ """
11
+
12
+ from .index import PDFIndex
13
+ from .pdf import PDF, PDFPage, format_toc, format_ocr
14
+ from .service import PDFService, PDFServiceConfig
15
+ from .ingest import ingest_pages_llm, iter_ingest_pages_llm
16
+ from .core import (
17
+ PDFIndexConfig,
18
+ SearchRequest,
19
+ SearchResponse,
20
+ ReadRequest,
21
+ ReadResponse,
22
+ )
23
+
24
+ __all__ = [
25
+ # Data models
26
+ "PDF",
27
+ "PDFPage",
28
+ # Index
29
+ "PDFIndex",
30
+ # Service
31
+ "PDFService",
32
+ "PDFServiceConfig",
33
+ # Config and API types
34
+ "PDFIndexConfig",
35
+ "SearchRequest",
36
+ "SearchResponse",
37
+ "ReadRequest",
38
+ "ReadResponse",
39
+ # Formatting helpers
40
+ "format_toc",
41
+ "format_ocr",
42
+ # Streaming OCR
43
+ "ingest_pages_llm",
44
+ "iter_ingest_pages_llm",
45
+ ]
@@ -0,0 +1,150 @@
1
+ """
2
+ Core types, models, and utilities shared across all PDF service implementations.
3
+
4
+ This module provides:
5
+ - PDFIndexConfig dataclass for index configuration
6
+ - Request/Response Pydantic models for the unified API
7
+ - Utility functions for data processing
8
+ """
9
+
10
+ from dataclasses import dataclass
11
+
12
+ from pydantic import BaseModel, Field
13
+
14
+
15
+ # =============================================================================
16
+ # Configuration
17
+ # =============================================================================
18
+
19
+
20
+ @dataclass
21
+ class PDFIndexConfig:
22
+ """Configuration for a single PDF index."""
23
+
24
+ name: str
25
+ description: str = ""
26
+ root_path: str | None = None # Directory with search/, pages.db, manifest.json
27
+
28
+
29
+ # =============================================================================
30
+ # Request/Response Models (Unified API)
31
+ # =============================================================================
32
+
33
+
34
+ class SearchRequest(BaseModel):
35
+ """Request model for search endpoint."""
36
+
37
+ index: str = Field(..., description="Name of the index to search")
38
+ queries: list[str] = Field(..., description="List of search query strings")
39
+ limit: int = Field(10, description="Maximum number of results to return")
40
+ pdf_filter: str | None = Field(
41
+ None, description="Optional PDF ID to filter results to a single document"
42
+ )
43
+ include_images: bool = Field(
44
+ False, description="If true, include page images in results"
45
+ )
46
+
47
+
48
+ class PageResult(BaseModel):
49
+ """A single page result from search."""
50
+
51
+ id: str = Field(..., description="Page ID in format 'pdf_id:page_index'")
52
+ pdf_id: str = Field(..., description="ID of the parent PDF")
53
+ page_index: int = Field(..., description="Zero-based page number")
54
+ text: str = Field("", description="OCR-extracted text")
55
+ summary: str | None = Field(None, description="AI-generated page summary")
56
+ caption: str | None = Field(
57
+ None, description="AI-generated caption for visual pages"
58
+ )
59
+ image: str | None = Field(None, description="Base64-encoded JPEG image")
60
+ preview: str | None = Field(None, description="Text preview for display")
61
+
62
+
63
+ class SearchResponse(BaseModel):
64
+ """Response model for search endpoint."""
65
+
66
+ index: str
67
+ queries: list[str]
68
+ results: list[PageResult]
69
+ total: int
70
+
71
+
72
+ class ReadRequest(BaseModel):
73
+ """Request model for read endpoint."""
74
+
75
+ index: str = Field(..., description="Name of the index to read from")
76
+ pdf_id: str = Field(..., description="ID of the PDF to read")
77
+ pages: list[int] | str = Field(
78
+ ...,
79
+ description="List of page indices to read, or a range string like '1-5' or '-1' for all",
80
+ )
81
+ include_images: bool = Field(True, description="If true, include page images")
82
+
83
+
84
+ class ReadResponse(BaseModel):
85
+ """Response model for read endpoint."""
86
+
87
+ index: str
88
+ pdf_id: str
89
+ pages: list[PageResult]
90
+
91
+
92
+ class PDFInfo(BaseModel):
93
+ """Information about a single PDF in an index."""
94
+
95
+ id: str
96
+ filename: str
97
+ title: str | None = None
98
+ summary: str | None = None
99
+ num_pages: int
100
+
101
+
102
+ class ListPDFsResponse(BaseModel):
103
+ """Response model for listing PDFs in an index."""
104
+
105
+ index: str
106
+ pdfs: list[PDFInfo]
107
+
108
+
109
+ class IndexInfo(BaseModel):
110
+ """Information about a single index."""
111
+
112
+ name: str
113
+ description: str = ""
114
+ pdf_count: int = 0
115
+
116
+
117
+ class IndicesResponse(BaseModel):
118
+ """Response model for listing indices."""
119
+
120
+ indices: list[IndexInfo]
121
+
122
+
123
+ class HealthResponse(BaseModel):
124
+ """Response model for health check."""
125
+
126
+ status: str
127
+ indices: list[str]
128
+ server: str | None = None
129
+
130
+
131
+ # =============================================================================
132
+ # Exports
133
+ # =============================================================================
134
+
135
+ __all__ = [
136
+ # Configuration
137
+ "PDFIndexConfig",
138
+ # Request models
139
+ "SearchRequest",
140
+ "ReadRequest",
141
+ # Response models
142
+ "PageResult",
143
+ "SearchResponse",
144
+ "ReadResponse",
145
+ "PDFInfo",
146
+ "ListPDFsResponse",
147
+ "IndexInfo",
148
+ "IndicesResponse",
149
+ "HealthResponse",
150
+ ]