Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/).

## [Unreleased]

### Changed
- First-use processing now runs in a detached subprocess so Marker/Torch startup cannot block the MCP request loop; status responses include an ETA and retry interval.
- Hybrid collection searches remain bounded: grep covers the collection, while semantic fanout above 20 papers returns an explicit partial status instead of risking a client timeout.
- Scoped semantic searches reuse one embedding model across paper databases.

### Fixed
- Direct PDF lookup and status checks no longer import Torch just to derive an output path.
- Direct PDF lookup never scans sibling directories, and explicitly requested library scans skip unreadable children.

## [0.5.2] - 2026-08-28

### Fixed
Expand Down
15 changes: 10 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -134,11 +134,16 @@ many MCP clients. The first call returns promptly:
}
```

Processing continues after that response. Poll `get_paper_info` using `paper_dir`; when
it reports `status: "ready"`, retry the original search. Already-processed grep searches
do not initialize the semantic model. RAG and hybrid searches initialize it when needed.
Search no longer performs an unconditional remote-library sync, which previously allowed
a local query to block for up to five minutes.
Processing runs in a detached process and continues after that response without blocking
status calls. Poll `get_paper_info` using `paper_dir`; when it reports `status: "ready"`,
retry the original search. Already-processed grep searches do not initialize the semantic
model. RAG and hybrid searches initialize one shared model per call when needed.

To keep collection-wide calls below normal MCP deadlines, semantic search accepts at most
20 paper directories per call. A broader `hybrid` call returns bounded grep results with
`status: "partial"` and an explicit `semantic_search.status: "skipped_source_limit"`;
select up to 20 papers for semantic retrieval. Search no longer performs an unconditional
remote-library sync, which previously allowed a local query to block for up to five minutes.

### `get_paper_info`

Expand Down
26 changes: 26 additions & 0 deletions paper_intelligence/processing_worker.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
"""Detached worker for first-use PDF conversion and indexing."""

import argparse
from pathlib import Path


def main() -> None:
parser = argparse.ArgumentParser()
parser.add_argument("--source", required=True)
parser.add_argument("--paper-dir", required=True)
parser.add_argument("--use-llm", action="store_true")
args = parser.parse_args()

# Import only in the child. Importing the processing stack in the MCP server can
# make the request loop unresponsive while Marker, Torch, and embedding models load.
from .tools.search import _run_processing_job

_run_processing_job(
Path(args.source),
Path(args.paper_dir),
use_llm=args.use_llm,
)


if __name__ == "__main__":
main()
6 changes: 5 additions & 1 deletion paper_intelligence/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@
"Pass PDF paths directly to search. First use queues 1-3 minute background "
"processing and returns status='processing'; call get_paper_info on the returned "
"paper_dir until status='ready', then retry search. Searches inspect only the "
"sources explicitly supplied."
"sources explicitly supplied. Semantic search is limited to 20 papers per call; "
"broader hybrid searches return collection-wide grep results and a partial status."
),
)

Expand Down Expand Up @@ -49,6 +50,9 @@ def search(
include_context: Include surrounding lines in results
use_llm: Use LLM for better PDF conversion (slower)

Semantic retrieval is limited to 20 ready papers per call. Broader hybrid
searches return bounded grep results plus an explicit partial status.

Returns:
Search results with content, location, and relevance scores
"""
Expand Down
10 changes: 8 additions & 2 deletions paper_intelligence/tools/convert.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,14 @@
from pathlib import Path
from typing import Optional

# Set device preference for Apple Silicon MPS
if not os.environ.get("TORCH_DEVICE"):

def _set_device_preference() -> None:
"""Choose an accelerator only inside the detached conversion worker."""
if os.environ.get("TORCH_DEVICE"):
return
try:
import torch

if torch.backends.mps.is_available():
os.environ["TORCH_DEVICE"] = "mps"
elif torch.cuda.is_available():
Expand Down Expand Up @@ -87,6 +91,8 @@ def convert_pdf(
- message: Status message
- images_dir: Path to extracted images (if any)
"""
_set_device_preference()

from marker.converters.pdf import PdfConverter
from marker.models import create_model_dict
from marker.output import text_from_rendered
Expand Down
Loading
Loading