capture-runtime-client 0.4.0__py3-none-any.whl

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 (48) hide show
  1. capture_runtime_client/__init__.py +41 -0
  2. capture_runtime_client/client.py +852 -0
  3. capture_runtime_client/codec.py +124 -0
  4. capture_runtime_client/contracts.py +272 -0
  5. capture_runtime_client/errors.py +170 -0
  6. capture_runtime_client/private/__init__.py +1 -0
  7. capture_runtime_client/private/assets/__init__.py +1 -0
  8. capture_runtime_client/private/assets/contract-set.json +1 -0
  9. capture_runtime_client/private/assets/contract-set.sha256 +1 -0
  10. capture_runtime_client/private/generated_models.py +627 -0
  11. capture_runtime_client/private/schemas/__init__.py +1 -0
  12. capture_runtime_client/private/schemas/capture-block.schema.json +151 -0
  13. capture_runtime_client/private/schemas/capture-document.schema.json +356 -0
  14. capture_runtime_client/private/schemas/capture-engine.schema.json +40 -0
  15. capture_runtime_client/private/schemas/capture-event-v2.schema.json +301 -0
  16. capture_runtime_client/private/schemas/capture-failure-v2.schema.json +40 -0
  17. capture_runtime_client/private/schemas/capture-operation-v2.schema.json +207 -0
  18. capture_runtime_client/private/schemas/capture-source.schema.json +34 -0
  19. capture_runtime_client/private/schemas/error-body-v2.schema.json +34 -0
  20. capture_runtime_client/private/schemas/error-envelope-v2.schema.json +49 -0
  21. capture_runtime_client/private/schemas/finalize-ingestion-v2.schema.json +27 -0
  22. capture_runtime_client/private/schemas/ingestion-v2.schema.json +127 -0
  23. capture_runtime_client/private/schemas/open-ingestion-v2.schema.json +79 -0
  24. capture_runtime_client/private/schemas/page-locator.schema.json +49 -0
  25. capture_runtime_client/private/schemas/partial-capture-v2.schema.json +267 -0
  26. capture_runtime_client/private/schemas/raw-capture-segment.schema.json +125 -0
  27. capture_runtime_client/private/schemas/raw-capture.schema.json +260 -0
  28. capture_runtime_client/private/schemas/report-structuring-failure-v2.schema.json +28 -0
  29. capture_runtime_client/private/schemas/runtime-artifact-descriptor-v2.schema.json +34 -0
  30. capture_runtime_client/private/schemas/runtime-installation-v2.schema.json +126 -0
  31. capture_runtime_client/private/schemas/runtime-installations-v2.schema.json +143 -0
  32. capture_runtime_client/private/schemas/runtime-model-installation-v2.schema.json +121 -0
  33. capture_runtime_client/private/schemas/runtime-model-installations-v2.schema.json +138 -0
  34. capture_runtime_client/private/schemas/runtime-model-option-v2.schema.json +80 -0
  35. capture_runtime_client/private/schemas/runtime-model-options-v2.schema.json +103 -0
  36. capture_runtime_client/private/schemas/runtime-ready.schema.json +88 -0
  37. capture_runtime_client/private/schemas/runtime-requirement-v2.schema.json +120 -0
  38. capture_runtime_client/private/schemas/runtime-requirements-v2.schema.json +137 -0
  39. capture_runtime_client/private/schemas/runtime-streaming-capabilities-v2.schema.json +64 -0
  40. capture_runtime_client/private/schemas/start-capture-v2.schema.json +59 -0
  41. capture_runtime_client/private/schemas/start-runtime-installation-v2.schema.json +26 -0
  42. capture_runtime_client/private/schemas/start-runtime-model-installation-v2.schema.json +21 -0
  43. capture_runtime_client/private/schemas/time-locator.schema.json +27 -0
  44. capture_runtime_client/py.typed +1 -0
  45. capture_runtime_client/transport.py +158 -0
  46. capture_runtime_client-0.4.0.dist-info/METADATA +18 -0
  47. capture_runtime_client-0.4.0.dist-info/RECORD +48 -0
  48. capture_runtime_client-0.4.0.dist-info/WHEEL +4 -0
@@ -0,0 +1,124 @@
1
+ """Private wire codecs shared by HTTP and in-memory adapters."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from collections.abc import Iterator
6
+ from typing import Any
7
+
8
+ import httpx
9
+ from pydantic import BaseModel, ValidationError
10
+
11
+ from .errors import (
12
+ CaptureAuthenticationError,
13
+ CaptureRemoteError,
14
+ CaptureRuntimeProtocolError,
15
+ )
16
+
17
+
18
+ def decode_model[ModelT: BaseModel](response: httpx.Response, model: type[ModelT]) -> ModelT:
19
+ payload = decode_json(response)
20
+ try:
21
+ return model.model_validate(payload)
22
+ except ValidationError as error:
23
+ raise CaptureRuntimeProtocolError(
24
+ f"Capture Runtime returned invalid {model.__name__} data.",
25
+ error.errors(include_url=False),
26
+ ) from error
27
+
28
+
29
+ def decode_json(response: httpx.Response) -> Any:
30
+ if response.status_code >= 400:
31
+ try:
32
+ envelope = response.json()
33
+ except ValueError:
34
+ envelope = None
35
+ error = envelope.get("error") if isinstance(envelope, dict) else None
36
+ error_map = error if isinstance(error, dict) else {}
37
+ code = str(error_map.get("code", f"http_{response.status_code}"))
38
+ message = str(
39
+ error_map.get("message", f"Capture Runtime request failed ({response.status_code}).")
40
+ )
41
+ details = error_map.get("details")
42
+ details_map = details if isinstance(details, dict) else {}
43
+ category = error_map.get("category", details_map.get("category"))
44
+ retryable = error_map.get(
45
+ "retryable", details_map.get("retryable", response.status_code >= 500)
46
+ )
47
+ issues = error_map.get("issues", details_map.get("issues"))
48
+ request_id = (
49
+ response.headers.get("x-request-id")
50
+ or response.headers.get("x-correlation-id")
51
+ or _optional_text(error_map.get("requestId", details_map.get("requestId")))
52
+ )
53
+ if response.status_code in {401, 403} or code in {"unauthorized", "authentication_failed"}:
54
+ raise CaptureAuthenticationError(
55
+ message,
56
+ status_code=response.status_code,
57
+ details=details,
58
+ request_id=request_id,
59
+ )
60
+ raise CaptureRemoteError(
61
+ status_code=response.status_code,
62
+ code=code,
63
+ message=message,
64
+ category=_optional_text(category),
65
+ retryable=bool(retryable),
66
+ details=details,
67
+ issues=issues if isinstance(issues, list) else None,
68
+ request_id=request_id,
69
+ )
70
+ if response.status_code == 204:
71
+ return None
72
+ try:
73
+ return response.json()
74
+ except ValueError as error:
75
+ raise CaptureRuntimeProtocolError("Capture Runtime returned invalid JSON.") from error
76
+
77
+
78
+ def iter_sse(lines: Iterator[bytes]) -> Iterator[dict[str, str]]:
79
+ event: dict[str, str] = {}
80
+ data: list[str] = []
81
+ buffer = ""
82
+
83
+ def consume(line: str) -> dict[str, str] | None:
84
+ nonlocal event, data
85
+ if not line:
86
+ if data:
87
+ result = {**event, "data": "\n".join(data)}
88
+ event = {}
89
+ data = []
90
+ return result
91
+ event = {}
92
+ data = []
93
+ return None
94
+ if line.startswith(":"):
95
+ return None
96
+ key, separator, value = line.partition(":")
97
+ if separator and value.startswith(" "):
98
+ value = value[1:]
99
+ if key == "data":
100
+ data.append(value)
101
+ elif key in {"id", "event"}:
102
+ event[key] = value
103
+ return None
104
+
105
+ for raw in lines:
106
+ buffer += raw.decode("utf-8")
107
+ while "\n" in buffer:
108
+ line, buffer = buffer.split("\n", 1)
109
+ result = consume(line[:-1] if line.endswith("\r") else line)
110
+ if result is not None:
111
+ yield result
112
+ if buffer:
113
+ result = consume(buffer[:-1] if buffer.endswith("\r") else buffer)
114
+ if result is not None:
115
+ yield result
116
+ if data:
117
+ yield {**event, "data": "\n".join(data)}
118
+
119
+
120
+ __all__ = ["decode_json", "decode_model", "iter_sse"]
121
+
122
+
123
+ def _optional_text(value: Any) -> str | None:
124
+ return value if isinstance(value, str) and value else None
@@ -0,0 +1,272 @@
1
+ """Public v2 contract DTOs backed by private generated wire models.
2
+
3
+ The generated module is an SDK build input only. Consumers receive deliberate
4
+ canonical names from this module; generated identifiers never form part of the
5
+ package's public surface.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import hashlib
11
+ import json
12
+ from importlib.resources import files
13
+ from typing import Any, cast
14
+
15
+ from pydantic import Field
16
+
17
+ from .private import generated_models as _generated
18
+
19
+ for _generated_name in (
20
+ "RuntimeModelInstallationV2",
21
+ "RuntimeModelInstallationsV2",
22
+ "RuntimeInstallationV2",
23
+ "RuntimeInstallationsV2",
24
+ "CaptureOperationV2",
25
+ "PartialCaptureV2",
26
+ "CaptureEventV2",
27
+ ):
28
+ getattr(_generated, _generated_name).model_rebuild(
29
+ force=True,
30
+ _types_namespace=vars(_generated),
31
+ )
32
+
33
+ CAPTURE_API_VERSION = "2.0"
34
+ CAPTURE_DOCUMENT_SCHEMA_VERSION = _generated.CAPTURE_DOCUMENT_SCHEMA_VERSION
35
+ CAPTURE_RUNTIME_VERSION = "0.4.0"
36
+ CAPTURE_DOCUMENT_SCHEMA_ID = (
37
+ "https://github.com/gx-capture/capture-workbench/schema/capture-document-v2.schema.json"
38
+ )
39
+ CAPTURE_DOCUMENT_SCHEMA_SHA256 = "850afd212d049c25da41d3867ba5477451a6a2c6c7e41f116fe60f26b6a35335"
40
+ _CONTRACT_SET_ASSETS = files("capture_runtime_client.private.assets")
41
+ CAPTURE_CONTRACT_SET_SHA256 = _CONTRACT_SET_ASSETS.joinpath(
42
+ "contract-set.sha256"
43
+ ).read_text(encoding="ascii").strip()
44
+ if hashlib.sha256(
45
+ _CONTRACT_SET_ASSETS.joinpath("contract-set.json").read_bytes()
46
+ ).hexdigest() != CAPTURE_CONTRACT_SET_SHA256:
47
+ raise RuntimeError("Packaged contract-set asset does not match its SHA-256 allowlist.")
48
+
49
+
50
+ # Public v2 DTOs. These subclasses intentionally create a stable public seam
51
+ # without exporting the generated module or its model names. Pydantic keeps
52
+ # inherited strict/alias validation, while each class has a canonical v2 name
53
+ # for consumers and package introspection.
54
+ class CaptureSource(_generated.CaptureSource):
55
+ pass
56
+
57
+
58
+ class CaptureEngine(_generated.CaptureEngine):
59
+ pass
60
+
61
+
62
+ class RawCaptureSegment(_generated.RawCaptureSegment):
63
+ pass
64
+
65
+
66
+ class CaptureBlock(_generated.CaptureBlock):
67
+ pass
68
+
69
+
70
+ class CaptureFailure(_generated.CaptureFailureV2):
71
+ pass
72
+
73
+
74
+ class CaptureDocument(_generated.CaptureDocument):
75
+ pass
76
+
77
+
78
+ class RawCapture(_generated.RawCapture):
79
+ pass
80
+
81
+
82
+ class RuntimeArtifactDescriptor(_generated.RuntimeArtifactDescriptorV2):
83
+ pass
84
+
85
+
86
+ class RuntimeRequirement(_generated.RuntimeRequirementV2):
87
+ pass
88
+
89
+
90
+ class RuntimeRequirements(_generated.RuntimeRequirementsV2):
91
+ pass
92
+
93
+
94
+ class RuntimeModelOption(_generated.RuntimeModelOptionV2):
95
+ pass
96
+
97
+
98
+ class RuntimeModelOptions(_generated.RuntimeModelOptionsV2):
99
+ pass
100
+
101
+
102
+ class RuntimeModelInstallation(_generated.RuntimeModelInstallationV2):
103
+ pass
104
+
105
+
106
+ class RuntimeModelInstallations(_generated.RuntimeModelInstallationsV2):
107
+ pass
108
+
109
+
110
+ class RuntimeInstallation(_generated.RuntimeInstallationV2):
111
+ pass
112
+
113
+
114
+ class RuntimeInstallations(_generated.RuntimeInstallationsV2):
115
+ pass
116
+
117
+
118
+ class OpenIngestion(_generated.OpenIngestionV2):
119
+ pass
120
+
121
+
122
+ class Ingestion(_generated.IngestionV2):
123
+ pass
124
+
125
+
126
+ class StartCapture(_generated.StartCaptureV2):
127
+ pass
128
+
129
+
130
+ class FinalizeIngestion(_generated.FinalizeIngestionV2):
131
+ pass
132
+
133
+
134
+ class CaptureOperation(_generated.CaptureOperationV2):
135
+ pass
136
+
137
+
138
+ class PartialCapture(_generated.PartialCaptureV2):
139
+ pass
140
+
141
+
142
+ class CaptureEvent(_generated.CaptureEventV2):
143
+ pass
144
+
145
+
146
+ class RuntimeStreamingCapabilities(_generated.RuntimeStreamingCapabilitiesV2):
147
+ pass
148
+
149
+
150
+ class RuntimeReady(_generated.StrictModel):
151
+ """General v2 readiness payload shared by discovery clients."""
152
+
153
+ ready: bool
154
+ service: str
155
+ api_version: str
156
+ runtime_version: str
157
+ capture_document_schema_version: str
158
+ capture_document_schema_sha256: str | None = None
159
+ schema_sha256: str | None = None
160
+ contract_set_version: str = "2"
161
+ capabilities: dict[str, Any] = Field(default_factory=dict)
162
+ message: str | None = None
163
+
164
+
165
+ class ReportStructuringFailure(_generated.ReportStructuringFailureV2):
166
+ pass
167
+
168
+
169
+ class ErrorBody(_generated.ErrorBodyV2):
170
+ pass
171
+
172
+
173
+ class ErrorEnvelope(_generated.ErrorEnvelopeV2):
174
+ pass
175
+
176
+
177
+ CaptureLocator = _generated.CaptureLocator
178
+ PageLocator = _generated.PageLocator
179
+ TimeLocator = _generated.TimeLocator
180
+
181
+ CaptureSourceKind = _generated.CaptureSourceKind
182
+ StructuringMode = _generated.StructuringMode
183
+ RuntimeInstallationStatus = _generated.RuntimeInstallationStatus
184
+ RuntimeModelOptionStatus = _generated.RuntimeModelOptionStatus
185
+ RuntimeRequirementStatus = _generated.RuntimeRequirementStatus
186
+ StreamingIngestionMode = _generated.StreamingIngestionMode
187
+ StreamingIngestionStatus = _generated.StreamingIngestionStatus
188
+ StreamingCaptureStatus = _generated.StreamingCaptureStatus
189
+ StreamingEventType = _generated.StreamingEventType
190
+ CaptureRequirementId = _generated.CaptureRequirementId
191
+ project_source_text = _generated.project_source_text
192
+
193
+
194
+ def _load_contract_schema(name: str) -> dict[str, Any]:
195
+ filename = {
196
+ "RawCapture": "raw-capture.schema.json",
197
+ "CaptureDocument": "capture-document.schema.json",
198
+ }.get(name)
199
+ if filename is None:
200
+ raise ValueError(f"Unknown generated contract schema: {name}")
201
+ return cast(
202
+ dict[str, Any],
203
+ json.loads(files("capture_runtime_client.private.schemas").joinpath(filename).read_text()),
204
+ )
205
+
206
+
207
+ def _load_contract_manifest() -> dict[str, Any]:
208
+ return {
209
+ "manifestVersion": "1",
210
+ "packageVersion": CAPTURE_RUNTIME_VERSION,
211
+ "runtimeVersion": CAPTURE_RUNTIME_VERSION,
212
+ "apiVersion": CAPTURE_API_VERSION,
213
+ "captureDocumentSchemaVersion": CAPTURE_DOCUMENT_SCHEMA_VERSION,
214
+ "captureDocumentSchemaId": CAPTURE_DOCUMENT_SCHEMA_ID,
215
+ "captureDocumentSchemaSha256": CAPTURE_DOCUMENT_SCHEMA_SHA256,
216
+ }
217
+
218
+
219
+ def _load_contract_constraints() -> dict[str, Any]:
220
+ return {"invariants": []}
221
+
222
+
223
+ __all__ = [
224
+ "CAPTURE_API_VERSION",
225
+ "CAPTURE_DOCUMENT_SCHEMA_ID",
226
+ "CAPTURE_DOCUMENT_SCHEMA_SHA256",
227
+ "CAPTURE_CONTRACT_SET_SHA256",
228
+ "CAPTURE_DOCUMENT_SCHEMA_VERSION",
229
+ "CAPTURE_RUNTIME_VERSION",
230
+ "CaptureLocator",
231
+ "PageLocator",
232
+ "TimeLocator",
233
+ "CaptureSource",
234
+ "CaptureEngine",
235
+ "RawCaptureSegment",
236
+ "CaptureBlock",
237
+ "CaptureFailure",
238
+ "CaptureDocument",
239
+ "RawCapture",
240
+ "RuntimeArtifactDescriptor",
241
+ "RuntimeRequirement",
242
+ "RuntimeRequirements",
243
+ "RuntimeModelOption",
244
+ "RuntimeModelOptions",
245
+ "RuntimeModelInstallation",
246
+ "RuntimeModelInstallations",
247
+ "RuntimeInstallation",
248
+ "RuntimeInstallations",
249
+ "OpenIngestion",
250
+ "Ingestion",
251
+ "StartCapture",
252
+ "FinalizeIngestion",
253
+ "CaptureOperation",
254
+ "PartialCapture",
255
+ "CaptureEvent",
256
+ "RuntimeStreamingCapabilities",
257
+ "RuntimeReady",
258
+ "ReportStructuringFailure",
259
+ "ErrorBody",
260
+ "ErrorEnvelope",
261
+ "CaptureSourceKind",
262
+ "StructuringMode",
263
+ "RuntimeInstallationStatus",
264
+ "RuntimeModelOptionStatus",
265
+ "RuntimeRequirementStatus",
266
+ "StreamingIngestionMode",
267
+ "StreamingIngestionStatus",
268
+ "StreamingCaptureStatus",
269
+ "StreamingEventType",
270
+ "CaptureRequirementId",
271
+ "project_source_text",
272
+ ]
@@ -0,0 +1,170 @@
1
+ """Stable, credential-safe client exception types."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import re
6
+ from typing import Any
7
+
8
+ _BEARER = re.compile(r"(?i)\bbearer\s+[A-Za-z0-9._~+/=-]+")
9
+ _SECRET_FIELD = re.compile(r"(?:token|authorization|credential|secret|password)", re.I)
10
+
11
+
12
+ def redact_text(value: str) -> str:
13
+ return _BEARER.sub("Bearer [redacted]", value)[:500]
14
+
15
+
16
+ def redact_value(value: Any, *, depth: int = 0) -> Any:
17
+ if depth > 4:
18
+ return "[redacted]"
19
+ if isinstance(value, str):
20
+ return redact_text(value)
21
+ if isinstance(value, list):
22
+ return [redact_value(item, depth=depth + 1) for item in value]
23
+ if isinstance(value, dict):
24
+ return {
25
+ str(key)[:128]: "[redacted]"
26
+ if _SECRET_FIELD.search(str(key))
27
+ else redact_value(item, depth=depth + 1)
28
+ for key, item in value.items()
29
+ }
30
+ return value
31
+
32
+
33
+ class CaptureRuntimeError(RuntimeError):
34
+ """Base error with stable, redacted wire diagnostics.
35
+
36
+ ``CaptureRemoteError`` and the narrower taxonomy subclasses below preserve
37
+ the fields emitted by a runtime error envelope. The historical
38
+ ``CaptureRuntime*`` names remain available as compatibility aliases.
39
+ """
40
+
41
+ category = "runtime"
42
+
43
+ def __init__(
44
+ self,
45
+ *,
46
+ status_code: int,
47
+ code: str,
48
+ message: str,
49
+ details: Any = None,
50
+ category: str | None = None,
51
+ retryable: bool = False,
52
+ issues: list[dict[str, Any]] | None = None,
53
+ request_id: str | None = None,
54
+ ) -> None:
55
+ self.status_code = status_code
56
+ self.code = code
57
+ self.runtime_message = redact_text(message)
58
+ self.details = redact_value(details)
59
+ self.category = category or self.category
60
+ self.retryable = retryable
61
+ self.issues = redact_value(issues) if issues is not None else []
62
+ self.request_id = request_id
63
+ super().__init__(f"Capture Runtime request failed ({code}): {self.runtime_message}")
64
+
65
+
66
+ class CaptureTransportError(CaptureRuntimeError):
67
+ category = "transport"
68
+
69
+ def __init__(self, message: str, details: Any = None, *, code: str = "transport_error") -> None:
70
+ super().__init__(
71
+ status_code=0,
72
+ code=code,
73
+ message=message,
74
+ details=details,
75
+ category=self.category,
76
+ retryable=True,
77
+ )
78
+
79
+
80
+ class CaptureAuthenticationError(CaptureRuntimeError):
81
+ category = "authentication"
82
+
83
+ def __init__(
84
+ self,
85
+ message: str = "Capture Runtime authentication failed.",
86
+ *,
87
+ status_code: int = 401,
88
+ details: Any = None,
89
+ request_id: str | None = None,
90
+ ) -> None:
91
+ super().__init__(
92
+ status_code=status_code,
93
+ code="unauthorized",
94
+ message=message,
95
+ details=details,
96
+ category=self.category,
97
+ request_id=request_id,
98
+ )
99
+
100
+
101
+ class CaptureCompatibilityError(CaptureRuntimeError):
102
+ category = "compatibility"
103
+
104
+ def __init__(self, message: str, details: Any = None) -> None:
105
+ super().__init__(
106
+ status_code=0,
107
+ code="incompatible_runtime",
108
+ message=message,
109
+ details=details,
110
+ category=self.category,
111
+ )
112
+
113
+
114
+ class CaptureProtocolError(CaptureRuntimeError):
115
+ category = "protocol"
116
+
117
+ def __init__(self, message: str, details: Any = None) -> None:
118
+ super().__init__(
119
+ status_code=0,
120
+ code="protocol_error",
121
+ message=message,
122
+ details=details,
123
+ category=self.category,
124
+ )
125
+
126
+
127
+ class CaptureRemoteError(CaptureRuntimeError):
128
+ category = "remote"
129
+
130
+ def __init__(
131
+ self,
132
+ *,
133
+ status_code: int,
134
+ code: str,
135
+ message: str,
136
+ category: str | None = None,
137
+ retryable: bool = False,
138
+ details: Any = None,
139
+ issues: list[dict[str, Any]] | None = None,
140
+ request_id: str | None = None,
141
+ ) -> None:
142
+ super().__init__(
143
+ status_code=status_code,
144
+ code=code,
145
+ message=message,
146
+ details=details,
147
+ category=category or self.category,
148
+ retryable=retryable,
149
+ issues=issues,
150
+ request_id=request_id,
151
+ )
152
+
153
+
154
+ # Compatibility names used by the first SDK preview.
155
+ CaptureRuntimeProtocolError = CaptureProtocolError
156
+ CaptureRuntimeCompatibilityError = CaptureCompatibilityError
157
+
158
+
159
+ __all__ = [
160
+ "CaptureAuthenticationError",
161
+ "CaptureCompatibilityError",
162
+ "CaptureProtocolError",
163
+ "CaptureRemoteError",
164
+ "CaptureTransportError",
165
+ "CaptureRuntimeCompatibilityError",
166
+ "CaptureRuntimeError",
167
+ "CaptureRuntimeProtocolError",
168
+ "redact_text",
169
+ "redact_value",
170
+ ]
@@ -0,0 +1 @@
1
+ """Private generated SDK inputs; do not import from consumer applications."""
@@ -0,0 +1 @@
1
+ """Packaged canonical runtime contract-set assets."""