docpull 6.0.0__tar.gz → 6.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {docpull-6.0.0/src/docpull.egg-info → docpull-6.1.0}/PKG-INFO +43 -18
- {docpull-6.0.0 → docpull-6.1.0}/README.md +31 -12
- {docpull-6.0.0 → docpull-6.1.0}/pyproject.toml +13 -6
- {docpull-6.0.0 → docpull-6.1.0}/src/docpull/__init__.py +2 -2
- {docpull-6.0.0 → docpull-6.1.0}/src/docpull/benchmark.py +42 -4
- {docpull-6.0.0 → docpull-6.1.0}/src/docpull/cli.py +35 -1
- {docpull-6.0.0 → docpull-6.1.0}/src/docpull/conversion/special_cases.py +148 -8
- {docpull-6.0.0 → docpull-6.1.0}/src/docpull/conversion/trafilatura_extractor.py +1 -1
- {docpull-6.0.0 → docpull-6.1.0}/src/docpull/core/fetcher.py +12 -0
- docpull-6.1.0/src/docpull/document_parse.py +1204 -0
- docpull-6.1.0/src/docpull/document_worker.py +137 -0
- {docpull-6.0.0 → docpull-6.1.0}/src/docpull/mcp/server.py +59 -4
- {docpull-6.0.0 → docpull-6.1.0}/src/docpull/mcp/tools.py +17 -3
- {docpull-6.0.0 → docpull-6.1.0}/src/docpull/models/config.py +22 -0
- {docpull-6.0.0 → docpull-6.1.0}/src/docpull/pipeline/steps/convert.py +76 -26
- {docpull-6.0.0 → docpull-6.1.0}/src/docpull/pipeline/steps/fetch.py +6 -1
- {docpull-6.0.0 → docpull-6.1.0}/src/docpull/pipeline/steps/metadata.py +4 -0
- {docpull-6.0.0 → docpull-6.1.0}/src/docpull/security/download_policy.py +24 -3
- {docpull-6.0.0 → docpull-6.1.0/src/docpull.egg-info}/PKG-INFO +43 -18
- {docpull-6.0.0 → docpull-6.1.0}/src/docpull.egg-info/SOURCES.txt +2 -0
- {docpull-6.0.0 → docpull-6.1.0}/src/docpull.egg-info/requires.txt +10 -3
- {docpull-6.0.0 → docpull-6.1.0}/tests/test_benchmark.py +2 -0
- {docpull-6.0.0 → docpull-6.1.0}/tests/test_ci_policy.py +46 -2
- {docpull-6.0.0 → docpull-6.1.0}/tests/test_cli.py +12 -0
- {docpull-6.0.0 → docpull-6.1.0}/tests/test_convert_step_new.py +75 -0
- docpull-6.1.0/tests/test_document_parse.py +634 -0
- {docpull-6.0.0 → docpull-6.1.0}/tests/test_mcp_server.py +9 -0
- {docpull-6.0.0 → docpull-6.1.0}/tests/test_release_a_plus_check_script.py +18 -0
- docpull-6.1.0/tests/test_reproducible_release_build.py +179 -0
- {docpull-6.0.0 → docpull-6.1.0}/tests/test_security_hardening.py +40 -0
- {docpull-6.0.0 → docpull-6.1.0}/tests/test_special_cases.py +142 -0
- {docpull-6.0.0 → docpull-6.1.0}/tests/test_surface_contract.py +3 -0
- docpull-6.0.0/src/docpull/document_parse.py +0 -643
- docpull-6.0.0/tests/test_document_parse.py +0 -133
- {docpull-6.0.0 → docpull-6.1.0}/LICENSE +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/setup.cfg +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/src/docpull/__main__.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/src/docpull/accounting.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/src/docpull/agent_publish.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/src/docpull/auth_cli.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/src/docpull/basis.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/src/docpull/cache/__init__.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/src/docpull/cache/frontier.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/src/docpull/cache/manager.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/src/docpull/cache/streaming_dedup.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/src/docpull/context_aliases.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/src/docpull/context_ci.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/src/docpull/context_packs/__init__.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/src/docpull/context_packs/_legacy_cli.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/src/docpull/context_packs/brand.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/src/docpull/context_packs/cli.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/src/docpull/context_packs/common.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/src/docpull/context_packs/dataset.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/src/docpull/context_packs/feed.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/src/docpull/context_packs/openapi.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/src/docpull/context_packs/package.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/src/docpull/context_packs/paper.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/src/docpull/context_packs/product.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/src/docpull/context_packs/repo.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/src/docpull/context_packs/schema_extract.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/src/docpull/context_packs/search.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/src/docpull/context_packs/standards.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/src/docpull/context_packs/styleguide.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/src/docpull/context_packs/transcript.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/src/docpull/context_packs/typed.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/src/docpull/context_packs/typed_models.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/src/docpull/context_packs/visuals.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/src/docpull/context_packs/wiki.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/src/docpull/conversion/__init__.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/src/docpull/conversion/article_cleanup.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/src/docpull/conversion/chunking.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/src/docpull/conversion/ensemble.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/src/docpull/conversion/extractor.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/src/docpull/conversion/filings.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/src/docpull/conversion/markdown.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/src/docpull/conversion/protocols.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/src/docpull/core/__init__.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/src/docpull/discovery/__init__.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/src/docpull/discovery/_fetch.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/src/docpull/discovery/composite.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/src/docpull/discovery/contracts.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/src/docpull/discovery/crawler.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/src/docpull/discovery/filters.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/src/docpull/discovery/link_extractors/__init__.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/src/docpull/discovery/link_extractors/enhanced.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/src/docpull/discovery/link_extractors/protocols.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/src/docpull/discovery/link_extractors/static.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/src/docpull/discovery/protocols.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/src/docpull/discovery/sitemap.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/src/docpull/discovery_cli.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/src/docpull/doctor.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/src/docpull/eval_grade.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/src/docpull/evidence_pack.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/src/docpull/exports.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/src/docpull/fixtures/__init__.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/src/docpull/fixtures/parallel-search-extract.json +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/src/docpull/free_core.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/src/docpull/free_core_smoke.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/src/docpull/graph.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/src/docpull/hosted.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/src/docpull/http/__init__.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/src/docpull/http/client.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/src/docpull/http/protocols.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/src/docpull/http/rate_limiter.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/src/docpull/judge.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/src/docpull/local_workflows.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/src/docpull/mcp/__init__.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/src/docpull/mcp/sources.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/src/docpull/metadata_extractor.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/src/docpull/models/__init__.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/src/docpull/models/document.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/src/docpull/models/events.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/src/docpull/models/profiles.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/src/docpull/models/run.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/src/docpull/monitor.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/src/docpull/output_contract.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/src/docpull/pack_reader.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/src/docpull/pack_tools.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/src/docpull/parallel_workflows.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/src/docpull/parity.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/src/docpull/parity_cli.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/src/docpull/passk.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/src/docpull/pipeline/__init__.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/src/docpull/pipeline/base.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/src/docpull/pipeline/manifest.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/src/docpull/pipeline/steps/__init__.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/src/docpull/pipeline/steps/chunk.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/src/docpull/pipeline/steps/dedup.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/src/docpull/pipeline/steps/render.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/src/docpull/pipeline/steps/save.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/src/docpull/pipeline/steps/save_json.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/src/docpull/pipeline/steps/save_ndjson.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/src/docpull/pipeline/steps/save_okf.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/src/docpull/pipeline/steps/save_sqlite.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/src/docpull/pipeline/steps/validate.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/src/docpull/policy.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/src/docpull/policy_cli.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/src/docpull/project.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/src/docpull/provider_adapters.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/src/docpull/provider_capabilities.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/src/docpull/provider_cli.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/src/docpull/provider_keys.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/src/docpull/provider_probes.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/src/docpull/py.typed +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/src/docpull/redaction.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/src/docpull/rendering.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/src/docpull/scraper.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/src/docpull/security/__init__.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/src/docpull/security/robots.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/src/docpull/security/url_validator.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/src/docpull/server.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/src/docpull/share.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/src/docpull/skill_export.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/src/docpull/source_scoring.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/src/docpull/surface.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/src/docpull/time_utils.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/src/docpull.egg-info/dependency_links.txt +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/src/docpull.egg-info/entry_points.txt +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/src/docpull.egg-info/top_level.txt +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/tests/test_accounting.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/tests/test_auth_cli.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/tests/test_basis.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/tests/test_cache_conditional_get.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/tests/test_chunking.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/tests/test_claim_audit_script.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/tests/test_context_ci.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/tests/test_context_packs.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/tests/test_conversion.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/tests/test_discovery.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/tests/test_discovery_contracts.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/tests/test_doctor.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/tests/test_document_record.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/tests/test_evidence_pack.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/tests/test_exports.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/tests/test_extractor_ensemble.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/tests/test_feed_pack.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/tests/test_free_core.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/tests/test_frontier_resume.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/tests/test_graph.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/tests/test_hosted.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/tests/test_integration.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/tests/test_judge.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/tests/test_link_extractors.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/tests/test_live_web_smoke.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/tests/test_local_workflows.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/tests/test_mcp_tools.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/tests/test_metadata_extractor.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/tests/test_monitor.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/tests/test_naming.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/tests/test_openapi_pack.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/tests/test_optional_extras_installed.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/tests/test_output_contract.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/tests/test_outputs_e2e.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/tests/test_pack_server.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/tests/test_pack_tools.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/tests/test_parallel_workflows.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/tests/test_parity_workflows.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/tests/test_passk.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/tests/test_pipeline.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/tests/test_policy.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/tests/test_policy_cli.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/tests/test_project.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/tests/test_provider_adapters.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/tests/test_provider_cli.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/tests/test_provider_keys.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/tests/test_provider_probes.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/tests/test_public_feature_smoke.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/tests/test_rate_limiter.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/tests/test_real_feature_smoke_script.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/tests/test_real_site_regressions.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/tests/test_rendering.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/tests/test_save_ndjson.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/tests/test_save_sqlite.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/tests/test_share.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/tests/test_source_scoring.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/tests/test_time_utils.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/tests/test_trafilatura_extractor.py +0 -0
- {docpull-6.0.0 → docpull-6.1.0}/tests/test_typed_knowledge_packs.py +0 -0
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: docpull
|
|
3
|
-
Version: 6.
|
|
3
|
+
Version: 6.1.0
|
|
4
4
|
Summary: Declare, sync, diff, and lock context dependencies for AI agents
|
|
5
5
|
Author-email: Zachary Roth <support@raintree.technology>
|
|
6
6
|
Maintainer-email: Raintree Technology <support@raintree.technology>
|
|
7
7
|
License-Expression: MIT
|
|
8
|
-
Project-URL: Homepage, https://
|
|
9
|
-
Project-URL: Documentation, https://
|
|
8
|
+
Project-URL: Homepage, https://github.com/raintree-technology/docpull
|
|
9
|
+
Project-URL: Documentation, https://github.com/raintree-technology/docpull#readme
|
|
10
10
|
Project-URL: Repository, https://github.com/raintree-technology/docpull
|
|
11
11
|
Project-URL: Source Code, https://github.com/raintree-technology/docpull
|
|
12
12
|
Project-URL: Bug Tracker, https://github.com/raintree-technology/docpull/issues
|
|
@@ -71,11 +71,16 @@ Requires-Dist: uvicorn>=0.30.0; extra == "serve"
|
|
|
71
71
|
Provides-Extra: parquet
|
|
72
72
|
Requires-Dist: pyarrow>=16.0.0; extra == "parquet"
|
|
73
73
|
Provides-Extra: markitdown
|
|
74
|
-
Requires-Dist:
|
|
74
|
+
Requires-Dist: pypdf>=6.14.2; extra == "markitdown"
|
|
75
|
+
Requires-Dist: markitdown[docx,pdf]>=0.1.0; extra == "markitdown"
|
|
75
76
|
Provides-Extra: unstructured
|
|
77
|
+
Requires-Dist: pypdf>=6.14.2; extra == "unstructured"
|
|
76
78
|
Requires-Dist: unstructured[all-docs]>=0.16.0; extra == "unstructured"
|
|
79
|
+
Provides-Extra: pdf
|
|
80
|
+
Requires-Dist: pypdf>=6.14.2; extra == "pdf"
|
|
77
81
|
Provides-Extra: parse
|
|
78
|
-
Requires-Dist:
|
|
82
|
+
Requires-Dist: pypdf>=6.14.2; extra == "parse"
|
|
83
|
+
Requires-Dist: markitdown[docx,pdf]>=0.1.0; extra == "parse"
|
|
79
84
|
Requires-Dist: unstructured[all-docs]>=0.16.0; extra == "parse"
|
|
80
85
|
Provides-Extra: presidio
|
|
81
86
|
Requires-Dist: presidio-analyzer>=2.2.0; extra == "presidio"
|
|
@@ -96,7 +101,8 @@ Requires-Dist: python-multipart>=0.0.27; extra == "all"
|
|
|
96
101
|
Requires-Dist: starlette>=1.3.1; extra == "all"
|
|
97
102
|
Requires-Dist: uvicorn>=0.30.0; extra == "all"
|
|
98
103
|
Requires-Dist: pyarrow>=16.0.0; extra == "all"
|
|
99
|
-
Requires-Dist:
|
|
104
|
+
Requires-Dist: pypdf>=6.14.2; extra == "all"
|
|
105
|
+
Requires-Dist: markitdown[docx,pdf]>=0.1.0; extra == "all"
|
|
100
106
|
Requires-Dist: unstructured[all-docs]>=0.16.0; extra == "all"
|
|
101
107
|
Requires-Dist: presidio-analyzer>=2.2.0; extra == "all"
|
|
102
108
|
Requires-Dist: e2b>=2.0.0; extra == "all"
|
|
@@ -126,10 +132,12 @@ Dynamic: license-file
|
|
|
126
132
|
[](https://github.com/raintree-technology/docpull/stargazers)
|
|
127
133
|
[](https://github.com/raintree-technology/docpull/blob/main/LICENSE)
|
|
128
134
|
|
|
135
|
+
<!-- mcp-name: io.github.raintree-technology/docpull -->
|
|
136
|
+
|
|
129
137
|
DocPull is a local-first dependency manager for AI context. Define the public
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
138
|
+
web sources an agent depends on, sync them into cited context packs, diff what
|
|
139
|
+
changed, and export reproducible context for Cursor, Claude, OpenAI,
|
|
140
|
+
LlamaIndex, LangChain, MCP clients, and RAG pipelines.
|
|
133
141
|
|
|
134
142
|
The core workflow is a `docpull.yaml` plus a `.docpull/context.lock.json`,
|
|
135
143
|
similar in spirit to code dependency manifests and lockfiles:
|
|
@@ -141,7 +149,7 @@ docpull install
|
|
|
141
149
|
docpull deps
|
|
142
150
|
docpull sync
|
|
143
151
|
docpull diff
|
|
144
|
-
docpull export context-pack --target
|
|
152
|
+
docpull export context-pack --target openai
|
|
145
153
|
```
|
|
146
154
|
|
|
147
155
|
Bundled aliases such as `stripe`, `react`, `postgres`, `openai`, and
|
|
@@ -179,6 +187,12 @@ pages such as blogs, API references, OpenAPI specs, changelogs, vendor pages,
|
|
|
179
187
|
product pages, filings, docs sites, and other pages where the useful content is
|
|
180
188
|
available in HTML or embedded page data.
|
|
181
189
|
|
|
190
|
+
Direct fetches also recognize standards-style plain text and common textual
|
|
191
|
+
formats from their response media type, including extensionless URLs. Remote
|
|
192
|
+
documents remain denied by default; explicitly opt into local PDF parsing with
|
|
193
|
+
`docpull URL --remote-documents pdf` after installing `docpull[markitdown]` or
|
|
194
|
+
`docpull[parse]`. This does not enable a browser, cloud parser, or paid route.
|
|
195
|
+
|
|
182
196
|
DocPull is browser-free by default. JS-only pages are skipped with a clear
|
|
183
197
|
reason unless you explicitly opt into a local renderer. See
|
|
184
198
|
[Web Source Boundary](docs/scraping-boundary.md) and
|
|
@@ -504,12 +518,11 @@ More examples live in [CLI Recipes](docs/examples/README.md).
|
|
|
504
518
|
With an explicit `--skill-agent`, docpull stores the fetched corpus under
|
|
505
519
|
`.docpull/skills/<name>/references` and creates agent-specific wrappers that
|
|
506
520
|
point at that corpus. `--skill-agent claude` writes a Claude Code skill under
|
|
507
|
-
`.claude/skills/<name>/`, `--skill-agent
|
|
508
|
-
`.
|
|
509
|
-
|
|
510
|
-
`--skill-agent
|
|
511
|
-
|
|
512
|
-
their active agent wrappers.
|
|
521
|
+
`.claude/skills/<name>/`, and `--skill-agent cursor` writes a Cursor project
|
|
522
|
+
rule at `.cursor/rules/<name>.mdc`. Use `--skill-agent all` to create every
|
|
523
|
+
supported wrapper. If you pass `--output-dir`, docpull stages the generated
|
|
524
|
+
corpus there; explicit `--skill-agent` targets still write their active agent
|
|
525
|
+
wrappers.
|
|
513
526
|
|
|
514
527
|
Use docpull when you need to:
|
|
515
528
|
|
|
@@ -591,6 +604,18 @@ MarkItDown or Unstructured parsers for complex office/PDF files when installed.
|
|
|
591
604
|
Install `docpull[markitdown]`, `docpull[unstructured]`, or `docpull[parse]`
|
|
592
605
|
for those backends.
|
|
593
606
|
|
|
607
|
+
An HTTPS PDF can enter the ordinary fetch contract only with explicit consent:
|
|
608
|
+
|
|
609
|
+
```bash
|
|
610
|
+
docpull https://example.com/handbook.pdf --remote-documents pdf \
|
|
611
|
+
--remote-document-backend auto -o ./packs/handbook
|
|
612
|
+
```
|
|
613
|
+
|
|
614
|
+
The response must identify as PDF and contain a PDF signature. DocPull parses
|
|
615
|
+
it locally through a mode-`0600` temporary file, records the source hash and
|
|
616
|
+
parser provenance, and removes the temporary source. Other remote download
|
|
617
|
+
types remain blocked.
|
|
618
|
+
|
|
594
619
|
Every file-backed run writes `corpus.manifest.json` with stable document IDs,
|
|
595
620
|
chunk IDs, hashes, output paths, and chunk counts. See
|
|
596
621
|
[Corpus Manifest](docs/corpus-manifest.md).
|
|
@@ -719,7 +744,7 @@ part of the package release contract.
|
|
|
719
744
|
- `docpull export` writes local files for OpenAI vector JSONL, LangChain,
|
|
720
745
|
LlamaIndex, DSPy, Sheets CSV/TSV, n8n workflow JSON, Vercel AI SDK JSON,
|
|
721
746
|
CrewAI JSON, warehouse NDJSON, optional Parquet via `docpull[parquet]`, and
|
|
722
|
-
|
|
747
|
+
agent reference bundles.
|
|
723
748
|
## Security Defaults
|
|
724
749
|
|
|
725
750
|
- HTTPS-only fetching with robots.txt compliance.
|
|
@@ -753,7 +778,7 @@ docpull URL --preview-urls
|
|
|
753
778
|
|
|
754
779
|
## Links
|
|
755
780
|
|
|
756
|
-
- [
|
|
781
|
+
- [Documentation](https://github.com/raintree-technology/docpull#readme)
|
|
757
782
|
- [PyPI](https://pypi.org/project/docpull/)
|
|
758
783
|
- [GitHub](https://github.com/raintree-technology/docpull)
|
|
759
784
|
- [Metrics](METRICS.md)
|
|
@@ -8,10 +8,12 @@
|
|
|
8
8
|
[](https://github.com/raintree-technology/docpull/stargazers)
|
|
9
9
|
[](https://github.com/raintree-technology/docpull/blob/main/LICENSE)
|
|
10
10
|
|
|
11
|
+
<!-- mcp-name: io.github.raintree-technology/docpull -->
|
|
12
|
+
|
|
11
13
|
DocPull is a local-first dependency manager for AI context. Define the public
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
14
|
+
web sources an agent depends on, sync them into cited context packs, diff what
|
|
15
|
+
changed, and export reproducible context for Cursor, Claude, OpenAI,
|
|
16
|
+
LlamaIndex, LangChain, MCP clients, and RAG pipelines.
|
|
15
17
|
|
|
16
18
|
The core workflow is a `docpull.yaml` plus a `.docpull/context.lock.json`,
|
|
17
19
|
similar in spirit to code dependency manifests and lockfiles:
|
|
@@ -23,7 +25,7 @@ docpull install
|
|
|
23
25
|
docpull deps
|
|
24
26
|
docpull sync
|
|
25
27
|
docpull diff
|
|
26
|
-
docpull export context-pack --target
|
|
28
|
+
docpull export context-pack --target openai
|
|
27
29
|
```
|
|
28
30
|
|
|
29
31
|
Bundled aliases such as `stripe`, `react`, `postgres`, `openai`, and
|
|
@@ -61,6 +63,12 @@ pages such as blogs, API references, OpenAPI specs, changelogs, vendor pages,
|
|
|
61
63
|
product pages, filings, docs sites, and other pages where the useful content is
|
|
62
64
|
available in HTML or embedded page data.
|
|
63
65
|
|
|
66
|
+
Direct fetches also recognize standards-style plain text and common textual
|
|
67
|
+
formats from their response media type, including extensionless URLs. Remote
|
|
68
|
+
documents remain denied by default; explicitly opt into local PDF parsing with
|
|
69
|
+
`docpull URL --remote-documents pdf` after installing `docpull[markitdown]` or
|
|
70
|
+
`docpull[parse]`. This does not enable a browser, cloud parser, or paid route.
|
|
71
|
+
|
|
64
72
|
DocPull is browser-free by default. JS-only pages are skipped with a clear
|
|
65
73
|
reason unless you explicitly opt into a local renderer. See
|
|
66
74
|
[Web Source Boundary](docs/scraping-boundary.md) and
|
|
@@ -386,12 +394,11 @@ More examples live in [CLI Recipes](docs/examples/README.md).
|
|
|
386
394
|
With an explicit `--skill-agent`, docpull stores the fetched corpus under
|
|
387
395
|
`.docpull/skills/<name>/references` and creates agent-specific wrappers that
|
|
388
396
|
point at that corpus. `--skill-agent claude` writes a Claude Code skill under
|
|
389
|
-
`.claude/skills/<name>/`, `--skill-agent
|
|
390
|
-
`.
|
|
391
|
-
|
|
392
|
-
`--skill-agent
|
|
393
|
-
|
|
394
|
-
their active agent wrappers.
|
|
397
|
+
`.claude/skills/<name>/`, and `--skill-agent cursor` writes a Cursor project
|
|
398
|
+
rule at `.cursor/rules/<name>.mdc`. Use `--skill-agent all` to create every
|
|
399
|
+
supported wrapper. If you pass `--output-dir`, docpull stages the generated
|
|
400
|
+
corpus there; explicit `--skill-agent` targets still write their active agent
|
|
401
|
+
wrappers.
|
|
395
402
|
|
|
396
403
|
Use docpull when you need to:
|
|
397
404
|
|
|
@@ -473,6 +480,18 @@ MarkItDown or Unstructured parsers for complex office/PDF files when installed.
|
|
|
473
480
|
Install `docpull[markitdown]`, `docpull[unstructured]`, or `docpull[parse]`
|
|
474
481
|
for those backends.
|
|
475
482
|
|
|
483
|
+
An HTTPS PDF can enter the ordinary fetch contract only with explicit consent:
|
|
484
|
+
|
|
485
|
+
```bash
|
|
486
|
+
docpull https://example.com/handbook.pdf --remote-documents pdf \
|
|
487
|
+
--remote-document-backend auto -o ./packs/handbook
|
|
488
|
+
```
|
|
489
|
+
|
|
490
|
+
The response must identify as PDF and contain a PDF signature. DocPull parses
|
|
491
|
+
it locally through a mode-`0600` temporary file, records the source hash and
|
|
492
|
+
parser provenance, and removes the temporary source. Other remote download
|
|
493
|
+
types remain blocked.
|
|
494
|
+
|
|
476
495
|
Every file-backed run writes `corpus.manifest.json` with stable document IDs,
|
|
477
496
|
chunk IDs, hashes, output paths, and chunk counts. See
|
|
478
497
|
[Corpus Manifest](docs/corpus-manifest.md).
|
|
@@ -601,7 +620,7 @@ part of the package release contract.
|
|
|
601
620
|
- `docpull export` writes local files for OpenAI vector JSONL, LangChain,
|
|
602
621
|
LlamaIndex, DSPy, Sheets CSV/TSV, n8n workflow JSON, Vercel AI SDK JSON,
|
|
603
622
|
CrewAI JSON, warehouse NDJSON, optional Parquet via `docpull[parquet]`, and
|
|
604
|
-
|
|
623
|
+
agent reference bundles.
|
|
605
624
|
## Security Defaults
|
|
606
625
|
|
|
607
626
|
- HTTPS-only fetching with robots.txt compliance.
|
|
@@ -635,7 +654,7 @@ docpull URL --preview-urls
|
|
|
635
654
|
|
|
636
655
|
## Links
|
|
637
656
|
|
|
638
|
-
- [
|
|
657
|
+
- [Documentation](https://github.com/raintree-technology/docpull#readme)
|
|
639
658
|
- [PyPI](https://pypi.org/project/docpull/)
|
|
640
659
|
- [GitHub](https://github.com/raintree-technology/docpull)
|
|
641
660
|
- [Metrics](METRICS.md)
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "docpull"
|
|
7
|
-
version = "6.
|
|
7
|
+
version = "6.1.0"
|
|
8
8
|
dynamic = []
|
|
9
9
|
description = "Declare, sync, diff, and lock context dependencies for AI agents"
|
|
10
10
|
readme = {file = "README.md", content-type = "text/markdown"}
|
|
@@ -123,13 +123,19 @@ parquet = [
|
|
|
123
123
|
"pyarrow>=16.0.0",
|
|
124
124
|
]
|
|
125
125
|
markitdown = [
|
|
126
|
-
"
|
|
126
|
+
"pypdf>=6.14.2",
|
|
127
|
+
"markitdown[docx,pdf]>=0.1.0",
|
|
127
128
|
]
|
|
128
129
|
unstructured = [
|
|
130
|
+
"pypdf>=6.14.2",
|
|
129
131
|
"unstructured[all-docs]>=0.16.0",
|
|
130
132
|
]
|
|
133
|
+
pdf = [
|
|
134
|
+
"pypdf>=6.14.2",
|
|
135
|
+
]
|
|
131
136
|
parse = [
|
|
132
|
-
"
|
|
137
|
+
"pypdf>=6.14.2",
|
|
138
|
+
"markitdown[docx,pdf]>=0.1.0",
|
|
133
139
|
"unstructured[all-docs]>=0.16.0",
|
|
134
140
|
]
|
|
135
141
|
presidio = [
|
|
@@ -154,7 +160,8 @@ all = [
|
|
|
154
160
|
"starlette>=1.3.1",
|
|
155
161
|
"uvicorn>=0.30.0",
|
|
156
162
|
"pyarrow>=16.0.0",
|
|
157
|
-
"
|
|
163
|
+
"pypdf>=6.14.2",
|
|
164
|
+
"markitdown[docx,pdf]>=0.1.0",
|
|
158
165
|
"unstructured[all-docs]>=0.16.0",
|
|
159
166
|
"presidio-analyzer>=2.2.0",
|
|
160
167
|
"e2b>=2.0.0",
|
|
@@ -179,8 +186,8 @@ dev = [
|
|
|
179
186
|
docpull = "docpull.cli:main"
|
|
180
187
|
|
|
181
188
|
[project.urls]
|
|
182
|
-
Homepage = "https://
|
|
183
|
-
Documentation = "https://
|
|
189
|
+
Homepage = "https://github.com/raintree-technology/docpull"
|
|
190
|
+
Documentation = "https://github.com/raintree-technology/docpull#readme"
|
|
184
191
|
Repository = "https://github.com/raintree-technology/docpull"
|
|
185
192
|
"Source Code" = "https://github.com/raintree-technology/docpull"
|
|
186
193
|
"Bug Tracker" = "https://github.com/raintree-technology/docpull/issues"
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"""
|
|
2
|
-
docpull -
|
|
2
|
+
docpull - Context dependencies for AI agents.
|
|
3
3
|
|
|
4
4
|
Usage:
|
|
5
5
|
from docpull import Fetcher, DocpullConfig, ProfileName
|
|
@@ -14,7 +14,7 @@ Usage:
|
|
|
14
14
|
print(event)
|
|
15
15
|
"""
|
|
16
16
|
|
|
17
|
-
__version__ = "6.
|
|
17
|
+
__version__ = "6.1.0"
|
|
18
18
|
|
|
19
19
|
from .cache import CacheManager, StreamingDeduplicator
|
|
20
20
|
from .context_ci import CIThresholds, ContextCIError, run_context_ci
|
|
@@ -200,8 +200,8 @@ TOOL_DOC_TARGETS: tuple[_BenchmarkTarget, ...] = (
|
|
|
200
200
|
_BenchmarkTarget(
|
|
201
201
|
id="docpull_docs",
|
|
202
202
|
label="DocPull docs",
|
|
203
|
-
url="https://
|
|
204
|
-
include_domains=("
|
|
203
|
+
url="https://github.com/raintree-technology/docpull",
|
|
204
|
+
include_domains=("github.com",),
|
|
205
205
|
objective="Build an agent context pack for DocPull documentation",
|
|
206
206
|
queries=("DocPull documentation CLI provider benchmark docs",),
|
|
207
207
|
freshness_terms=("changelog", "release", "benchmark"),
|
|
@@ -963,13 +963,45 @@ def run_quick_benchmark(
|
|
|
963
963
|
|
|
964
964
|
report_path = run_dir / "benchmark.report.json"
|
|
965
965
|
markdown_path = run_dir / "benchmark.summary.md"
|
|
966
|
+
config_path = run_dir / "benchmark.config.json"
|
|
967
|
+
generated_at = utc_now_iso()
|
|
968
|
+
safe_provider_status = _benchmark_provider_statuses(provider_status)
|
|
966
969
|
artifacts: dict[str, str] = {
|
|
970
|
+
"config": str(config_path),
|
|
967
971
|
"json": str(report_path),
|
|
968
972
|
"markdown": str(markdown_path),
|
|
969
973
|
}
|
|
974
|
+
benchmark_config = {
|
|
975
|
+
"schema_version": BENCHMARK_SCHEMA_VERSION,
|
|
976
|
+
"generated_at": generated_at,
|
|
977
|
+
"run_dir": str(run_dir),
|
|
978
|
+
"target_url": targets[0].url,
|
|
979
|
+
"target_set": target_set,
|
|
980
|
+
"targets": [target.report_dict() for target in targets],
|
|
981
|
+
"requested_providers": requested_providers,
|
|
982
|
+
"enabled_providers": ["core", *providers],
|
|
983
|
+
"skipped_providers": skipped_providers,
|
|
984
|
+
"provider_status": safe_provider_status,
|
|
985
|
+
"runs_per_case": runs,
|
|
986
|
+
"budget_limit_usd": budget_limit,
|
|
987
|
+
"zero_dollar": zero_dollar,
|
|
988
|
+
"settings": {
|
|
989
|
+
"max_pages": max_pages,
|
|
990
|
+
"max_depth": max_depth,
|
|
991
|
+
"max_concurrent": max_concurrent,
|
|
992
|
+
"per_host_concurrent": per_host_concurrent,
|
|
993
|
+
"cache_enabled": cache_enabled,
|
|
994
|
+
"cached_pass": cached_pass,
|
|
995
|
+
"mode": mode,
|
|
996
|
+
"max_search_results": max_search_results,
|
|
997
|
+
"extract_limit": extract_limit,
|
|
998
|
+
"max_estimated_cost": max_estimated_cost,
|
|
999
|
+
},
|
|
1000
|
+
"cost_normalization": _cost_normalization_metadata(tavily_credit_usd),
|
|
1001
|
+
}
|
|
970
1002
|
report = {
|
|
971
1003
|
"schema_version": BENCHMARK_SCHEMA_VERSION,
|
|
972
|
-
"generated_at":
|
|
1004
|
+
"generated_at": generated_at,
|
|
973
1005
|
"run_dir": str(run_dir),
|
|
974
1006
|
"target_url": targets[0].url,
|
|
975
1007
|
"target_set": target_set,
|
|
@@ -981,7 +1013,7 @@ def run_quick_benchmark(
|
|
|
981
1013
|
"skipped_providers": skipped_providers,
|
|
982
1014
|
"budget_limit_usd": budget_limit,
|
|
983
1015
|
"zero_dollar": zero_dollar,
|
|
984
|
-
"provider_status":
|
|
1016
|
+
"provider_status": safe_provider_status,
|
|
985
1017
|
"cost_normalization": _cost_normalization_metadata(tavily_credit_usd),
|
|
986
1018
|
"runs_per_case": runs,
|
|
987
1019
|
"trace": trace.metadata(),
|
|
@@ -1011,6 +1043,10 @@ def run_quick_benchmark(
|
|
|
1011
1043
|
report["zero_dollar_completion"] = _zero_dollar_completion(cases)
|
|
1012
1044
|
trace.finish(report)
|
|
1013
1045
|
report["trace"] = trace.metadata()
|
|
1046
|
+
config_path.write_text(
|
|
1047
|
+
json.dumps(benchmark_config, indent=2, ensure_ascii=False) + "\n",
|
|
1048
|
+
encoding="utf-8",
|
|
1049
|
+
)
|
|
1014
1050
|
report_path.write_text(json.dumps(report, indent=2, ensure_ascii=False) + "\n", encoding="utf-8")
|
|
1015
1051
|
markdown_path.write_text(_markdown_report(report), encoding="utf-8")
|
|
1016
1052
|
return report
|
|
@@ -3848,6 +3884,7 @@ def _article_markdown(report: dict[str, Any], *, title: str) -> str:
|
|
|
3848
3884
|
"",
|
|
3849
3885
|
"## Artifacts",
|
|
3850
3886
|
"",
|
|
3887
|
+
f"- Config: `{artifacts.get('config')}`",
|
|
3851
3888
|
f"- JSON report: `{artifacts.get('json')}`",
|
|
3852
3889
|
f"- Summary: `{artifacts.get('markdown')}`",
|
|
3853
3890
|
"",
|
|
@@ -3977,6 +4014,7 @@ def _legacy_article_markdown(report: dict[str, Any], *, title: str) -> str:
|
|
|
3977
4014
|
"",
|
|
3978
4015
|
"## Artifacts",
|
|
3979
4016
|
"",
|
|
4017
|
+
f"- Config: `{artifacts.get('config')}`",
|
|
3980
4018
|
f"- JSON report: `{artifacts.get('json')}`",
|
|
3981
4019
|
f"- Summary: `{artifacts.get('markdown')}`",
|
|
3982
4020
|
"",
|
|
@@ -335,6 +335,32 @@ Examples:
|
|
|
335
335
|
action="store_true",
|
|
336
336
|
help="Stream NDJSON records to stdout as each page completes (implies --format ndjson)",
|
|
337
337
|
)
|
|
338
|
+
parser.add_argument(
|
|
339
|
+
"--remote-documents",
|
|
340
|
+
choices=["off", "pdf"],
|
|
341
|
+
default=None,
|
|
342
|
+
help="Explicitly download and locally parse selected remote document types (default: off)",
|
|
343
|
+
)
|
|
344
|
+
parser.add_argument(
|
|
345
|
+
"--remote-document-backend",
|
|
346
|
+
choices=["auto", "pypdf", "markitdown", "unstructured"],
|
|
347
|
+
default=None,
|
|
348
|
+
help="Local parser backend for --remote-documents (default: auto)",
|
|
349
|
+
)
|
|
350
|
+
parser.add_argument(
|
|
351
|
+
"--remote-document-timeout-seconds",
|
|
352
|
+
type=int,
|
|
353
|
+
default=None,
|
|
354
|
+
metavar="SECONDS",
|
|
355
|
+
help="Wall-time limit for each isolated remote-document parser (default: 60)",
|
|
356
|
+
)
|
|
357
|
+
parser.add_argument(
|
|
358
|
+
"--remote-document-memory-mib",
|
|
359
|
+
type=int,
|
|
360
|
+
default=None,
|
|
361
|
+
metavar="MIB",
|
|
362
|
+
help="Address-space limit for each isolated remote-document parser (default: 1024)",
|
|
363
|
+
)
|
|
338
364
|
|
|
339
365
|
# Crawl settings
|
|
340
366
|
crawl_group = parser.add_argument_group("crawl settings")
|
|
@@ -677,6 +703,14 @@ def run_fetcher(args: argparse.Namespace) -> int:
|
|
|
677
703
|
filter_kwargs["enable_special_cases"] = False
|
|
678
704
|
if args.strict_js_required:
|
|
679
705
|
filter_kwargs["strict_js_required"] = True
|
|
706
|
+
if args.remote_documents:
|
|
707
|
+
filter_kwargs["remote_documents"] = args.remote_documents
|
|
708
|
+
if args.remote_document_backend:
|
|
709
|
+
filter_kwargs["remote_document_backend"] = args.remote_document_backend
|
|
710
|
+
if args.remote_document_timeout_seconds is not None:
|
|
711
|
+
filter_kwargs["remote_document_timeout_seconds"] = args.remote_document_timeout_seconds
|
|
712
|
+
if args.remote_document_memory_mib is not None:
|
|
713
|
+
filter_kwargs["remote_document_memory_mib"] = args.remote_document_memory_mib
|
|
680
714
|
if filter_kwargs:
|
|
681
715
|
config_kwargs["content_filter"] = filter_kwargs
|
|
682
716
|
|
|
@@ -1164,7 +1198,7 @@ def run_render_cli(argv: list[str]) -> int:
|
|
|
1164
1198
|
args.cloud_max_estimated_cost if backend in {"vercel-sandbox", "e2b-sandbox"} else None,
|
|
1165
1199
|
)
|
|
1166
1200
|
cloud_estimated_cost = (
|
|
1167
|
-
estimate_cloud_render_cost_usd(
|
|
1201
|
+
estimate_cloud_render_cost_usd(backend, config)
|
|
1168
1202
|
if backend in {"vercel-sandbox", "e2b-sandbox"}
|
|
1169
1203
|
else 0.0
|
|
1170
1204
|
)
|
|
@@ -16,6 +16,7 @@ import json
|
|
|
16
16
|
import logging
|
|
17
17
|
import re
|
|
18
18
|
from dataclasses import dataclass
|
|
19
|
+
from email.message import Message
|
|
19
20
|
from typing import Any, Protocol
|
|
20
21
|
from urllib.parse import urlparse
|
|
21
22
|
|
|
@@ -710,33 +711,143 @@ class RawTextExtractor:
|
|
|
710
711
|
|
|
711
712
|
name = "raw_text"
|
|
712
713
|
|
|
713
|
-
_TEXT_SUFFIXES = (
|
|
714
|
+
_TEXT_SUFFIXES = (
|
|
715
|
+
".csv",
|
|
716
|
+
".json",
|
|
717
|
+
".jsonl",
|
|
718
|
+
".markdown",
|
|
719
|
+
".md",
|
|
720
|
+
".mdx",
|
|
721
|
+
".ndjson",
|
|
722
|
+
".rst",
|
|
723
|
+
".text",
|
|
724
|
+
".tsv",
|
|
725
|
+
".txt",
|
|
726
|
+
".xml",
|
|
727
|
+
".yaml",
|
|
728
|
+
".yml",
|
|
729
|
+
)
|
|
730
|
+
_TEXT_CONTENT_TYPES = {
|
|
731
|
+
"application/json",
|
|
732
|
+
"application/ld+json",
|
|
733
|
+
"application/x-ndjson",
|
|
734
|
+
"application/xml",
|
|
735
|
+
"application/yaml",
|
|
736
|
+
"application/x-yaml",
|
|
737
|
+
"text/csv",
|
|
738
|
+
"text/markdown",
|
|
739
|
+
"text/plain",
|
|
740
|
+
"text/tab-separated-values",
|
|
741
|
+
"text/x-markdown",
|
|
742
|
+
"text/x-rst",
|
|
743
|
+
"text/xml",
|
|
744
|
+
"text/yaml",
|
|
745
|
+
}
|
|
714
746
|
_HTML_MARKERS = ("<html", "<body", "<!doctype")
|
|
715
|
-
|
|
716
|
-
|
|
747
|
+
_STRUCTURED_LANGUAGES = {
|
|
748
|
+
".csv": "csv",
|
|
749
|
+
".json": "json",
|
|
750
|
+
".jsonl": "ndjson",
|
|
751
|
+
".ndjson": "ndjson",
|
|
752
|
+
".rst": "rst",
|
|
753
|
+
".tsv": "tsv",
|
|
754
|
+
".xml": "xml",
|
|
755
|
+
".yaml": "yaml",
|
|
756
|
+
".yml": "yaml",
|
|
757
|
+
}
|
|
758
|
+
_MEDIA_LANGUAGES = {
|
|
759
|
+
"application/json": "json",
|
|
760
|
+
"application/ld+json": "json",
|
|
761
|
+
"application/x-ndjson": "ndjson",
|
|
762
|
+
"application/xml": "xml",
|
|
763
|
+
"application/yaml": "yaml",
|
|
764
|
+
"application/x-yaml": "yaml",
|
|
765
|
+
"text/csv": "csv",
|
|
766
|
+
"text/tab-separated-values": "tsv",
|
|
767
|
+
"text/x-rst": "rst",
|
|
768
|
+
"text/xml": "xml",
|
|
769
|
+
"text/yaml": "yaml",
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
def try_extract(
|
|
773
|
+
self,
|
|
774
|
+
html: bytes,
|
|
775
|
+
url: str,
|
|
776
|
+
*,
|
|
777
|
+
content_type: str | None = None,
|
|
778
|
+
) -> SpecialCaseResult | None:
|
|
717
779
|
parsed = urlparse(url)
|
|
718
780
|
path = parsed.path.lower()
|
|
719
|
-
|
|
781
|
+
media_type = self._media_type(content_type)
|
|
782
|
+
identified_by_type = media_type in self._TEXT_CONTENT_TYPES
|
|
783
|
+
if not identified_by_type and not path.endswith(self._TEXT_SUFFIXES):
|
|
720
784
|
return None
|
|
721
785
|
|
|
722
|
-
text =
|
|
786
|
+
text = self._decode_text(html, content_type).strip()
|
|
723
787
|
if not text:
|
|
724
788
|
return None
|
|
789
|
+
language = self._structured_language(path, media_type)
|
|
725
790
|
lower_head = text[:500].lower()
|
|
726
|
-
if any(marker in lower_head for marker in self._HTML_MARKERS):
|
|
791
|
+
if language is None and any(marker in lower_head for marker in self._HTML_MARKERS):
|
|
727
792
|
return None
|
|
728
|
-
if
|
|
793
|
+
if (
|
|
794
|
+
path.endswith((".txt", ".text"))
|
|
795
|
+
and not identified_by_type
|
|
796
|
+
and not self._looks_like_docs_text(text, path)
|
|
797
|
+
):
|
|
729
798
|
return None
|
|
730
799
|
|
|
731
800
|
title = self._extract_title(text) or parsed.path.rsplit("/", 1)[-1] or "Document"
|
|
732
801
|
source_type = "llms_txt" if path.endswith(("/llms.txt", "/llms-full.txt")) else self.name
|
|
802
|
+
markdown = self._fenced(text, language) if language else self._safe_direct_markdown(text)
|
|
733
803
|
return SpecialCaseResult(
|
|
734
|
-
markdown=
|
|
804
|
+
markdown=markdown,
|
|
735
805
|
title=title,
|
|
736
806
|
source_type=source_type,
|
|
737
807
|
extra={"framework": source_type},
|
|
738
808
|
)
|
|
739
809
|
|
|
810
|
+
@classmethod
|
|
811
|
+
def _structured_language(cls, path: str, media_type: str) -> str | None:
|
|
812
|
+
if media_type in {"text/markdown", "text/x-markdown"} or path.endswith((".markdown", ".md", ".mdx")):
|
|
813
|
+
return None
|
|
814
|
+
media_language = cls._MEDIA_LANGUAGES.get(media_type)
|
|
815
|
+
if media_language:
|
|
816
|
+
return media_language
|
|
817
|
+
return next(
|
|
818
|
+
(language for suffix, language in cls._STRUCTURED_LANGUAGES.items() if path.endswith(suffix)),
|
|
819
|
+
None,
|
|
820
|
+
)
|
|
821
|
+
|
|
822
|
+
@staticmethod
|
|
823
|
+
def _fenced(text: str, language: str) -> str:
|
|
824
|
+
longest_run = max((len(match.group(0)) for match in re.finditer(r"`+", text)), default=0)
|
|
825
|
+
fence = "`" * max(3, longest_run + 1)
|
|
826
|
+
return f"{fence}{language}\n{text.rstrip()}\n{fence}\n"
|
|
827
|
+
|
|
828
|
+
@staticmethod
|
|
829
|
+
def _safe_direct_markdown(text: str) -> str:
|
|
830
|
+
inert = re.sub(r"<(/?)script\b", r"<\1script", text, flags=re.IGNORECASE)
|
|
831
|
+
return inert.rstrip() + "\n"
|
|
832
|
+
|
|
833
|
+
@staticmethod
|
|
834
|
+
def _media_type(content_type: str | None) -> str:
|
|
835
|
+
return (content_type or "").split(";", 1)[0].strip().casefold()
|
|
836
|
+
|
|
837
|
+
@staticmethod
|
|
838
|
+
def _decode_text(body: bytes, content_type: str | None) -> str:
|
|
839
|
+
if body.startswith(b"\xef\xbb\xbf"):
|
|
840
|
+
return body.decode("utf-8-sig", errors="replace")
|
|
841
|
+
encoding = "utf-8"
|
|
842
|
+
if content_type:
|
|
843
|
+
message = Message()
|
|
844
|
+
message["content-type"] = content_type
|
|
845
|
+
encoding = message.get_content_charset() or encoding
|
|
846
|
+
try:
|
|
847
|
+
return body.decode(encoding)
|
|
848
|
+
except (LookupError, UnicodeDecodeError):
|
|
849
|
+
return body.decode("utf-8", errors="replace")
|
|
850
|
+
|
|
740
851
|
@staticmethod
|
|
741
852
|
def _looks_like_docs_text(text: str, path: str) -> bool:
|
|
742
853
|
if path.endswith(("/llms.txt", "/llms-full.txt")):
|
|
@@ -802,6 +913,33 @@ class SphinxObjectsInvExtractor:
|
|
|
802
913
|
)
|
|
803
914
|
|
|
804
915
|
|
|
916
|
+
class RfcEditorExtractor:
|
|
917
|
+
"""Preserve complete RFC Editor HTML documents instead of one scored section."""
|
|
918
|
+
|
|
919
|
+
name = "rfc_editor"
|
|
920
|
+
|
|
921
|
+
def try_extract(self, html: bytes, url: str) -> SpecialCaseResult | None:
|
|
922
|
+
parsed = urlparse(url)
|
|
923
|
+
if not _hostname_matches_domain(parsed.hostname or "", "rfc-editor.org"):
|
|
924
|
+
return None
|
|
925
|
+
if re.fullmatch(r"/rfc/rfc\d+\.html", parsed.path.casefold()) is None:
|
|
926
|
+
return None
|
|
927
|
+
|
|
928
|
+
from .extractor import MainContentExtractor
|
|
929
|
+
from .markdown import HtmlToMarkdown
|
|
930
|
+
|
|
931
|
+
extracted_html = MainContentExtractor(content_selectors=["body"]).extract(html, url)
|
|
932
|
+
markdown = HtmlToMarkdown().convert(extracted_html, url).strip()
|
|
933
|
+
if len(markdown) < 80:
|
|
934
|
+
return None
|
|
935
|
+
return SpecialCaseResult(
|
|
936
|
+
markdown=markdown + "\n",
|
|
937
|
+
title=_title_from_soup(_soup(html)),
|
|
938
|
+
source_type=self.name,
|
|
939
|
+
extra={"framework": "rfc_editor"},
|
|
940
|
+
)
|
|
941
|
+
|
|
942
|
+
|
|
805
943
|
class MdxSourceExtractor:
|
|
806
944
|
"""Rewrite ``edit-this-page`` GitHub links to raw MDX source URLs.
|
|
807
945
|
|
|
@@ -842,6 +980,7 @@ class MdxSourceExtractor:
|
|
|
842
980
|
DEFAULT_CHAIN: list[SpecialCaseExtractor] = [
|
|
843
981
|
OpenApiExtractor(),
|
|
844
982
|
RawTextExtractor(),
|
|
983
|
+
RfcEditorExtractor(),
|
|
845
984
|
MintlifyExtractor(),
|
|
846
985
|
NextDataExtractor(),
|
|
847
986
|
MkDocsMaterialExtractor(),
|
|
@@ -969,6 +1108,7 @@ __all__ = [
|
|
|
969
1108
|
"RawTextExtractor",
|
|
970
1109
|
"ReadMeExtractor",
|
|
971
1110
|
"RedocScalarExtractor",
|
|
1111
|
+
"RfcEditorExtractor",
|
|
972
1112
|
"SpecialCaseExtractor",
|
|
973
1113
|
"SpecialCaseResult",
|
|
974
1114
|
"SphinxObjectsInvExtractor",
|