docpull 5.0.0__tar.gz → 5.0.2__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-5.0.0/src/docpull.egg-info → docpull-5.0.2}/PKG-INFO +2 -2
- {docpull-5.0.0 → docpull-5.0.2}/README.md +1 -1
- {docpull-5.0.0 → docpull-5.0.2}/pyproject.toml +1 -1
- {docpull-5.0.0 → docpull-5.0.2}/src/docpull/__init__.py +6 -1
- {docpull-5.0.0 → docpull-5.0.2}/src/docpull/cli.py +5 -0
- {docpull-5.0.0 → docpull-5.0.2}/src/docpull/mcp/server.py +281 -71
- docpull-5.0.2/src/docpull/share.py +547 -0
- {docpull-5.0.0 → docpull-5.0.2/src/docpull.egg-info}/PKG-INFO +2 -2
- {docpull-5.0.0 → docpull-5.0.2}/src/docpull.egg-info/SOURCES.txt +2 -0
- {docpull-5.0.0 → docpull-5.0.2}/tests/test_mcp_server.py +65 -0
- docpull-5.0.2/tests/test_share.py +101 -0
- {docpull-5.0.0 → docpull-5.0.2}/tests/test_surface_contract.py +5 -0
- {docpull-5.0.0 → docpull-5.0.2}/LICENSE +0 -0
- {docpull-5.0.0 → docpull-5.0.2}/setup.cfg +0 -0
- {docpull-5.0.0 → docpull-5.0.2}/src/docpull/__main__.py +0 -0
- {docpull-5.0.0 → docpull-5.0.2}/src/docpull/accounting.py +0 -0
- {docpull-5.0.0 → docpull-5.0.2}/src/docpull/auth_cli.py +0 -0
- {docpull-5.0.0 → docpull-5.0.2}/src/docpull/benchmark.py +0 -0
- {docpull-5.0.0 → docpull-5.0.2}/src/docpull/cache/__init__.py +0 -0
- {docpull-5.0.0 → docpull-5.0.2}/src/docpull/cache/frontier.py +0 -0
- {docpull-5.0.0 → docpull-5.0.2}/src/docpull/cache/manager.py +0 -0
- {docpull-5.0.0 → docpull-5.0.2}/src/docpull/cache/streaming_dedup.py +0 -0
- {docpull-5.0.0 → docpull-5.0.2}/src/docpull/conversion/__init__.py +0 -0
- {docpull-5.0.0 → docpull-5.0.2}/src/docpull/conversion/chunking.py +0 -0
- {docpull-5.0.0 → docpull-5.0.2}/src/docpull/conversion/extractor.py +0 -0
- {docpull-5.0.0 → docpull-5.0.2}/src/docpull/conversion/filings.py +0 -0
- {docpull-5.0.0 → docpull-5.0.2}/src/docpull/conversion/markdown.py +0 -0
- {docpull-5.0.0 → docpull-5.0.2}/src/docpull/conversion/protocols.py +0 -0
- {docpull-5.0.0 → docpull-5.0.2}/src/docpull/conversion/special_cases.py +0 -0
- {docpull-5.0.0 → docpull-5.0.2}/src/docpull/conversion/trafilatura_extractor.py +0 -0
- {docpull-5.0.0 → docpull-5.0.2}/src/docpull/core/__init__.py +0 -0
- {docpull-5.0.0 → docpull-5.0.2}/src/docpull/core/fetcher.py +0 -0
- {docpull-5.0.0 → docpull-5.0.2}/src/docpull/discovery/__init__.py +0 -0
- {docpull-5.0.0 → docpull-5.0.2}/src/docpull/discovery/_fetch.py +0 -0
- {docpull-5.0.0 → docpull-5.0.2}/src/docpull/discovery/composite.py +0 -0
- {docpull-5.0.0 → docpull-5.0.2}/src/docpull/discovery/contracts.py +0 -0
- {docpull-5.0.0 → docpull-5.0.2}/src/docpull/discovery/crawler.py +0 -0
- {docpull-5.0.0 → docpull-5.0.2}/src/docpull/discovery/filters.py +0 -0
- {docpull-5.0.0 → docpull-5.0.2}/src/docpull/discovery/link_extractors/__init__.py +0 -0
- {docpull-5.0.0 → docpull-5.0.2}/src/docpull/discovery/link_extractors/enhanced.py +0 -0
- {docpull-5.0.0 → docpull-5.0.2}/src/docpull/discovery/link_extractors/protocols.py +0 -0
- {docpull-5.0.0 → docpull-5.0.2}/src/docpull/discovery/link_extractors/static.py +0 -0
- {docpull-5.0.0 → docpull-5.0.2}/src/docpull/discovery/protocols.py +0 -0
- {docpull-5.0.0 → docpull-5.0.2}/src/docpull/discovery/sitemap.py +0 -0
- {docpull-5.0.0 → docpull-5.0.2}/src/docpull/discovery_cli.py +0 -0
- {docpull-5.0.0 → docpull-5.0.2}/src/docpull/doctor.py +0 -0
- {docpull-5.0.0 → docpull-5.0.2}/src/docpull/evidence_pack.py +0 -0
- {docpull-5.0.0 → docpull-5.0.2}/src/docpull/exports.py +0 -0
- {docpull-5.0.0 → docpull-5.0.2}/src/docpull/fixtures/__init__.py +0 -0
- {docpull-5.0.0 → docpull-5.0.2}/src/docpull/fixtures/parallel-search-extract.json +0 -0
- {docpull-5.0.0 → docpull-5.0.2}/src/docpull/graph.py +0 -0
- {docpull-5.0.0 → docpull-5.0.2}/src/docpull/http/__init__.py +0 -0
- {docpull-5.0.0 → docpull-5.0.2}/src/docpull/http/client.py +0 -0
- {docpull-5.0.0 → docpull-5.0.2}/src/docpull/http/protocols.py +0 -0
- {docpull-5.0.0 → docpull-5.0.2}/src/docpull/http/rate_limiter.py +0 -0
- {docpull-5.0.0 → docpull-5.0.2}/src/docpull/judge.py +0 -0
- {docpull-5.0.0 → docpull-5.0.2}/src/docpull/local_workflows.py +0 -0
- {docpull-5.0.0 → docpull-5.0.2}/src/docpull/mcp/__init__.py +0 -0
- {docpull-5.0.0 → docpull-5.0.2}/src/docpull/mcp/sources.py +0 -0
- {docpull-5.0.0 → docpull-5.0.2}/src/docpull/mcp/tools.py +0 -0
- {docpull-5.0.0 → docpull-5.0.2}/src/docpull/metadata_extractor.py +0 -0
- {docpull-5.0.0 → docpull-5.0.2}/src/docpull/models/__init__.py +0 -0
- {docpull-5.0.0 → docpull-5.0.2}/src/docpull/models/config.py +0 -0
- {docpull-5.0.0 → docpull-5.0.2}/src/docpull/models/document.py +0 -0
- {docpull-5.0.0 → docpull-5.0.2}/src/docpull/models/events.py +0 -0
- {docpull-5.0.0 → docpull-5.0.2}/src/docpull/models/profiles.py +0 -0
- {docpull-5.0.0 → docpull-5.0.2}/src/docpull/models/run.py +0 -0
- {docpull-5.0.0 → docpull-5.0.2}/src/docpull/monitor.py +0 -0
- {docpull-5.0.0 → docpull-5.0.2}/src/docpull/pack_reader.py +0 -0
- {docpull-5.0.0 → docpull-5.0.2}/src/docpull/pack_tools.py +0 -0
- {docpull-5.0.0 → docpull-5.0.2}/src/docpull/parallel_workflows.py +0 -0
- {docpull-5.0.0 → docpull-5.0.2}/src/docpull/parity.py +0 -0
- {docpull-5.0.0 → docpull-5.0.2}/src/docpull/parity_cli.py +0 -0
- {docpull-5.0.0 → docpull-5.0.2}/src/docpull/passk.py +0 -0
- {docpull-5.0.0 → docpull-5.0.2}/src/docpull/pipeline/__init__.py +0 -0
- {docpull-5.0.0 → docpull-5.0.2}/src/docpull/pipeline/base.py +0 -0
- {docpull-5.0.0 → docpull-5.0.2}/src/docpull/pipeline/manifest.py +0 -0
- {docpull-5.0.0 → docpull-5.0.2}/src/docpull/pipeline/steps/__init__.py +0 -0
- {docpull-5.0.0 → docpull-5.0.2}/src/docpull/pipeline/steps/chunk.py +0 -0
- {docpull-5.0.0 → docpull-5.0.2}/src/docpull/pipeline/steps/convert.py +0 -0
- {docpull-5.0.0 → docpull-5.0.2}/src/docpull/pipeline/steps/dedup.py +0 -0
- {docpull-5.0.0 → docpull-5.0.2}/src/docpull/pipeline/steps/fetch.py +0 -0
- {docpull-5.0.0 → docpull-5.0.2}/src/docpull/pipeline/steps/metadata.py +0 -0
- {docpull-5.0.0 → docpull-5.0.2}/src/docpull/pipeline/steps/render.py +0 -0
- {docpull-5.0.0 → docpull-5.0.2}/src/docpull/pipeline/steps/save.py +0 -0
- {docpull-5.0.0 → docpull-5.0.2}/src/docpull/pipeline/steps/save_json.py +0 -0
- {docpull-5.0.0 → docpull-5.0.2}/src/docpull/pipeline/steps/save_ndjson.py +0 -0
- {docpull-5.0.0 → docpull-5.0.2}/src/docpull/pipeline/steps/save_okf.py +0 -0
- {docpull-5.0.0 → docpull-5.0.2}/src/docpull/pipeline/steps/save_sqlite.py +0 -0
- {docpull-5.0.0 → docpull-5.0.2}/src/docpull/pipeline/steps/validate.py +0 -0
- {docpull-5.0.0 → docpull-5.0.2}/src/docpull/policy.py +0 -0
- {docpull-5.0.0 → docpull-5.0.2}/src/docpull/policy_cli.py +0 -0
- {docpull-5.0.0 → docpull-5.0.2}/src/docpull/provider_adapters.py +0 -0
- {docpull-5.0.0 → docpull-5.0.2}/src/docpull/provider_capabilities.py +0 -0
- {docpull-5.0.0 → docpull-5.0.2}/src/docpull/provider_cli.py +0 -0
- {docpull-5.0.0 → docpull-5.0.2}/src/docpull/provider_keys.py +0 -0
- {docpull-5.0.0 → docpull-5.0.2}/src/docpull/provider_probes.py +0 -0
- {docpull-5.0.0 → docpull-5.0.2}/src/docpull/py.typed +0 -0
- {docpull-5.0.0 → docpull-5.0.2}/src/docpull/rendering.py +0 -0
- {docpull-5.0.0 → docpull-5.0.2}/src/docpull/scraper.py +0 -0
- {docpull-5.0.0 → docpull-5.0.2}/src/docpull/security/__init__.py +0 -0
- {docpull-5.0.0 → docpull-5.0.2}/src/docpull/security/download_policy.py +0 -0
- {docpull-5.0.0 → docpull-5.0.2}/src/docpull/security/robots.py +0 -0
- {docpull-5.0.0 → docpull-5.0.2}/src/docpull/security/url_validator.py +0 -0
- {docpull-5.0.0 → docpull-5.0.2}/src/docpull/server.py +0 -0
- {docpull-5.0.0 → docpull-5.0.2}/src/docpull/skill_export.py +0 -0
- {docpull-5.0.0 → docpull-5.0.2}/src/docpull/source_scoring.py +0 -0
- {docpull-5.0.0 → docpull-5.0.2}/src/docpull/time_utils.py +0 -0
- {docpull-5.0.0 → docpull-5.0.2}/src/docpull.egg-info/dependency_links.txt +0 -0
- {docpull-5.0.0 → docpull-5.0.2}/src/docpull.egg-info/entry_points.txt +0 -0
- {docpull-5.0.0 → docpull-5.0.2}/src/docpull.egg-info/requires.txt +0 -0
- {docpull-5.0.0 → docpull-5.0.2}/src/docpull.egg-info/top_level.txt +0 -0
- {docpull-5.0.0 → docpull-5.0.2}/tests/test_accounting.py +0 -0
- {docpull-5.0.0 → docpull-5.0.2}/tests/test_auth_cli.py +0 -0
- {docpull-5.0.0 → docpull-5.0.2}/tests/test_benchmark.py +0 -0
- {docpull-5.0.0 → docpull-5.0.2}/tests/test_cache_conditional_get.py +0 -0
- {docpull-5.0.0 → docpull-5.0.2}/tests/test_chunking.py +0 -0
- {docpull-5.0.0 → docpull-5.0.2}/tests/test_ci_policy.py +0 -0
- {docpull-5.0.0 → docpull-5.0.2}/tests/test_cli.py +0 -0
- {docpull-5.0.0 → docpull-5.0.2}/tests/test_conversion.py +0 -0
- {docpull-5.0.0 → docpull-5.0.2}/tests/test_convert_step_new.py +0 -0
- {docpull-5.0.0 → docpull-5.0.2}/tests/test_discovery.py +0 -0
- {docpull-5.0.0 → docpull-5.0.2}/tests/test_discovery_contracts.py +0 -0
- {docpull-5.0.0 → docpull-5.0.2}/tests/test_doctor.py +0 -0
- {docpull-5.0.0 → docpull-5.0.2}/tests/test_document_record.py +0 -0
- {docpull-5.0.0 → docpull-5.0.2}/tests/test_evidence_pack.py +0 -0
- {docpull-5.0.0 → docpull-5.0.2}/tests/test_exports.py +0 -0
- {docpull-5.0.0 → docpull-5.0.2}/tests/test_frontier_resume.py +0 -0
- {docpull-5.0.0 → docpull-5.0.2}/tests/test_graph.py +0 -0
- {docpull-5.0.0 → docpull-5.0.2}/tests/test_integration.py +0 -0
- {docpull-5.0.0 → docpull-5.0.2}/tests/test_judge.py +0 -0
- {docpull-5.0.0 → docpull-5.0.2}/tests/test_link_extractors.py +0 -0
- {docpull-5.0.0 → docpull-5.0.2}/tests/test_local_workflows.py +0 -0
- {docpull-5.0.0 → docpull-5.0.2}/tests/test_mcp_tools.py +0 -0
- {docpull-5.0.0 → docpull-5.0.2}/tests/test_metadata_extractor.py +0 -0
- {docpull-5.0.0 → docpull-5.0.2}/tests/test_monitor.py +0 -0
- {docpull-5.0.0 → docpull-5.0.2}/tests/test_naming.py +0 -0
- {docpull-5.0.0 → docpull-5.0.2}/tests/test_outputs_e2e.py +0 -0
- {docpull-5.0.0 → docpull-5.0.2}/tests/test_pack_server.py +0 -0
- {docpull-5.0.0 → docpull-5.0.2}/tests/test_pack_tools.py +0 -0
- {docpull-5.0.0 → docpull-5.0.2}/tests/test_parallel_workflows.py +0 -0
- {docpull-5.0.0 → docpull-5.0.2}/tests/test_parity_workflows.py +0 -0
- {docpull-5.0.0 → docpull-5.0.2}/tests/test_passk.py +0 -0
- {docpull-5.0.0 → docpull-5.0.2}/tests/test_pipeline.py +0 -0
- {docpull-5.0.0 → docpull-5.0.2}/tests/test_policy.py +0 -0
- {docpull-5.0.0 → docpull-5.0.2}/tests/test_policy_cli.py +0 -0
- {docpull-5.0.0 → docpull-5.0.2}/tests/test_provider_adapters.py +0 -0
- {docpull-5.0.0 → docpull-5.0.2}/tests/test_provider_cli.py +0 -0
- {docpull-5.0.0 → docpull-5.0.2}/tests/test_provider_keys.py +0 -0
- {docpull-5.0.0 → docpull-5.0.2}/tests/test_provider_probes.py +0 -0
- {docpull-5.0.0 → docpull-5.0.2}/tests/test_rate_limiter.py +0 -0
- {docpull-5.0.0 → docpull-5.0.2}/tests/test_real_site_regressions.py +0 -0
- {docpull-5.0.0 → docpull-5.0.2}/tests/test_rendering.py +0 -0
- {docpull-5.0.0 → docpull-5.0.2}/tests/test_save_ndjson.py +0 -0
- {docpull-5.0.0 → docpull-5.0.2}/tests/test_save_sqlite.py +0 -0
- {docpull-5.0.0 → docpull-5.0.2}/tests/test_security_hardening.py +0 -0
- {docpull-5.0.0 → docpull-5.0.2}/tests/test_special_cases.py +0 -0
- {docpull-5.0.0 → docpull-5.0.2}/tests/test_time_utils.py +0 -0
- {docpull-5.0.0 → docpull-5.0.2}/tests/test_trafilatura_extractor.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: docpull
|
|
3
|
-
Version: 5.0.
|
|
3
|
+
Version: 5.0.2
|
|
4
4
|
Summary: Convert public web pages into clean Markdown for AI agents and RAG
|
|
5
5
|
Author-email: Zachary Roth <support@raintree.technology>
|
|
6
6
|
Maintainer-email: Raintree Technology <support@raintree.technology>
|
|
@@ -501,7 +501,7 @@ part of the package release contract.
|
|
|
501
501
|
rendering, authenticated-source checks, and cron-friendly monitors:
|
|
502
502
|
`docpull policy`, `docpull discover`, `docpull refresh`,
|
|
503
503
|
`docpull pack audit`, `docpull answer-pack`, `docpull export`,
|
|
504
|
-
`docpull serve`, `docpull render`, `docpull auth check`, and
|
|
504
|
+
`docpull serve`, `docpull share`, `docpull render`, `docpull auth check`, and
|
|
505
505
|
`docpull monitor`.
|
|
506
506
|
- `docpull export` writes local files for OpenAI vector JSONL, LangChain,
|
|
507
507
|
LlamaIndex, DSPy, Sheets CSV/TSV, n8n workflow JSON, Vercel AI SDK JSON,
|
|
@@ -389,7 +389,7 @@ part of the package release contract.
|
|
|
389
389
|
rendering, authenticated-source checks, and cron-friendly monitors:
|
|
390
390
|
`docpull policy`, `docpull discover`, `docpull refresh`,
|
|
391
391
|
`docpull pack audit`, `docpull answer-pack`, `docpull export`,
|
|
392
|
-
`docpull serve`, `docpull render`, `docpull auth check`, and
|
|
392
|
+
`docpull serve`, `docpull share`, `docpull render`, `docpull auth check`, and
|
|
393
393
|
`docpull monitor`.
|
|
394
394
|
- `docpull export` writes local files for OpenAI vector JSONL, LangChain,
|
|
395
395
|
LlamaIndex, DSPy, Sheets CSV/TSV, n8n workflow JSON, Vercel AI SDK JSON,
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "docpull"
|
|
7
|
-
version = "5.0.
|
|
7
|
+
version = "5.0.2"
|
|
8
8
|
dynamic = []
|
|
9
9
|
description = "Convert public web pages into clean Markdown for AI agents and RAG"
|
|
10
10
|
readme = {file = "README.md", content-type = "text/markdown"}
|
|
@@ -14,7 +14,7 @@ Usage:
|
|
|
14
14
|
print(event)
|
|
15
15
|
"""
|
|
16
16
|
|
|
17
|
-
__version__ = "5.0.
|
|
17
|
+
__version__ = "5.0.2"
|
|
18
18
|
|
|
19
19
|
from .cache import CacheManager, StreamingDeduplicator
|
|
20
20
|
from .conversion.chunking import Chunk, TokenCounter, chunk_markdown
|
|
@@ -94,6 +94,7 @@ from .scraper import (
|
|
|
94
94
|
scrape_site,
|
|
95
95
|
)
|
|
96
96
|
from .server import PackASGIApp, PackServerError, create_pack_app
|
|
97
|
+
from .share import ReportHTTPServer, ShareError, create_report_server, render_report_document
|
|
97
98
|
|
|
98
99
|
__all__ = [
|
|
99
100
|
"__version__",
|
|
@@ -134,6 +135,10 @@ __all__ = [
|
|
|
134
135
|
"create_pack_app",
|
|
135
136
|
"PackASGIApp",
|
|
136
137
|
"PackServerError",
|
|
138
|
+
"create_report_server",
|
|
139
|
+
"render_report_document",
|
|
140
|
+
"ReportHTTPServer",
|
|
141
|
+
"ShareError",
|
|
137
142
|
"ScrapeResult",
|
|
138
143
|
"ScrapeRunResult",
|
|
139
144
|
"Scraper",
|
|
@@ -206,6 +206,7 @@ Subcommands:
|
|
|
206
206
|
answer-pack Answer from local pack evidence with citations
|
|
207
207
|
export Export a pack for agent or RAG tools
|
|
208
208
|
serve Serve a local pack over localhost JSON routes
|
|
209
|
+
share Serve a Markdown or HTML report at a local URL
|
|
209
210
|
monitor Run cron-friendly local pack monitors
|
|
210
211
|
parallel Build optional Parallel-backed context packs
|
|
211
212
|
provider Check provider keys and run provider-backed context packs
|
|
@@ -1369,6 +1370,10 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
1369
1370
|
from .server import run_serve_cli
|
|
1370
1371
|
|
|
1371
1372
|
return run_serve_cli(raw_argv[1:])
|
|
1373
|
+
if raw_argv and raw_argv[0] == "share":
|
|
1374
|
+
from .share import run_share_cli
|
|
1375
|
+
|
|
1376
|
+
return run_share_cli(raw_argv[1:])
|
|
1372
1377
|
if raw_argv and raw_argv[0] == "export":
|
|
1373
1378
|
from .exports import run_export_cli
|
|
1374
1379
|
|
|
@@ -48,6 +48,7 @@ from __future__ import annotations
|
|
|
48
48
|
|
|
49
49
|
import argparse
|
|
50
50
|
import asyncio
|
|
51
|
+
import json
|
|
51
52
|
import logging
|
|
52
53
|
import sys
|
|
53
54
|
from collections.abc import Awaitable, Callable
|
|
@@ -55,9 +56,14 @@ from pathlib import Path
|
|
|
55
56
|
from typing import Any, Literal, cast
|
|
56
57
|
|
|
57
58
|
from ..accounting import (
|
|
59
|
+
ACCOUNTING_ARTIFACT,
|
|
60
|
+
RunAccounting,
|
|
61
|
+
blocked_action,
|
|
58
62
|
budget_block_payload,
|
|
63
|
+
default_route_steps,
|
|
59
64
|
effective_budget_limit,
|
|
60
|
-
|
|
65
|
+
maybe_write_run_accounting,
|
|
66
|
+
paid_action_blocked,
|
|
61
67
|
)
|
|
62
68
|
from ..discovery import (
|
|
63
69
|
CandidateSourceRecord,
|
|
@@ -136,6 +142,42 @@ SERVER_INSTRUCTIONS = (
|
|
|
136
142
|
# Tools that return free-form Markdown (fetch_url) intentionally omit a
|
|
137
143
|
# schema; the rest expose structured payloads alongside the rendered text.
|
|
138
144
|
|
|
145
|
+
_ACCOUNTING_OUTPUT_SCHEMA = {
|
|
146
|
+
"type": "object",
|
|
147
|
+
"description": (
|
|
148
|
+
"Non-secret run accounting receipt. Mirrors run.accounting.json when an artifact was written."
|
|
149
|
+
),
|
|
150
|
+
"properties": {
|
|
151
|
+
"schema_version": {"type": "integer"},
|
|
152
|
+
"generated_at": {"type": "string"},
|
|
153
|
+
"budget_limit_usd": {"type": ["number", "null"]},
|
|
154
|
+
"estimated_paid_cost_usd": {"type": "number"},
|
|
155
|
+
"actual_paid_cost_usd": {"type": ["number", "null"]},
|
|
156
|
+
"paid_request_count": {"type": "integer"},
|
|
157
|
+
"local_browser_seconds": {"type": "number"},
|
|
158
|
+
"http_request_count": {"type": "integer"},
|
|
159
|
+
"cache_hit_count": {"type": "integer"},
|
|
160
|
+
"blocked_actions": {"type": "array", "items": {"type": "object"}},
|
|
161
|
+
"route_steps": {"type": "array", "items": {"type": "object"}},
|
|
162
|
+
"command": {"type": "string"},
|
|
163
|
+
"metadata": {"type": "object"},
|
|
164
|
+
"artifact_path": {"type": "string"},
|
|
165
|
+
},
|
|
166
|
+
"required": [
|
|
167
|
+
"schema_version",
|
|
168
|
+
"generated_at",
|
|
169
|
+
"budget_limit_usd",
|
|
170
|
+
"estimated_paid_cost_usd",
|
|
171
|
+
"actual_paid_cost_usd",
|
|
172
|
+
"paid_request_count",
|
|
173
|
+
"local_browser_seconds",
|
|
174
|
+
"http_request_count",
|
|
175
|
+
"cache_hit_count",
|
|
176
|
+
"blocked_actions",
|
|
177
|
+
"route_steps",
|
|
178
|
+
],
|
|
179
|
+
}
|
|
180
|
+
|
|
139
181
|
_LIST_SOURCES_OUTPUT_SCHEMA = {
|
|
140
182
|
"type": "object",
|
|
141
183
|
"properties": {
|
|
@@ -274,6 +316,7 @@ _PARALLEL_PACK_OUTPUT_SCHEMA = {
|
|
|
274
316
|
"output_dir": {"type": "string"},
|
|
275
317
|
"dry_run": {"type": "boolean"},
|
|
276
318
|
"estimated_cost_usd": {"type": "number"},
|
|
319
|
+
"accounting": _ACCOUNTING_OUTPUT_SCHEMA,
|
|
277
320
|
},
|
|
278
321
|
"required": ["workflow", "output_dir"],
|
|
279
322
|
}
|
|
@@ -470,6 +513,7 @@ _ANSWER_PACK_OUTPUT_SCHEMA = {
|
|
|
470
513
|
"search": {"type": "object"},
|
|
471
514
|
"brief": {"type": "object"},
|
|
472
515
|
"artifacts": {"type": "object"},
|
|
516
|
+
"accounting": _ACCOUNTING_OUTPUT_SCHEMA,
|
|
473
517
|
},
|
|
474
518
|
"required": ["question", "answer", "search", "artifacts"],
|
|
475
519
|
}
|
|
@@ -494,8 +538,14 @@ _RENDER_URL_OUTPUT_SCHEMA = {
|
|
|
494
538
|
"sidecar_path": {"type": "string"},
|
|
495
539
|
"html_bytes": {"type": "integer"},
|
|
496
540
|
"html_sha256": {"type": "string"},
|
|
541
|
+
"output_dir": {"type": "string"},
|
|
542
|
+
"dry_run": {"type": "boolean"},
|
|
543
|
+
"blocked_by_budget": {"type": "boolean"},
|
|
544
|
+
"budget_limit_usd": {"type": ["number", "null"]},
|
|
545
|
+
"blocked_action": {"type": "object"},
|
|
546
|
+
"accounting": _ACCOUNTING_OUTPUT_SCHEMA,
|
|
497
547
|
},
|
|
498
|
-
"required": ["url", "backend"
|
|
548
|
+
"required": ["url", "backend"],
|
|
499
549
|
}
|
|
500
550
|
|
|
501
551
|
_DISCOVER_SOURCES_OUTPUT_SCHEMA = {
|
|
@@ -615,6 +665,43 @@ def _policy_arg(arguments: dict[str, Any]) -> PolicyConfig:
|
|
|
615
665
|
return PolicyConfig.from_file(policy_path) if policy_path else PolicyConfig()
|
|
616
666
|
|
|
617
667
|
|
|
668
|
+
def _read_accounting_payload(path: Path) -> dict[str, Any] | None:
|
|
669
|
+
try:
|
|
670
|
+
payload = json.loads(path.read_text(encoding="utf-8"))
|
|
671
|
+
except (OSError, json.JSONDecodeError):
|
|
672
|
+
return None
|
|
673
|
+
if not isinstance(payload, dict):
|
|
674
|
+
return None
|
|
675
|
+
payload["artifact_path"] = str(path)
|
|
676
|
+
return payload
|
|
677
|
+
|
|
678
|
+
|
|
679
|
+
def _mcp_accounting_payload(
|
|
680
|
+
accounting: RunAccounting,
|
|
681
|
+
*,
|
|
682
|
+
output_dir: Path | None = None,
|
|
683
|
+
budget_limit_usd: float | None,
|
|
684
|
+
paid_capable: bool,
|
|
685
|
+
) -> dict[str, Any]:
|
|
686
|
+
path = None
|
|
687
|
+
if output_dir is not None:
|
|
688
|
+
path = maybe_write_run_accounting(
|
|
689
|
+
output_dir,
|
|
690
|
+
budget_limit_usd=budget_limit_usd,
|
|
691
|
+
paid_capable=paid_capable,
|
|
692
|
+
accounting=accounting,
|
|
693
|
+
)
|
|
694
|
+
if path is not None:
|
|
695
|
+
artifact_payload = _read_accounting_payload(path)
|
|
696
|
+
if artifact_payload is not None:
|
|
697
|
+
return artifact_payload
|
|
698
|
+
return accounting.to_dict()
|
|
699
|
+
|
|
700
|
+
|
|
701
|
+
def _pack_accounting_payload(pack_dir: Path) -> dict[str, Any] | None:
|
|
702
|
+
return _read_accounting_payload(pack_dir / ACCOUNTING_ARTIFACT)
|
|
703
|
+
|
|
704
|
+
|
|
618
705
|
async def _dispatch_tool(
|
|
619
706
|
name: str,
|
|
620
707
|
arguments: dict[str, Any],
|
|
@@ -669,6 +756,26 @@ async def _dispatch_tool(
|
|
|
669
756
|
cloud_agent_browser_binary = arguments.get("cloud_agent_browser_binary", "agent-browser")
|
|
670
757
|
if not isinstance(cloud_agent_browser_binary, str):
|
|
671
758
|
raise ValueError("'cloud_agent_browser_binary' must be a string")
|
|
759
|
+
output_dir = _path_arg(arguments, "output_dir", "rendered")
|
|
760
|
+
estimated = 0.0
|
|
761
|
+
paid_capable_render = backend in {"vercel-sandbox", "e2b-sandbox"}
|
|
762
|
+
accounting = RunAccounting(
|
|
763
|
+
budget_limit_usd=budget_limit,
|
|
764
|
+
estimated_paid_cost_usd=estimated,
|
|
765
|
+
route_steps=default_route_steps(
|
|
766
|
+
include_local_render=True,
|
|
767
|
+
include_cloud=paid_capable_render,
|
|
768
|
+
budget_limit_usd=budget_limit,
|
|
769
|
+
),
|
|
770
|
+
command="mcp render_url",
|
|
771
|
+
metadata={
|
|
772
|
+
"mcp_tool": "render_url",
|
|
773
|
+
"url": url,
|
|
774
|
+
"runtime": runtime,
|
|
775
|
+
"backend": backend,
|
|
776
|
+
},
|
|
777
|
+
)
|
|
778
|
+
render_blocked = False
|
|
672
779
|
if backend in {"vercel-sandbox", "e2b-sandbox"}:
|
|
673
780
|
from ..models.config import RenderConfig
|
|
674
781
|
from ..rendering import estimate_cloud_render_cost_usd
|
|
@@ -682,40 +789,77 @@ async def _dispatch_tool(
|
|
|
682
789
|
timeout_seconds=float(timeout_arg),
|
|
683
790
|
),
|
|
684
791
|
)
|
|
685
|
-
|
|
686
|
-
|
|
792
|
+
accounting.estimated_paid_cost_usd = estimated
|
|
793
|
+
if paid_action_blocked(budget_limit, estimated_cost_usd=estimated):
|
|
794
|
+
render_blocked = True
|
|
795
|
+
accounting.blocked_actions.append(
|
|
796
|
+
blocked_action(
|
|
797
|
+
f"render:{backend}",
|
|
798
|
+
budget_limit_usd=budget_limit,
|
|
799
|
+
estimated_cost_usd=estimated,
|
|
800
|
+
provider=backend,
|
|
801
|
+
)
|
|
802
|
+
)
|
|
803
|
+
accounting_payload = _mcp_accounting_payload(
|
|
804
|
+
accounting,
|
|
805
|
+
output_dir=output_dir,
|
|
806
|
+
budget_limit_usd=budget_limit,
|
|
807
|
+
paid_capable=True,
|
|
808
|
+
)
|
|
809
|
+
result = ToolResult(
|
|
810
|
+
f"Render blocked by budget before {backend}.",
|
|
811
|
+
data={
|
|
812
|
+
"url": url,
|
|
813
|
+
"backend": backend,
|
|
814
|
+
"output_dir": str(output_dir),
|
|
815
|
+
"dry_run": False,
|
|
816
|
+
**budget_block_payload(
|
|
817
|
+
f"render:{backend}",
|
|
818
|
+
budget_limit_usd=budget_limit,
|
|
819
|
+
estimated_cost_usd=estimated,
|
|
820
|
+
provider=backend,
|
|
821
|
+
),
|
|
822
|
+
"accounting": accounting_payload,
|
|
823
|
+
},
|
|
824
|
+
)
|
|
825
|
+
else:
|
|
826
|
+
accounting.paid_request_count = 1
|
|
827
|
+
if not render_blocked:
|
|
828
|
+
artifact = await render_url_to_directory(
|
|
829
|
+
url,
|
|
830
|
+
output_dir,
|
|
831
|
+
config={
|
|
832
|
+
"mode": "agent-browser",
|
|
833
|
+
"backend": backend,
|
|
834
|
+
"allowed_domains": _string_list_arg(arguments, "allowed_domains"),
|
|
835
|
+
"timeout_seconds": float(timeout_arg),
|
|
836
|
+
"wait_for": wait_for,
|
|
837
|
+
"cloud_agent_browser_install": cloud_agent_browser_install,
|
|
838
|
+
"cloud_result_transport": cloud_result_transport,
|
|
839
|
+
"cloud_max_estimated_cost_usd": cloud_max_estimated_cost,
|
|
840
|
+
"cloud_agent_browser_binary": cloud_agent_browser_binary,
|
|
841
|
+
"e2b_template": template,
|
|
842
|
+
},
|
|
843
|
+
)
|
|
844
|
+
accounting_payload = _mcp_accounting_payload(
|
|
845
|
+
accounting,
|
|
846
|
+
output_dir=output_dir,
|
|
687
847
|
budget_limit_usd=budget_limit,
|
|
688
|
-
|
|
689
|
-
|
|
848
|
+
paid_capable=paid_capable_render,
|
|
849
|
+
)
|
|
850
|
+
payload = {
|
|
851
|
+
"url": artifact.page.url,
|
|
852
|
+
"backend": artifact.page.backend,
|
|
853
|
+
"html_path": str(artifact.html_path),
|
|
854
|
+
"sidecar_path": str(artifact.sidecar_path),
|
|
855
|
+
"html_bytes": artifact.page.html_bytes,
|
|
856
|
+
"html_sha256": artifact.page.html_sha256,
|
|
857
|
+
"accounting": accounting_payload,
|
|
858
|
+
}
|
|
859
|
+
result = ToolResult(
|
|
860
|
+
f"Rendered {artifact.page.html_bytes} bytes: {artifact.html_path}",
|
|
861
|
+
data=payload,
|
|
690
862
|
)
|
|
691
|
-
artifact = await render_url_to_directory(
|
|
692
|
-
url,
|
|
693
|
-
_path_arg(arguments, "output_dir", "rendered"),
|
|
694
|
-
config={
|
|
695
|
-
"mode": "agent-browser",
|
|
696
|
-
"backend": backend,
|
|
697
|
-
"allowed_domains": _string_list_arg(arguments, "allowed_domains"),
|
|
698
|
-
"timeout_seconds": float(timeout_arg),
|
|
699
|
-
"wait_for": wait_for,
|
|
700
|
-
"cloud_agent_browser_install": cloud_agent_browser_install,
|
|
701
|
-
"cloud_result_transport": cloud_result_transport,
|
|
702
|
-
"cloud_max_estimated_cost_usd": cloud_max_estimated_cost,
|
|
703
|
-
"cloud_agent_browser_binary": cloud_agent_browser_binary,
|
|
704
|
-
"e2b_template": template,
|
|
705
|
-
},
|
|
706
|
-
)
|
|
707
|
-
payload = {
|
|
708
|
-
"url": artifact.page.url,
|
|
709
|
-
"backend": artifact.page.backend,
|
|
710
|
-
"html_path": str(artifact.html_path),
|
|
711
|
-
"sidecar_path": str(artifact.sidecar_path),
|
|
712
|
-
"html_bytes": artifact.page.html_bytes,
|
|
713
|
-
"html_sha256": artifact.page.html_sha256,
|
|
714
|
-
}
|
|
715
|
-
result = ToolResult(
|
|
716
|
-
f"Rendered {artifact.page.html_bytes} bytes: {artifact.html_path}",
|
|
717
|
-
data=payload,
|
|
718
|
-
)
|
|
719
863
|
|
|
720
864
|
elif name == "ensure_docs":
|
|
721
865
|
source = _require_str(arguments, "source")
|
|
@@ -794,6 +938,7 @@ async def _dispatch_tool(
|
|
|
794
938
|
max_estimated_cost,
|
|
795
939
|
float(budget_arg) if budget_arg is not None else None,
|
|
796
940
|
)
|
|
941
|
+
output_dir = _path_arg(arguments, "output_dir", "packs/parallel-context-pack")
|
|
797
942
|
request_options = _build_request_options(
|
|
798
943
|
source_policy=source_policy,
|
|
799
944
|
fetch_policy=fetch_policy,
|
|
@@ -808,6 +953,32 @@ async def _dispatch_tool(
|
|
|
808
953
|
else None,
|
|
809
954
|
full_content=True,
|
|
810
955
|
)
|
|
956
|
+
blocked_by_budget = paid_action_blocked(budget_limit, estimated_cost_usd=estimated_cost)
|
|
957
|
+
accounting = RunAccounting(
|
|
958
|
+
budget_limit_usd=budget_limit,
|
|
959
|
+
estimated_paid_cost_usd=estimated_cost,
|
|
960
|
+
route_steps=default_route_steps(
|
|
961
|
+
include_provider=True,
|
|
962
|
+
budget_limit_usd=budget_limit,
|
|
963
|
+
),
|
|
964
|
+
command="mcp parallel_context_pack",
|
|
965
|
+
metadata={
|
|
966
|
+
"mcp_tool": "parallel_context_pack",
|
|
967
|
+
"objective": objective,
|
|
968
|
+
"query_count": len(queries),
|
|
969
|
+
"extract_limit": extract_limit,
|
|
970
|
+
"max_search_results": max_search_results,
|
|
971
|
+
},
|
|
972
|
+
)
|
|
973
|
+
if blocked_by_budget:
|
|
974
|
+
accounting.blocked_actions.append(
|
|
975
|
+
blocked_action(
|
|
976
|
+
"parallel:context-pack",
|
|
977
|
+
budget_limit_usd=budget_limit,
|
|
978
|
+
estimated_cost_usd=estimated_cost,
|
|
979
|
+
provider="parallel",
|
|
980
|
+
)
|
|
981
|
+
)
|
|
811
982
|
if bool(arguments.get("dry_run", False)):
|
|
812
983
|
blocked = (
|
|
813
984
|
budget_block_payload(
|
|
@@ -816,60 +987,95 @@ async def _dispatch_tool(
|
|
|
816
987
|
estimated_cost_usd=estimated_cost,
|
|
817
988
|
provider="parallel",
|
|
818
989
|
)
|
|
819
|
-
if
|
|
990
|
+
if blocked_by_budget
|
|
820
991
|
else {}
|
|
821
992
|
)
|
|
993
|
+
accounting_payload = _mcp_accounting_payload(
|
|
994
|
+
accounting,
|
|
995
|
+
output_dir=None,
|
|
996
|
+
budget_limit_usd=budget_limit,
|
|
997
|
+
paid_capable=True,
|
|
998
|
+
)
|
|
822
999
|
result = ToolResult(
|
|
823
1000
|
"Parallel context pack dry run.",
|
|
824
1001
|
data={
|
|
825
1002
|
"workflow": "context-pack",
|
|
826
|
-
"output_dir": str(
|
|
1003
|
+
"output_dir": str(output_dir),
|
|
827
1004
|
"dry_run": True,
|
|
828
1005
|
"estimated_cost_usd": estimated_cost,
|
|
829
1006
|
"budget_limit_usd": budget_limit,
|
|
830
1007
|
"request_options": request_options,
|
|
831
1008
|
**blocked,
|
|
1009
|
+
"accounting": accounting_payload,
|
|
832
1010
|
},
|
|
833
1011
|
)
|
|
834
1012
|
else:
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
1013
|
+
if blocked_by_budget:
|
|
1014
|
+
accounting_payload = _mcp_accounting_payload(
|
|
1015
|
+
accounting,
|
|
1016
|
+
output_dir=output_dir,
|
|
1017
|
+
budget_limit_usd=budget_limit,
|
|
1018
|
+
paid_capable=True,
|
|
1019
|
+
)
|
|
1020
|
+
result = ToolResult(
|
|
1021
|
+
"Parallel context pack blocked by budget before provider call.",
|
|
1022
|
+
data={
|
|
1023
|
+
"workflow": "context-pack",
|
|
1024
|
+
"output_dir": str(output_dir),
|
|
1025
|
+
"dry_run": False,
|
|
1026
|
+
"estimated_cost_usd": estimated_cost,
|
|
1027
|
+
"budget_limit_usd": budget_limit,
|
|
1028
|
+
"request_options": request_options,
|
|
1029
|
+
**budget_block_payload(
|
|
1030
|
+
"parallel:context-pack",
|
|
1031
|
+
budget_limit_usd=budget_limit,
|
|
1032
|
+
estimated_cost_usd=estimated_cost,
|
|
1033
|
+
provider="parallel",
|
|
1034
|
+
),
|
|
1035
|
+
"accounting": accounting_payload,
|
|
1036
|
+
},
|
|
1037
|
+
)
|
|
1038
|
+
elif estimated_cost > max_estimated_cost:
|
|
842
1039
|
raise ValueError(
|
|
843
1040
|
f"Estimated Parallel cost ${estimated_cost:.3f} exceeds max_estimated_cost "
|
|
844
1041
|
f"${max_estimated_cost:.3f}"
|
|
845
1042
|
)
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
1043
|
+
else:
|
|
1044
|
+
mode_arg = arguments.get("mode")
|
|
1045
|
+
mode = mode_arg if isinstance(mode_arg, str) else "advanced"
|
|
1046
|
+
accounting.paid_request_count = 1
|
|
1047
|
+
pack_path = await asyncio.to_thread(
|
|
1048
|
+
run_live_context_pack,
|
|
1049
|
+
objective=objective,
|
|
1050
|
+
queries=queries,
|
|
1051
|
+
output_dir=output_dir,
|
|
1052
|
+
mode=mode,
|
|
1053
|
+
extract_limit=extract_limit,
|
|
1054
|
+
max_tokens_per_file=DEFAULT_MAX_TOKENS,
|
|
1055
|
+
source_policy=source_policy,
|
|
1056
|
+
max_search_results=max_search_results,
|
|
1057
|
+
client_model=arguments.get("client_model")
|
|
1058
|
+
if isinstance(arguments.get("client_model"), str)
|
|
1059
|
+
else None,
|
|
1060
|
+
estimated_cost_usd=estimated_cost,
|
|
1061
|
+
)
|
|
1062
|
+
accounting.metadata["output_dir"] = str(pack_path)
|
|
1063
|
+
accounting_payload = _mcp_accounting_payload(
|
|
1064
|
+
accounting,
|
|
1065
|
+
output_dir=pack_path,
|
|
1066
|
+
budget_limit_usd=budget_limit,
|
|
1067
|
+
paid_capable=True,
|
|
1068
|
+
)
|
|
1069
|
+
result = ToolResult(
|
|
1070
|
+
f"Wrote Parallel context pack: {pack_path}",
|
|
1071
|
+
data={
|
|
1072
|
+
"workflow": "context-pack",
|
|
1073
|
+
"output_dir": str(pack_path),
|
|
1074
|
+
"dry_run": False,
|
|
1075
|
+
"estimated_cost_usd": estimated_cost,
|
|
1076
|
+
"accounting": accounting_payload,
|
|
1077
|
+
},
|
|
1078
|
+
)
|
|
873
1079
|
|
|
874
1080
|
elif name == "parallel_api_pack":
|
|
875
1081
|
source = _require_str(arguments, "source")
|
|
@@ -1135,13 +1341,17 @@ async def _dispatch_tool(
|
|
|
1135
1341
|
)
|
|
1136
1342
|
|
|
1137
1343
|
elif name == "answer_pack":
|
|
1344
|
+
answer_pack_dir = _path_arg(arguments, "pack_dir")
|
|
1138
1345
|
answer_payload: dict[str, Any] = await asyncio.to_thread(
|
|
1139
1346
|
answer_pack,
|
|
1140
|
-
|
|
1347
|
+
answer_pack_dir,
|
|
1141
1348
|
_require_str(arguments, "question"),
|
|
1142
1349
|
required_domains=_string_list_arg(arguments, "required_domains"),
|
|
1143
1350
|
limit=_coerce_int(arguments.get("limit"), name="limit", default=8),
|
|
1144
1351
|
)
|
|
1352
|
+
existing_accounting = _pack_accounting_payload(answer_pack_dir)
|
|
1353
|
+
if existing_accounting is not None:
|
|
1354
|
+
answer_payload["accounting"] = existing_accounting
|
|
1145
1355
|
answer_raw = answer_payload.get("answer")
|
|
1146
1356
|
answer_data: dict[str, Any] = answer_raw if isinstance(answer_raw, dict) else {}
|
|
1147
1357
|
result = ToolResult(
|