techrevati-runtime 0.2.0__tar.gz → 0.3.0.dev1__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.
- {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/CHANGELOG.md +129 -0
- {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/PKG-INFO +5 -5
- {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/README.md +4 -4
- techrevati_runtime-0.3.0.dev1/docs/api/governance.md +3 -0
- techrevati_runtime-0.3.0.dev1/docs/api/hooks.md +3 -0
- techrevati_runtime-0.3.0.dev1/docs/api/streaming.md +3 -0
- techrevati_runtime-0.3.0.dev1/docs/patterns/governance.md +194 -0
- techrevati_runtime-0.3.0.dev1/docs/patterns/hooks.md +262 -0
- techrevati_runtime-0.3.0.dev1/docs/patterns/streaming.md +177 -0
- {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/mkdocs.yml +6 -0
- {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/pyproject.toml +1 -1
- {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/src/techrevati/runtime/__init__.py +58 -0
- {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/src/techrevati/runtime/agent_events.py +54 -0
- techrevati_runtime-0.3.0.dev1/src/techrevati/runtime/governance.py +259 -0
- techrevati_runtime-0.3.0.dev1/src/techrevati/runtime/guardrails.py +433 -0
- techrevati_runtime-0.3.0.dev1/src/techrevati/runtime/hooks.py +515 -0
- {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/src/techrevati/runtime/orchestrator.py +341 -16
- {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/src/techrevati/runtime/otel.py +53 -2
- {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/src/techrevati/runtime/retry_policy.py +49 -10
- techrevati_runtime-0.3.0.dev1/src/techrevati/runtime/streaming.py +118 -0
- {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/src/techrevati/runtime/usage_tracking.py +37 -3
- techrevati_runtime-0.3.0.dev1/tests/test_async_guardrails.py +122 -0
- {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/tests/test_async_orchestrator.py +13 -13
- {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/tests/test_checkpoint.py +12 -12
- techrevati_runtime-0.3.0.dev1/tests/test_deprecation_warnings.py +66 -0
- techrevati_runtime-0.3.0.dev1/tests/test_governance.py +152 -0
- techrevati_runtime-0.3.0.dev1/tests/test_governance_events.py +101 -0
- techrevati_runtime-0.3.0.dev1/tests/test_governance_integration.py +200 -0
- {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/tests/test_guardrails.py +48 -11
- {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/tests/test_handoffs.py +5 -5
- techrevati_runtime-0.3.0.dev1/tests/test_hooks.py +422 -0
- {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/tests/test_max_iterations.py +7 -8
- {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/tests/test_orchestrator.py +18 -18
- {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/tests/test_otel.py +4 -4
- techrevati_runtime-0.3.0.dev1/tests/test_otel_atexit_cleanup.py +108 -0
- techrevati_runtime-0.3.0.dev1/tests/test_pattern_and_prompt_injection_guardrails.py +153 -0
- {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/tests/test_rate_limit.py +4 -4
- techrevati_runtime-0.3.0.dev1/tests/test_register_pricing_on_conflict.py +69 -0
- {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/tests/test_s0_regressions.py +3 -3
- {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/tests/test_s5_usage_limits_and_caching.py +1 -1
- {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/tests/test_sinks.py +6 -6
- techrevati_runtime-0.3.0.dev1/tests/test_step_retries.py +115 -0
- techrevati_runtime-0.3.0.dev1/tests/test_streaming.py +263 -0
- {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/tests/test_taskgroup_parallel_tools.py +5 -5
- techrevati_runtime-0.2.0/src/techrevati/runtime/guardrails.py +0 -138
- {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/.github/ISSUE_TEMPLATE/bug.md +0 -0
- {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/.github/ISSUE_TEMPLATE/feature.md +0 -0
- {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/.github/dependabot.yml +0 -0
- {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/.github/workflows/ci.yml +0 -0
- {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/.github/workflows/codeql.yml +0 -0
- {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/.github/workflows/docs.yml +0 -0
- {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/.github/workflows/release.yml +0 -0
- {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/.gitignore +0 -0
- {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/.pre-commit-config.yaml +0 -0
- {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/CODEOWNERS +0 -0
- {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/CONTRIBUTING.md +0 -0
- {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/LICENSE +0 -0
- {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/SECURITY.md +0 -0
- {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/docs/api/checkpoint.md +0 -0
- {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/docs/api/circuit_breaker.md +0 -0
- {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/docs/api/guardrails.md +0 -0
- {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/docs/api/handoffs.md +0 -0
- {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/docs/api/orchestrator.md +0 -0
- {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/docs/api/otel.md +0 -0
- {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/docs/api/persistence.md +0 -0
- {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/docs/api/rate_limit.md +0 -0
- {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/docs/api/retry_policy.md +0 -0
- {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/docs/api/routing.md +0 -0
- {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/docs/api/scheduler.md +0 -0
- {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/docs/api/sinks.md +0 -0
- {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/docs/api/usage_tracking.md +0 -0
- {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/docs/changelog.md +0 -0
- {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/docs/getting-started.md +0 -0
- {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/docs/index.md +0 -0
- {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/docs/migrating-from-0.0.x.md +0 -0
- {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/docs/migrating-from-0.1.x.md +0 -0
- {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/docs/patterns/agent-events.md +0 -0
- {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/docs/patterns/circuit-breaker.md +0 -0
- {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/docs/patterns/durability.md +0 -0
- {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/docs/patterns/lifecycle.md +0 -0
- {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/docs/patterns/orchestrator.md +0 -0
- {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/docs/patterns/permissions.md +0 -0
- {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/docs/patterns/policy.md +0 -0
- {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/docs/patterns/quality-gate.md +0 -0
- {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/docs/patterns/rate-limiting.md +0 -0
- {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/docs/patterns/retry.md +0 -0
- {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/docs/patterns/routing.md +0 -0
- {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/docs/patterns/usage-tracking.md +0 -0
- {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/docs/tutorials/end-to-end.md +0 -0
- {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/examples/durable_agent.py +0 -0
- {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/examples/parallel_tools.py +0 -0
- {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/examples/pricing.json +0 -0
- {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/examples/tiny_agent.py +0 -0
- {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/scripts/check_module_coverage.py +0 -0
- {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/src/techrevati/__init__.py +0 -0
- {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/src/techrevati/runtime/agent_lifecycle.py +0 -0
- {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/src/techrevati/runtime/checkpoint.py +0 -0
- {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/src/techrevati/runtime/circuit_breaker.py +0 -0
- {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/src/techrevati/runtime/data/pricing.json +0 -0
- {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/src/techrevati/runtime/handoffs.py +0 -0
- {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/src/techrevati/runtime/permissions.py +0 -0
- {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/src/techrevati/runtime/persistence.py +0 -0
- {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/src/techrevati/runtime/policy_engine.py +0 -0
- {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/src/techrevati/runtime/py.typed +0 -0
- {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/src/techrevati/runtime/quality_gate.py +0 -0
- {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/src/techrevati/runtime/rate_limit.py +0 -0
- {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/src/techrevati/runtime/routing.py +0 -0
- {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/src/techrevati/runtime/scheduler.py +0 -0
- {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/src/techrevati/runtime/sinks.py +0 -0
- {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/tests/__init__.py +0 -0
- {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/tests/conftest.py +0 -0
- {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/tests/test_agent_events.py +0 -0
- {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/tests/test_agent_lifecycle.py +0 -0
- {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/tests/test_async_circuit_breaker.py +0 -0
- {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/tests/test_circuit_breaker.py +0 -0
- {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/tests/test_otel_nesting.py +0 -0
- {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/tests/test_permissions.py +0 -0
- {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/tests/test_policy_engine.py +0 -0
- {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/tests/test_property_circuit_breaker.py +0 -0
- {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/tests/test_property_retry_policy.py +0 -0
- {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/tests/test_quality_gate.py +0 -0
- {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/tests/test_retry_policy.py +0 -0
- {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/tests/test_routing.py +0 -0
- {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/tests/test_s5_scheduler_persistence_async_policy.py +0 -0
- {techrevati_runtime-0.2.0 → techrevati_runtime-0.3.0.dev1}/tests/test_usage_tracking.py +0 -0
|
@@ -5,6 +5,135 @@ follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); the project
|
|
|
5
5
|
follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html), with the
|
|
6
6
|
caveat that 0.x APIs are explicitly unstable.
|
|
7
7
|
|
|
8
|
+
## [0.3.0.dev1] — 2026-05-22
|
|
9
|
+
|
|
10
|
+
First preview of the 0.3.0 milestone (EU AI Act compliance release).
|
|
11
|
+
Bundles Sprint 2 (governance plane + async guardrails) and Sprint 3
|
|
12
|
+
(streaming + mutating hook chain) plus all the 0.2.1 sharp-edges
|
|
13
|
+
fixes — 0.2.1 itself was committed but never tagged for PyPI, so its
|
|
14
|
+
fixes ride into 0.3.0 instead. Install with
|
|
15
|
+
`pip install --pre techrevati-runtime` to try the preview; the stable
|
|
16
|
+
channel still resolves to 0.2.0.
|
|
17
|
+
|
|
18
|
+
This is a **dev release** (PEP 440 `.dev1`). API surface is
|
|
19
|
+
forward-compatible with planned 0.3.0 features but may shift before the
|
|
20
|
+
final cut; pin to `==0.3.0.dev1` if you depend on the exact surface.
|
|
21
|
+
|
|
22
|
+
### Added — Sprint 3 (streaming + lifecycle hooks)
|
|
23
|
+
|
|
24
|
+
- **`AsyncOrchestrationSession.arun_turn_stream`** — structured
|
|
25
|
+
`StreamEvent` generator that reemits caller-produced text chunks and
|
|
26
|
+
terminates with a `final` event carrying the resolved usage snapshot.
|
|
27
|
+
Participates in the full session bookkeeping (iteration cap,
|
|
28
|
+
governance plane, rate limiter, usage tracker) exactly like
|
|
29
|
+
`arun_turn`. Cancellation is consumer-driven: wrap with
|
|
30
|
+
`contextlib.aclosing` to get deterministic cleanup of the upstream
|
|
31
|
+
generator; `session._last_stream_cancelled` flips to `True` on the
|
|
32
|
+
cancelled path.
|
|
33
|
+
- **`StreamEvent`** frozen dataclass + classmethod constructors
|
|
34
|
+
(`text`, `tool_call`, `tool_result`, `handoff`, `final`, `error`),
|
|
35
|
+
`to_dict()` / `to_json()` for wire serialization. `StreamEventType`
|
|
36
|
+
and `StreamFinalStatus` type aliases exported for consumer typing.
|
|
37
|
+
- **`Hook` + `AsyncHook` Protocols** — interceptor chain that *mutates*
|
|
38
|
+
data, in contrast to the observe-only `EventSink`. Methods are
|
|
39
|
+
optional via `hasattr` dispatch; implement only what you need.
|
|
40
|
+
- **`HookContext`** — mutable dataclass shared across the hook chain.
|
|
41
|
+
Fields: `role`, `phase`, `model`, `prompt`, `tool`, `args`, `extra`.
|
|
42
|
+
Pass via `hook_ctx=` on `run_turn` / `arun_turn` / `run_tool` /
|
|
43
|
+
`arun_tool` / `arun_turn_stream`.
|
|
44
|
+
- **`AgentSession(hooks=[...])`** — hook chain wired through every
|
|
45
|
+
sync + async session. Chain runs left-to-right; later hooks see
|
|
46
|
+
earlier mutations.
|
|
47
|
+
- **Built-in hooks:** `RedactPIIHook` (best-effort PII scrubbing for
|
|
48
|
+
strings, dicts, OpenAI-style message lists; runs `before_model` and
|
|
49
|
+
`after_model`), `LogModelIOHook` (stdlib logger emits model input +
|
|
50
|
+
output with truncation), `TokenBudgetCheckHook` (pre-flight token
|
|
51
|
+
budget guard that raises `HookBudgetExceededError`).
|
|
52
|
+
- **Docs:** `docs/patterns/streaming.md`, `docs/patterns/hooks.md`,
|
|
53
|
+
plus API reference stubs `docs/api/streaming.md`,
|
|
54
|
+
`docs/api/hooks.md`; mkdocs nav updated.
|
|
55
|
+
|
|
56
|
+
### Added — Sprint 2 (governance plane + async guardrails)
|
|
57
|
+
|
|
58
|
+
- **`GovernancePlane`** primitive — hard-stop limit enforcement
|
|
59
|
+
*outside* agent code, terminal on breach (does NOT route through
|
|
60
|
+
recovery). Built-in limits: `MaxIterationsLimit`, `MaxBudgetLimit`,
|
|
61
|
+
`MaxConsecutiveFailuresLimit`, `MaxToolCallsLimit`. Each carries a
|
|
62
|
+
`value`, `scope` (`session` for now), and `on_breach` mode
|
|
63
|
+
(`terminate` raises `GovernanceBreachError`; `alert` emits
|
|
64
|
+
`governance.alert` events and continues).
|
|
65
|
+
- **`AgentSession(governance=...)`** wired through both sync and async
|
|
66
|
+
sessions. The plane ticks turn counter pre-turn, tool counter
|
|
67
|
+
pre-tool, and cost / success-streak post-turn.
|
|
68
|
+
- **`AsyncGuardrail`** Protocol (`acheck_pre` / `acheck_post`) for
|
|
69
|
+
guardrails that need I/O. Mixed sync + async guardrail lists
|
|
70
|
+
supported on async sessions.
|
|
71
|
+
- **`PatternGuardrail`** (regex deny-list, one compiled alternation
|
|
72
|
+
per instance) and **`PromptInjectionGuardrail`** (subclass with 11
|
|
73
|
+
canonical injection signatures: instruction override, role hijack,
|
|
74
|
+
delimiter abuse, base64 blob, system-prompt extraction).
|
|
75
|
+
- **`governance.breach`** + **`governance.alert`** event names in
|
|
76
|
+
`AgentEventName` — emitted via `EventSink` before the breach
|
|
77
|
+
exception raises so audit logs catch them even when the exception
|
|
78
|
+
propagates past handlers.
|
|
79
|
+
- **Docs:** `docs/patterns/governance.md` + `docs/api/governance.md`.
|
|
80
|
+
|
|
81
|
+
## [0.2.1] — 2026-05-20
|
|
82
|
+
|
|
83
|
+
Sharp-edges patch landed the same day as 0.2.0 to close silent footguns
|
|
84
|
+
identified in the 0.3.0 migration audit. No new primitives; one
|
|
85
|
+
intentional soft-breaking semantic change to `GuardrailViolatedError`
|
|
86
|
+
(callers reading the legacy single-violation fields still work — see
|
|
87
|
+
"Changed" below).
|
|
88
|
+
|
|
89
|
+
### Fixed
|
|
90
|
+
|
|
91
|
+
- **`RecoveryRecipe.step_retries` is now honored** by `attempt_recovery`
|
|
92
|
+
and `aattempt_recovery`. Previously the field existed on the dataclass
|
|
93
|
+
but the recovery executors did not consume it — a recipe that set
|
|
94
|
+
`step_retries={RecoveryStep.RETRY_WITH_BACKOFF: 3}` silently ran the
|
|
95
|
+
step exactly once. Now the executor retries the step up to the
|
|
96
|
+
budgeted count before moving to the next step. Missing keys default
|
|
97
|
+
to a single attempt, preserving 0.2.0 behavior.
|
|
98
|
+
- **`OpenTelemetrySink` cleans up orphan parent spans on interpreter
|
|
99
|
+
exit.** If a process died between `AGENT_STARTED` / `PHASE_STARTED`
|
|
100
|
+
and the matching `AGENT_COMPLETED` / `AGENT_FAILED` / `PHASE_COMPLETED`,
|
|
101
|
+
the parent span previously stayed open in the exporter buffer and
|
|
102
|
+
corrupted the APM trace tree. An `atexit` hook now marks every
|
|
103
|
+
still-open parent with `error.type=abrupt_termination` and an `ERROR`
|
|
104
|
+
status before ending it. The hook is no-op on the clean-exit path.
|
|
105
|
+
|
|
106
|
+
### Added
|
|
107
|
+
|
|
108
|
+
- **`register_pricing(model, pricing, *, on_conflict="overwrite")`** — explicit
|
|
109
|
+
merge semantics. `"overwrite"` (default) preserves 0.2.0 behavior;
|
|
110
|
+
`"error"` raises `PricingAlreadyRegisteredError` on re-registration;
|
|
111
|
+
`"keep"` retains the existing entry and drops the new pricing silently.
|
|
112
|
+
Useful for "register defaults if not present" startup patterns.
|
|
113
|
+
- **`PricingAlreadyRegisteredError`** — exported from
|
|
114
|
+
`techrevati.runtime`. Subclass of `ValueError`, carries `.model`.
|
|
115
|
+
- **`GuardrailViolation`** dataclass — one entry in the new
|
|
116
|
+
`GuardrailViolatedError.violations` tuple. Carries `outcome`,
|
|
117
|
+
`guardrail` (name), `stage` (`"pre"` / `"post"`). Has `to_dict()` for
|
|
118
|
+
audit-log serialization.
|
|
119
|
+
- **`DeprecationWarning` on `Orchestrator(...)` instantiation** — emitted
|
|
120
|
+
once per process. `AgentSession` has been the canonical class name
|
|
121
|
+
since 0.2.0; the alias remains for a deprecation window and will be
|
|
122
|
+
removed in 0.3.0. Silent in import — only the first construction
|
|
123
|
+
warns.
|
|
124
|
+
|
|
125
|
+
### Changed
|
|
126
|
+
|
|
127
|
+
- **`GuardrailViolatedError.violations`** — every guardrail that fires
|
|
128
|
+
at the same stage is now collected and surfaced as a tuple on the
|
|
129
|
+
raised error, instead of short-circuiting on the first violation.
|
|
130
|
+
Required for EU AI Act Article 12 record-keeping (audit logs must
|
|
131
|
+
reflect the full set of guardrails that fired). Legacy callers that
|
|
132
|
+
read `error.outcome` / `error.guardrail` / `error.stage` still work —
|
|
133
|
+
those attributes mirror the first violation. The orchestrator now
|
|
134
|
+
runs every pre-check and post-check before raising; tests that
|
|
135
|
+
asserted short-circuit behavior have been updated.
|
|
136
|
+
|
|
8
137
|
## [0.2.0] — 2026-05-20
|
|
9
138
|
|
|
10
139
|
Durable execution, token-aware rate limiting, OTel agent-level span
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: techrevati-runtime
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0.dev1
|
|
4
4
|
Summary: Async-aware runtime primitives for multi-step LLM agent loops.
|
|
5
5
|
Project-URL: Homepage, https://github.com/Techrevati/runtime
|
|
6
6
|
Project-URL: Documentation, https://Techrevati.github.io/runtime
|
|
@@ -47,7 +47,7 @@ Description-Content-Type: text/markdown
|
|
|
47
47
|
[](#design-goals)
|
|
48
48
|
[](LICENSE)
|
|
49
49
|
|
|
50
|
-
Production-grade runtime primitives for multi-step LLM agent loops — sync **and** async, with retry classification, circuit-breaker protection, per-model cost tracking, opt-in budget enforcement, role-based tool gating, content guardrails, agent-to-agent handoffs, declarative policy, and OpenTelemetry GenAI semantic conventions out of the box. **Beta — 0.
|
|
50
|
+
Production-grade runtime primitives for multi-step LLM agent loops — sync **and** async, with retry classification, circuit-breaker protection, per-model cost tracking, opt-in budget enforcement, role-based tool gating, content guardrails, agent-to-agent handoffs, declarative policy, durable checkpointing, token-aware rate limiting, and OpenTelemetry GenAI semantic conventions out of the box. **Beta — 0.2.x; 0.x APIs remain explicitly unstable.**
|
|
51
51
|
|
|
52
52
|
```bash
|
|
53
53
|
pip install techrevati-runtime
|
|
@@ -190,20 +190,20 @@ print(tracker.format_cost())
|
|
|
190
190
|
- **OpenAI Agents SDK** is a *cohesive runtime* tied to OpenAI's models, with default tracing through their dashboards. Use it when you're committed to OpenAI and want the smoothest path.
|
|
191
191
|
- **`techrevati-runtime`** is a *zero-dep primitive set*. Sync + async. Vendor-neutral. Emits OpenTelemetry GenAI semantic conventions so the same APM dashboards that consume OpenAI Agents SDK telemetry will pick us up too. Bring your own model client and your own persistence — the runtime stays opinion-free.
|
|
192
192
|
|
|
193
|
-
The runtime
|
|
193
|
+
The runtime ships a pluggable `CheckpointSaver` protocol with `InMemorySaver` and `SqliteSaver` implementations (0.2.0) — enough for resume-from-checkpoint replay across restarts. It is still not a full durable workflow engine in the Temporal sense; pair with [Temporal](https://temporal.io/), [dbos](https://www.dbos.dev/), or LangGraph's checkpointer if you need cross-host scheduling, retries-as-history, or a durable timer service.
|
|
194
194
|
|
|
195
195
|
## Limitations (be honest with yourself before adopting)
|
|
196
196
|
|
|
197
197
|
- **Pricing must be registered.** The bundled `pricing.json` is intentionally empty. Without `register_pricing()` or `load_pricing_from_file()`, every cost calculation returns $0.00 (you will see a one-time warning per model).
|
|
198
198
|
- **Budget enforcement is opt-in.** Set `Orchestrator(enforce_budget=True)` to raise `BudgetExceededError`; the default merely records an event and continues.
|
|
199
199
|
- **Permissions are advisory.** `OrchestrationSession.run_tool()` enforces; `run_turn()` does not gate model calls. There is no sandbox — pair with OS-level isolation if needed.
|
|
200
|
-
- **
|
|
200
|
+
- **Durable execution is opt-in.** Default sessions are in-memory; pass a `CheckpointSaver` (e.g. `SqliteSaver`) plus a stable `thread_id` to get resume-from-checkpoint replay. Pair with Temporal/dbos if you need cross-host scheduling or durable timers.
|
|
201
201
|
- **Default sinks are in-memory ring buffers.** Long-running sessions need a durable `EventSink` and `UsageSink` (e.g. `OpenTelemetrySink`, or your own).
|
|
202
202
|
- **`CircuitBreaker` state is per-process.** Each replica counts its own failures. Add a shared coordinator if you need fleet-wide breaker state.
|
|
203
203
|
|
|
204
204
|
## Status
|
|
205
205
|
|
|
206
|
-
`techrevati-runtime` is at version **0.
|
|
206
|
+
`techrevati-runtime` is at version **0.2.0** (beta). This release ships durable execution (`CheckpointSaver` + `SqliteSaver`), token-aware rate limiting (`RateLimiter` / `AsyncRateLimiter`), provider routing, per-session `UsageLimits`, nested OTel agent spans, persistent SQLite sinks, and supply-chain hardening (CycloneDX SBOM + CodeQL + zero-deps smoke). The `AgentSession` rename and OTel wire-format change are the two soft-breaking items — see [docs/migrating-from-0.1.x.md](docs/migrating-from-0.1.x.md). 0.x APIs remain unstable; breaking changes will continue to be gated by deprecation warnings. Pinning Python 3.11+ for `from __future__ import annotations` ergonomics and modern asyncio.
|
|
207
207
|
|
|
208
208
|
See [CHANGELOG.md](CHANGELOG.md) for the per-sprint release notes and [docs/tutorials/end-to-end.md](docs/tutorials/end-to-end.md) for a guided tour of every primitive.
|
|
209
209
|
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
[](#design-goals)
|
|
7
7
|
[](LICENSE)
|
|
8
8
|
|
|
9
|
-
Production-grade runtime primitives for multi-step LLM agent loops — sync **and** async, with retry classification, circuit-breaker protection, per-model cost tracking, opt-in budget enforcement, role-based tool gating, content guardrails, agent-to-agent handoffs, declarative policy, and OpenTelemetry GenAI semantic conventions out of the box. **Beta — 0.
|
|
9
|
+
Production-grade runtime primitives for multi-step LLM agent loops — sync **and** async, with retry classification, circuit-breaker protection, per-model cost tracking, opt-in budget enforcement, role-based tool gating, content guardrails, agent-to-agent handoffs, declarative policy, durable checkpointing, token-aware rate limiting, and OpenTelemetry GenAI semantic conventions out of the box. **Beta — 0.2.x; 0.x APIs remain explicitly unstable.**
|
|
10
10
|
|
|
11
11
|
```bash
|
|
12
12
|
pip install techrevati-runtime
|
|
@@ -149,20 +149,20 @@ print(tracker.format_cost())
|
|
|
149
149
|
- **OpenAI Agents SDK** is a *cohesive runtime* tied to OpenAI's models, with default tracing through their dashboards. Use it when you're committed to OpenAI and want the smoothest path.
|
|
150
150
|
- **`techrevati-runtime`** is a *zero-dep primitive set*. Sync + async. Vendor-neutral. Emits OpenTelemetry GenAI semantic conventions so the same APM dashboards that consume OpenAI Agents SDK telemetry will pick us up too. Bring your own model client and your own persistence — the runtime stays opinion-free.
|
|
151
151
|
|
|
152
|
-
The runtime
|
|
152
|
+
The runtime ships a pluggable `CheckpointSaver` protocol with `InMemorySaver` and `SqliteSaver` implementations (0.2.0) — enough for resume-from-checkpoint replay across restarts. It is still not a full durable workflow engine in the Temporal sense; pair with [Temporal](https://temporal.io/), [dbos](https://www.dbos.dev/), or LangGraph's checkpointer if you need cross-host scheduling, retries-as-history, or a durable timer service.
|
|
153
153
|
|
|
154
154
|
## Limitations (be honest with yourself before adopting)
|
|
155
155
|
|
|
156
156
|
- **Pricing must be registered.** The bundled `pricing.json` is intentionally empty. Without `register_pricing()` or `load_pricing_from_file()`, every cost calculation returns $0.00 (you will see a one-time warning per model).
|
|
157
157
|
- **Budget enforcement is opt-in.** Set `Orchestrator(enforce_budget=True)` to raise `BudgetExceededError`; the default merely records an event and continues.
|
|
158
158
|
- **Permissions are advisory.** `OrchestrationSession.run_tool()` enforces; `run_turn()` does not gate model calls. There is no sandbox — pair with OS-level isolation if needed.
|
|
159
|
-
- **
|
|
159
|
+
- **Durable execution is opt-in.** Default sessions are in-memory; pass a `CheckpointSaver` (e.g. `SqliteSaver`) plus a stable `thread_id` to get resume-from-checkpoint replay. Pair with Temporal/dbos if you need cross-host scheduling or durable timers.
|
|
160
160
|
- **Default sinks are in-memory ring buffers.** Long-running sessions need a durable `EventSink` and `UsageSink` (e.g. `OpenTelemetrySink`, or your own).
|
|
161
161
|
- **`CircuitBreaker` state is per-process.** Each replica counts its own failures. Add a shared coordinator if you need fleet-wide breaker state.
|
|
162
162
|
|
|
163
163
|
## Status
|
|
164
164
|
|
|
165
|
-
`techrevati-runtime` is at version **0.
|
|
165
|
+
`techrevati-runtime` is at version **0.2.0** (beta). This release ships durable execution (`CheckpointSaver` + `SqliteSaver`), token-aware rate limiting (`RateLimiter` / `AsyncRateLimiter`), provider routing, per-session `UsageLimits`, nested OTel agent spans, persistent SQLite sinks, and supply-chain hardening (CycloneDX SBOM + CodeQL + zero-deps smoke). The `AgentSession` rename and OTel wire-format change are the two soft-breaking items — see [docs/migrating-from-0.1.x.md](docs/migrating-from-0.1.x.md). 0.x APIs remain unstable; breaking changes will continue to be gated by deprecation warnings. Pinning Python 3.11+ for `from __future__ import annotations` ergonomics and modern asyncio.
|
|
166
166
|
|
|
167
167
|
See [CHANGELOG.md](CHANGELOG.md) for the per-sprint release notes and [docs/tutorials/end-to-end.md](docs/tutorials/end-to-end.md) for a guided tour of every primitive.
|
|
168
168
|
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
# Governance Plane
|
|
2
|
+
|
|
3
|
+
`GovernancePlane` is the runtime's last line of defense: hard-stop
|
|
4
|
+
limits enforced *outside* agent code so the agent cannot bypass them
|
|
5
|
+
via recovery. When a limit configured with `on_breach="terminate"` is
|
|
6
|
+
exceeded, the orchestrator raises `GovernanceBreachError`, which the
|
|
7
|
+
session marks as `FAILED` and re-raises **without** going through the
|
|
8
|
+
failure classifier or the recovery loop.
|
|
9
|
+
|
|
10
|
+
This is the technical primitive auditors expect for EU AI Act
|
|
11
|
+
deployments — Article 14 (human oversight via stopping conditions),
|
|
12
|
+
Article 15 (robustness / fail-safes), and Article 26 (deployer
|
|
13
|
+
monitoring + reporting). The full article-by-article compliance mapping
|
|
14
|
+
ships in 0.3.0 Sprint 6 (`docs/compliance/`).
|
|
15
|
+
|
|
16
|
+
## When to use this
|
|
17
|
+
|
|
18
|
+
- **You ship to EU customers** and any user-deployed system would meet
|
|
19
|
+
the Annex III "high-risk" definition. Article 9 risk management,
|
|
20
|
+
Article 12 record-keeping, and Article 26 deployer-side monitoring
|
|
21
|
+
all want a runtime kill-switch.
|
|
22
|
+
- **Cost is dollars per token, not milliseconds per request.** A
|
|
23
|
+
budget cap that the agent could in principle catch + recover from is
|
|
24
|
+
not a hard cap. `MaxBudgetLimit(on_breach="terminate")` is.
|
|
25
|
+
- **The agent runs unattended and must stop on its own.** Production
|
|
26
|
+
multi-step agent loops can drift; a 25-turn iteration cap + a
|
|
27
|
+
consecutive-failure cap rules out the canonical runaway-loop
|
|
28
|
+
failure mode.
|
|
29
|
+
- **You need a rollout signal before flipping a knob to hard-stop.**
|
|
30
|
+
Use `on_breach="alert"` first; measure breach rates from the
|
|
31
|
+
`governance.alert` events; flip to `"terminate"` when you trust the
|
|
32
|
+
threshold.
|
|
33
|
+
|
|
34
|
+
## When NOT to use this
|
|
35
|
+
|
|
36
|
+
- For *recoverable* token / cost ceilings inside one session that the
|
|
37
|
+
agent is allowed to react to. Use [`UsageLimits`](usage-tracking.md)
|
|
38
|
+
instead — its `UsageLimitExceededError` is catchable and recovery
|
|
39
|
+
flows can respond.
|
|
40
|
+
- For per-tool authorization. Use [`PermissionEnforcer`](permissions.md).
|
|
41
|
+
- For pattern blocking on tool inputs or outputs. Use the
|
|
42
|
+
built-in `PatternGuardrail` / `PromptInjectionGuardrail` (or a
|
|
43
|
+
custom `Guardrail`) — see [Permissions](permissions.md) and
|
|
44
|
+
[API: Guardrails](../api/guardrails.md).
|
|
45
|
+
|
|
46
|
+
## Quickstart
|
|
47
|
+
|
|
48
|
+
```python
|
|
49
|
+
from techrevati.runtime import (
|
|
50
|
+
AgentSession,
|
|
51
|
+
GovernancePlane,
|
|
52
|
+
MaxBudgetLimit,
|
|
53
|
+
MaxConsecutiveFailuresLimit,
|
|
54
|
+
MaxIterationsLimit,
|
|
55
|
+
MaxToolCallsLimit,
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
plane = GovernancePlane(
|
|
59
|
+
limits=(
|
|
60
|
+
MaxIterationsLimit(value=25, on_breach="terminate"),
|
|
61
|
+
MaxBudgetLimit(value=5.00, on_breach="terminate"),
|
|
62
|
+
MaxConsecutiveFailuresLimit(value=3, on_breach="terminate"),
|
|
63
|
+
MaxToolCallsLimit(value=100, on_breach="alert"),
|
|
64
|
+
),
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
session = AgentSession(role="writer", phase="draft", governance=plane)
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
The orchestrator ticks the plane's counters at three points:
|
|
71
|
+
|
|
72
|
+
- **Pre-turn** — `record_turn_start()` and `enforce()`. The iteration
|
|
73
|
+
cap fires here.
|
|
74
|
+
- **Pre-tool** — `record_tool_call()` and `enforce()`. The tool-call
|
|
75
|
+
cap fires here.
|
|
76
|
+
- **Post-turn** — `record_success()` or `record_failure()`,
|
|
77
|
+
`record_cost(cost_delta)`, and `enforce()`. The budget and
|
|
78
|
+
consecutive-failures caps fire here.
|
|
79
|
+
|
|
80
|
+
## The four built-in limits
|
|
81
|
+
|
|
82
|
+
### `MaxIterationsLimit`
|
|
83
|
+
|
|
84
|
+
Caps total turns in the session. Distinct from
|
|
85
|
+
`AgentSession.max_iterations` — that one raises a recoverable
|
|
86
|
+
`MaxIterationsExceededError`; this one is terminal.
|
|
87
|
+
|
|
88
|
+
```python
|
|
89
|
+
MaxIterationsLimit(value=25, on_breach="terminate")
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### `MaxBudgetLimit`
|
|
93
|
+
|
|
94
|
+
Caps cumulative cost in USD. Distinct from `UsageLimits.cost_usd_max`
|
|
95
|
+
— that one is recoverable; this one is terminal.
|
|
96
|
+
|
|
97
|
+
```python
|
|
98
|
+
MaxBudgetLimit(value=5.00, on_breach="terminate")
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### `MaxConsecutiveFailuresLimit`
|
|
102
|
+
|
|
103
|
+
Counts consecutive failures. A single successful turn resets the
|
|
104
|
+
counter to zero. Catches "the agent retries the same broken thing
|
|
105
|
+
forever" failure modes that per-step retry budgets alone do not.
|
|
106
|
+
|
|
107
|
+
```python
|
|
108
|
+
MaxConsecutiveFailuresLimit(value=3, on_breach="terminate")
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### `MaxToolCallsLimit`
|
|
112
|
+
|
|
113
|
+
Caps total tool invocations in the session. Distinct from
|
|
114
|
+
`UsageLimits.tool_calls_max` only in being terminal.
|
|
115
|
+
|
|
116
|
+
```python
|
|
117
|
+
MaxToolCallsLimit(value=100, on_breach="alert")
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
## `on_breach` modes
|
|
121
|
+
|
|
122
|
+
| Mode | Behavior |
|
|
123
|
+
|---|---|
|
|
124
|
+
| `"terminate"` (default) | Raises `GovernanceBreachError`. Worker → `FAILED`. Recovery loop is **NOT** invoked. |
|
|
125
|
+
| `"alert"` | Emits a `governance.alert` event on every breached evaluation. Session continues. |
|
|
126
|
+
|
|
127
|
+
Rolling out a new limit safely: deploy with `"alert"` for 1–2 weeks,
|
|
128
|
+
observe the `governance.alert` event rate, then flip to `"terminate"`.
|
|
129
|
+
|
|
130
|
+
## Event surface
|
|
131
|
+
|
|
132
|
+
Two new `AgentEventName` values surface in 0.3.0:
|
|
133
|
+
|
|
134
|
+
- `governance.breach` — emitted **before** `GovernanceBreachError`
|
|
135
|
+
raises so downstream sinks see the breach in the audit log even when
|
|
136
|
+
the exception propagates past them.
|
|
137
|
+
- `governance.alert` — emitted once per evaluation per breached
|
|
138
|
+
alert-mode limit.
|
|
139
|
+
|
|
140
|
+
Both carry `data = {limit_name, observed, ceiling, scope}` for sink
|
|
141
|
+
serialization.
|
|
142
|
+
|
|
143
|
+
## Composing with `UsageLimits`
|
|
144
|
+
|
|
145
|
+
These two primitives are not redundant — they sit at different layers.
|
|
146
|
+
|
|
147
|
+
```python
|
|
148
|
+
sess = AgentSession(
|
|
149
|
+
role="writer",
|
|
150
|
+
phase="draft",
|
|
151
|
+
# Soft cap: agent code can catch UsageLimitExceededError and react.
|
|
152
|
+
usage_limits=UsageLimits(total_tokens_max=200_000),
|
|
153
|
+
# Hard cap: governance breach terminates the session regardless.
|
|
154
|
+
governance=GovernancePlane(
|
|
155
|
+
limits=(MaxBudgetLimit(value=10.00, on_breach="terminate"),),
|
|
156
|
+
),
|
|
157
|
+
)
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
A common pattern is: `usage_limits` cap at 80% of the budget, `governance`
|
|
161
|
+
hard-stop at 100%. The agent gets a recoverable warning before the
|
|
162
|
+
session dies.
|
|
163
|
+
|
|
164
|
+
## Tuning the knobs
|
|
165
|
+
|
|
166
|
+
| Knob | Reasonable range | Notes |
|
|
167
|
+
|---|---|---|
|
|
168
|
+
| `MaxIterationsLimit.value` | 10–50 for production loops | Same default as OpenAI Agents SDK. |
|
|
169
|
+
| `MaxBudgetLimit.value` | per-customer / per-session limit | Pair with `UsageLimits.cost_usd_max` at 80%. |
|
|
170
|
+
| `MaxConsecutiveFailuresLimit.value` | 2–5 | Below 2 is twitchy; above 5 hides real reliability bugs. |
|
|
171
|
+
| `MaxToolCallsLimit.value` | 5×–10× expected | Useful as an alert before flipping to terminate. |
|
|
172
|
+
| `on_breach="alert"` | Always start here for new limits | Measure first, terminate second. |
|
|
173
|
+
|
|
174
|
+
## Anti-patterns
|
|
175
|
+
|
|
176
|
+
- **Catching `GovernanceBreachError` and retrying.** Don't. The point
|
|
177
|
+
of the plane is that the agent cannot bypass it. If you want
|
|
178
|
+
recoverable behavior, use `UsageLimits` instead.
|
|
179
|
+
- **Putting business logic in `GovernanceState.record_*`.** The state
|
|
180
|
+
object is a counter; do not subclass it to fire side effects on
|
|
181
|
+
every tick. Add a custom `EventSink` for that.
|
|
182
|
+
- **One plane per turn.** Construct the plane once and pass it to
|
|
183
|
+
`AgentSession`; do not create a new plane on each turn — counters
|
|
184
|
+
reset.
|
|
185
|
+
- **Mixing `"alert"` and `"terminate"` randomly across limits.** Pick a
|
|
186
|
+
rollout phase per limit, document it, and don't half-migrate.
|
|
187
|
+
|
|
188
|
+
## Sources
|
|
189
|
+
|
|
190
|
+
- Waxell — *AI Agent Circuit Breakers: The Reliability Pattern Production
|
|
191
|
+
Teams Are Missing* — [https://dev.to/waxell/ai-agent-circuit-breakers-...](https://dev.to/waxell/ai-agent-circuit-breakers-the-reliability-pattern-production-teams-are-missing-5bpg)
|
|
192
|
+
- DZone — *Engineering Hard-Stop Safety Into Autonomous Agent
|
|
193
|
+
Workflows* — [https://dzone.com/articles/algorithmic-circuit-breakers-agent-safety](https://dzone.com/articles/algorithmic-circuit-breakers-agent-safety)
|
|
194
|
+
- EU AI Act Articles 9, 12, 14, 15, 26 — [artificialintelligenceact.eu](https://artificialintelligenceact.eu/section/3-2/)
|