openbox-sdk-python 1.1.0__py3-none-any.whl → 1.2.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.
openbox_core/__init__.py CHANGED
@@ -37,7 +37,7 @@ from .errors import (
37
37
  # governance; eagerly it can deadlock package init as a circular import, and
38
38
  # lazily it can recurse unboundedly when a per-request header builder resolves
39
39
  # the version. Keep in sync with pyproject.toml on release.
40
- __version__ = "1.1.0"
40
+ __version__ = "1.2.0"
41
41
 
42
42
  __all__ = [
43
43
  "__version__",
@@ -20,9 +20,9 @@ __all__ = [
20
20
  "GuardrailsResult",
21
21
  "EvaluationResult",
22
22
  "ApprovalResult",
23
- "RetryPlan",
24
- "RetryDirective",
25
- "handle_retryable_block",
23
+ "Patch",
24
+ "PatchDirective",
25
+ "handle_patch",
26
26
  ]
27
27
 
28
28
 
@@ -106,8 +106,8 @@ class GuardrailsResult:
106
106
  return [r.get("reason", "") for r in self.reasons if r.get("reason")]
107
107
 
108
108
 
109
- # Sentinel distinguishing an absent ``retry_plan`` field from a present JSON ``null``.
110
- # ``dict.get("retry_plan")`` would collapse both to None; membership via this sentinel does not.
109
+ # Sentinel distinguishing an absent ``patch`` field from a present JSON ``null``.
110
+ # ``dict.get("patch")`` would collapse both to None; membership via this sentinel does not.
111
111
  _MISSING = object()
112
112
 
113
113
  # JS Number.MAX_SAFE_INTEGER. Integral numbers must fit within +/- this bound so ``new_input`` can
@@ -120,7 +120,7 @@ def _numbers_are_safe(value: Any) -> bool:
120
120
  """Every number (recursively) must be finite and, if integral, within the JS-safe range.
121
121
 
122
122
  ``bool`` is a subclass of ``int`` but is not a number here; a nested bool is a plain leaf
123
- (top-level ``new_input`` booleans are rejected separately by :func:`_parse_retry_plan`).
123
+ (top-level ``new_input`` booleans are rejected separately by :func:`_parse_patch`).
124
124
  """
125
125
  if isinstance(value, bool):
126
126
  return True
@@ -140,22 +140,22 @@ def _numbers_are_safe(value: Any) -> bool:
140
140
 
141
141
 
142
142
  @dataclass
143
- class RetryPlan:
144
- """Optional remediation hint carried on a BLOCK verdict (policy-produced or admin-retry).
143
+ class Patch:
144
+ """Optional remediation hint carried on a BLOCK verdict (policy-produced or admin-patch).
145
145
 
146
146
  ``new_input`` may be null | str | number | list | dict (boolean rejected). A present
147
- ``new_input`` of ``None`` is a valid plan (retry with the original input) and is distinct
148
- from an absent ``retry_plan`` field.
147
+ ``new_input`` of ``None`` is a valid patch (reuses the original input) and is distinct
148
+ from an absent ``patch`` field.
149
149
  """
150
150
 
151
151
  new_input: Any = None
152
152
 
153
153
 
154
154
  @dataclass
155
- class RetryDirective:
156
- """Caller-facing view of a retryable BLOCK, surfaced only via :func:`handle_retryable_block`.
155
+ class PatchDirective:
156
+ """Caller-facing view of a BLOCK with patch, surfaced only via :func:`handle_patch`.
157
157
 
158
- It is a remediation hint, not proof that a retry was evaluated or executed.
158
+ It is a remediation hint, not proof that a patch was evaluated or executed.
159
159
  """
160
160
 
161
161
  new_input: Any = None
@@ -163,28 +163,28 @@ class RetryDirective:
163
163
  reason: str | None = None
164
164
 
165
165
 
166
- def _parse_retry_plan(container: dict[str, Any]) -> RetryPlan | None:
167
- """Parse an optional ``retry_plan`` from a result dict against the frozen wire contract.
166
+ def _parse_patch(container: dict[str, Any]) -> Patch | None:
167
+ """Parse an optional ``patch`` from a result dict against the frozen wire contract.
168
168
 
169
- Returns a plan only when ``retry_plan`` is present, is a dict whose ONLY key is ``new_input``,
169
+ Returns a patch only when ``patch`` is present, is a dict whose ONLY key is ``new_input``,
170
170
  and ``new_input`` is null/str/number/list/dict (boolean rejected) with every number finite and,
171
171
  if integral, within the JS-safe integer range. An absent field, a JSON ``null`` field, or any
172
172
  contract violation yields ``None`` (treated as absent — never an error). A present
173
- ``new_input`` of ``None`` is preserved as a valid plan.
173
+ ``new_input`` of ``None`` is preserved as a valid patch.
174
174
  """
175
- plan = container.get("retry_plan", _MISSING)
176
- if plan is _MISSING or not isinstance(plan, dict):
175
+ patch = container.get("patch", _MISSING)
176
+ if patch is _MISSING or not isinstance(patch, dict):
177
177
  return None
178
178
  # Exactly the single key new_input (also guarantees new_input is present, not merely null).
179
- if set(plan.keys()) != {"new_input"}:
179
+ if set(patch.keys()) != {"new_input"}:
180
180
  return None
181
- new_input = plan["new_input"]
181
+ new_input = patch["new_input"]
182
182
  # bool is a subclass of int — reject a boolean new_input BEFORE the number check.
183
183
  if isinstance(new_input, bool):
184
184
  return None
185
185
  if not _numbers_are_safe(new_input):
186
186
  return None
187
- return RetryPlan(new_input=new_input)
187
+ return Patch(new_input=new_input)
188
188
 
189
189
 
190
190
  @dataclass
@@ -215,7 +215,7 @@ class EvaluationResult:
215
215
  fallback_used: bool = False # True when fail-open produced this result
216
216
  diagnostics: list[Any] = field(default_factory=list)
217
217
  raw: dict[str, Any] = field(default_factory=dict)
218
- retry_plan: RetryPlan | None = None # Optional BLOCK remediation hint; None = absent
218
+ patch: Patch | None = None # Optional BLOCK remediation hint; None = absent
219
219
 
220
220
  @property
221
221
  def guardrails_result(self) -> GuardrailsResult | None:
@@ -266,7 +266,7 @@ class EvaluationResult:
266
266
  fallback_used=bool(data.get("fallback_used", False)),
267
267
  diagnostics=data.get("diagnostics") or [],
268
268
  raw=dict(data),
269
- retry_plan=_parse_retry_plan(data),
269
+ patch=_parse_patch(data),
270
270
  )
271
271
 
272
272
  @classmethod
@@ -299,7 +299,7 @@ class ApprovalResult:
299
299
  approval_expiration_time: str | None = None
300
300
  expired: bool = False
301
301
  raw: dict[str, Any] = field(default_factory=dict)
302
- retry_plan: RetryPlan | None = None # Present only on an admin-retry BLOCK
302
+ patch: Patch | None = None # Present only on an admin-patch BLOCK
303
303
 
304
304
  # Known decision vocabulary for approvals (current values + accepted aliases).
305
305
  # Anything OUTSIDE this set parses to None (pending) — the evaluate-path
@@ -345,7 +345,7 @@ class ApprovalResult:
345
345
  approval_expiration_time=data.get("approval_expiration_time"),
346
346
  expired=bool(data.get("expired", False)),
347
347
  raw=dict(data),
348
- retry_plan=_parse_retry_plan(data),
348
+ patch=_parse_patch(data),
349
349
  )
350
350
 
351
351
  @property
@@ -376,21 +376,22 @@ class ApprovalResult:
376
376
  return self.verdict in (Verdict.REQUIRE_APPROVAL, Verdict.CONSTRAIN)
377
377
 
378
378
 
379
- def handle_retryable_block(
379
+ def handle_patch(
380
380
  result: EvaluationResult | ApprovalResult,
381
- ) -> RetryDirective | None:
382
- """Opt-in, pure inspector: surface a retry directive from a retryable BLOCK.
381
+ ) -> PatchDirective | None:
382
+ """Opt-in, pure inspector: surface a patch directive from a BLOCK with patch.
383
383
 
384
- Returns a :class:`RetryDirective` ONLY for ``verdict == Verdict.BLOCK`` with a present, valid
385
- ``retry_plan``. Returns ``None`` for plain BLOCK, every non-BLOCK verdict (including HALT),
384
+ Returns a :class:`PatchDirective` ONLY for ``verdict == Verdict.BLOCK`` with a present, valid
385
+ ``patch``. Returns ``None`` for plain BLOCK, every non-BLOCK verdict (including HALT),
386
386
  a pending/``None`` verdict, and an expired :class:`ApprovalResult`.
387
387
 
388
- This does NOT change enforcement: importing or exposing it never auto-retries. A caller must
389
- invoke it explicitly, and it makes no claim that a replacement was evaluated or executed.
388
+ This does NOT change enforcement: importing or exposing it never auto-applies the patch. A
389
+ caller must invoke it explicitly, and it makes no claim that a replacement was evaluated or
390
+ executed.
390
391
 
391
392
  The gate is deliberately ``verdict == Verdict.BLOCK`` — NOT ``is_blocking()`` /
392
393
  ``verdict.should_stop()``, which also match HALT (and, for approvals, expired results) and would
393
- violate the wire-contract invariant that HALT always strips the plan.
394
+ violate the wire-contract invariant that HALT always strips the patch.
394
395
  """
395
396
  # A stale/expired poll result must not surface a directive, even if verdict == BLOCK.
396
397
  if isinstance(result, ApprovalResult) and result.expired:
@@ -400,8 +401,8 @@ def handle_retryable_block(
400
401
  if getattr(result, "verdict", None) != Verdict.BLOCK:
401
402
  return None
402
403
 
403
- plan = result.retry_plan
404
- if plan is None:
404
+ patch = result.patch
405
+ if patch is None:
405
406
  return None
406
407
 
407
408
  # governance_event_id is typed on EvaluationResult; ApprovalResult carries it only in raw.
@@ -410,8 +411,8 @@ def handle_retryable_block(
410
411
  else:
411
412
  governance_event_id = result.raw.get("governance_event_id") or result.raw.get("id")
412
413
 
413
- return RetryDirective(
414
- new_input=plan.new_input,
414
+ return PatchDirective(
415
+ new_input=patch.new_input,
415
416
  governance_event_id=governance_event_id,
416
417
  reason=result.reason,
417
418
  )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: openbox-sdk-python
3
- Version: 1.1.0
3
+ Version: 1.2.0
4
4
  Summary: OpenBox base SDK - governance contracts, strict gate, identity/signing, evaluate client, context runtime, OTel span wire serialization, and generic instrumentation shared by every OpenBox framework SDK
5
5
  Author-email: OpenBox Team <tino@openbox.ai>
6
6
  License-Expression: MIT
@@ -1,4 +1,4 @@
1
- openbox_core/__init__.py,sha256=d-dbV1PKppLsYU1yNUnfvwswrIC7Rap0syzbwx8NWdo,1983
1
+ openbox_core/__init__.py,sha256=PYBEmt7aGSXdg_PUsxqHbAyUXBafHC9EJRudnEZhK0E,1983
2
2
  openbox_core/approvals.py,sha256=eobLN4rSBvaFaTbsPExu2XcGS5fSuWvo0GC1DR18w0I,4400
3
3
  openbox_core/client.py,sha256=Mo3HSFj7VmDYLiTJ0FnJdxNdetjU-NHK8IrBU7AL4vU,12615
4
4
  openbox_core/config.py,sha256=-YasfXrRmA2u_iywtFF0AysAp88hzHWu19je5cy4mo8,9684
@@ -20,7 +20,7 @@ openbox_core/contracts/__init__.py,sha256=7uBSUIxfUG6uZDsotQmT1jjXs3G75M2hFHtYEl
20
20
  openbox_core/contracts/context.py,sha256=V-eVbB-NIrXI1NKduengG0GVx7q1wKawyhwZh-D0C9Y,2873
21
21
  openbox_core/contracts/events.py,sha256=Loy5IW4UyPiffBVYK06IxiID6NKsNQjoTLguBb-j4Uw,13428
22
22
  openbox_core/contracts/otel_spans.py,sha256=PnElhgdOM_qlYVF9thPxvhNxloXOb3fpgSXDQN3evWg,10753
23
- openbox_core/contracts/results.py,sha256=Mm9MKDKduk8x65fsUIC7xgen1xvbiX8e3-KnuyVjImY,16185
23
+ openbox_core/contracts/results.py,sha256=2ysg0wpOfA4IeKZ_wNd0RYJAX_fG3OSgreMb6hSrBX4,16075
24
24
  openbox_core/hooks/__init__.py,sha256=jAZGEbN-sZbBD7o_tV-9ZDhBki88yHXKVQf4qhhkYTc,63
25
25
  openbox_core/hooks/events.py,sha256=PQSzH_8vLyUopjCDu6dxCtVh7OvNbiWnLx87fCY5jnU,2129
26
26
  openbox_core/hooks/preflight.py,sha256=uTgwuiV_WkV5RorbWz3tk0FXV85B1YIcjsDfFj-b6Fg,12520
@@ -47,6 +47,6 @@ openbox_core/validation/span_normalization.py,sha256=mB8GghMwOo3WGdKNrm6g8krP7Km
47
47
  openbox_core/wire/__init__.py,sha256=bI-snmNkEvLKqyYl1fItOCwAo46ehKD9vykOFfqaZz0,105
48
48
  openbox_core/wire/core_span.py,sha256=U_d93VSa8992DQ_dqP2dgzp4Vu7eEROishH_oQZFuvM,5059
49
49
  openbox_core/wire/evaluate_payload.py,sha256=RQ9TaeMLQ08YSmN1FYlvVFfOy0FLp0ve6LBLDi2ZKeI,2027
50
- openbox_sdk_python-1.1.0.dist-info/METADATA,sha256=M9FRSJ4hn1Zn3vaEBtDQ9nf2q1ckSeYWdsY81vLRpgQ,4385
51
- openbox_sdk_python-1.1.0.dist-info/WHEEL,sha256=lCkmxWfQsSc9CfIClYeavTdQeEX2toPqufh9gI35EQA,87
52
- openbox_sdk_python-1.1.0.dist-info/RECORD,,
50
+ openbox_sdk_python-1.2.0.dist-info/METADATA,sha256=lKukdD0C2JtaTGVyKtz2PZ69VtqSBQRgifu70IxKNIQ,4385
51
+ openbox_sdk_python-1.2.0.dist-info/WHEEL,sha256=lCkmxWfQsSc9CfIClYeavTdQeEX2toPqufh9gI35EQA,87
52
+ openbox_sdk_python-1.2.0.dist-info/RECORD,,