sciwrite-lint 0.4.0__tar.gz → 0.5.1__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.
- {sciwrite_lint-0.4.0/sciwrite_lint.egg-info → sciwrite_lint-0.5.1}/PKG-INFO +34 -7
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/README.md +33 -6
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/pyproject.toml +3 -1
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint/__init__.py +1 -1
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint/__main__.py +52 -56
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint/cli/misc/parse.py +1 -1
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint/cli/verify.py +12 -5
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint/eval_claims.py +107 -89
- sciwrite_lint-0.5.1/sciwrite_lint/llm/manuscript_cache.py +173 -0
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint/manuscript_store.py +6 -1
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint/pdf/grobid.py +24 -8
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint/pipeline/checks.py +85 -5
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint/pipeline/claims.py +0 -1
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint/references/workspace_db/__init__.py +13 -0
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint/references/workspace_db/_core.py +43 -5
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint/references/workspace_db/claim_results.py +8 -3
- sciwrite_lint-0.5.1/sciwrite_lint/references/workspace_db/manuscript_check_cache.py +103 -0
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint/tex_parser.py +74 -0
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1/sciwrite_lint.egg-info}/PKG-INFO +34 -7
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint.egg-info/SOURCES.txt +2 -1
- sciwrite_lint-0.4.0/sciwrite_lint/claude_backend.py +0 -94
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/LICENSE +0 -0
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint/_network.py +0 -0
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint/api.py +0 -0
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint/api_keys.py +0 -0
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint/checks/__init__.py +0 -0
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint/checks/_diagnostics.py +0 -0
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint/checks/_section_utils.py +0 -0
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint/checks/cite_purpose.py +0 -0
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint/checks/claim_support.py +0 -0
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint/checks/cross_section_consistency.py +0 -0
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint/checks/dangling_cite.py +0 -0
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint/checks/dangling_ref.py +0 -0
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint/checks/full_paper_consistency.py +0 -0
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint/checks/internal_consistency_pairs.py +0 -0
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint/checks/prose_quality.py +0 -0
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint/checks/ref_internal_checks.py +0 -0
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint/checks/reference_accuracy.py +0 -0
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint/checks/reference_exists.py +0 -0
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint/checks/reference_unreliable.py +0 -0
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint/checks/registry.py +0 -0
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint/checks/retracted_cite.py +0 -0
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint/checks/structure_promises.py +0 -0
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint/checks/unreferenced_figure.py +0 -0
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint/claims.py +0 -0
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint/claude_cli.py +0 -0
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint/cli/__init__.py +0 -0
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint/cli/_common.py +0 -0
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint/cli/check.py +0 -0
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint/cli/config.py +0 -0
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint/cli/fetch.py +0 -0
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint/cli/misc/__init__.py +0 -0
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint/cli/misc/_monitor.py +0 -0
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint/cli/misc/containers.py +0 -0
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint/cli/misc/dismiss_claim.py +0 -0
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint/cli/misc/grobid.py +0 -0
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint/cli/misc/init.py +0 -0
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint/cli/misc/override.py +0 -0
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint/cli/misc/vision.py +0 -0
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint/cli/misc/vllm.py +0 -0
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint/cli/rank.py +0 -0
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint/config.py +0 -0
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint/cross_paper.py +0 -0
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint/exceptions.py +0 -0
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint/footnote_urls.py +0 -0
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint/fulltext/__init__.py +0 -0
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint/fulltext/_common.py +0 -0
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint/fulltext/_download.py +0 -0
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint/fulltext/_orchestrator.py +0 -0
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint/fulltext/_search.py +0 -0
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint/fulltext/_validation.py +0 -0
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint/fulltext/arxiv.py +0 -0
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint/fulltext/biorxiv.py +0 -0
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint/fulltext/core.py +0 -0
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint/fulltext/eric.py +0 -0
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint/fulltext/europepmc.py +0 -0
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint/fulltext/hal.py +0 -0
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint/fulltext/ideas.py +0 -0
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint/fulltext/nasa_ads.py +0 -0
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint/fulltext/nber.py +0 -0
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint/fulltext/osf.py +0 -0
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint/fulltext/pmc.py +0 -0
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint/fulltext/s2.py +0 -0
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint/fulltext/unpaywall.py +0 -0
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint/latex_to_markdown.py +0 -0
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint/llm/__init__.py +0 -0
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint/llm/concurrency_optimizer/__init__.py +0 -0
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint/llm/concurrency_optimizer/compute_cap.py +0 -0
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint/llm/concurrency_optimizer/controller.py +0 -0
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint/llm/concurrency_optimizer/decide.py +0 -0
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint/llm/concurrency_optimizer/host_metrics.py +0 -0
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint/llm/concurrency_optimizer/metrics_probe.py +0 -0
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint/llm/concurrency_optimizer/registry.py +0 -0
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint/llm/concurrency_optimizer/telemetry.py +0 -0
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint/llm/concurrency_optimizer/vllm_metrics.py +0 -0
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint/llm/concurrency_optimizer/wiring.py +0 -0
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint/llm_utils.py +0 -0
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint/local_sources.py +0 -0
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint/mhtml.py +0 -0
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint/models.py +0 -0
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint/oa/__init__.py +0 -0
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint/oa/_models.py +0 -0
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint/oa/_pdf.py +0 -0
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint/oa/_search.py +0 -0
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint/oa/_web.py +0 -0
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint/pdf/__init__.py +0 -0
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint/pdf/pdf_download.py +0 -0
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint/pipeline/__init__.py +0 -0
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint/pipeline/embeddings.py +0 -0
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint/pipeline/fetch.py +0 -0
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint/pipeline/orchestration.py +0 -0
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint/pipeline/parse.py +0 -0
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint/pipeline/pdf_context.py +0 -0
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint/pipeline/ref_internal.py +0 -0
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint/pipeline/runners.py +0 -0
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint/pipeline/staged.py +0 -0
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint/pipeline/swap.py +0 -0
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint/pipeline/tracking.py +0 -0
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint/pipeline/unreliable.py +0 -0
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint/pipeline/verify.py +0 -0
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint/pipeline/vision.py +0 -0
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint/prompt_safety.py +0 -0
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint/py.typed +0 -0
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint/rate_limiter.py +0 -0
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint/references/__init__.py +0 -0
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint/references/_embed_timing.py +0 -0
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint/references/chain.py +0 -0
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint/references/citations.py +0 -0
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint/references/crossref.py +0 -0
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint/references/embedding_store.py +0 -0
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint/references/matching.py +0 -0
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint/references/metadata.py +0 -0
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint/references/reference_store.py +0 -0
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint/references/retraction_watch.py +0 -0
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint/references/workspace_db/bib_checks.py +0 -0
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint/references/workspace_db/citation_metadata.py +0 -0
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint/references/workspace_db/manuscript_citations.py +0 -0
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint/references/workspace_db/parse_cache.py +0 -0
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint/references/workspace_db/pipeline_stage.py +0 -0
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint/references/workspace_db/query_vectors.py +0 -0
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint/references/workspace_db/ref_internal.py +0 -0
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint/references/workspace_db/registry.py +0 -0
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint/references/workspace_db/vision_cache.py +0 -0
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint/report.py +0 -0
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint/schemas.py +0 -0
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint/scoring/__init__.py +0 -0
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint/scoring/contribution.py +0 -0
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint/scoring/scilint_score.py +0 -0
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint/usage.py +0 -0
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint/vision/__init__.py +0 -0
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint/vision/cache.py +0 -0
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint/vision/describe.py +0 -0
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint/vision/image_extraction.py +0 -0
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint/vision/pipeline.py +0 -0
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint/vllm/__init__.py +0 -0
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint/vllm/metrics.py +0 -0
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint/vllm/vllm_server.py +0 -0
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint/web.py +0 -0
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint.egg-info/dependency_links.txt +0 -0
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint.egg-info/entry_points.txt +0 -0
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint.egg-info/requires.txt +0 -0
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/sciwrite_lint.egg-info/top_level.txt +0 -0
- {sciwrite_lint-0.4.0 → sciwrite_lint-0.5.1}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sciwrite-lint
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.5.1
|
|
4
4
|
Summary: A linter for scientific manuscripts — reference verification, consistency checking, and structural validation.
|
|
5
5
|
Author: Sergey Samsonau
|
|
6
6
|
Maintainer-email: Authentic Research Partners LLC <info@arpconnect.com>
|
|
@@ -147,7 +147,7 @@ sciwrite-lint contributions paper.pdf --format json
|
|
|
147
147
|
|
|
148
148
|
### Optimizations
|
|
149
149
|
|
|
150
|
-
Three models — a vision model (Qwen3-VL-2B default, or 8B FP8 via `--vision-backend vllm` for +15% accuracy), an embedding model (Arctic Embed), and an 8B reasoning LLM (Qwen3 via vLLM) — share a single consumer GPU. The pipeline runs each in its own stage and explicitly stops one before starting the next, so only one ever owns GPU memory at a time (same code path on WSL2 and native Linux). FP8 weights and KV cache (Ada Lovelace+) and per-paper SQLite caching
|
|
150
|
+
Three models — a vision model (Qwen3-VL-2B default, or 8B FP8 via `--vision-backend vllm` for +15% accuracy), an embedding model (Arctic Embed), and an 8B reasoning LLM (Qwen3 via vLLM) — share a single consumer GPU. The pipeline runs each in its own stage and explicitly stops one before starting the next, so only one ever owns GPU memory at a time (same code path on WSL2 and native Linux). FP8 weights and KV cache (Ada Lovelace+) and per-paper SQLite caching are baseline; every cache layer pins its inputs by hash so editing the paper, replacing a figure, or refreshing a reference recomputes only what's affected (see the *Iterative editing* subsection under Usage — `--fresh` is not part of the normal loop). On top of that:
|
|
151
151
|
|
|
152
152
|
- **Cost-aware verify-claim ladder** — sentence chunk → paragraph chunk → whole section. Each level fans out top-N candidates in parallel; stops on a conclusive verdict. Most claims resolve at the cheap sentence level (~200-token prompts) instead of paying for whole sections (~5K tokens), with full-section escalation reserved for hard cases
|
|
153
153
|
- **Prefix-first prompt structure** — shared context placed before variable content in all prompts, maximizing vLLM's automatic prefix caching
|
|
@@ -158,11 +158,11 @@ Three models — a vision model (Qwen3-VL-2B default, or 8B FP8 via `--vision-ba
|
|
|
158
158
|
- **14-source full-text cascade** — early exit on first successful download across arXiv, Semantic Scholar, OpenAlex, PubMed Central, Europe PMC, Unpaywall, bioRxiv, NBER, RePEc/IDEAS, HAL, ERIC, NASA ADS, OSF Preprints, CORE
|
|
159
159
|
- **Live monitoring** *(advanced)* — `sciwrite-lint containers monitor` shows service health, VRAM usage, and KV cache utilization in a terminal dashboard
|
|
160
160
|
|
|
161
|
-
|
|
161
|
+
The pipeline automatically swaps vLLM containers to free GPU for embedding and vision stages (~50× faster than CPU).
|
|
162
162
|
|
|
163
163
|
## Install
|
|
164
164
|
|
|
165
|
-
**Assumed setup:** A workstation with an NVIDIA GPU (16+ GB VRAM). Tested on WSL2 with NVIDIA driver 546.01+. Native Linux
|
|
165
|
+
**Assumed setup:** A workstation with an NVIDIA GPU (16+ GB VRAM). Tested on WSL2 with NVIDIA driver 546.01+. Native Linux uses the same GPU-sequencing code path with no extra configuration (see [docs/services.md](https://github.com/authentic-research-partners/sciwrite-lint/blob/main/docs/services.md#embedding-device)), though it isn't actively tested. Not tested on macOS.
|
|
166
166
|
|
|
167
167
|
Requires [uv](https://docs.astral.sh/uv/getting-started/installation/), a container runtime (podman or docker), CUDA drivers, and [NVIDIA Container Toolkit](https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/latest/install-guide.html).
|
|
168
168
|
|
|
@@ -207,6 +207,8 @@ sciwrite-lint containers start # start GROBID + vLLM (needs GPU for vLLM
|
|
|
207
207
|
sciwrite-lint containers monitor # live dashboard: service health, VRAM, KV cache
|
|
208
208
|
```
|
|
209
209
|
|
|
210
|
+
`init` detects `.tex` files and their `.bib` references and generates a `.sciwrite-lint.toml` config. Review to confirm the right files were detected.
|
|
211
|
+
|
|
210
212
|
### WSL2: disable CUDA Sysmem Fallback (recommended)
|
|
211
213
|
|
|
212
214
|
By default Windows silently spills GPU overflow into system RAM via PCIe (30–100× slower than VRAM); you'll see this as a sudden throughput collapse during the heaviest pipeline stages with no error in the logs. Tell the NVIDIA driver to fail loudly instead:
|
|
@@ -217,8 +219,6 @@ Driver 546.01+. After this, WSL2 behaves like native Linux on overflow (CUDA OOM
|
|
|
217
219
|
|
|
218
220
|

|
|
219
221
|
|
|
220
|
-
`init` detects `.tex` files and their `.bib` references and generates a `.sciwrite-lint.toml` config. Review to confirm the right files were detected.
|
|
221
|
-
|
|
222
222
|
**Providing references manually — two drop folders.** sciwrite-lint reads two local directories before going to the open-access waterfall, kept separate because the folder itself signals how much to trust the source:
|
|
223
223
|
|
|
224
224
|
- `local_pdfs/` — **academic sources**. Accepts `.pdf` (primary) and `.md` summaries you've written yourself. Use this for paywalled papers, for OA papers whose publisher requires a browser (Cloudflare/JS walls, Cell, Springer, some PMC pages), and for anything you'd cite as peer-reviewed evidence.
|
|
@@ -280,9 +280,36 @@ sciwrite-lint contributions --paper my_paper # add contribution axes to SciLi
|
|
|
280
280
|
sciwrite-lint contributions paper.pdf # standalone file scoring
|
|
281
281
|
```
|
|
282
282
|
|
|
283
|
+
**Supported inputs.**
|
|
284
|
+
|
|
285
|
+
*The manuscript you check:*
|
|
286
|
+
|
|
287
|
+
| Format | Notes |
|
|
288
|
+
|---|---|
|
|
289
|
+
| `.tex` | LaTeX — hand-written, or pandoc-generated (e.g. from Markdown); pandoc's heading anchors are unwrapped automatically. |
|
|
290
|
+
| `.pdf` | Parsed via GROBID (container required). |
|
|
291
|
+
|
|
292
|
+
*Cited sources you optionally supply (drop folders — see [Setup](#setup)):*
|
|
293
|
+
|
|
294
|
+
| Folder | Accepts | Trust level |
|
|
295
|
+
|---|---|---|
|
|
296
|
+
| `local_pdfs/` | `.pdf`, `.md` (your own summaries) | academic |
|
|
297
|
+
| `local_web/` | `.mhtml` / `.mht`, `.md` | web capture |
|
|
298
|
+
|
|
283
299
|
`check` runs the full pipeline in one command: text checks → figure analysis → LLM consistency → reference verification via APIs → download and parse cited papers → claim verification → consistency checks on cited papers → bibliography verification → aggregate reliability scores → SciLint Score. An initial run on a 50-reference paper takes up to 30 minutes (dominated by downloads and claim verification); subsequent cached runs complete in minutes.
|
|
284
300
|
|
|
285
|
-
|
|
301
|
+
### Iterative editing — no `--fresh` needed
|
|
302
|
+
|
|
303
|
+
`sciwrite-lint check` invalidates cached results whenever the inputs they were derived from change, so rerunning after an edit just works. Concretely:
|
|
304
|
+
|
|
305
|
+
- **Edit a sentence or paragraph** → the manuscript-LLM checks (prose-quality, cross-section consistency, etc.) re-run only on the touched sentences and their paragraph siblings; untouched prose stays cached
|
|
306
|
+
- **Edit a paragraph around a citation** → the claim verifier reruns on the edited prose
|
|
307
|
+
- **Replace a figure** (overwrite the PNG/PDF in your tree) → the vision cache re-describes it
|
|
308
|
+
- **Swap the local LLM model** (e.g. `qwen3` ↔ `gemma3` via `--model`) → manuscript-check rows and claim verdicts no longer count as cache hits
|
|
309
|
+
- **Overwrite a reference PDF** in your drop folder, or **re-fetch** an open-access PDF that has new bytes → the parser, embeddings, and claim verifier all rerun against the new content
|
|
310
|
+
- **Upgrade a summary `.md` to a real PDF** in your drop folder → claims are re-verified against the richer evidence
|
|
311
|
+
|
|
312
|
+
Use `--fresh` only when you want to wipe the workspace deliberately (it backs up the existing workspace before recreating it). It is not the answer to "I edited the paper and a finding looks stale" — if you ever see that, file an issue; a cache invariant is wrong and we'll fix it.
|
|
286
313
|
|
|
287
314
|
Use `--checks ID[,ID...]` to run only the listed checks (comma-separated), disabling the rest for this invocation. Useful for fast iteration during editing (`--checks prose-quality` runs just the prose review), for debugging a single check, or for scripting a two-pass workflow. Unknown check IDs fail loudly — run `sciwrite-lint checks` to list available IDs.
|
|
288
315
|
|
|
@@ -81,7 +81,7 @@ sciwrite-lint contributions paper.pdf --format json
|
|
|
81
81
|
|
|
82
82
|
### Optimizations
|
|
83
83
|
|
|
84
|
-
Three models — a vision model (Qwen3-VL-2B default, or 8B FP8 via `--vision-backend vllm` for +15% accuracy), an embedding model (Arctic Embed), and an 8B reasoning LLM (Qwen3 via vLLM) — share a single consumer GPU. The pipeline runs each in its own stage and explicitly stops one before starting the next, so only one ever owns GPU memory at a time (same code path on WSL2 and native Linux). FP8 weights and KV cache (Ada Lovelace+) and per-paper SQLite caching
|
|
84
|
+
Three models — a vision model (Qwen3-VL-2B default, or 8B FP8 via `--vision-backend vllm` for +15% accuracy), an embedding model (Arctic Embed), and an 8B reasoning LLM (Qwen3 via vLLM) — share a single consumer GPU. The pipeline runs each in its own stage and explicitly stops one before starting the next, so only one ever owns GPU memory at a time (same code path on WSL2 and native Linux). FP8 weights and KV cache (Ada Lovelace+) and per-paper SQLite caching are baseline; every cache layer pins its inputs by hash so editing the paper, replacing a figure, or refreshing a reference recomputes only what's affected (see the *Iterative editing* subsection under Usage — `--fresh` is not part of the normal loop). On top of that:
|
|
85
85
|
|
|
86
86
|
- **Cost-aware verify-claim ladder** — sentence chunk → paragraph chunk → whole section. Each level fans out top-N candidates in parallel; stops on a conclusive verdict. Most claims resolve at the cheap sentence level (~200-token prompts) instead of paying for whole sections (~5K tokens), with full-section escalation reserved for hard cases
|
|
87
87
|
- **Prefix-first prompt structure** — shared context placed before variable content in all prompts, maximizing vLLM's automatic prefix caching
|
|
@@ -92,11 +92,11 @@ Three models — a vision model (Qwen3-VL-2B default, or 8B FP8 via `--vision-ba
|
|
|
92
92
|
- **14-source full-text cascade** — early exit on first successful download across arXiv, Semantic Scholar, OpenAlex, PubMed Central, Europe PMC, Unpaywall, bioRxiv, NBER, RePEc/IDEAS, HAL, ERIC, NASA ADS, OSF Preprints, CORE
|
|
93
93
|
- **Live monitoring** *(advanced)* — `sciwrite-lint containers monitor` shows service health, VRAM usage, and KV cache utilization in a terminal dashboard
|
|
94
94
|
|
|
95
|
-
|
|
95
|
+
The pipeline automatically swaps vLLM containers to free GPU for embedding and vision stages (~50× faster than CPU).
|
|
96
96
|
|
|
97
97
|
## Install
|
|
98
98
|
|
|
99
|
-
**Assumed setup:** A workstation with an NVIDIA GPU (16+ GB VRAM). Tested on WSL2 with NVIDIA driver 546.01+. Native Linux
|
|
99
|
+
**Assumed setup:** A workstation with an NVIDIA GPU (16+ GB VRAM). Tested on WSL2 with NVIDIA driver 546.01+. Native Linux uses the same GPU-sequencing code path with no extra configuration (see [docs/services.md](https://github.com/authentic-research-partners/sciwrite-lint/blob/main/docs/services.md#embedding-device)), though it isn't actively tested. Not tested on macOS.
|
|
100
100
|
|
|
101
101
|
Requires [uv](https://docs.astral.sh/uv/getting-started/installation/), a container runtime (podman or docker), CUDA drivers, and [NVIDIA Container Toolkit](https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/latest/install-guide.html).
|
|
102
102
|
|
|
@@ -141,6 +141,8 @@ sciwrite-lint containers start # start GROBID + vLLM (needs GPU for vLLM
|
|
|
141
141
|
sciwrite-lint containers monitor # live dashboard: service health, VRAM, KV cache
|
|
142
142
|
```
|
|
143
143
|
|
|
144
|
+
`init` detects `.tex` files and their `.bib` references and generates a `.sciwrite-lint.toml` config. Review to confirm the right files were detected.
|
|
145
|
+
|
|
144
146
|
### WSL2: disable CUDA Sysmem Fallback (recommended)
|
|
145
147
|
|
|
146
148
|
By default Windows silently spills GPU overflow into system RAM via PCIe (30–100× slower than VRAM); you'll see this as a sudden throughput collapse during the heaviest pipeline stages with no error in the logs. Tell the NVIDIA driver to fail loudly instead:
|
|
@@ -151,8 +153,6 @@ Driver 546.01+. After this, WSL2 behaves like native Linux on overflow (CUDA OOM
|
|
|
151
153
|
|
|
152
154
|

|
|
153
155
|
|
|
154
|
-
`init` detects `.tex` files and their `.bib` references and generates a `.sciwrite-lint.toml` config. Review to confirm the right files were detected.
|
|
155
|
-
|
|
156
156
|
**Providing references manually — two drop folders.** sciwrite-lint reads two local directories before going to the open-access waterfall, kept separate because the folder itself signals how much to trust the source:
|
|
157
157
|
|
|
158
158
|
- `local_pdfs/` — **academic sources**. Accepts `.pdf` (primary) and `.md` summaries you've written yourself. Use this for paywalled papers, for OA papers whose publisher requires a browser (Cloudflare/JS walls, Cell, Springer, some PMC pages), and for anything you'd cite as peer-reviewed evidence.
|
|
@@ -214,9 +214,36 @@ sciwrite-lint contributions --paper my_paper # add contribution axes to SciLi
|
|
|
214
214
|
sciwrite-lint contributions paper.pdf # standalone file scoring
|
|
215
215
|
```
|
|
216
216
|
|
|
217
|
+
**Supported inputs.**
|
|
218
|
+
|
|
219
|
+
*The manuscript you check:*
|
|
220
|
+
|
|
221
|
+
| Format | Notes |
|
|
222
|
+
|---|---|
|
|
223
|
+
| `.tex` | LaTeX — hand-written, or pandoc-generated (e.g. from Markdown); pandoc's heading anchors are unwrapped automatically. |
|
|
224
|
+
| `.pdf` | Parsed via GROBID (container required). |
|
|
225
|
+
|
|
226
|
+
*Cited sources you optionally supply (drop folders — see [Setup](#setup)):*
|
|
227
|
+
|
|
228
|
+
| Folder | Accepts | Trust level |
|
|
229
|
+
|---|---|---|
|
|
230
|
+
| `local_pdfs/` | `.pdf`, `.md` (your own summaries) | academic |
|
|
231
|
+
| `local_web/` | `.mhtml` / `.mht`, `.md` | web capture |
|
|
232
|
+
|
|
217
233
|
`check` runs the full pipeline in one command: text checks → figure analysis → LLM consistency → reference verification via APIs → download and parse cited papers → claim verification → consistency checks on cited papers → bibliography verification → aggregate reliability scores → SciLint Score. An initial run on a 50-reference paper takes up to 30 minutes (dominated by downloads and claim verification); subsequent cached runs complete in minutes.
|
|
218
234
|
|
|
219
|
-
|
|
235
|
+
### Iterative editing — no `--fresh` needed
|
|
236
|
+
|
|
237
|
+
`sciwrite-lint check` invalidates cached results whenever the inputs they were derived from change, so rerunning after an edit just works. Concretely:
|
|
238
|
+
|
|
239
|
+
- **Edit a sentence or paragraph** → the manuscript-LLM checks (prose-quality, cross-section consistency, etc.) re-run only on the touched sentences and their paragraph siblings; untouched prose stays cached
|
|
240
|
+
- **Edit a paragraph around a citation** → the claim verifier reruns on the edited prose
|
|
241
|
+
- **Replace a figure** (overwrite the PNG/PDF in your tree) → the vision cache re-describes it
|
|
242
|
+
- **Swap the local LLM model** (e.g. `qwen3` ↔ `gemma3` via `--model`) → manuscript-check rows and claim verdicts no longer count as cache hits
|
|
243
|
+
- **Overwrite a reference PDF** in your drop folder, or **re-fetch** an open-access PDF that has new bytes → the parser, embeddings, and claim verifier all rerun against the new content
|
|
244
|
+
- **Upgrade a summary `.md` to a real PDF** in your drop folder → claims are re-verified against the richer evidence
|
|
245
|
+
|
|
246
|
+
Use `--fresh` only when you want to wipe the workspace deliberately (it backs up the existing workspace before recreating it). It is not the answer to "I edited the paper and a finding looks stale" — if you ever see that, file an issue; a cache invariant is wrong and we'll fix it.
|
|
220
247
|
|
|
221
248
|
Use `--checks ID[,ID...]` to run only the listed checks (comma-separated), disabling the rest for this invocation. Useful for fast iteration during editing (`--checks prose-quality` runs just the prose review), for debugging a single check, or for scripting a two-pass workflow. Unknown check IDs fail loudly — run `sciwrite-lint checks` to list available IDs.
|
|
222
249
|
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "sciwrite-lint"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.5.1"
|
|
8
8
|
description = "A linter for scientific manuscripts — reference verification, consistency checking, and structural validation."
|
|
9
9
|
requires-python = ">=3.13,<3.14"
|
|
10
10
|
license = "MIT"
|
|
@@ -86,3 +86,5 @@ sciwrite-lint = "sciwrite_lint.__main__:main"
|
|
|
86
86
|
[tool.setuptools.packages.find]
|
|
87
87
|
include = ["sciwrite_lint*"]
|
|
88
88
|
|
|
89
|
+
[tool.setuptools.package-data]
|
|
90
|
+
|
|
@@ -14,8 +14,6 @@ import argparse
|
|
|
14
14
|
import sys
|
|
15
15
|
from pathlib import Path
|
|
16
16
|
|
|
17
|
-
from loguru import logger
|
|
18
|
-
|
|
19
17
|
from sciwrite_lint import __version__
|
|
20
18
|
from sciwrite_lint.cli._common import (
|
|
21
19
|
_classify_verify_issue as _classify_verify_issue,
|
|
@@ -42,6 +40,31 @@ _REPLACE_HELP = (
|
|
|
42
40
|
# ---------------------------------------------------------------------------
|
|
43
41
|
|
|
44
42
|
|
|
43
|
+
def _register_optional_extensions(
|
|
44
|
+
sub: argparse._SubParsersAction, # type: ignore[type-arg]
|
|
45
|
+
) -> None:
|
|
46
|
+
"""Load CLI extensions discovered under ``sciwrite_lint``.
|
|
47
|
+
|
|
48
|
+
An underscore-prefixed subpackage is a CLI extension when its
|
|
49
|
+
``__init__.py`` defines ``register_cli(sub)``. Discovery walks
|
|
50
|
+
``sciwrite_lint.__path__`` via ``pkgutil``, so no extension package
|
|
51
|
+
names appear in this source — adding or removing one is a filesystem
|
|
52
|
+
change.
|
|
53
|
+
"""
|
|
54
|
+
import importlib
|
|
55
|
+
import pkgutil
|
|
56
|
+
|
|
57
|
+
import sciwrite_lint as _pkg
|
|
58
|
+
|
|
59
|
+
for _finder, name, ispkg in pkgutil.iter_modules(_pkg.__path__):
|
|
60
|
+
if not ispkg or not name.startswith("_"):
|
|
61
|
+
continue
|
|
62
|
+
ext = importlib.import_module(f"{_pkg.__name__}.{name}")
|
|
63
|
+
register = getattr(ext, "register_cli", None)
|
|
64
|
+
if register is not None:
|
|
65
|
+
register(sub)
|
|
66
|
+
|
|
67
|
+
|
|
45
68
|
def main(argv: list[str] | None = None) -> int:
|
|
46
69
|
from sciwrite_lint.cli.check import run_check, run_checks_list
|
|
47
70
|
from sciwrite_lint.cli.config import run_config
|
|
@@ -125,47 +148,8 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
125
148
|
p_checks = sub.add_parser("checks", help="List all registered checks")
|
|
126
149
|
p_checks.set_defaults(func=run_checks_list)
|
|
127
150
|
|
|
128
|
-
#
|
|
129
|
-
|
|
130
|
-
# while it stabilizes. Use ``importlib.util.find_spec`` to check whether
|
|
131
|
-
# the module is present rather than try/except ImportError (separately
|
|
132
|
-
# banned). Public installs (pip from PyPI) won't have it; dev installs
|
|
133
|
-
# do. Check the parent ``sciwrite_lint.aicode_lint`` first because
|
|
134
|
-
# ``find_spec`` raises ``ModuleNotFoundError`` on a missing intermediate
|
|
135
|
-
# path; once the parent is confirmed present, the child lookup is safe.
|
|
136
|
-
import importlib.util as _importlib_util
|
|
137
|
-
|
|
138
|
-
if _importlib_util.find_spec("sciwrite_lint.aicode_lint") is not None:
|
|
139
|
-
from sciwrite_lint.aicode_lint.cli import run_aicode_lint
|
|
140
|
-
|
|
141
|
-
p_aicode = sub.add_parser(
|
|
142
|
-
"aicode-lint",
|
|
143
|
-
help="LLM code review on Python sources via local vLLM (semantic antipatterns)",
|
|
144
|
-
)
|
|
145
|
-
p_aicode.add_argument(
|
|
146
|
-
"paths",
|
|
147
|
-
nargs="*",
|
|
148
|
-
help="Files, directories, or globs to scan (default: sciwrite_lint/**/*.py)",
|
|
149
|
-
)
|
|
150
|
-
p_aicode.add_argument(
|
|
151
|
-
"--rules",
|
|
152
|
-
default=None,
|
|
153
|
-
metavar="ID[,ID...]",
|
|
154
|
-
help="Run only the listed rule IDs (comma-separated). Default: all built-in rules.",
|
|
155
|
-
)
|
|
156
|
-
p_aicode.add_argument(
|
|
157
|
-
"--list-rules",
|
|
158
|
-
action="store_true",
|
|
159
|
-
help="Print the rule registry and exit.",
|
|
160
|
-
)
|
|
161
|
-
p_aicode.add_argument(
|
|
162
|
-
"--format",
|
|
163
|
-
choices=["terminal", "json"],
|
|
164
|
-
default=None,
|
|
165
|
-
help="Output format (default: terminal).",
|
|
166
|
-
)
|
|
167
|
-
p_aicode.add_argument("--config", help="Path to .sciwrite-lint.toml")
|
|
168
|
-
p_aicode.set_defaults(func=run_aicode_lint)
|
|
151
|
+
# Load optional underscore-prefixed CLI extension subpackages (if any).
|
|
152
|
+
_register_optional_extensions(sub)
|
|
169
153
|
|
|
170
154
|
# --- init ---
|
|
171
155
|
p_init = sub.add_parser(
|
|
@@ -270,12 +254,6 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
270
254
|
# --- verify-claims ---
|
|
271
255
|
p_vc = sub.add_parser("verify-claims", help="Verify claims against cited sources")
|
|
272
256
|
p_vc.add_argument("--paper", required=True, help="Paper to verify")
|
|
273
|
-
p_vc.add_argument(
|
|
274
|
-
"--backend",
|
|
275
|
-
choices=["vllm", "claude"],
|
|
276
|
-
default="vllm",
|
|
277
|
-
help="vllm (local LLM, fast) or claude (Claude CLI, deep)",
|
|
278
|
-
)
|
|
279
257
|
p_vc.add_argument(
|
|
280
258
|
"--model",
|
|
281
259
|
choices=_TEXT_MODELS,
|
|
@@ -499,14 +477,32 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
499
477
|
p_vllm.print_help()
|
|
500
478
|
return 0
|
|
501
479
|
|
|
502
|
-
# Set up file logging from config (stderr sink stays at INFO by default)
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
480
|
+
# Set up file logging from config (stderr sink stays at INFO by default).
|
|
481
|
+
# ``load_config`` already returns ``LintConfig()`` when no config file is
|
|
482
|
+
# discovered; reaching the exception path means a file WAS found (or
|
|
483
|
+
# explicitly passed via --config) but failed to parse or validate.
|
|
484
|
+
# Surface that instead of silently substituting hardcoded defaults — a
|
|
485
|
+
# broken TOML otherwise produces results computed against the wrong
|
|
486
|
+
# workspace, paper list, or API keys.
|
|
487
|
+
#
|
|
488
|
+
# The ``init`` command is the one exception: its job is to scaffold a
|
|
489
|
+
# config, so it must run against a missing, broken, or sentinel TOML
|
|
490
|
+
# without aborting. Use ``LintConfig()`` for logging setup only and let
|
|
491
|
+
# init proceed to (over)write the file.
|
|
492
|
+
if args.command == "init":
|
|
509
493
|
config = LintConfig()
|
|
494
|
+
else:
|
|
495
|
+
try:
|
|
496
|
+
config = load_config(
|
|
497
|
+
Path(args.config) if getattr(args, "config", None) else None
|
|
498
|
+
)
|
|
499
|
+
except Exception as e:
|
|
500
|
+
cfg_arg = getattr(args, "config", None)
|
|
501
|
+
cfg_hint = f" ({cfg_arg})" if cfg_arg else " (.sciwrite-lint.toml)"
|
|
502
|
+
raise SystemExit(
|
|
503
|
+
f"ERROR: failed to load configuration{cfg_hint}: {type(e).__name__}: {e}\n"
|
|
504
|
+
" Fix the TOML or remove the file to use built-in defaults."
|
|
505
|
+
) from e
|
|
510
506
|
_setup_logging(config)
|
|
511
507
|
|
|
512
508
|
return args.func(args)
|
|
@@ -84,7 +84,7 @@ def run_parse(args: argparse.Namespace) -> int:
|
|
|
84
84
|
n = compute_and_store_embeddings(key, text, refs_dir)
|
|
85
85
|
print(f" {key}: {n} chunks")
|
|
86
86
|
except Exception as e:
|
|
87
|
-
|
|
87
|
+
logger.warning("parse: embedding failed for {}: {}", key, e)
|
|
88
88
|
|
|
89
89
|
print(f"\n Summary: {cached} cached, {parsed} parsed, {failed} failed")
|
|
90
90
|
if failed:
|
|
@@ -335,6 +335,7 @@ def run_ref_health(args: argparse.Namespace) -> int:
|
|
|
335
335
|
academic_unmatched: list[Path] = []
|
|
336
336
|
web_matched: dict[str, Path] = {}
|
|
337
337
|
web_unmatched: list[Path] = []
|
|
338
|
+
local_match_error: str | None = None
|
|
338
339
|
try:
|
|
339
340
|
if tex_file:
|
|
340
341
|
from sciwrite_lint.config import load_config as _load_cfg
|
|
@@ -356,8 +357,13 @@ def run_ref_health(args: argparse.Namespace) -> int:
|
|
|
356
357
|
_cfg.local_web_dir, titles, WEB_SUFFIXES
|
|
357
358
|
)
|
|
358
359
|
except Exception as e:
|
|
359
|
-
|
|
360
|
-
|
|
360
|
+
# Record the failure so the report renders a visible notice instead
|
|
361
|
+
# of silently producing a ref-health output with the local-source
|
|
362
|
+
# section missing. Log at WARNING (not DEBUG) so the operator sees
|
|
363
|
+
# it even when running with the default log level.
|
|
364
|
+
local_match_error = f"{type(e).__name__}: {e}"
|
|
365
|
+
logger.warning(
|
|
366
|
+
"ref-health: local source matching skipped ({})", local_match_error
|
|
361
367
|
)
|
|
362
368
|
|
|
363
369
|
# --- Output ---
|
|
@@ -398,7 +404,9 @@ def run_ref_health(args: argparse.Namespace) -> int:
|
|
|
398
404
|
if web_count:
|
|
399
405
|
print(f" ({web_count} web resources)")
|
|
400
406
|
|
|
401
|
-
# Local sources — academic (PDFs + MD summaries) + web (MD + MHTML)
|
|
407
|
+
# Local sources — academic (PDFs + MD summaries) + web (MD + MHTML).
|
|
408
|
+
# Matching failures are surfaced via logger.warning (above) so they
|
|
409
|
+
# appear in the operator's terminal regardless of report formatting.
|
|
402
410
|
if academic_matched or academic_unmatched:
|
|
403
411
|
print("\n LOCAL ACADEMIC SOURCES")
|
|
404
412
|
if academic_matched:
|
|
@@ -425,7 +433,7 @@ def run_ref_health(args: argparse.Namespace) -> int:
|
|
|
425
433
|
|
|
426
434
|
|
|
427
435
|
def run_verify_claims(args: argparse.Namespace) -> int:
|
|
428
|
-
"""Verify claims against cited sources using local
|
|
436
|
+
"""Verify claims against cited sources using local vLLM."""
|
|
429
437
|
from sciwrite_lint.cli._common import _load_config, _resolve_paper
|
|
430
438
|
|
|
431
439
|
config = _load_config(args)
|
|
@@ -446,7 +454,6 @@ def run_verify_claims(args: argparse.Namespace) -> int:
|
|
|
446
454
|
refs_dir,
|
|
447
455
|
config=config,
|
|
448
456
|
bib_path=pc.bib,
|
|
449
|
-
backend=args.backend,
|
|
450
457
|
model=args.model,
|
|
451
458
|
key_filter=args.key,
|
|
452
459
|
limit=args.limit,
|