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.
Files changed (76) hide show
  1. {capture_runtime_client-0.4.1 → capture_runtime_client-0.4.3}/.gitignore +12 -10
  2. {capture_runtime_client-0.4.1 → capture_runtime_client-0.4.3}/PKG-INFO +1 -1
  3. {capture_runtime_client-0.4.1 → capture_runtime_client-0.4.3}/README.md +8 -8
  4. {capture_runtime_client-0.4.1 → capture_runtime_client-0.4.3}/project.json +25 -25
  5. {capture_runtime_client-0.4.1 → capture_runtime_client-0.4.3}/pyproject.toml +51 -49
  6. {capture_runtime_client-0.4.1 → capture_runtime_client-0.4.3}/src/capture_runtime_client/__init__.py +41 -41
  7. capture_runtime_client-0.4.3/src/capture_runtime_client/_discovery.py +353 -0
  8. capture_runtime_client-0.4.3/src/capture_runtime_client/_error_mapping.py +62 -0
  9. capture_runtime_client-0.4.3/src/capture_runtime_client/_retry.py +82 -0
  10. capture_runtime_client-0.4.3/src/capture_runtime_client/_transport.py +35 -0
  11. {capture_runtime_client-0.4.1 → capture_runtime_client-0.4.3}/src/capture_runtime_client/client.py +724 -994
  12. {capture_runtime_client-0.4.1 → capture_runtime_client-0.4.3}/src/capture_runtime_client/codec.py +129 -124
  13. {capture_runtime_client-0.4.1 → capture_runtime_client-0.4.3}/src/capture_runtime_client/contracts.py +365 -312
  14. {capture_runtime_client-0.4.1 → capture_runtime_client-0.4.3}/src/capture_runtime_client/errors.py +182 -170
  15. {capture_runtime_client-0.4.1 → capture_runtime_client-0.4.3}/src/capture_runtime_client/private/__init__.py +1 -1
  16. {capture_runtime_client-0.4.1 → capture_runtime_client-0.4.3}/src/capture_runtime_client/private/assets/__init__.py +1 -1
  17. capture_runtime_client-0.4.3/src/capture_runtime_client/private/assets/contract-set.json +1 -0
  18. capture_runtime_client-0.4.3/src/capture_runtime_client/private/assets/contract-set.sha256 +1 -0
  19. {capture_runtime_client-0.4.1 → capture_runtime_client-0.4.3}/src/capture_runtime_client/private/generated_models.py +1145 -767
  20. {capture_runtime_client-0.4.1 → capture_runtime_client-0.4.3}/src/capture_runtime_client/private/schemas/__init__.py +1 -1
  21. {capture_runtime_client-0.4.1 → capture_runtime_client-0.4.3}/src/capture_runtime_client/private/schemas/capture-block.schema.json +151 -151
  22. {capture_runtime_client-0.4.1 → capture_runtime_client-0.4.3}/src/capture_runtime_client/private/schemas/capture-engine.schema.json +40 -40
  23. {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
  24. {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
  25. capture_runtime_client-0.4.3/src/capture_runtime_client/private/schemas/capture-ocr-projection-v3.schema.json +472 -0
  26. {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
  27. {capture_runtime_client-0.4.1 → capture_runtime_client-0.4.3}/src/capture_runtime_client/private/schemas/capture-source.schema.json +34 -34
  28. {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
  29. {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
  30. {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
  31. {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
  32. {capture_runtime_client-0.4.1 → capture_runtime_client-0.4.3}/src/capture_runtime_client/private/schemas/ingestion-v2.schema.json +127 -127
  33. capture_runtime_client-0.4.3/src/capture_runtime_client/private/schemas/ocr-box-v3.schema.json +65 -0
  34. capture_runtime_client-0.4.3/src/capture_runtime_client/private/schemas/ocr-compute-preflight-v2.schema.json +128 -0
  35. capture_runtime_client-0.4.3/src/capture_runtime_client/private/schemas/ocr-page-projection-v3.schema.json +330 -0
  36. capture_runtime_client-0.4.3/src/capture_runtime_client/private/schemas/ocr-page-scope-v2.schema.json +37 -0
  37. capture_runtime_client-0.4.3/src/capture_runtime_client/private/schemas/ocr-point-v3.schema.json +22 -0
  38. capture_runtime_client-0.4.3/src/capture_runtime_client/private/schemas/ocr-provenance-resolved-v3.schema.json +55 -0
  39. capture_runtime_client-0.4.3/src/capture_runtime_client/private/schemas/ocr-provenance-unavailable-v3.schema.json +44 -0
  40. capture_runtime_client-0.4.3/src/capture_runtime_client/private/schemas/ocr-provenance-v3.schema.json +118 -0
  41. capture_runtime_client-0.4.3/src/capture_runtime_client/private/schemas/ocr-raster-v3.schema.json +34 -0
  42. {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
  43. {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
  44. {capture_runtime_client-0.4.1 → capture_runtime_client-0.4.3}/src/capture_runtime_client/private/schemas/page-locator.schema.json +49 -49
  45. {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
  46. {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
  47. {capture_runtime_client-0.4.1 → capture_runtime_client-0.4.3}/src/capture_runtime_client/private/schemas/raw-capture.schema.json +308 -260
  48. {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
  49. {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
  50. {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
  51. {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
  52. {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
  53. {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
  54. {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
  55. {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
  56. capture_runtime_client-0.4.3/src/capture_runtime_client/private/schemas/runtime-ready.schema.json +227 -0
  57. {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
  58. {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
  59. {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
  60. {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
  61. {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
  62. {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
  63. {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
  64. {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
  65. {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
  66. {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
  67. {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
  68. {capture_runtime_client-0.4.1 → capture_runtime_client-0.4.3}/src/capture_runtime_client/private/schemas/time-locator.schema.json +27 -27
  69. {capture_runtime_client-0.4.1 → capture_runtime_client-0.4.3}/src/capture_runtime_client/py.typed +1 -1
  70. {capture_runtime_client-0.4.1 → capture_runtime_client-0.4.3}/src/capture_runtime_client/transport.py +165 -158
  71. {capture_runtime_client-0.4.1 → capture_runtime_client-0.4.3}/tests/test_client_sdk.py +1299 -732
  72. {capture_runtime_client-0.4.1 → capture_runtime_client-0.4.3}/uv.lock +437 -437
  73. capture_runtime_client-0.4.1/src/capture_runtime_client/private/assets/contract-set.json +0 -1
  74. capture_runtime_client-0.4.1/src/capture_runtime_client/private/assets/contract-set.sha256 +0 -1
  75. capture_runtime_client-0.4.1/src/capture_runtime_client/private/schemas/runtime-ready.schema.json +0 -88
  76. {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
- tmp
6
- out-tsc
7
-
8
- # dependencies
9
- node_modules
10
- tools/local-registry/storage/
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,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: capture-runtime-client
3
- Version: 0.4.1
3
+ Version: 0.4.3
4
4
  Summary: Capture Runtime HTTP, in-memory, discovery, and protocol client SDK.
5
5
  License: MIT
6
6
  Requires-Python: <3.15,>=3.12
@@ -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.1"
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 = "b28366f022533192c063056bbf64cacfd09390815c65408066369dd61094e278"
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
- [tool.ruff]
41
- line-length = 100
42
- target-version = "py312"
43
-
44
- [tool.ruff.lint]
45
- select = ["B", "E", "F", "I", "UP"]
46
-
47
- [tool.mypy]
48
- python_version = "3.12"
49
- strict = true
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
@@ -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
+ ]