docpull 4.1.0__tar.gz → 4.2.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.
- {docpull-4.1.0/src/docpull.egg-info → docpull-4.2.0}/PKG-INFO +117 -15
- {docpull-4.1.0 → docpull-4.2.0}/README.md +113 -14
- {docpull-4.1.0 → docpull-4.2.0}/pyproject.toml +5 -1
- {docpull-4.1.0 → docpull-4.2.0}/src/docpull/__init__.py +1 -1
- docpull-4.2.0/src/docpull/benchmark.py +2922 -0
- {docpull-4.1.0 → docpull-4.2.0}/src/docpull/cli.py +8 -0
- {docpull-4.1.0 → docpull-4.2.0}/src/docpull/pack_tools.py +29 -12
- {docpull-4.1.0 → docpull-4.2.0}/src/docpull/parallel_workflows.py +35 -104
- {docpull-4.1.0 → docpull-4.2.0}/src/docpull/pipeline/steps/save_ndjson.py +56 -0
- docpull-4.2.0/src/docpull/provider_cli.py +426 -0
- docpull-4.2.0/src/docpull/provider_keys.py +217 -0
- {docpull-4.1.0 → docpull-4.2.0/src/docpull.egg-info}/PKG-INFO +117 -15
- {docpull-4.1.0 → docpull-4.2.0}/src/docpull.egg-info/SOURCES.txt +5 -0
- {docpull-4.1.0 → docpull-4.2.0}/src/docpull.egg-info/requires.txt +4 -0
- docpull-4.2.0/tests/test_benchmark.py +645 -0
- {docpull-4.1.0 → docpull-4.2.0}/tests/test_pack_tools.py +42 -0
- {docpull-4.1.0 → docpull-4.2.0}/tests/test_parallel_workflows.py +41 -0
- docpull-4.2.0/tests/test_provider_cli.py +85 -0
- {docpull-4.1.0 → docpull-4.2.0}/tests/test_save_ndjson.py +8 -0
- {docpull-4.1.0 → docpull-4.2.0}/LICENSE +0 -0
- {docpull-4.1.0 → docpull-4.2.0}/setup.cfg +0 -0
- {docpull-4.1.0 → docpull-4.2.0}/src/docpull/__main__.py +0 -0
- {docpull-4.1.0 → docpull-4.2.0}/src/docpull/cache/__init__.py +0 -0
- {docpull-4.1.0 → docpull-4.2.0}/src/docpull/cache/frontier.py +0 -0
- {docpull-4.1.0 → docpull-4.2.0}/src/docpull/cache/manager.py +0 -0
- {docpull-4.1.0 → docpull-4.2.0}/src/docpull/cache/streaming_dedup.py +0 -0
- {docpull-4.1.0 → docpull-4.2.0}/src/docpull/conversion/__init__.py +0 -0
- {docpull-4.1.0 → docpull-4.2.0}/src/docpull/conversion/chunking.py +0 -0
- {docpull-4.1.0 → docpull-4.2.0}/src/docpull/conversion/extractor.py +0 -0
- {docpull-4.1.0 → docpull-4.2.0}/src/docpull/conversion/markdown.py +0 -0
- {docpull-4.1.0 → docpull-4.2.0}/src/docpull/conversion/protocols.py +0 -0
- {docpull-4.1.0 → docpull-4.2.0}/src/docpull/conversion/special_cases.py +0 -0
- {docpull-4.1.0 → docpull-4.2.0}/src/docpull/conversion/trafilatura_extractor.py +0 -0
- {docpull-4.1.0 → docpull-4.2.0}/src/docpull/core/__init__.py +0 -0
- {docpull-4.1.0 → docpull-4.2.0}/src/docpull/core/fetcher.py +0 -0
- {docpull-4.1.0 → docpull-4.2.0}/src/docpull/discovery/__init__.py +0 -0
- {docpull-4.1.0 → docpull-4.2.0}/src/docpull/discovery/_fetch.py +0 -0
- {docpull-4.1.0 → docpull-4.2.0}/src/docpull/discovery/composite.py +0 -0
- {docpull-4.1.0 → docpull-4.2.0}/src/docpull/discovery/crawler.py +0 -0
- {docpull-4.1.0 → docpull-4.2.0}/src/docpull/discovery/filters.py +0 -0
- {docpull-4.1.0 → docpull-4.2.0}/src/docpull/discovery/link_extractors/__init__.py +0 -0
- {docpull-4.1.0 → docpull-4.2.0}/src/docpull/discovery/link_extractors/enhanced.py +0 -0
- {docpull-4.1.0 → docpull-4.2.0}/src/docpull/discovery/link_extractors/protocols.py +0 -0
- {docpull-4.1.0 → docpull-4.2.0}/src/docpull/discovery/link_extractors/static.py +0 -0
- {docpull-4.1.0 → docpull-4.2.0}/src/docpull/discovery/protocols.py +0 -0
- {docpull-4.1.0 → docpull-4.2.0}/src/docpull/discovery/sitemap.py +0 -0
- {docpull-4.1.0 → docpull-4.2.0}/src/docpull/doctor.py +0 -0
- {docpull-4.1.0 → docpull-4.2.0}/src/docpull/fixtures/__init__.py +0 -0
- {docpull-4.1.0 → docpull-4.2.0}/src/docpull/fixtures/parallel-search-extract.json +0 -0
- {docpull-4.1.0 → docpull-4.2.0}/src/docpull/http/__init__.py +0 -0
- {docpull-4.1.0 → docpull-4.2.0}/src/docpull/http/client.py +0 -0
- {docpull-4.1.0 → docpull-4.2.0}/src/docpull/http/protocols.py +0 -0
- {docpull-4.1.0 → docpull-4.2.0}/src/docpull/http/rate_limiter.py +0 -0
- {docpull-4.1.0 → docpull-4.2.0}/src/docpull/mcp/__init__.py +0 -0
- {docpull-4.1.0 → docpull-4.2.0}/src/docpull/mcp/server.py +0 -0
- {docpull-4.1.0 → docpull-4.2.0}/src/docpull/mcp/sources.py +0 -0
- {docpull-4.1.0 → docpull-4.2.0}/src/docpull/mcp/tools.py +0 -0
- {docpull-4.1.0 → docpull-4.2.0}/src/docpull/metadata_extractor.py +0 -0
- {docpull-4.1.0 → docpull-4.2.0}/src/docpull/models/__init__.py +0 -0
- {docpull-4.1.0 → docpull-4.2.0}/src/docpull/models/config.py +0 -0
- {docpull-4.1.0 → docpull-4.2.0}/src/docpull/models/document.py +0 -0
- {docpull-4.1.0 → docpull-4.2.0}/src/docpull/models/events.py +0 -0
- {docpull-4.1.0 → docpull-4.2.0}/src/docpull/models/profiles.py +0 -0
- {docpull-4.1.0 → docpull-4.2.0}/src/docpull/models/run.py +0 -0
- {docpull-4.1.0 → docpull-4.2.0}/src/docpull/pipeline/__init__.py +0 -0
- {docpull-4.1.0 → docpull-4.2.0}/src/docpull/pipeline/base.py +0 -0
- {docpull-4.1.0 → docpull-4.2.0}/src/docpull/pipeline/manifest.py +0 -0
- {docpull-4.1.0 → docpull-4.2.0}/src/docpull/pipeline/steps/__init__.py +0 -0
- {docpull-4.1.0 → docpull-4.2.0}/src/docpull/pipeline/steps/chunk.py +0 -0
- {docpull-4.1.0 → docpull-4.2.0}/src/docpull/pipeline/steps/convert.py +0 -0
- {docpull-4.1.0 → docpull-4.2.0}/src/docpull/pipeline/steps/dedup.py +0 -0
- {docpull-4.1.0 → docpull-4.2.0}/src/docpull/pipeline/steps/fetch.py +0 -0
- {docpull-4.1.0 → docpull-4.2.0}/src/docpull/pipeline/steps/metadata.py +0 -0
- {docpull-4.1.0 → docpull-4.2.0}/src/docpull/pipeline/steps/save.py +0 -0
- {docpull-4.1.0 → docpull-4.2.0}/src/docpull/pipeline/steps/save_json.py +0 -0
- {docpull-4.1.0 → docpull-4.2.0}/src/docpull/pipeline/steps/save_sqlite.py +0 -0
- {docpull-4.1.0 → docpull-4.2.0}/src/docpull/pipeline/steps/validate.py +0 -0
- {docpull-4.1.0 → docpull-4.2.0}/src/docpull/py.typed +0 -0
- {docpull-4.1.0 → docpull-4.2.0}/src/docpull/security/__init__.py +0 -0
- {docpull-4.1.0 → docpull-4.2.0}/src/docpull/security/download_policy.py +0 -0
- {docpull-4.1.0 → docpull-4.2.0}/src/docpull/security/robots.py +0 -0
- {docpull-4.1.0 → docpull-4.2.0}/src/docpull/security/url_validator.py +0 -0
- {docpull-4.1.0 → docpull-4.2.0}/src/docpull/source_scoring.py +0 -0
- {docpull-4.1.0 → docpull-4.2.0}/src/docpull/time_utils.py +0 -0
- {docpull-4.1.0 → docpull-4.2.0}/src/docpull.egg-info/dependency_links.txt +0 -0
- {docpull-4.1.0 → docpull-4.2.0}/src/docpull.egg-info/entry_points.txt +0 -0
- {docpull-4.1.0 → docpull-4.2.0}/src/docpull.egg-info/top_level.txt +0 -0
- {docpull-4.1.0 → docpull-4.2.0}/tests/test_cache_conditional_get.py +0 -0
- {docpull-4.1.0 → docpull-4.2.0}/tests/test_chunking.py +0 -0
- {docpull-4.1.0 → docpull-4.2.0}/tests/test_ci_policy.py +0 -0
- {docpull-4.1.0 → docpull-4.2.0}/tests/test_cli.py +0 -0
- {docpull-4.1.0 → docpull-4.2.0}/tests/test_conversion.py +0 -0
- {docpull-4.1.0 → docpull-4.2.0}/tests/test_convert_step_new.py +0 -0
- {docpull-4.1.0 → docpull-4.2.0}/tests/test_discovery.py +0 -0
- {docpull-4.1.0 → docpull-4.2.0}/tests/test_frontier_resume.py +0 -0
- {docpull-4.1.0 → docpull-4.2.0}/tests/test_integration.py +0 -0
- {docpull-4.1.0 → docpull-4.2.0}/tests/test_link_extractors.py +0 -0
- {docpull-4.1.0 → docpull-4.2.0}/tests/test_mcp_server.py +0 -0
- {docpull-4.1.0 → docpull-4.2.0}/tests/test_mcp_tools.py +0 -0
- {docpull-4.1.0 → docpull-4.2.0}/tests/test_naming.py +0 -0
- {docpull-4.1.0 → docpull-4.2.0}/tests/test_outputs_e2e.py +0 -0
- {docpull-4.1.0 → docpull-4.2.0}/tests/test_pipeline.py +0 -0
- {docpull-4.1.0 → docpull-4.2.0}/tests/test_real_site_regressions.py +0 -0
- {docpull-4.1.0 → docpull-4.2.0}/tests/test_save_sqlite.py +0 -0
- {docpull-4.1.0 → docpull-4.2.0}/tests/test_security_hardening.py +0 -0
- {docpull-4.1.0 → docpull-4.2.0}/tests/test_special_cases.py +0 -0
- {docpull-4.1.0 → docpull-4.2.0}/tests/test_time_utils.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: docpull
|
|
3
|
-
Version: 4.
|
|
3
|
+
Version: 4.2.0
|
|
4
4
|
Summary: Pull documentation from the web and convert to clean markdown
|
|
5
5
|
Author-email: Zachary Roth <support@raintree.technology>
|
|
6
6
|
Maintainer-email: Raintree Technology <support@raintree.technology>
|
|
@@ -64,6 +64,8 @@ Requires-Dist: python-multipart>=0.0.27; extra == "mcp"
|
|
|
64
64
|
Requires-Dist: starlette>=1.0.1; extra == "mcp"
|
|
65
65
|
Provides-Extra: parallel
|
|
66
66
|
Requires-Dist: parallel-web>=1.0.1; extra == "parallel"
|
|
67
|
+
Provides-Extra: observability
|
|
68
|
+
Requires-Dist: raindrop-ai>=0.0.50; extra == "observability"
|
|
67
69
|
Provides-Extra: llm
|
|
68
70
|
Requires-Dist: tiktoken>=0.7.0; extra == "llm"
|
|
69
71
|
Provides-Extra: all
|
|
@@ -76,6 +78,7 @@ Requires-Dist: pyjwt>=2.13.0; extra == "all"
|
|
|
76
78
|
Requires-Dist: python-multipart>=0.0.27; extra == "all"
|
|
77
79
|
Requires-Dist: starlette>=1.0.1; extra == "all"
|
|
78
80
|
Requires-Dist: parallel-web>=1.0.1; extra == "all"
|
|
81
|
+
Requires-Dist: raindrop-ai>=0.0.50; extra == "all"
|
|
79
82
|
Provides-Extra: dev
|
|
80
83
|
Requires-Dist: pytest>=7.0.0; extra == "dev"
|
|
81
84
|
Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
|
|
@@ -93,7 +96,7 @@ Dynamic: license-file
|
|
|
93
96
|
|
|
94
97
|
# docpull
|
|
95
98
|
|
|
96
|
-
**Security-hardened, browser-free crawler that turns
|
|
99
|
+
**Security-hardened, browser-free web scraper and crawler that turns server-rendered pages into clean, AI-ready Markdown — fast.**
|
|
97
100
|
|
|
98
101
|
[](https://www.python.org/downloads/)
|
|
99
102
|
[](https://badge.fury.io/py/docpull)
|
|
@@ -106,15 +109,16 @@ Dynamic: license-file
|
|
|
106
109
|
</a>
|
|
107
110
|
</p>
|
|
108
111
|
|
|
109
|
-
docpull
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
112
|
+
docpull is a web scraper for static and server-rendered sites, with
|
|
113
|
+
documentation crawling as its sharpest default workflow. It uses async HTTP (not
|
|
114
|
+
Playwright) to fetch pages, discover links, extract main content, and write clean
|
|
115
|
+
Markdown with source-URL frontmatter — in seconds, with a small install
|
|
116
|
+
footprint. It won't render JavaScript, but for the large class of sites that
|
|
117
|
+
don't need it (API references, vendor docs, Python/Go stdlib, blogs, OpenAPI
|
|
118
|
+
specs, Next.js and Docusaurus builds), it is a fast, auditable,
|
|
119
|
+
sandbox-friendly way to pipe web content into an LLM context, a RAG index, or an
|
|
120
|
+
offline archive. SSRF, XXE, DNS-rebinding, and CRLF-injection protections are on
|
|
121
|
+
by default — a necessity when an AI agent is choosing the URLs.
|
|
118
122
|
|
|
119
123
|
## Install
|
|
120
124
|
|
|
@@ -125,22 +129,24 @@ pip install docpull
|
|
|
125
129
|
pip install 'docpull[llm]' # tiktoken for token-accurate chunking
|
|
126
130
|
pip install 'docpull[trafilatura]' # alternative extractor for noisy pages
|
|
127
131
|
pip install 'docpull[mcp]' # run as an MCP server for AI agents
|
|
132
|
+
pip install 'docpull[parallel]' # Parallel API context packs
|
|
133
|
+
pip install 'docpull[observability]' # Raindrop benchmark tracing
|
|
128
134
|
pip install 'docpull[all]' # everything above
|
|
129
135
|
```
|
|
130
136
|
|
|
131
137
|
## Quick start
|
|
132
138
|
|
|
133
139
|
```bash
|
|
134
|
-
#
|
|
140
|
+
# Scrape a page graph and save Markdown
|
|
135
141
|
docpull https://docs.example.com
|
|
136
142
|
|
|
137
|
-
#
|
|
143
|
+
# Scrape one page, no crawl — the fast path for agents
|
|
138
144
|
docpull https://docs.example.com/guide --single
|
|
139
145
|
|
|
140
146
|
# LLM-ready NDJSON with 4k-token chunks streamed to stdout
|
|
141
147
|
docpull https://docs.example.com --profile llm --stream | jq .
|
|
142
148
|
|
|
143
|
-
# Mirror
|
|
149
|
+
# Mirror scraped content for offline use
|
|
144
150
|
docpull https://docs.example.com --profile mirror --cache
|
|
145
151
|
```
|
|
146
152
|
|
|
@@ -274,7 +280,32 @@ value and reports the key source (`env`, `project_env`, `user_config`, or
|
|
|
274
280
|
`missing`). It does not make a live Parallel call or prove the key is valid. Use
|
|
275
281
|
`--json` when an agent or CI job needs machine-readable configuration status.
|
|
276
282
|
|
|
277
|
-
|
|
283
|
+
## Optional live providers
|
|
284
|
+
|
|
285
|
+
Parallel, Tavily, and Exa are equal optional live providers for benchmark and
|
|
286
|
+
provider context-pack workflows. You can configure zero, one, two, or all three
|
|
287
|
+
API keys. docpull looks for keys in this order: environment variables, project
|
|
288
|
+
`.env.local`, then `~/.config/docpull/secrets.env`. Missing providers are skipped
|
|
289
|
+
instead of failing provider-neutral runs.
|
|
290
|
+
|
|
291
|
+
```bash
|
|
292
|
+
docpull providers auth --json
|
|
293
|
+
docpull providers init parallel
|
|
294
|
+
docpull providers init tavily
|
|
295
|
+
docpull providers init exa
|
|
296
|
+
|
|
297
|
+
docpull providers context-pack "Compare AI web-search APIs for agents" \
|
|
298
|
+
--provider auto \
|
|
299
|
+
--query "AI web search API" \
|
|
300
|
+
--include-domain docs.parallel.ai \
|
|
301
|
+
--output-dir ./packs/provider-comparison
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
Use `--provider all` to request all three and record skipped-provider metadata
|
|
305
|
+
for keys or optional SDKs that are not configured. Use repeated `--provider`
|
|
306
|
+
values to run a specific subset.
|
|
307
|
+
|
|
308
|
+
A Parallel pack contains:
|
|
278
309
|
|
|
279
310
|
- `AGENT_CONTEXT.md` — agent load plan with source order, pack signals, warnings, errors, and artifact map.
|
|
280
311
|
- `documents.ndjson` — chunked records for agents and RAG pipelines.
|
|
@@ -547,6 +578,77 @@ Reproduce with `make benchmark` (requires `aiohttp`; runs the gated
|
|
|
547
578
|
benchmark in `tests/benchmarks/` and prints progress plus a JSON line you can
|
|
548
579
|
pipe into trend tooling).
|
|
549
580
|
|
|
581
|
+
For real-site and live-provider context-pack benchmarks, use the first-class
|
|
582
|
+
benchmark harness:
|
|
583
|
+
|
|
584
|
+
```bash
|
|
585
|
+
docpull benchmark quick
|
|
586
|
+
docpull benchmark quick --provider auto --max-estimated-cost 0.05
|
|
587
|
+
docpull benchmark quick --provider all --max-estimated-cost 0.10
|
|
588
|
+
docpull benchmark quick --target-set provider-matrix --provider all \
|
|
589
|
+
--max-pages 8 --max-depth 1 --max-search-results 5 --extract-limit 2 \
|
|
590
|
+
--max-estimated-cost 0.10
|
|
591
|
+
docpull benchmark article .bench/runs/<run>/benchmark.report.json
|
|
592
|
+
```
|
|
593
|
+
|
|
594
|
+
`docpull benchmark quick` writes `benchmark.report.json` and
|
|
595
|
+
`benchmark.summary.md` under `.bench/runs/<timestamp>/` by default. The core
|
|
596
|
+
cases measure an LLM-profile crawl plus a cached rerun. Passing `--provider auto`
|
|
597
|
+
runs every locally ready provider. Passing `--provider all` requests Parallel,
|
|
598
|
+
Tavily, and Exa, then records missing keys or optional SDKs in
|
|
599
|
+
`skipped_providers` without failing the benchmark. Live Parallel calls still use
|
|
600
|
+
the local `--max-estimated-cost` guard before any work starts.
|
|
601
|
+
|
|
602
|
+
Use `--target-set tool-docs` to run a cross-pull matrix across the Parallel,
|
|
603
|
+
Exa, Tavily, Raindrop, and DocPull documentation sites. Use `--target-set provider-matrix`
|
|
604
|
+
to add three low-cap adversarial public targets for JS-heavy docs, noisy
|
|
605
|
+
archived navigation, and freshness-sensitive pricing. Matrix runs skip the
|
|
606
|
+
cached core pass by default so the headline grid stays provider x target; pass
|
|
607
|
+
`--cached-pass` to force cache measurement across the matrix.
|
|
608
|
+
|
|
609
|
+
The compatibility flags `--parallel`, `--tavily`, and `--exa` still work as
|
|
610
|
+
provider aliases. Tavily Search + Extract and Exa Search-with-contents are
|
|
611
|
+
normalized into the same local
|
|
612
|
+
`documents.ndjson`, `corpus.manifest.json`, `sources.md`, provider `*.pack.json`,
|
|
613
|
+
`pack.score.json`, and `source.scores.json` artifacts as the core and Parallel
|
|
614
|
+
cases. The report keeps the legacy pack score and adds weighted benchmark
|
|
615
|
+
sub-scores for coverage, cleanliness, source fidelity, freshness, and density.
|
|
616
|
+
Keys can be exported as `PARALLEL_API_KEY`, `TAVILY_API_KEY`, and `EXA_API_KEY`
|
|
617
|
+
or stored together in `~/.config/docpull/secrets.env`; docpull does not write
|
|
618
|
+
keys into benchmark artifacts.
|
|
619
|
+
|
|
620
|
+
Tavily usage is credit-based, so set `TAVILY_CREDIT_USD` or pass
|
|
621
|
+
`--tavily-credit-usd` to convert credits into estimated dollars for cost
|
|
622
|
+
comparisons. Without that value, Tavily credits are still recorded in
|
|
623
|
+
`cost_units` but excluded from normalized USD totals.
|
|
624
|
+
|
|
625
|
+
To trace the benchmark in Raindrop for a publishable observability/eval loop,
|
|
626
|
+
install the optional SDK and pass `--trace raindrop`:
|
|
627
|
+
|
|
628
|
+
```bash
|
|
629
|
+
pip install 'docpull[parallel,observability]'
|
|
630
|
+
export PARALLEL_API_KEY="<your-parallel-api-key>"
|
|
631
|
+
export TAVILY_API_KEY="<your-tavily-api-key>"
|
|
632
|
+
export TAVILY_CREDIT_USD="<account-credit-value>"
|
|
633
|
+
export EXA_API_KEY="<your-exa-api-key>"
|
|
634
|
+
export RAINDROP_WRITE_KEY="<your-raindrop-write-key>"
|
|
635
|
+
docpull benchmark quick --target-set provider-matrix --provider all --trace raindrop \
|
|
636
|
+
--max-pages 8 --max-depth 1 --max-search-results 5 --extract-limit 2 \
|
|
637
|
+
--max-estimated-cost 0.10
|
|
638
|
+
docpull benchmark article .bench/runs/<run>/benchmark.report.json
|
|
639
|
+
```
|
|
640
|
+
|
|
641
|
+
Raindrop traces are metadata-only by default: docpull records timings, counts,
|
|
642
|
+
scores, costs, selected URLs, provider, workflow, target, prompt, settings, and
|
|
643
|
+
artifact paths, but does not send scraped page content unless a future caller
|
|
644
|
+
explicitly adds that behavior. `RAINDROP_WRITE_KEY` can be exported or stored in
|
|
645
|
+
the same `~/.config/docpull/secrets.env` file as the provider keys.
|
|
646
|
+
|
|
647
|
+
When Raindrop tracing is enabled, DocPull also emits Raindrop signals for
|
|
648
|
+
attention-worthy benchmark cells: failed cases, low scores, slow cases,
|
|
649
|
+
high-cost cells, and score-dimension warnings. The benchmark report stores the
|
|
650
|
+
Raindrop event id and signal counts so traced runs can be audited later.
|
|
651
|
+
|
|
550
652
|
## Troubleshooting
|
|
551
653
|
|
|
552
654
|
```bash
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# docpull
|
|
2
2
|
|
|
3
|
-
**Security-hardened, browser-free crawler that turns
|
|
3
|
+
**Security-hardened, browser-free web scraper and crawler that turns server-rendered pages into clean, AI-ready Markdown — fast.**
|
|
4
4
|
|
|
5
5
|
[](https://www.python.org/downloads/)
|
|
6
6
|
[](https://badge.fury.io/py/docpull)
|
|
@@ -13,15 +13,16 @@
|
|
|
13
13
|
</a>
|
|
14
14
|
</p>
|
|
15
15
|
|
|
16
|
-
docpull
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
16
|
+
docpull is a web scraper for static and server-rendered sites, with
|
|
17
|
+
documentation crawling as its sharpest default workflow. It uses async HTTP (not
|
|
18
|
+
Playwright) to fetch pages, discover links, extract main content, and write clean
|
|
19
|
+
Markdown with source-URL frontmatter — in seconds, with a small install
|
|
20
|
+
footprint. It won't render JavaScript, but for the large class of sites that
|
|
21
|
+
don't need it (API references, vendor docs, Python/Go stdlib, blogs, OpenAPI
|
|
22
|
+
specs, Next.js and Docusaurus builds), it is a fast, auditable,
|
|
23
|
+
sandbox-friendly way to pipe web content into an LLM context, a RAG index, or an
|
|
24
|
+
offline archive. SSRF, XXE, DNS-rebinding, and CRLF-injection protections are on
|
|
25
|
+
by default — a necessity when an AI agent is choosing the URLs.
|
|
25
26
|
|
|
26
27
|
## Install
|
|
27
28
|
|
|
@@ -32,22 +33,24 @@ pip install docpull
|
|
|
32
33
|
pip install 'docpull[llm]' # tiktoken for token-accurate chunking
|
|
33
34
|
pip install 'docpull[trafilatura]' # alternative extractor for noisy pages
|
|
34
35
|
pip install 'docpull[mcp]' # run as an MCP server for AI agents
|
|
36
|
+
pip install 'docpull[parallel]' # Parallel API context packs
|
|
37
|
+
pip install 'docpull[observability]' # Raindrop benchmark tracing
|
|
35
38
|
pip install 'docpull[all]' # everything above
|
|
36
39
|
```
|
|
37
40
|
|
|
38
41
|
## Quick start
|
|
39
42
|
|
|
40
43
|
```bash
|
|
41
|
-
#
|
|
44
|
+
# Scrape a page graph and save Markdown
|
|
42
45
|
docpull https://docs.example.com
|
|
43
46
|
|
|
44
|
-
#
|
|
47
|
+
# Scrape one page, no crawl — the fast path for agents
|
|
45
48
|
docpull https://docs.example.com/guide --single
|
|
46
49
|
|
|
47
50
|
# LLM-ready NDJSON with 4k-token chunks streamed to stdout
|
|
48
51
|
docpull https://docs.example.com --profile llm --stream | jq .
|
|
49
52
|
|
|
50
|
-
# Mirror
|
|
53
|
+
# Mirror scraped content for offline use
|
|
51
54
|
docpull https://docs.example.com --profile mirror --cache
|
|
52
55
|
```
|
|
53
56
|
|
|
@@ -181,7 +184,32 @@ value and reports the key source (`env`, `project_env`, `user_config`, or
|
|
|
181
184
|
`missing`). It does not make a live Parallel call or prove the key is valid. Use
|
|
182
185
|
`--json` when an agent or CI job needs machine-readable configuration status.
|
|
183
186
|
|
|
184
|
-
|
|
187
|
+
## Optional live providers
|
|
188
|
+
|
|
189
|
+
Parallel, Tavily, and Exa are equal optional live providers for benchmark and
|
|
190
|
+
provider context-pack workflows. You can configure zero, one, two, or all three
|
|
191
|
+
API keys. docpull looks for keys in this order: environment variables, project
|
|
192
|
+
`.env.local`, then `~/.config/docpull/secrets.env`. Missing providers are skipped
|
|
193
|
+
instead of failing provider-neutral runs.
|
|
194
|
+
|
|
195
|
+
```bash
|
|
196
|
+
docpull providers auth --json
|
|
197
|
+
docpull providers init parallel
|
|
198
|
+
docpull providers init tavily
|
|
199
|
+
docpull providers init exa
|
|
200
|
+
|
|
201
|
+
docpull providers context-pack "Compare AI web-search APIs for agents" \
|
|
202
|
+
--provider auto \
|
|
203
|
+
--query "AI web search API" \
|
|
204
|
+
--include-domain docs.parallel.ai \
|
|
205
|
+
--output-dir ./packs/provider-comparison
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
Use `--provider all` to request all three and record skipped-provider metadata
|
|
209
|
+
for keys or optional SDKs that are not configured. Use repeated `--provider`
|
|
210
|
+
values to run a specific subset.
|
|
211
|
+
|
|
212
|
+
A Parallel pack contains:
|
|
185
213
|
|
|
186
214
|
- `AGENT_CONTEXT.md` — agent load plan with source order, pack signals, warnings, errors, and artifact map.
|
|
187
215
|
- `documents.ndjson` — chunked records for agents and RAG pipelines.
|
|
@@ -454,6 +482,77 @@ Reproduce with `make benchmark` (requires `aiohttp`; runs the gated
|
|
|
454
482
|
benchmark in `tests/benchmarks/` and prints progress plus a JSON line you can
|
|
455
483
|
pipe into trend tooling).
|
|
456
484
|
|
|
485
|
+
For real-site and live-provider context-pack benchmarks, use the first-class
|
|
486
|
+
benchmark harness:
|
|
487
|
+
|
|
488
|
+
```bash
|
|
489
|
+
docpull benchmark quick
|
|
490
|
+
docpull benchmark quick --provider auto --max-estimated-cost 0.05
|
|
491
|
+
docpull benchmark quick --provider all --max-estimated-cost 0.10
|
|
492
|
+
docpull benchmark quick --target-set provider-matrix --provider all \
|
|
493
|
+
--max-pages 8 --max-depth 1 --max-search-results 5 --extract-limit 2 \
|
|
494
|
+
--max-estimated-cost 0.10
|
|
495
|
+
docpull benchmark article .bench/runs/<run>/benchmark.report.json
|
|
496
|
+
```
|
|
497
|
+
|
|
498
|
+
`docpull benchmark quick` writes `benchmark.report.json` and
|
|
499
|
+
`benchmark.summary.md` under `.bench/runs/<timestamp>/` by default. The core
|
|
500
|
+
cases measure an LLM-profile crawl plus a cached rerun. Passing `--provider auto`
|
|
501
|
+
runs every locally ready provider. Passing `--provider all` requests Parallel,
|
|
502
|
+
Tavily, and Exa, then records missing keys or optional SDKs in
|
|
503
|
+
`skipped_providers` without failing the benchmark. Live Parallel calls still use
|
|
504
|
+
the local `--max-estimated-cost` guard before any work starts.
|
|
505
|
+
|
|
506
|
+
Use `--target-set tool-docs` to run a cross-pull matrix across the Parallel,
|
|
507
|
+
Exa, Tavily, Raindrop, and DocPull documentation sites. Use `--target-set provider-matrix`
|
|
508
|
+
to add three low-cap adversarial public targets for JS-heavy docs, noisy
|
|
509
|
+
archived navigation, and freshness-sensitive pricing. Matrix runs skip the
|
|
510
|
+
cached core pass by default so the headline grid stays provider x target; pass
|
|
511
|
+
`--cached-pass` to force cache measurement across the matrix.
|
|
512
|
+
|
|
513
|
+
The compatibility flags `--parallel`, `--tavily`, and `--exa` still work as
|
|
514
|
+
provider aliases. Tavily Search + Extract and Exa Search-with-contents are
|
|
515
|
+
normalized into the same local
|
|
516
|
+
`documents.ndjson`, `corpus.manifest.json`, `sources.md`, provider `*.pack.json`,
|
|
517
|
+
`pack.score.json`, and `source.scores.json` artifacts as the core and Parallel
|
|
518
|
+
cases. The report keeps the legacy pack score and adds weighted benchmark
|
|
519
|
+
sub-scores for coverage, cleanliness, source fidelity, freshness, and density.
|
|
520
|
+
Keys can be exported as `PARALLEL_API_KEY`, `TAVILY_API_KEY`, and `EXA_API_KEY`
|
|
521
|
+
or stored together in `~/.config/docpull/secrets.env`; docpull does not write
|
|
522
|
+
keys into benchmark artifacts.
|
|
523
|
+
|
|
524
|
+
Tavily usage is credit-based, so set `TAVILY_CREDIT_USD` or pass
|
|
525
|
+
`--tavily-credit-usd` to convert credits into estimated dollars for cost
|
|
526
|
+
comparisons. Without that value, Tavily credits are still recorded in
|
|
527
|
+
`cost_units` but excluded from normalized USD totals.
|
|
528
|
+
|
|
529
|
+
To trace the benchmark in Raindrop for a publishable observability/eval loop,
|
|
530
|
+
install the optional SDK and pass `--trace raindrop`:
|
|
531
|
+
|
|
532
|
+
```bash
|
|
533
|
+
pip install 'docpull[parallel,observability]'
|
|
534
|
+
export PARALLEL_API_KEY="<your-parallel-api-key>"
|
|
535
|
+
export TAVILY_API_KEY="<your-tavily-api-key>"
|
|
536
|
+
export TAVILY_CREDIT_USD="<account-credit-value>"
|
|
537
|
+
export EXA_API_KEY="<your-exa-api-key>"
|
|
538
|
+
export RAINDROP_WRITE_KEY="<your-raindrop-write-key>"
|
|
539
|
+
docpull benchmark quick --target-set provider-matrix --provider all --trace raindrop \
|
|
540
|
+
--max-pages 8 --max-depth 1 --max-search-results 5 --extract-limit 2 \
|
|
541
|
+
--max-estimated-cost 0.10
|
|
542
|
+
docpull benchmark article .bench/runs/<run>/benchmark.report.json
|
|
543
|
+
```
|
|
544
|
+
|
|
545
|
+
Raindrop traces are metadata-only by default: docpull records timings, counts,
|
|
546
|
+
scores, costs, selected URLs, provider, workflow, target, prompt, settings, and
|
|
547
|
+
artifact paths, but does not send scraped page content unless a future caller
|
|
548
|
+
explicitly adds that behavior. `RAINDROP_WRITE_KEY` can be exported or stored in
|
|
549
|
+
the same `~/.config/docpull/secrets.env` file as the provider keys.
|
|
550
|
+
|
|
551
|
+
When Raindrop tracing is enabled, DocPull also emits Raindrop signals for
|
|
552
|
+
attention-worthy benchmark cells: failed cases, low scores, slow cases,
|
|
553
|
+
high-cost cells, and score-dimension warnings. The benchmark report stores the
|
|
554
|
+
Raindrop event id and signal counts so traced runs can be audited later.
|
|
555
|
+
|
|
457
556
|
## Troubleshooting
|
|
458
557
|
|
|
459
558
|
```bash
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "docpull"
|
|
7
|
-
version = "4.
|
|
7
|
+
version = "4.2.0"
|
|
8
8
|
dynamic = []
|
|
9
9
|
description = "Pull documentation from the web and convert to clean markdown"
|
|
10
10
|
readme = {file = "README.md", content-type = "text/markdown"}
|
|
@@ -97,6 +97,9 @@ mcp = [
|
|
|
97
97
|
parallel = [
|
|
98
98
|
"parallel-web>=1.0.1",
|
|
99
99
|
]
|
|
100
|
+
observability = [
|
|
101
|
+
"raindrop-ai>=0.0.50",
|
|
102
|
+
]
|
|
100
103
|
llm = [
|
|
101
104
|
"tiktoken>=0.7.0",
|
|
102
105
|
]
|
|
@@ -110,6 +113,7 @@ all = [
|
|
|
110
113
|
"python-multipart>=0.0.27",
|
|
111
114
|
"starlette>=1.0.1",
|
|
112
115
|
"parallel-web>=1.0.1",
|
|
116
|
+
"raindrop-ai>=0.0.50",
|
|
113
117
|
]
|
|
114
118
|
dev = [
|
|
115
119
|
"pytest>=7.0.0",
|