docpull 4.0.1__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.
Files changed (110) hide show
  1. {docpull-4.0.1/src/docpull.egg-info → docpull-4.2.0}/PKG-INFO +266 -21
  2. docpull-4.2.0/README.md +575 -0
  3. {docpull-4.0.1 → docpull-4.2.0}/pyproject.toml +10 -2
  4. {docpull-4.0.1 → docpull-4.2.0}/src/docpull/__init__.py +1 -1
  5. docpull-4.2.0/src/docpull/benchmark.py +2922 -0
  6. {docpull-4.0.1 → docpull-4.2.0}/src/docpull/cli.py +20 -0
  7. {docpull-4.0.1 → docpull-4.2.0}/src/docpull/conversion/chunking.py +56 -23
  8. {docpull-4.0.1 → docpull-4.2.0}/src/docpull/conversion/extractor.py +129 -1
  9. docpull-4.2.0/src/docpull/conversion/markdown.py +516 -0
  10. {docpull-4.0.1 → docpull-4.2.0}/src/docpull/conversion/special_cases.py +108 -3
  11. {docpull-4.0.1 → docpull-4.2.0}/src/docpull/core/fetcher.py +6 -4
  12. docpull-4.2.0/src/docpull/fixtures/__init__.py +1 -0
  13. docpull-4.2.0/src/docpull/fixtures/parallel-search-extract.json +103 -0
  14. {docpull-4.0.1 → docpull-4.2.0}/src/docpull/http/client.py +80 -11
  15. {docpull-4.0.1 → docpull-4.2.0}/src/docpull/mcp/server.py +299 -1
  16. {docpull-4.0.1 → docpull-4.2.0}/src/docpull/mcp/sources.py +1 -0
  17. docpull-4.2.0/src/docpull/pack_tools.py +517 -0
  18. docpull-4.2.0/src/docpull/parallel_workflows.py +5740 -0
  19. {docpull-4.0.1 → docpull-4.2.0}/src/docpull/pipeline/steps/convert.py +7 -0
  20. {docpull-4.0.1 → docpull-4.2.0}/src/docpull/pipeline/steps/fetch.py +8 -24
  21. {docpull-4.0.1 → docpull-4.2.0}/src/docpull/pipeline/steps/save_ndjson.py +56 -0
  22. docpull-4.2.0/src/docpull/provider_cli.py +426 -0
  23. docpull-4.2.0/src/docpull/provider_keys.py +217 -0
  24. docpull-4.2.0/src/docpull/security/__init__.py +13 -0
  25. docpull-4.2.0/src/docpull/security/download_policy.py +339 -0
  26. docpull-4.2.0/src/docpull/source_scoring.py +98 -0
  27. {docpull-4.0.1 → docpull-4.2.0/src/docpull.egg-info}/PKG-INFO +266 -21
  28. {docpull-4.0.1 → docpull-4.2.0}/src/docpull.egg-info/SOURCES.txt +13 -0
  29. {docpull-4.0.1 → docpull-4.2.0}/src/docpull.egg-info/requires.txt +8 -0
  30. docpull-4.2.0/tests/test_benchmark.py +645 -0
  31. {docpull-4.0.1 → docpull-4.2.0}/tests/test_chunking.py +24 -0
  32. {docpull-4.0.1 → docpull-4.2.0}/tests/test_cli.py +30 -0
  33. {docpull-4.0.1 → docpull-4.2.0}/tests/test_conversion.py +88 -0
  34. {docpull-4.0.1 → docpull-4.2.0}/tests/test_convert_step_new.py +25 -0
  35. {docpull-4.0.1 → docpull-4.2.0}/tests/test_mcp_server.py +13 -0
  36. {docpull-4.0.1 → docpull-4.2.0}/tests/test_mcp_tools.py +1 -0
  37. docpull-4.2.0/tests/test_pack_tools.py +283 -0
  38. docpull-4.2.0/tests/test_parallel_workflows.py +2246 -0
  39. docpull-4.2.0/tests/test_provider_cli.py +85 -0
  40. {docpull-4.0.1 → docpull-4.2.0}/tests/test_save_ndjson.py +8 -0
  41. {docpull-4.0.1 → docpull-4.2.0}/tests/test_security_hardening.py +237 -0
  42. {docpull-4.0.1 → docpull-4.2.0}/tests/test_special_cases.py +99 -1
  43. docpull-4.0.1/README.md +0 -336
  44. docpull-4.0.1/src/docpull/conversion/markdown.py +0 -280
  45. docpull-4.0.1/src/docpull/security/__init__.py +0 -6
  46. {docpull-4.0.1 → docpull-4.2.0}/LICENSE +0 -0
  47. {docpull-4.0.1 → docpull-4.2.0}/setup.cfg +0 -0
  48. {docpull-4.0.1 → docpull-4.2.0}/src/docpull/__main__.py +0 -0
  49. {docpull-4.0.1 → docpull-4.2.0}/src/docpull/cache/__init__.py +0 -0
  50. {docpull-4.0.1 → docpull-4.2.0}/src/docpull/cache/frontier.py +0 -0
  51. {docpull-4.0.1 → docpull-4.2.0}/src/docpull/cache/manager.py +0 -0
  52. {docpull-4.0.1 → docpull-4.2.0}/src/docpull/cache/streaming_dedup.py +0 -0
  53. {docpull-4.0.1 → docpull-4.2.0}/src/docpull/conversion/__init__.py +0 -0
  54. {docpull-4.0.1 → docpull-4.2.0}/src/docpull/conversion/protocols.py +0 -0
  55. {docpull-4.0.1 → docpull-4.2.0}/src/docpull/conversion/trafilatura_extractor.py +0 -0
  56. {docpull-4.0.1 → docpull-4.2.0}/src/docpull/core/__init__.py +0 -0
  57. {docpull-4.0.1 → docpull-4.2.0}/src/docpull/discovery/__init__.py +0 -0
  58. {docpull-4.0.1 → docpull-4.2.0}/src/docpull/discovery/_fetch.py +0 -0
  59. {docpull-4.0.1 → docpull-4.2.0}/src/docpull/discovery/composite.py +0 -0
  60. {docpull-4.0.1 → docpull-4.2.0}/src/docpull/discovery/crawler.py +0 -0
  61. {docpull-4.0.1 → docpull-4.2.0}/src/docpull/discovery/filters.py +0 -0
  62. {docpull-4.0.1 → docpull-4.2.0}/src/docpull/discovery/link_extractors/__init__.py +0 -0
  63. {docpull-4.0.1 → docpull-4.2.0}/src/docpull/discovery/link_extractors/enhanced.py +0 -0
  64. {docpull-4.0.1 → docpull-4.2.0}/src/docpull/discovery/link_extractors/protocols.py +0 -0
  65. {docpull-4.0.1 → docpull-4.2.0}/src/docpull/discovery/link_extractors/static.py +0 -0
  66. {docpull-4.0.1 → docpull-4.2.0}/src/docpull/discovery/protocols.py +0 -0
  67. {docpull-4.0.1 → docpull-4.2.0}/src/docpull/discovery/sitemap.py +0 -0
  68. {docpull-4.0.1 → docpull-4.2.0}/src/docpull/doctor.py +0 -0
  69. {docpull-4.0.1 → docpull-4.2.0}/src/docpull/http/__init__.py +0 -0
  70. {docpull-4.0.1 → docpull-4.2.0}/src/docpull/http/protocols.py +0 -0
  71. {docpull-4.0.1 → docpull-4.2.0}/src/docpull/http/rate_limiter.py +0 -0
  72. {docpull-4.0.1 → docpull-4.2.0}/src/docpull/mcp/__init__.py +0 -0
  73. {docpull-4.0.1 → docpull-4.2.0}/src/docpull/mcp/tools.py +0 -0
  74. {docpull-4.0.1 → docpull-4.2.0}/src/docpull/metadata_extractor.py +0 -0
  75. {docpull-4.0.1 → docpull-4.2.0}/src/docpull/models/__init__.py +0 -0
  76. {docpull-4.0.1 → docpull-4.2.0}/src/docpull/models/config.py +0 -0
  77. {docpull-4.0.1 → docpull-4.2.0}/src/docpull/models/document.py +0 -0
  78. {docpull-4.0.1 → docpull-4.2.0}/src/docpull/models/events.py +0 -0
  79. {docpull-4.0.1 → docpull-4.2.0}/src/docpull/models/profiles.py +0 -0
  80. {docpull-4.0.1 → docpull-4.2.0}/src/docpull/models/run.py +0 -0
  81. {docpull-4.0.1 → docpull-4.2.0}/src/docpull/pipeline/__init__.py +0 -0
  82. {docpull-4.0.1 → docpull-4.2.0}/src/docpull/pipeline/base.py +0 -0
  83. {docpull-4.0.1 → docpull-4.2.0}/src/docpull/pipeline/manifest.py +0 -0
  84. {docpull-4.0.1 → docpull-4.2.0}/src/docpull/pipeline/steps/__init__.py +0 -0
  85. {docpull-4.0.1 → docpull-4.2.0}/src/docpull/pipeline/steps/chunk.py +0 -0
  86. {docpull-4.0.1 → docpull-4.2.0}/src/docpull/pipeline/steps/dedup.py +0 -0
  87. {docpull-4.0.1 → docpull-4.2.0}/src/docpull/pipeline/steps/metadata.py +0 -0
  88. {docpull-4.0.1 → docpull-4.2.0}/src/docpull/pipeline/steps/save.py +0 -0
  89. {docpull-4.0.1 → docpull-4.2.0}/src/docpull/pipeline/steps/save_json.py +0 -0
  90. {docpull-4.0.1 → docpull-4.2.0}/src/docpull/pipeline/steps/save_sqlite.py +0 -0
  91. {docpull-4.0.1 → docpull-4.2.0}/src/docpull/pipeline/steps/validate.py +0 -0
  92. {docpull-4.0.1 → docpull-4.2.0}/src/docpull/py.typed +0 -0
  93. {docpull-4.0.1 → docpull-4.2.0}/src/docpull/security/robots.py +0 -0
  94. {docpull-4.0.1 → docpull-4.2.0}/src/docpull/security/url_validator.py +0 -0
  95. {docpull-4.0.1 → docpull-4.2.0}/src/docpull/time_utils.py +0 -0
  96. {docpull-4.0.1 → docpull-4.2.0}/src/docpull.egg-info/dependency_links.txt +0 -0
  97. {docpull-4.0.1 → docpull-4.2.0}/src/docpull.egg-info/entry_points.txt +0 -0
  98. {docpull-4.0.1 → docpull-4.2.0}/src/docpull.egg-info/top_level.txt +0 -0
  99. {docpull-4.0.1 → docpull-4.2.0}/tests/test_cache_conditional_get.py +0 -0
  100. {docpull-4.0.1 → docpull-4.2.0}/tests/test_ci_policy.py +0 -0
  101. {docpull-4.0.1 → docpull-4.2.0}/tests/test_discovery.py +0 -0
  102. {docpull-4.0.1 → docpull-4.2.0}/tests/test_frontier_resume.py +0 -0
  103. {docpull-4.0.1 → docpull-4.2.0}/tests/test_integration.py +0 -0
  104. {docpull-4.0.1 → docpull-4.2.0}/tests/test_link_extractors.py +0 -0
  105. {docpull-4.0.1 → docpull-4.2.0}/tests/test_naming.py +0 -0
  106. {docpull-4.0.1 → docpull-4.2.0}/tests/test_outputs_e2e.py +0 -0
  107. {docpull-4.0.1 → docpull-4.2.0}/tests/test_pipeline.py +0 -0
  108. {docpull-4.0.1 → docpull-4.2.0}/tests/test_real_site_regressions.py +0 -0
  109. {docpull-4.0.1 → docpull-4.2.0}/tests/test_save_sqlite.py +0 -0
  110. {docpull-4.0.1 → 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.0.1
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>
@@ -62,6 +62,10 @@ Requires-Dist: mcp>=1.0.0; extra == "mcp"
62
62
  Requires-Dist: pyjwt>=2.13.0; extra == "mcp"
63
63
  Requires-Dist: python-multipart>=0.0.27; extra == "mcp"
64
64
  Requires-Dist: starlette>=1.0.1; extra == "mcp"
65
+ Provides-Extra: parallel
66
+ Requires-Dist: parallel-web>=1.0.1; extra == "parallel"
67
+ Provides-Extra: observability
68
+ Requires-Dist: raindrop-ai>=0.0.50; extra == "observability"
65
69
  Provides-Extra: llm
66
70
  Requires-Dist: tiktoken>=0.7.0; extra == "llm"
67
71
  Provides-Extra: all
@@ -73,6 +77,8 @@ Requires-Dist: mcp>=1.0.0; extra == "all"
73
77
  Requires-Dist: pyjwt>=2.13.0; extra == "all"
74
78
  Requires-Dist: python-multipart>=0.0.27; extra == "all"
75
79
  Requires-Dist: starlette>=1.0.1; extra == "all"
80
+ Requires-Dist: parallel-web>=1.0.1; extra == "all"
81
+ Requires-Dist: raindrop-ai>=0.0.50; extra == "all"
76
82
  Provides-Extra: dev
77
83
  Requires-Dist: pytest>=7.0.0; extra == "dev"
78
84
  Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
@@ -90,7 +96,7 @@ Dynamic: license-file
90
96
 
91
97
  # docpull
92
98
 
93
- **Security-hardened, browser-free crawler that turns static documentation sites into clean, AI-ready Markdown — fast.**
99
+ **Security-hardened, browser-free web scraper and crawler that turns server-rendered pages into clean, AI-ready Markdown — fast.**
94
100
 
95
101
  [![Python 3.10+](https://img.shields.io/badge/python-3.10+-blue.svg)](https://www.python.org/downloads/)
96
102
  [![PyPI version](https://badge.fury.io/py/docpull.svg)](https://badge.fury.io/py/docpull)
@@ -103,15 +109,16 @@ Dynamic: license-file
103
109
  </a>
104
110
  </p>
105
111
 
106
- docpull uses async HTTP (not Playwright) to fetch server-rendered pages,
107
- extracts main content, and writes clean Markdown with source-URL frontmatter
108
- in seconds, with a small install footprint. It won't render JavaScript, but for
109
- the large class of docs that don't need it (API references, Python/Go stdlib,
110
- most dev-tool docs, OpenAPI specs, Next.js and Docusaurus builds), it is a
111
- fast, auditable, sandbox-friendly way to pipe documentation into an LLM context,
112
- a RAG index, or an offline archive. SSRF, XXE, DNS-rebinding, and
113
- CRLF-injection protections are on by default a necessity when an AI agent
114
- is choosing the URLs.
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.
115
122
 
116
123
  ## Install
117
124
 
@@ -122,22 +129,24 @@ pip install docpull
122
129
  pip install 'docpull[llm]' # tiktoken for token-accurate chunking
123
130
  pip install 'docpull[trafilatura]' # alternative extractor for noisy pages
124
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
125
134
  pip install 'docpull[all]' # everything above
126
135
  ```
127
136
 
128
137
  ## Quick start
129
138
 
130
139
  ```bash
131
- # Crawl and save Markdown
140
+ # Scrape a page graph and save Markdown
132
141
  docpull https://docs.example.com
133
142
 
134
- # One page, no crawl — the fast path for agents
143
+ # Scrape one page, no crawl — the fast path for agents
135
144
  docpull https://docs.example.com/guide --single
136
145
 
137
146
  # LLM-ready NDJSON with 4k-token chunks streamed to stdout
138
147
  docpull https://docs.example.com --profile llm --stream | jq .
139
148
 
140
- # Mirror a site for offline use
149
+ # Mirror scraped content for offline use
141
150
  docpull https://docs.example.com --profile mirror --cache
142
151
  ```
143
152
 
@@ -220,6 +229,166 @@ docpull https://site.com --profile mirror # Full archive, polite, cached, hier
220
229
  docpull https://site.com --profile quick # Sampling: 50 pages, depth 2.
221
230
  ```
222
231
 
232
+ ## Parallel context packs
233
+
234
+ `docpull[parallel]` adds an optional source-discovery and research layer on top
235
+ of Parallel web intelligence APIs. Use the core crawler when you already know
236
+ the docs URL. Use Parallel packs when an agent needs current web sources found,
237
+ extracted, researched, organized, and loaded as durable local context.
238
+
239
+ Parallel handles live Search, Extract, Task, Entity Search, FindAll, TaskGroup,
240
+ and Monitor calls. docpull turns those results into local, inspectable packs
241
+ with Markdown, NDJSON, manifests, hashes, source indexes, workflow metadata, and
242
+ an `AGENT_CONTEXT.md` load plan so agents do not need to reverse-engineer raw API
243
+ responses before using the pack.
244
+
245
+ Live calls use your own Parallel API key. docpull never proxies requests through
246
+ a shared account or writes the key into pack artifacts. For normal PyPI installs,
247
+ `docpull parallel init` stores the key in a local secrets file with restricted
248
+ permissions so it works across shells and agent sessions. Pack artifacts can
249
+ still contain source content, workflow inputs/outputs, selected URLs, and
250
+ metadata, so treat them as research data.
251
+
252
+ ```bash
253
+ pip install 'docpull[parallel]'
254
+
255
+ # Recommended local setup. Prompts securely and writes ~/.config/docpull/secrets.env.
256
+ docpull parallel init
257
+ docpull parallel auth --json
258
+
259
+ # Optional project-local setup for agent worktrees. Writes .env.local and
260
+ # adds it to .gitignore.
261
+ docpull parallel init --project
262
+
263
+ # CI can still use a normal environment secret instead.
264
+ export PARALLEL_API_KEY="<your-parallel-api-key>"
265
+
266
+ docpull parallel context-pack "Compare AI web-search APIs for agents" \
267
+ --query "AI web search API" \
268
+ --query "agent web extraction API" \
269
+ --include-domain parallel.ai \
270
+ --exclude-domain onparallel.com \
271
+ --output-dir ./packs/ai-web-search \
272
+ --mode advanced \
273
+ --extract-limit 8 \
274
+ --max-estimated-cost 0.05 \
275
+ --task-brief
276
+ ```
277
+
278
+ `docpull parallel auth` checks local SDK/key presence without printing the key
279
+ value and reports the key source (`env`, `project_env`, `user_config`, or
280
+ `missing`). It does not make a live Parallel call or prove the key is valid. Use
281
+ `--json` when an agent or CI job needs machine-readable configuration status.
282
+
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:
309
+
310
+ - `AGENT_CONTEXT.md` — agent load plan with source order, pack signals, warnings, errors, and artifact map.
311
+ - `documents.ndjson` — chunked records for agents and RAG pipelines.
312
+ - `corpus.manifest.json` — stable docpull record manifest.
313
+ - `parallel.pack.json` — Parallel workflow metadata, selected URLs, errors, IDs, and usage.
314
+ - `sources.md` and `sources/*.md` — human-readable sources and extracted Markdown.
315
+ - `brief.md` when `--task-brief` is enabled.
316
+
317
+ That makes the Parallel layer more than a pass-through wrapper: Parallel finds
318
+ and enriches live web sources, while docpull normalizes them into a repeatable
319
+ context pack that can be scored, diffed, audited, and loaded offline.
320
+
321
+ For planning without spending credits, use `--dry-run`. For repeatable packs,
322
+ use `--include-domain`, `--exclude-domain`, `--after-date`, and `--max-search-results`
323
+ to pin the source policy that appears later in `parallel.pack.json`.
324
+ Use `--fetch-max-age-seconds`, `--fetch-timeout-seconds`,
325
+ `--disable-cache-fallback`, `--excerpt-chars-per-result`, and `--location`
326
+ when you need Parallel's freshness, excerpt-size, and geo controls.
327
+
328
+ Offline fixtures are supported for demos and tests without a Parallel account:
329
+
330
+ ```bash
331
+ docpull parallel demo --output-dir ./packs/demo
332
+ ```
333
+
334
+ When working from the source checkout, the same fixture can also be imported
335
+ directly with `docpull parallel import ./docs/examples/parallel-search-extract.json`.
336
+
337
+ The core workflow is Search + Extract + optional Task. Broader pack workflows
338
+ cover Search-only snapshots, docs discovery plans, Extract-known-URL packs,
339
+ core-fetch-with-Parallel-fallback packs, diff briefs, Task lifecycle snapshots,
340
+ Entity Search, FindAll lifecycle actions, TaskGroup batches, Monitor
341
+ metadata/events, API specs, and local source scoring.
342
+ See [docs/parallel.md](docs/parallel.md) for the Parallel product cross-reference
343
+ and the boundary between docpull, Parallel MCP, and planned workflows.
344
+
345
+ Additional Parallel-backed artifact commands:
346
+
347
+ ```bash
348
+ # Readiness and API-key setup checks.
349
+ docpull parallel auth
350
+ docpull parallel init
351
+ docpull parallel init --project
352
+
353
+ # Search + Extract + optional Task brief.
354
+ docpull parallel context-pack "Compare AI web-search APIs for agents" \
355
+ --query "AI web search API" \
356
+ --include-domain parallel.ai \
357
+ --dry-run
358
+
359
+ # Search-only, docs-discovery, known-URL extract, fallback, and structured Task packs.
360
+ docpull parallel search-pack "Parallel Search API docs" --query "Parallel Search API" --dry-run
361
+ docpull parallel discover-docs "Find Parallel API docs" \
362
+ --query "Parallel Search API docs" \
363
+ --include-domain docs.parallel.ai \
364
+ --dry-run
365
+ docpull parallel extract-pack https://docs.parallel.ai/api-reference/search/search --dry-run
366
+ docpull parallel fallback-pack https://docs.parallel.ai/api-reference/search/search --dry-run
367
+ docpull parallel task-pack "Research Parallel Search API" \
368
+ --source-include-domain docs.parallel.ai \
369
+ --output-schema-json '{"type":"object","properties":{"summary":{"type":"string"}},"required":["summary"]}' \
370
+ --dry-run
371
+
372
+ # Entity dossiers, FindAll lifecycle, batch enrichment, monitors, and API specs.
373
+ docpull parallel entity-pack "AI developer infrastructure companies" --entity-type companies
374
+ docpull parallel findall-pack "AI developer infrastructure companies" --generator preview --dry-run
375
+ docpull parallel findall-ingest-pack "Find companies with public API docs"
376
+ docpull parallel findall-result-pack findall_123 --output-dir ./packs/findall-result
377
+ docpull parallel taskgroup-pack ./companies.json --prompt-template "Research {company}" --dry-run
378
+ docpull parallel taskgroup-pack ./companies.json --prompt-template "Research {company}" --wait
379
+ docpull parallel monitor-pack create "New vendor pricing changes" --dry-run
380
+ docpull parallel monitor-pack create --type snapshot --task-run-id run_123 --dry-run
381
+ docpull parallel monitor-pack list --status active --output-dir ./packs/monitors
382
+ docpull parallel monitor-pack events monitor_123 --cursor next_cursor --output-dir ./packs/events
383
+ docpull parallel api-pack https://docs.parallel.ai/public-openapi.json --output-dir ./packs/parallel-openapi
384
+ docpull parallel run ./docs/examples/parallel-llms-api-pack.yaml --dry-run
385
+ docpull parallel run ./docs/examples/parallel-openapi-api-pack.yaml --dry-run
386
+ docpull pack score ./packs/parallel-openapi
387
+ docpull pack sources ./packs/parallel-openapi --require-domain docs.parallel.ai
388
+ docpull pack diff ./packs/old ./packs/new --markdown ./packs/changes.md
389
+ docpull parallel diff-brief ./packs/old ./packs/new --dry-run
390
+ ```
391
+
223
392
  ## MCP server
224
393
 
225
394
  docpull ships an MCP (Model Context Protocol) server so AI agents can call it
@@ -271,17 +440,21 @@ pip install 'docpull[mcp]'
271
440
 
272
441
  See [plugin/README.md](plugin/README.md) for details.
273
442
 
274
- Tools exposed (8 total — read tools advertise `readOnlyHint` so hosts that auto-approve safe tools won't prompt):
443
+ Tools exposed (12 total — read tools advertise `readOnlyHint` so hosts that auto-approve safe tools won't prompt):
275
444
 
276
445
  Read:
277
446
  - `fetch_url(url, max_tokens?)` — one-shot fetch, no crawl. HTTPS-only, SSRF-validated.
278
- - `list_sources(category?)` — show available aliases (react, nextjs, fastapi, …)
447
+ - `list_sources(category?)` — show available aliases (react, nextjs, parallel, fastapi, …)
279
448
  - `list_indexed()` — what has been fetched locally, with last-fetched age
280
449
  - `grep_docs(pattern, library?, limit?, context?)` — regex search across fetched Markdown (length-capped + wall-clock budgeted to mitigate ReDoS)
281
450
  - `read_doc(library, path, line_start?, line_end?)` — read a specific cached file, optionally line-sliced
451
+ - `pack_score(pack_dir, required_domains?)` — score a local context pack for agent readiness.
452
+ - `pack_diff(old_pack_dir, new_pack_dir)` — compare pack URL sets and content hashes.
282
453
 
283
454
  Write:
284
455
  - `ensure_docs(source, force?, profile?)` — fetch a named library (cached 7 days). Forwards progress to clients that supply a `progressToken`.
456
+ - `parallel_context_pack(...)` — build or dry-run a Parallel Search + Extract pack without shelling out.
457
+ - `parallel_api_pack(source, kind?, output_dir?)` — build a pack from `llms.txt` or OpenAPI.
285
458
  - `add_source(name, url, description?, category?, max_pages?, force?)` — register a user alias (HTTPS-only, atomic write to `sources.yaml`).
286
459
  - `remove_source(name, delete_cache?)` — drop a user alias and (optionally) its cached docs.
287
460
 
@@ -388,15 +561,16 @@ End-to-end numbers from `tests/benchmarks/test_10k_pages.py` against a
388
561
  synthetic 10,000-page localhost site (RAG profile, `max_concurrent=50`,
389
562
  `per_host_concurrent=50`, HTTP keep-alive, 5% injected duplicate content).
390
563
  The benchmark emits progress every 1,000 pages plus a final JSON report for
391
- trend tooling.
564
+ trend tooling. Wall time varies by machine; the value below is the latest
565
+ local audit run.
392
566
 
393
567
  | Metric | Value |
394
568
  |---|---|
395
- | Total wall time | ~333 s |
569
+ | Total wall time | ~309 s |
396
570
  | Pages fetched / skipped / failed | 9,501 / 499 / 0 |
397
- | Time to first saved page | ~130 ms |
398
- | Per-page latency p50 / p95 / p99 | ~0 / 166 / 232 ms |
399
- | Peak RSS delta from baseline | ~94 MB |
571
+ | Time to first saved page | ~119 ms |
572
+ | Per-page latency p50 / p95 / p99 | ~0 / 168 / 271 ms |
573
+ | Peak RSS delta from baseline | ~93 MB |
400
574
  | Cache manifest size on disk | ~8.9 MB |
401
575
  | Duplicates detected (5% injected) | 499 / 500 |
402
576
 
@@ -404,6 +578,77 @@ Reproduce with `make benchmark` (requires `aiohttp`; runs the gated
404
578
  benchmark in `tests/benchmarks/` and prints progress plus a JSON line you can
405
579
  pipe into trend tooling).
406
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
+
407
652
  ## Troubleshooting
408
653
 
409
654
  ```bash