capture-runtime-client 0.4.1__tar.gz → 0.4.3__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.
- {capture_runtime_client-0.4.1 → capture_runtime_client-0.4.3}/.gitignore +12 -10
- {capture_runtime_client-0.4.1 → capture_runtime_client-0.4.3}/PKG-INFO +1 -1
- {capture_runtime_client-0.4.1 → capture_runtime_client-0.4.3}/README.md +8 -8
- {capture_runtime_client-0.4.1 → capture_runtime_client-0.4.3}/project.json +25 -25
- {capture_runtime_client-0.4.1 → capture_runtime_client-0.4.3}/pyproject.toml +51 -49
- {capture_runtime_client-0.4.1 → capture_runtime_client-0.4.3}/src/capture_runtime_client/__init__.py +41 -41
- capture_runtime_client-0.4.3/src/capture_runtime_client/_discovery.py +353 -0
- capture_runtime_client-0.4.3/src/capture_runtime_client/_error_mapping.py +62 -0
- capture_runtime_client-0.4.3/src/capture_runtime_client/_retry.py +82 -0
- capture_runtime_client-0.4.3/src/capture_runtime_client/_transport.py +35 -0
- {capture_runtime_client-0.4.1 → capture_runtime_client-0.4.3}/src/capture_runtime_client/client.py +724 -994
- {capture_runtime_client-0.4.1 → capture_runtime_client-0.4.3}/src/capture_runtime_client/codec.py +129 -124
- {capture_runtime_client-0.4.1 → capture_runtime_client-0.4.3}/src/capture_runtime_client/contracts.py +365 -312
- {capture_runtime_client-0.4.1 → capture_runtime_client-0.4.3}/src/capture_runtime_client/errors.py +182 -170
- {capture_runtime_client-0.4.1 → capture_runtime_client-0.4.3}/src/capture_runtime_client/private/__init__.py +1 -1
- {capture_runtime_client-0.4.1 → capture_runtime_client-0.4.3}/src/capture_runtime_client/private/assets/__init__.py +1 -1
- capture_runtime_client-0.4.3/src/capture_runtime_client/private/assets/contract-set.json +1 -0
- capture_runtime_client-0.4.3/src/capture_runtime_client/private/assets/contract-set.sha256 +1 -0
- {capture_runtime_client-0.4.1 → capture_runtime_client-0.4.3}/src/capture_runtime_client/private/generated_models.py +1145 -767
- {capture_runtime_client-0.4.1 → capture_runtime_client-0.4.3}/src/capture_runtime_client/private/schemas/__init__.py +1 -1
- {capture_runtime_client-0.4.1 → capture_runtime_client-0.4.3}/src/capture_runtime_client/private/schemas/capture-block.schema.json +151 -151
- {capture_runtime_client-0.4.1 → capture_runtime_client-0.4.3}/src/capture_runtime_client/private/schemas/capture-engine.schema.json +40 -40
- {capture_runtime_client-0.4.1 → capture_runtime_client-0.4.3}/src/capture_runtime_client/private/schemas/capture-event-v2.schema.json +301 -301
- {capture_runtime_client-0.4.1 → capture_runtime_client-0.4.3}/src/capture_runtime_client/private/schemas/capture-failure-v2.schema.json +40 -40
- capture_runtime_client-0.4.3/src/capture_runtime_client/private/schemas/capture-ocr-projection-v3.schema.json +472 -0
- {capture_runtime_client-0.4.1 → capture_runtime_client-0.4.3}/src/capture_runtime_client/private/schemas/capture-operation-v2.schema.json +207 -207
- {capture_runtime_client-0.4.1 → capture_runtime_client-0.4.3}/src/capture_runtime_client/private/schemas/capture-source.schema.json +34 -34
- {capture_runtime_client-0.4.1 → capture_runtime_client-0.4.3}/src/capture_runtime_client/private/schemas/capture-streaming-result.schema.json +655 -607
- {capture_runtime_client-0.4.1 → capture_runtime_client-0.4.3}/src/capture_runtime_client/private/schemas/error-body-v2.schema.json +34 -34
- {capture_runtime_client-0.4.1 → capture_runtime_client-0.4.3}/src/capture_runtime_client/private/schemas/error-envelope-v2.schema.json +49 -49
- {capture_runtime_client-0.4.1 → capture_runtime_client-0.4.3}/src/capture_runtime_client/private/schemas/finalize-ingestion-v2.schema.json +27 -27
- {capture_runtime_client-0.4.1 → capture_runtime_client-0.4.3}/src/capture_runtime_client/private/schemas/ingestion-v2.schema.json +127 -127
- capture_runtime_client-0.4.3/src/capture_runtime_client/private/schemas/ocr-box-v3.schema.json +65 -0
- capture_runtime_client-0.4.3/src/capture_runtime_client/private/schemas/ocr-compute-preflight-v2.schema.json +128 -0
- capture_runtime_client-0.4.3/src/capture_runtime_client/private/schemas/ocr-page-projection-v3.schema.json +330 -0
- capture_runtime_client-0.4.3/src/capture_runtime_client/private/schemas/ocr-page-scope-v2.schema.json +37 -0
- capture_runtime_client-0.4.3/src/capture_runtime_client/private/schemas/ocr-point-v3.schema.json +22 -0
- capture_runtime_client-0.4.3/src/capture_runtime_client/private/schemas/ocr-provenance-resolved-v3.schema.json +55 -0
- capture_runtime_client-0.4.3/src/capture_runtime_client/private/schemas/ocr-provenance-unavailable-v3.schema.json +44 -0
- capture_runtime_client-0.4.3/src/capture_runtime_client/private/schemas/ocr-provenance-v3.schema.json +118 -0
- capture_runtime_client-0.4.3/src/capture_runtime_client/private/schemas/ocr-raster-v3.schema.json +34 -0
- {capture_runtime_client-0.4.1 → capture_runtime_client-0.4.3}/src/capture_runtime_client/private/schemas/open-ingestion-v2.schema.json +79 -79
- {capture_runtime_client-0.4.1 → capture_runtime_client-0.4.3}/src/capture_runtime_client/private/schemas/open-structuring-session-v2.schema.json +118 -118
- {capture_runtime_client-0.4.1 → capture_runtime_client-0.4.3}/src/capture_runtime_client/private/schemas/page-locator.schema.json +49 -49
- {capture_runtime_client-0.4.1 → capture_runtime_client-0.4.3}/src/capture_runtime_client/private/schemas/partial-capture-v2.schema.json +267 -267
- {capture_runtime_client-0.4.1 → capture_runtime_client-0.4.3}/src/capture_runtime_client/private/schemas/raw-capture-segment.schema.json +125 -125
- {capture_runtime_client-0.4.1 → capture_runtime_client-0.4.3}/src/capture_runtime_client/private/schemas/raw-capture.schema.json +308 -260
- {capture_runtime_client-0.4.1 → capture_runtime_client-0.4.3}/src/capture_runtime_client/private/schemas/report-structuring-failure-v2.schema.json +28 -28
- {capture_runtime_client-0.4.1 → capture_runtime_client-0.4.3}/src/capture_runtime_client/private/schemas/runtime-artifact-descriptor-v2.schema.json +34 -34
- {capture_runtime_client-0.4.1 → capture_runtime_client-0.4.3}/src/capture_runtime_client/private/schemas/runtime-installation-v2.schema.json +126 -126
- {capture_runtime_client-0.4.1 → capture_runtime_client-0.4.3}/src/capture_runtime_client/private/schemas/runtime-installations-v2.schema.json +143 -143
- {capture_runtime_client-0.4.1 → capture_runtime_client-0.4.3}/src/capture_runtime_client/private/schemas/runtime-model-installation-v2.schema.json +121 -121
- {capture_runtime_client-0.4.1 → capture_runtime_client-0.4.3}/src/capture_runtime_client/private/schemas/runtime-model-installations-v2.schema.json +138 -138
- {capture_runtime_client-0.4.1 → capture_runtime_client-0.4.3}/src/capture_runtime_client/private/schemas/runtime-model-option-v2.schema.json +80 -80
- {capture_runtime_client-0.4.1 → capture_runtime_client-0.4.3}/src/capture_runtime_client/private/schemas/runtime-model-options-v2.schema.json +103 -103
- capture_runtime_client-0.4.3/src/capture_runtime_client/private/schemas/runtime-ready.schema.json +227 -0
- {capture_runtime_client-0.4.1 → capture_runtime_client-0.4.3}/src/capture_runtime_client/private/schemas/runtime-requirement-v2.schema.json +120 -120
- {capture_runtime_client-0.4.1 → capture_runtime_client-0.4.3}/src/capture_runtime_client/private/schemas/runtime-requirements-v2.schema.json +137 -137
- {capture_runtime_client-0.4.1 → capture_runtime_client-0.4.3}/src/capture_runtime_client/private/schemas/runtime-streaming-capabilities-v2.schema.json +64 -64
- {capture_runtime_client-0.4.1 → capture_runtime_client-0.4.3}/src/capture_runtime_client/private/schemas/start-capture-v2.schema.json +76 -59
- {capture_runtime_client-0.4.1 → capture_runtime_client-0.4.3}/src/capture_runtime_client/private/schemas/start-runtime-installation-v2.schema.json +26 -26
- {capture_runtime_client-0.4.1 → capture_runtime_client-0.4.3}/src/capture_runtime_client/private/schemas/start-runtime-model-installation-v2.schema.json +21 -21
- {capture_runtime_client-0.4.1 → capture_runtime_client-0.4.3}/src/capture_runtime_client/private/schemas/structuring-batch-v2.schema.json +96 -96
- {capture_runtime_client-0.4.1 → capture_runtime_client-0.4.3}/src/capture_runtime_client/private/schemas/structuring-provider-capability-v2.schema.json +68 -68
- {capture_runtime_client-0.4.1 → capture_runtime_client-0.4.3}/src/capture_runtime_client/private/schemas/structuring-semantic-block-v2.schema.json +43 -43
- {capture_runtime_client-0.4.1 → capture_runtime_client-0.4.3}/src/capture_runtime_client/private/schemas/structuring-session-v2.schema.json +188 -188
- {capture_runtime_client-0.4.1 → capture_runtime_client-0.4.3}/src/capture_runtime_client/private/schemas/submit-structuring-batch-v2.schema.json +76 -76
- {capture_runtime_client-0.4.1 → capture_runtime_client-0.4.3}/src/capture_runtime_client/private/schemas/time-locator.schema.json +27 -27
- {capture_runtime_client-0.4.1 → capture_runtime_client-0.4.3}/src/capture_runtime_client/py.typed +1 -1
- {capture_runtime_client-0.4.1 → capture_runtime_client-0.4.3}/src/capture_runtime_client/transport.py +165 -158
- {capture_runtime_client-0.4.1 → capture_runtime_client-0.4.3}/tests/test_client_sdk.py +1299 -732
- {capture_runtime_client-0.4.1 → capture_runtime_client-0.4.3}/uv.lock +437 -437
- capture_runtime_client-0.4.1/src/capture_runtime_client/private/assets/contract-set.json +0 -1
- capture_runtime_client-0.4.1/src/capture_runtime_client/private/assets/contract-set.sha256 +0 -1
- capture_runtime_client-0.4.1/src/capture_runtime_client/private/schemas/runtime-ready.schema.json +0 -88
- {capture_runtime_client-0.4.1 → capture_runtime_client-0.4.3}/src/capture_runtime_client/private/schemas/capture-document.schema.json +0 -0
|
@@ -1,13 +1,15 @@
|
|
|
1
|
-
# See https://docs.github.com/en/get-started/getting-started-with-git/ignoring-files for more about ignoring files.
|
|
2
|
-
|
|
3
|
-
# compiled output
|
|
4
|
-
dist
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
1
|
+
# See https://docs.github.com/en/get-started/getting-started-with-git/ignoring-files for more about ignoring files.
|
|
2
|
+
|
|
3
|
+
# compiled output
|
|
4
|
+
dist
|
|
5
|
+
output/
|
|
6
|
+
proofshot-artifacts/
|
|
7
|
+
tmp
|
|
8
|
+
out-tsc
|
|
9
|
+
|
|
10
|
+
# dependencies
|
|
11
|
+
node_modules
|
|
12
|
+
tools/local-registry/storage/
|
|
11
13
|
tools/local-registry/htpasswd
|
|
12
14
|
|
|
13
15
|
# IDEs and editors
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
# capture-runtime-client
|
|
2
|
-
|
|
3
|
-
Backend-side Capture Runtime client SDK. It owns loopback endpoint
|
|
4
|
-
validation, readiness/schema-hash negotiation, authenticated HTTP and
|
|
5
|
-
deterministic in-memory transports, canonical v2 request methods, SSE decoding, and
|
|
6
|
-
redacted common errors. Generated Pydantic models are private SDK build input;
|
|
7
|
-
consumers import them from `capture_runtime_client`, never from a generated
|
|
8
|
-
contracts package.
|
|
1
|
+
# capture-runtime-client
|
|
2
|
+
|
|
3
|
+
Backend-side Capture Runtime client SDK. It owns loopback endpoint
|
|
4
|
+
validation, readiness/schema-hash negotiation, authenticated HTTP and
|
|
5
|
+
deterministic in-memory transports, canonical v2 request methods, SSE decoding, and
|
|
6
|
+
redacted common errors. Generated Pydantic models are private SDK build input;
|
|
7
|
+
consumers import them from `capture_runtime_client`, never from a generated
|
|
8
|
+
contracts package.
|
|
@@ -1,25 +1,25 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "capture-runtime-client-python",
|
|
3
|
-
"sourceRoot": "packages/capture-runtime-client-python/src",
|
|
4
|
-
"projectType": "library",
|
|
5
|
-
"tags": ["scope:capture", "type:sdk"],
|
|
6
|
-
"targets": {
|
|
7
|
-
"test": {
|
|
8
|
-
"executor": "nx:run-commands",
|
|
9
|
-
"options": { "command": "uv run --project packages/capture-runtime-client-python pytest packages/capture-runtime-client-python/tests" }
|
|
10
|
-
},
|
|
11
|
-
"lint": {
|
|
12
|
-
"executor": "nx:run-commands",
|
|
13
|
-
"options": { "command": "uv run --project packages/capture-runtime-client-python ruff check packages/capture-runtime-client-python/src packages/capture-runtime-client-python/tests" }
|
|
14
|
-
},
|
|
15
|
-
"typecheck": {
|
|
16
|
-
"executor": "nx:run-commands",
|
|
17
|
-
"options": { "command": "uv run --project packages/capture-runtime-client-python mypy packages/capture-runtime-client-python/src" }
|
|
18
|
-
},
|
|
19
|
-
"build": {
|
|
20
|
-
"executor": "nx:run-commands",
|
|
21
|
-
"outputs": ["{projectRoot}/dist"],
|
|
22
|
-
"options": { "command": "uv build --project packages/capture-runtime-client-python" }
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "capture-runtime-client-python",
|
|
3
|
+
"sourceRoot": "packages/capture-runtime-client-python/src",
|
|
4
|
+
"projectType": "library",
|
|
5
|
+
"tags": ["scope:capture", "type:sdk"],
|
|
6
|
+
"targets": {
|
|
7
|
+
"test": {
|
|
8
|
+
"executor": "nx:run-commands",
|
|
9
|
+
"options": { "command": "uv run --project packages/capture-runtime-client-python pytest packages/capture-runtime-client-python/tests" }
|
|
10
|
+
},
|
|
11
|
+
"lint": {
|
|
12
|
+
"executor": "nx:run-commands",
|
|
13
|
+
"options": { "command": "uv run --project packages/capture-runtime-client-python ruff check packages/capture-runtime-client-python/src packages/capture-runtime-client-python/tests" }
|
|
14
|
+
},
|
|
15
|
+
"typecheck": {
|
|
16
|
+
"executor": "nx:run-commands",
|
|
17
|
+
"options": { "command": "uv run --project packages/capture-runtime-client-python mypy packages/capture-runtime-client-python/src" }
|
|
18
|
+
},
|
|
19
|
+
"build": {
|
|
20
|
+
"executor": "nx:run-commands",
|
|
21
|
+
"outputs": ["{projectRoot}/dist"],
|
|
22
|
+
"options": { "command": "uv build --project packages/capture-runtime-client-python" }
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -1,49 +1,51 @@
|
|
|
1
|
-
[build-system]
|
|
2
|
-
requires = ["hatchling>=1.27.0,<2.0.0"]
|
|
3
|
-
build-backend = "hatchling.build"
|
|
4
|
-
|
|
5
|
-
[project]
|
|
6
|
-
name = "capture-runtime-client"
|
|
7
|
-
version = "0.4.
|
|
8
|
-
description = "Capture Runtime HTTP, in-memory, discovery, and protocol client SDK."
|
|
9
|
-
readme = "README.md"
|
|
10
|
-
requires-python = ">=3.12,<3.15"
|
|
11
|
-
license = { text = "MIT" }
|
|
12
|
-
dependencies = [
|
|
13
|
-
"httpx>=0.27.0,<1.0.0",
|
|
14
|
-
"pydantic>=2.13.4,<3.0.0",
|
|
15
|
-
]
|
|
16
|
-
|
|
17
|
-
[tool.capture_runtime_client]
|
|
18
|
-
contract_set_sha256 = "
|
|
19
|
-
|
|
20
|
-
[tool.hatch.build.targets.wheel]
|
|
21
|
-
packages = ["src/capture_runtime_client"]
|
|
22
|
-
|
|
23
|
-
[tool.hatch.build.targets.wheel.force-include]
|
|
24
|
-
"src/capture_runtime_client/py.typed" = "capture_runtime_client/py.typed"
|
|
25
|
-
"src/capture_runtime_client/private/assets/contract-set.json" = "capture_runtime_client/private/assets/contract-set.json"
|
|
26
|
-
"src/capture_runtime_client/private/assets/contract-set.sha256" = "capture_runtime_client/private/assets/contract-set.sha256"
|
|
27
|
-
|
|
28
|
-
[dependency-groups]
|
|
29
|
-
dev = [
|
|
30
|
-
"mypy>=1.14.0,<2.0.0",
|
|
31
|
-
"pytest>=8.3.0,<9.0.0",
|
|
32
|
-
"ruff>=0.9.0,<1.0.0",
|
|
33
|
-
]
|
|
34
|
-
|
|
35
|
-
[tool.pytest.ini_options]
|
|
36
|
-
pythonpath = ["src"]
|
|
37
|
-
testpaths = ["tests"]
|
|
38
|
-
addopts = "-ra"
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
[
|
|
48
|
-
|
|
49
|
-
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling>=1.27.0,<2.0.0"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "capture-runtime-client"
|
|
7
|
+
version = "0.4.3"
|
|
8
|
+
description = "Capture Runtime HTTP, in-memory, discovery, and protocol client SDK."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.12,<3.15"
|
|
11
|
+
license = { text = "MIT" }
|
|
12
|
+
dependencies = [
|
|
13
|
+
"httpx>=0.27.0,<1.0.0",
|
|
14
|
+
"pydantic>=2.13.4,<3.0.0",
|
|
15
|
+
]
|
|
16
|
+
|
|
17
|
+
[tool.capture_runtime_client]
|
|
18
|
+
contract_set_sha256 = "232ef06bf547e79120df28f39303e73b0e5842beace5910a422f15dfb5e2acbc"
|
|
19
|
+
|
|
20
|
+
[tool.hatch.build.targets.wheel]
|
|
21
|
+
packages = ["src/capture_runtime_client"]
|
|
22
|
+
|
|
23
|
+
[tool.hatch.build.targets.wheel.force-include]
|
|
24
|
+
"src/capture_runtime_client/py.typed" = "capture_runtime_client/py.typed"
|
|
25
|
+
"src/capture_runtime_client/private/assets/contract-set.json" = "capture_runtime_client/private/assets/contract-set.json"
|
|
26
|
+
"src/capture_runtime_client/private/assets/contract-set.sha256" = "capture_runtime_client/private/assets/contract-set.sha256"
|
|
27
|
+
|
|
28
|
+
[dependency-groups]
|
|
29
|
+
dev = [
|
|
30
|
+
"mypy>=1.14.0,<2.0.0",
|
|
31
|
+
"pytest>=8.3.0,<9.0.0",
|
|
32
|
+
"ruff>=0.9.0,<1.0.0",
|
|
33
|
+
]
|
|
34
|
+
|
|
35
|
+
[tool.pytest.ini_options]
|
|
36
|
+
pythonpath = ["src"]
|
|
37
|
+
testpaths = ["tests"]
|
|
38
|
+
addopts = "-ra"
|
|
39
|
+
# Remove tmp_path directories after every session; tests must not leave temp files.
|
|
40
|
+
tmp_path_retention_policy = "failed"
|
|
41
|
+
|
|
42
|
+
[tool.ruff]
|
|
43
|
+
line-length = 100
|
|
44
|
+
target-version = "py312"
|
|
45
|
+
|
|
46
|
+
[tool.ruff.lint]
|
|
47
|
+
select = ["B", "E", "F", "I", "UP"]
|
|
48
|
+
|
|
49
|
+
[tool.mypy]
|
|
50
|
+
python_version = "3.12"
|
|
51
|
+
strict = true
|
{capture_runtime_client-0.4.1 → capture_runtime_client-0.4.3}/src/capture_runtime_client/__init__.py
RENAMED
|
@@ -1,41 +1,41 @@
|
|
|
1
|
-
"""Public Capture Runtime client SDK."""
|
|
2
|
-
|
|
3
|
-
from .client import CaptureRuntimeClient, CaptureStreamingResult, CaptureUpload, RuntimeDiscovery
|
|
4
|
-
from .contracts import * # noqa: F401,F403
|
|
5
|
-
from .contracts import __all__ as _contract_names
|
|
6
|
-
from .errors import (
|
|
7
|
-
CaptureAuthenticationError,
|
|
8
|
-
CaptureCompatibilityError,
|
|
9
|
-
CaptureProtocolError,
|
|
10
|
-
CaptureRemoteError,
|
|
11
|
-
CaptureRuntimeCompatibilityError,
|
|
12
|
-
CaptureRuntimeError,
|
|
13
|
-
CaptureRuntimeProtocolError,
|
|
14
|
-
CaptureTransportError,
|
|
15
|
-
)
|
|
16
|
-
from .transport import (
|
|
17
|
-
HttpRuntimeTransport,
|
|
18
|
-
InMemoryRoute,
|
|
19
|
-
InMemoryRuntimeTransport,
|
|
20
|
-
validate_loopback_base_url,
|
|
21
|
-
)
|
|
22
|
-
|
|
23
|
-
__all__ = [
|
|
24
|
-
"CaptureRuntimeClient",
|
|
25
|
-
"CaptureAuthenticationError",
|
|
26
|
-
"CaptureCompatibilityError",
|
|
27
|
-
"CaptureProtocolError",
|
|
28
|
-
"CaptureRemoteError",
|
|
29
|
-
"CaptureTransportError",
|
|
30
|
-
"CaptureRuntimeCompatibilityError",
|
|
31
|
-
"CaptureRuntimeError",
|
|
32
|
-
"CaptureRuntimeProtocolError",
|
|
33
|
-
"CaptureStreamingResult",
|
|
34
|
-
"CaptureUpload",
|
|
35
|
-
"HttpRuntimeTransport",
|
|
36
|
-
"InMemoryRoute",
|
|
37
|
-
"InMemoryRuntimeTransport",
|
|
38
|
-
"RuntimeDiscovery",
|
|
39
|
-
"validate_loopback_base_url",
|
|
40
|
-
*_contract_names,
|
|
41
|
-
]
|
|
1
|
+
"""Public Capture Runtime client SDK."""
|
|
2
|
+
|
|
3
|
+
from .client import CaptureRuntimeClient, CaptureStreamingResult, CaptureUpload, RuntimeDiscovery
|
|
4
|
+
from .contracts import * # noqa: F401,F403
|
|
5
|
+
from .contracts import __all__ as _contract_names
|
|
6
|
+
from .errors import (
|
|
7
|
+
CaptureAuthenticationError,
|
|
8
|
+
CaptureCompatibilityError,
|
|
9
|
+
CaptureProtocolError,
|
|
10
|
+
CaptureRemoteError,
|
|
11
|
+
CaptureRuntimeCompatibilityError,
|
|
12
|
+
CaptureRuntimeError,
|
|
13
|
+
CaptureRuntimeProtocolError,
|
|
14
|
+
CaptureTransportError,
|
|
15
|
+
)
|
|
16
|
+
from .transport import (
|
|
17
|
+
HttpRuntimeTransport,
|
|
18
|
+
InMemoryRoute,
|
|
19
|
+
InMemoryRuntimeTransport,
|
|
20
|
+
validate_loopback_base_url,
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
__all__ = [
|
|
24
|
+
"CaptureRuntimeClient",
|
|
25
|
+
"CaptureAuthenticationError",
|
|
26
|
+
"CaptureCompatibilityError",
|
|
27
|
+
"CaptureProtocolError",
|
|
28
|
+
"CaptureRemoteError",
|
|
29
|
+
"CaptureTransportError",
|
|
30
|
+
"CaptureRuntimeCompatibilityError",
|
|
31
|
+
"CaptureRuntimeError",
|
|
32
|
+
"CaptureRuntimeProtocolError",
|
|
33
|
+
"CaptureStreamingResult",
|
|
34
|
+
"CaptureUpload",
|
|
35
|
+
"HttpRuntimeTransport",
|
|
36
|
+
"InMemoryRoute",
|
|
37
|
+
"InMemoryRuntimeTransport",
|
|
38
|
+
"RuntimeDiscovery",
|
|
39
|
+
"validate_loopback_base_url",
|
|
40
|
+
*_contract_names,
|
|
41
|
+
]
|
|
@@ -0,0 +1,353 @@
|
|
|
1
|
+
"""Private authenticated runtime discovery and contract validation."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import hashlib
|
|
6
|
+
import json
|
|
7
|
+
import re
|
|
8
|
+
from collections.abc import Callable, Collection, Mapping
|
|
9
|
+
from dataclasses import dataclass
|
|
10
|
+
from typing import Any
|
|
11
|
+
|
|
12
|
+
import httpx
|
|
13
|
+
|
|
14
|
+
from ._error_mapping import decode_response
|
|
15
|
+
from .codec import decode_model
|
|
16
|
+
from .contracts import (
|
|
17
|
+
CAPTURE_API_VERSION,
|
|
18
|
+
CAPTURE_DOCUMENT_SCHEMA_SHA256,
|
|
19
|
+
CAPTURE_DOCUMENT_SCHEMA_VERSION,
|
|
20
|
+
CAPTURE_RUNTIME_VERSION,
|
|
21
|
+
RuntimeReady,
|
|
22
|
+
RuntimeStreamingCapabilities,
|
|
23
|
+
StructuringMode,
|
|
24
|
+
)
|
|
25
|
+
from .errors import CaptureRuntimeCompatibilityError, CaptureRuntimeProtocolError
|
|
26
|
+
|
|
27
|
+
RequestFn = Callable[..., httpx.Response]
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@dataclass(frozen=True, slots=True)
|
|
31
|
+
class RuntimeDiscovery:
|
|
32
|
+
"""Authenticated compatibility evidence cached by ``CaptureRuntimeClient``."""
|
|
33
|
+
|
|
34
|
+
ready: RuntimeReady
|
|
35
|
+
streaming: RuntimeStreamingCapabilities | None
|
|
36
|
+
schema_sha256: str
|
|
37
|
+
contract_index: Mapping[str, Any]
|
|
38
|
+
contract_bundle: Mapping[str, Any]
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def handshake(request: RequestFn) -> RuntimeReady:
|
|
42
|
+
"""Validate readiness using the SDK's compiled v2 compatibility identity."""
|
|
43
|
+
response = request("GET", "/v2/health/ready")
|
|
44
|
+
payload = decode_response(response)
|
|
45
|
+
if not isinstance(payload, Mapping):
|
|
46
|
+
raise CaptureRuntimeProtocolError("Capture Runtime readiness response is not an object.")
|
|
47
|
+
assert_compatible_payload(payload)
|
|
48
|
+
ready = decode_model(httpx.Response(200, json=payload), RuntimeReady)
|
|
49
|
+
assert_compatible(ready)
|
|
50
|
+
return ready
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def discover(
|
|
54
|
+
request: RequestFn,
|
|
55
|
+
*,
|
|
56
|
+
allowed_contract_set_sha256: Collection[str],
|
|
57
|
+
) -> RuntimeDiscovery:
|
|
58
|
+
"""Perform one authenticated, immutable contract discovery transaction."""
|
|
59
|
+
ready = handshake(request)
|
|
60
|
+
index_response = request("GET", "/meta/v2/contracts")
|
|
61
|
+
index = decode_response(index_response)
|
|
62
|
+
if not isinstance(index, Mapping):
|
|
63
|
+
raise CaptureRuntimeProtocolError("Capture Runtime contract index is not an object.")
|
|
64
|
+
validate_contract_index(index, ready)
|
|
65
|
+
href = index.get("href")
|
|
66
|
+
if not isinstance(href, str) or not href.startswith("/meta/v2/contracts/sha256/"):
|
|
67
|
+
raise CaptureRuntimeProtocolError("Capture Runtime contract index href is invalid.")
|
|
68
|
+
href_digest = href.removeprefix("/meta/v2/contracts/sha256/")
|
|
69
|
+
if not re.fullmatch(r"[0-9a-f]{64}", href_digest) or href_digest != index.get("sha256"):
|
|
70
|
+
raise CaptureRuntimeCompatibilityError(
|
|
71
|
+
"Capture Runtime contract index href digest does not match its advertised bundle hash."
|
|
72
|
+
)
|
|
73
|
+
bundle_response = request("GET", href)
|
|
74
|
+
bundle_bytes = bundle_response.content
|
|
75
|
+
digest = hashlib.sha256(bundle_bytes).hexdigest()
|
|
76
|
+
if digest not in allowed_contract_set_sha256:
|
|
77
|
+
raise CaptureRuntimeCompatibilityError(
|
|
78
|
+
"Capture Runtime contract bundle identity is not allowlisted."
|
|
79
|
+
)
|
|
80
|
+
etag = bundle_response.headers.get("etag")
|
|
81
|
+
if (
|
|
82
|
+
digest != index.get("sha256")
|
|
83
|
+
or bundle_response.headers.get("x-contract-sha256") not in {None, digest}
|
|
84
|
+
or (etag is not None and etag not in {digest, f'"{digest}"'})
|
|
85
|
+
):
|
|
86
|
+
raise CaptureRuntimeCompatibilityError(
|
|
87
|
+
"Capture Runtime contract bundle hash is incompatible."
|
|
88
|
+
)
|
|
89
|
+
try:
|
|
90
|
+
bundle = json.loads(bundle_bytes)
|
|
91
|
+
except (UnicodeDecodeError, json.JSONDecodeError) as error:
|
|
92
|
+
raise CaptureRuntimeProtocolError(
|
|
93
|
+
"Capture Runtime contract bundle is not valid JSON."
|
|
94
|
+
) from error
|
|
95
|
+
schema_sha256 = validate_contract_bundle(bundle, digest)
|
|
96
|
+
streaming = decode_model(
|
|
97
|
+
request("GET", "/v2/streaming/health/ready"), RuntimeStreamingCapabilities
|
|
98
|
+
)
|
|
99
|
+
return RuntimeDiscovery(
|
|
100
|
+
ready=ready,
|
|
101
|
+
streaming=streaming,
|
|
102
|
+
schema_sha256=schema_sha256,
|
|
103
|
+
contract_index=index,
|
|
104
|
+
contract_bundle=bundle,
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def assert_compatible(ready: RuntimeReady) -> None:
|
|
109
|
+
failures: list[str] = []
|
|
110
|
+
if ready.api_version != CAPTURE_API_VERSION:
|
|
111
|
+
failures.append(f"API version {ready.api_version} is unsupported")
|
|
112
|
+
if ready.capture_document_schema_version != CAPTURE_DOCUMENT_SCHEMA_VERSION:
|
|
113
|
+
failures.append("CaptureDocument schema version is unsupported")
|
|
114
|
+
if ready.runtime_version != CAPTURE_RUNTIME_VERSION:
|
|
115
|
+
failures.append(
|
|
116
|
+
f"runtime version {ready.runtime_version} is incompatible with "
|
|
117
|
+
f"{CAPTURE_RUNTIME_VERSION}"
|
|
118
|
+
)
|
|
119
|
+
structuring_modes = ready.capabilities.get("structuringModes", [])
|
|
120
|
+
if (
|
|
121
|
+
StructuringMode.HOST.value not in structuring_modes
|
|
122
|
+
and StructuringMode.RUNTIME.value not in structuring_modes
|
|
123
|
+
):
|
|
124
|
+
failures.append("runtime exposes no structuring mode")
|
|
125
|
+
if failures:
|
|
126
|
+
raise CaptureRuntimeCompatibilityError("; ".join(failures))
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def assert_compatible_payload(payload: Mapping[str, Any]) -> None:
|
|
130
|
+
failures: list[str] = []
|
|
131
|
+
if payload.get("apiVersion") != CAPTURE_API_VERSION:
|
|
132
|
+
failures.append("API version is unsupported")
|
|
133
|
+
if payload.get("captureDocumentSchemaVersion") != CAPTURE_DOCUMENT_SCHEMA_VERSION:
|
|
134
|
+
failures.append("CaptureDocument schema version is unsupported")
|
|
135
|
+
if failures:
|
|
136
|
+
raise CaptureRuntimeCompatibilityError("; ".join(failures))
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def validate_contract_index(index: Mapping[str, Any], ready: RuntimeReady) -> None:
|
|
140
|
+
required = {
|
|
141
|
+
"catalogVersion",
|
|
142
|
+
"runtimeVersion",
|
|
143
|
+
"contractSetVersion",
|
|
144
|
+
"surfaces",
|
|
145
|
+
"sha256",
|
|
146
|
+
"href",
|
|
147
|
+
}
|
|
148
|
+
if not required.issubset(index):
|
|
149
|
+
raise CaptureRuntimeProtocolError(
|
|
150
|
+
"Capture Runtime contract index is missing required fields."
|
|
151
|
+
)
|
|
152
|
+
if index.get("catalogVersion") != "2" or index.get("contractSetVersion") != "2":
|
|
153
|
+
raise CaptureRuntimeCompatibilityError(
|
|
154
|
+
"Capture Runtime contract catalog version is unsupported."
|
|
155
|
+
)
|
|
156
|
+
if index.get("runtimeVersion") != ready.runtime_version:
|
|
157
|
+
raise CaptureRuntimeCompatibilityError(
|
|
158
|
+
"Capture Runtime contract catalog runtimeVersion is incompatible."
|
|
159
|
+
)
|
|
160
|
+
if not isinstance(index.get("sha256"), str) or not re.fullmatch(
|
|
161
|
+
r"[0-9a-f]{64}", index["sha256"]
|
|
162
|
+
):
|
|
163
|
+
raise CaptureRuntimeProtocolError("Capture Runtime contract index hash is invalid.")
|
|
164
|
+
surfaces = index.get("surfaces")
|
|
165
|
+
if not isinstance(surfaces, list) or {
|
|
166
|
+
surface.get("id") for surface in surfaces if isinstance(surface, Mapping)
|
|
167
|
+
} != {"v2"}:
|
|
168
|
+
raise CaptureRuntimeCompatibilityError(
|
|
169
|
+
"Capture Runtime contract catalog does not expose the v2 surface."
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
def validate_contract_bundle(bundle: Any, digest: str) -> str:
|
|
174
|
+
if (
|
|
175
|
+
not isinstance(bundle, Mapping)
|
|
176
|
+
or bundle.get("contractSetVersion") != "2"
|
|
177
|
+
or bundle.get("schemaDialect") != "https://json-schema.org/draft/2020-12/schema"
|
|
178
|
+
):
|
|
179
|
+
raise CaptureRuntimeProtocolError("Capture Runtime contract bundle is invalid.")
|
|
180
|
+
for field in ("surfaces", "schemas", "operations", "problems", "invariants"):
|
|
181
|
+
if not isinstance(bundle.get(field), list):
|
|
182
|
+
raise CaptureRuntimeProtocolError(
|
|
183
|
+
f"Capture Runtime contract bundle {field} is invalid."
|
|
184
|
+
)
|
|
185
|
+
surfaces = bundle["surfaces"]
|
|
186
|
+
surface_ids = {surface.get("id") for surface in surfaces if isinstance(surface, Mapping)}
|
|
187
|
+
if surface_ids != {"v2"}:
|
|
188
|
+
raise CaptureRuntimeCompatibilityError(
|
|
189
|
+
"Capture Runtime contract bundle does not expose the v2 surface."
|
|
190
|
+
)
|
|
191
|
+
operations_value = bundle["operations"]
|
|
192
|
+
if not isinstance(operations_value, list):
|
|
193
|
+
raise CaptureRuntimeProtocolError("Capture Runtime contract bundle operations are invalid.")
|
|
194
|
+
operations: list[Any] = operations_value
|
|
195
|
+
for operation in operations:
|
|
196
|
+
if not isinstance(operation, Mapping) or not all(
|
|
197
|
+
isinstance(operation.get(field), expected)
|
|
198
|
+
for field, expected in (
|
|
199
|
+
("path", str),
|
|
200
|
+
("method", str),
|
|
201
|
+
("surface", str),
|
|
202
|
+
("body", Mapping),
|
|
203
|
+
("requiredHeaders", list),
|
|
204
|
+
("idempotency", Mapping),
|
|
205
|
+
("responseStatusCodes", list),
|
|
206
|
+
)
|
|
207
|
+
):
|
|
208
|
+
raise CaptureRuntimeProtocolError(
|
|
209
|
+
"Capture Runtime contract operation metadata is invalid."
|
|
210
|
+
)
|
|
211
|
+
operation_paths = {
|
|
212
|
+
operation.get("path") for operation in operations if isinstance(operation, Mapping)
|
|
213
|
+
}
|
|
214
|
+
required_paths = {
|
|
215
|
+
"/v2/health/ready",
|
|
216
|
+
"/v2/streaming/health/ready",
|
|
217
|
+
"/v2/runtime/requirements",
|
|
218
|
+
"/v2/runtime/installations",
|
|
219
|
+
"/v2/runtime/model-options",
|
|
220
|
+
"/v2/runtime/model-installations",
|
|
221
|
+
"/v2/captures",
|
|
222
|
+
"/v2/captures/{capture_id}/events",
|
|
223
|
+
"/v2/captures/{capture_id}/raw",
|
|
224
|
+
"/v2/captures/{capture_id}/ocr",
|
|
225
|
+
"/v2/captures/{capture_id}/result",
|
|
226
|
+
"/v2/captures/{capture_id}/structure/session",
|
|
227
|
+
"/v2/captures/{capture_id}/structure/session/batches/{batch_index}",
|
|
228
|
+
}
|
|
229
|
+
if not required_paths.issubset(operation_paths):
|
|
230
|
+
raise CaptureRuntimeCompatibilityError(
|
|
231
|
+
"Capture Runtime contract bundle does not advertise the required client surface."
|
|
232
|
+
)
|
|
233
|
+
|
|
234
|
+
def find_operation(path: str, method: str | None = None) -> Mapping[str, Any] | None:
|
|
235
|
+
return next(
|
|
236
|
+
(
|
|
237
|
+
item
|
|
238
|
+
for item in operations
|
|
239
|
+
if isinstance(item, Mapping)
|
|
240
|
+
and item.get("path") == path
|
|
241
|
+
and (method is None or item.get("method") == method)
|
|
242
|
+
),
|
|
243
|
+
None,
|
|
244
|
+
)
|
|
245
|
+
|
|
246
|
+
upload = find_operation("/v2/captures")
|
|
247
|
+
chunk = find_operation("/v2/ingestions/{ingestion_id}/chunks/{chunk_index}")
|
|
248
|
+
events = find_operation("/v2/captures/{capture_id}/events")
|
|
249
|
+
ocr = find_operation("/v2/captures/{capture_id}/ocr", "GET")
|
|
250
|
+
session_open = find_operation("/v2/captures/{capture_id}/structure/session", "POST")
|
|
251
|
+
batch_get = find_operation(
|
|
252
|
+
"/v2/captures/{capture_id}/structure/session/batches/{batch_index}", "GET"
|
|
253
|
+
)
|
|
254
|
+
batch_submit = find_operation(
|
|
255
|
+
"/v2/captures/{capture_id}/structure/session/batches/{batch_index}", "PUT"
|
|
256
|
+
)
|
|
257
|
+
if (
|
|
258
|
+
upload is None
|
|
259
|
+
or not isinstance(upload.get("body"), Mapping)
|
|
260
|
+
or upload["body"].get("kind") not in {"json", "none"}
|
|
261
|
+
or "X-Idempotency-Key" not in upload.get("requiredHeaders", [])
|
|
262
|
+
):
|
|
263
|
+
raise CaptureRuntimeCompatibilityError(
|
|
264
|
+
"Capture Runtime v2 capture metadata is incompatible."
|
|
265
|
+
)
|
|
266
|
+
if (
|
|
267
|
+
chunk is None
|
|
268
|
+
or not isinstance(chunk.get("body"), Mapping)
|
|
269
|
+
or chunk["body"].get("kind") != "binary"
|
|
270
|
+
or not all(
|
|
271
|
+
header in chunk.get("requiredHeaders", [])
|
|
272
|
+
for header in ("Content-Range", "Digest", "X-Idempotency-Key")
|
|
273
|
+
)
|
|
274
|
+
):
|
|
275
|
+
raise CaptureRuntimeCompatibilityError("Capture Runtime v2 chunk metadata is incompatible.")
|
|
276
|
+
streaming = events.get("streaming") if events is not None else None
|
|
277
|
+
if (
|
|
278
|
+
events is None
|
|
279
|
+
or events.get("mediaType") != "text/event-stream"
|
|
280
|
+
or not isinstance(streaming, Mapping)
|
|
281
|
+
or streaming.get("kind") != "sse"
|
|
282
|
+
or streaming.get("lastEventIdHeader") != "Last-Event-ID"
|
|
283
|
+
):
|
|
284
|
+
raise CaptureRuntimeCompatibilityError("Capture Runtime SSE metadata is incompatible.")
|
|
285
|
+
if (
|
|
286
|
+
ocr is None
|
|
287
|
+
or not isinstance(ocr.get("body"), Mapping)
|
|
288
|
+
or ocr["body"].get("kind") != "none"
|
|
289
|
+
or ocr.get("responseSchema") != "CaptureOcrProjectionV3"
|
|
290
|
+
or 200 not in ocr.get("responseStatusCodes", [])
|
|
291
|
+
):
|
|
292
|
+
raise CaptureRuntimeCompatibilityError("Capture Runtime OCR metadata is incompatible.")
|
|
293
|
+
if (
|
|
294
|
+
session_open is None
|
|
295
|
+
or not isinstance(session_open.get("body"), Mapping)
|
|
296
|
+
or session_open["body"].get("kind") != "json"
|
|
297
|
+
or "X-Idempotency-Key" not in session_open.get("requiredHeaders", [])
|
|
298
|
+
or session_open.get("idempotency", {}).get("mode") != "required"
|
|
299
|
+
or batch_get is None
|
|
300
|
+
or not isinstance(batch_get.get("body"), Mapping)
|
|
301
|
+
or batch_get["body"].get("kind") != "none"
|
|
302
|
+
or batch_submit is None
|
|
303
|
+
or not isinstance(batch_submit.get("body"), Mapping)
|
|
304
|
+
or batch_submit["body"].get("kind") != "json"
|
|
305
|
+
or "X-Idempotency-Key" not in batch_submit.get("requiredHeaders", [])
|
|
306
|
+
or batch_submit.get("idempotency", {}).get("mode") != "required"
|
|
307
|
+
):
|
|
308
|
+
raise CaptureRuntimeCompatibilityError(
|
|
309
|
+
"Capture Runtime pull-session metadata is incompatible."
|
|
310
|
+
)
|
|
311
|
+
canonical_bundle = json.dumps(
|
|
312
|
+
bundle,
|
|
313
|
+
ensure_ascii=False,
|
|
314
|
+
sort_keys=True,
|
|
315
|
+
separators=(",", ":"),
|
|
316
|
+
allow_nan=False,
|
|
317
|
+
).encode("utf-8")
|
|
318
|
+
if hashlib.sha256(canonical_bundle).hexdigest() != digest:
|
|
319
|
+
raise CaptureRuntimeCompatibilityError(
|
|
320
|
+
"Capture Runtime contract bundle bytes are not canonical."
|
|
321
|
+
)
|
|
322
|
+
schemas = bundle.get("schemas")
|
|
323
|
+
document = (
|
|
324
|
+
next(
|
|
325
|
+
(
|
|
326
|
+
item
|
|
327
|
+
for item in schemas
|
|
328
|
+
if isinstance(item, Mapping) and item.get("name") == "CaptureDocument"
|
|
329
|
+
),
|
|
330
|
+
None,
|
|
331
|
+
)
|
|
332
|
+
if isinstance(schemas, list)
|
|
333
|
+
else None
|
|
334
|
+
)
|
|
335
|
+
if (
|
|
336
|
+
not isinstance(document, Mapping)
|
|
337
|
+
or document.get("schemaSha256") != CAPTURE_DOCUMENT_SCHEMA_SHA256
|
|
338
|
+
):
|
|
339
|
+
raise CaptureRuntimeCompatibilityError(
|
|
340
|
+
"Capture Runtime document schema hash is incompatible."
|
|
341
|
+
)
|
|
342
|
+
return str(document["schemaSha256"])
|
|
343
|
+
|
|
344
|
+
|
|
345
|
+
__all__ = [
|
|
346
|
+
"RuntimeDiscovery",
|
|
347
|
+
"assert_compatible",
|
|
348
|
+
"assert_compatible_payload",
|
|
349
|
+
"discover",
|
|
350
|
+
"handshake",
|
|
351
|
+
"validate_contract_bundle",
|
|
352
|
+
"validate_contract_index",
|
|
353
|
+
]
|