openbox-temporal-sdk-python 1.1.0__tar.gz → 1.1.2__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.
- openbox_temporal_sdk_python-1.1.2/CHANGELOG.md +124 -0
- {openbox_temporal_sdk_python-1.1.0 → openbox_temporal_sdk_python-1.1.2}/PKG-INFO +91 -17
- {openbox_temporal_sdk_python-1.1.0 → openbox_temporal_sdk_python-1.1.2}/README.md +88 -12
- {openbox_temporal_sdk_python-1.1.0 → openbox_temporal_sdk_python-1.1.2}/docs/code-standards.md +29 -15
- {openbox_temporal_sdk_python-1.1.0 → openbox_temporal_sdk_python-1.1.2}/docs/codebase-summary.md +28 -9
- {openbox_temporal_sdk_python-1.1.0 → openbox_temporal_sdk_python-1.1.2}/docs/system-architecture.md +33 -0
- openbox_temporal_sdk_python-1.1.2/docs/temporal-plugin-integration-guide.md +147 -0
- {openbox_temporal_sdk_python-1.1.0 → openbox_temporal_sdk_python-1.1.2}/openbox/__init__.py +13 -0
- openbox_temporal_sdk_python-1.1.2/openbox/activities.py +250 -0
- openbox_temporal_sdk_python-1.1.2/openbox/activity_interceptor.py +676 -0
- {openbox_temporal_sdk_python-1.1.0 → openbox_temporal_sdk_python-1.1.2}/openbox/client.py +11 -4
- {openbox_temporal_sdk_python-1.1.0 → openbox_temporal_sdk_python-1.1.2}/openbox/config.py +13 -7
- {openbox_temporal_sdk_python-1.1.0 → openbox_temporal_sdk_python-1.1.2}/openbox/context_propagation.py +4 -1
- {openbox_temporal_sdk_python-1.1.0 → openbox_temporal_sdk_python-1.1.2}/openbox/db_governance_hooks.py +247 -345
- {openbox_temporal_sdk_python-1.1.0 → openbox_temporal_sdk_python-1.1.2}/openbox/errors.py +33 -4
- {openbox_temporal_sdk_python-1.1.0 → openbox_temporal_sdk_python-1.1.2}/openbox/file_governance_hooks.py +67 -25
- {openbox_temporal_sdk_python-1.1.0 → openbox_temporal_sdk_python-1.1.2}/openbox/hitl.py +5 -0
- {openbox_temporal_sdk_python-1.1.0 → openbox_temporal_sdk_python-1.1.2}/openbox/hook_governance.py +83 -22
- {openbox_temporal_sdk_python-1.1.0 → openbox_temporal_sdk_python-1.1.2}/openbox/http_governance_hooks.py +193 -83
- {openbox_temporal_sdk_python-1.1.0 → openbox_temporal_sdk_python-1.1.2}/openbox/otel_setup.py +104 -117
- openbox_temporal_sdk_python-1.1.2/openbox/plugin.py +210 -0
- {openbox_temporal_sdk_python-1.1.0 → openbox_temporal_sdk_python-1.1.2}/openbox/span_processor.py +40 -12
- {openbox_temporal_sdk_python-1.1.0 → openbox_temporal_sdk_python-1.1.2}/openbox/tracing.py +83 -106
- {openbox_temporal_sdk_python-1.1.0 → openbox_temporal_sdk_python-1.1.2}/openbox/types.py +17 -5
- {openbox_temporal_sdk_python-1.1.0 → openbox_temporal_sdk_python-1.1.2}/openbox/verdict_handler.py +17 -7
- {openbox_temporal_sdk_python-1.1.0 → openbox_temporal_sdk_python-1.1.2}/openbox/worker.py +38 -16
- openbox_temporal_sdk_python-1.1.2/openbox/workflow_interceptor.py +365 -0
- {openbox_temporal_sdk_python-1.1.0 → openbox_temporal_sdk_python-1.1.2}/pyproject.toml +5 -7
- {openbox_temporal_sdk_python-1.1.0 → openbox_temporal_sdk_python-1.1.2}/tests/conftest.py +9 -4
- {openbox_temporal_sdk_python-1.1.0 → openbox_temporal_sdk_python-1.1.2}/tests/test_activities.py +41 -15
- {openbox_temporal_sdk_python-1.1.0 → openbox_temporal_sdk_python-1.1.2}/tests/test_activity_interceptor.py +410 -195
- {openbox_temporal_sdk_python-1.1.0 → openbox_temporal_sdk_python-1.1.2}/tests/test_config.py +16 -7
- {openbox_temporal_sdk_python-1.1.0 → openbox_temporal_sdk_python-1.1.2}/tests/test_db_governance_hooks.py +149 -71
- {openbox_temporal_sdk_python-1.1.0 → openbox_temporal_sdk_python-1.1.2}/tests/test_file_governance_hooks.py +111 -43
- openbox_temporal_sdk_python-1.1.2/tests/test_http_body_truncation.py +75 -0
- {openbox_temporal_sdk_python-1.1.0 → openbox_temporal_sdk_python-1.1.2}/tests/test_otel_setup.py +372 -175
- openbox_temporal_sdk_python-1.1.2/tests/test_plugin.py +266 -0
- openbox_temporal_sdk_python-1.1.2/tests/test_plugin_integration.py +281 -0
- {openbox_temporal_sdk_python-1.1.0 → openbox_temporal_sdk_python-1.1.2}/tests/test_psycopg2_hooks_verify.py +10 -3
- {openbox_temporal_sdk_python-1.1.0 → openbox_temporal_sdk_python-1.1.2}/tests/test_span_processor.py +22 -9
- {openbox_temporal_sdk_python-1.1.0 → openbox_temporal_sdk_python-1.1.2}/tests/test_tracing.py +54 -17
- {openbox_temporal_sdk_python-1.1.0 → openbox_temporal_sdk_python-1.1.2}/tests/test_worker.py +81 -50
- {openbox_temporal_sdk_python-1.1.0 → openbox_temporal_sdk_python-1.1.2}/tests/test_workflow_interceptor.py +134 -99
- {openbox_temporal_sdk_python-1.1.0 → openbox_temporal_sdk_python-1.1.2}/uv.lock +107 -619
- openbox_temporal_sdk_python-1.1.0/CHANGELOG.md +0 -68
- openbox_temporal_sdk_python-1.1.0/openbox/activities.py +0 -198
- openbox_temporal_sdk_python-1.1.0/openbox/activity_interceptor.py +0 -626
- openbox_temporal_sdk_python-1.1.0/openbox/workflow_interceptor.py +0 -275
- {openbox_temporal_sdk_python-1.1.0 → openbox_temporal_sdk_python-1.1.2}/.github/workflows/publish.yml +0 -0
- {openbox_temporal_sdk_python-1.1.0 → openbox_temporal_sdk_python-1.1.2}/.github/workflows/sonarqube.yaml +0 -0
- {openbox_temporal_sdk_python-1.1.0 → openbox_temporal_sdk_python-1.1.2}/.gitignore +0 -0
- {openbox_temporal_sdk_python-1.1.0 → openbox_temporal_sdk_python-1.1.2}/.python-version +0 -0
- {openbox_temporal_sdk_python-1.1.0 → openbox_temporal_sdk_python-1.1.2}/.repomixignore +0 -0
- {openbox_temporal_sdk_python-1.1.0 → openbox_temporal_sdk_python-1.1.2}/LICENSE +0 -0
- {openbox_temporal_sdk_python-1.1.0 → openbox_temporal_sdk_python-1.1.2}/docs/changelog-hook-level-governance.md +0 -0
- {openbox_temporal_sdk_python-1.1.0 → openbox_temporal_sdk_python-1.1.2}/docs/configuration.md +0 -0
- {openbox_temporal_sdk_python-1.1.0 → openbox_temporal_sdk_python-1.1.2}/docs/project-overview-pdr.md +0 -0
- {openbox_temporal_sdk_python-1.1.0 → openbox_temporal_sdk_python-1.1.2}/openbox/py.typed +0 -0
- {openbox_temporal_sdk_python-1.1.0 → openbox_temporal_sdk_python-1.1.2}/release-manifest.json +0 -0
- {openbox_temporal_sdk_python-1.1.0 → openbox_temporal_sdk_python-1.1.2}/repomix-output.xml +0 -0
- {openbox_temporal_sdk_python-1.1.0 → openbox_temporal_sdk_python-1.1.2}/sonar-project.properties +0 -0
- {openbox_temporal_sdk_python-1.1.0 → openbox_temporal_sdk_python-1.1.2}/tests/__init__.py +0 -0
- {openbox_temporal_sdk_python-1.1.0 → openbox_temporal_sdk_python-1.1.2}/tests/test_types.py +0 -0
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to OpenBox SDK for Temporal Workflows.
|
|
4
|
+
|
|
5
|
+
## [1.1.2] - 2026-04-22
|
|
6
|
+
|
|
7
|
+
### Security
|
|
8
|
+
|
|
9
|
+
- **API key no longer flows through workflow history.** `send_governance_event` refactored into a `GovernanceActivities` class that holds credentials on `self`; activity inputs carry only `payload`, `timeout`, and `on_api_error`. Before this change, anyone with Describe permissions on the namespace could read the API key from the recorded activity input.
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- W3C trace propagation through Temporal headers, on by default via `enable_trace_propagation=True` on `OpenBoxPlugin` and `create_openbox_worker()`. Uses `temporalio.contrib.opentelemetry.TracingInterceptor` so spans started by the caller stitch to workflow/activity spans on the worker side.
|
|
14
|
+
- `ApplicationError` type constants in `errors.py` (`GOVERNANCE_HALT_ERROR_TYPE`, `GOVERNANCE_BLOCK_ERROR_TYPE`, `GOVERNANCE_API_ERROR_TYPE`, `GOVERNANCE_STOP_ERROR_TYPE`) — single source of truth for governance error routing.
|
|
15
|
+
- `openbox.activities.GovernanceActivities` class + `build_governance_activities()` factory.
|
|
16
|
+
|
|
17
|
+
### Fixed
|
|
18
|
+
|
|
19
|
+
- **Workflow exception shadowing** — `WorkflowFailed` event send is now wrapped in `try/except`, so a `GovernanceHaltError` from `fail_closed + API down` no longer replaces the real workflow error.
|
|
20
|
+
- **String-matching on exception types** — `workflow_interceptor` now inspects `ApplicationError.type` via the exception chain instead of `"GovernanceHalt" in str(e)`. Eliminates false positives when a user workflow happens to emit an error message containing a governance keyword.
|
|
21
|
+
- **Race creating governance HTTP client** — `hook_governance._get_sync_client` / `_get_async_client` now use double-checked locking. Previously two concurrent activities could each create a client, with the losing instance getting garbage-collected while its connection pool leaked.
|
|
22
|
+
- **Replayer plugin coverage** — `test_plugin_integration.py` now passes `plugins=[plugin]` to `Replayer`, so replay tests validate interceptor determinism, not just user workflow code.
|
|
23
|
+
- Version-pin mismatch — comments in `openbox/__init__.py` said `temporalio >= 1.24.0` but pyproject/README pin `1.23.0`. Corrected to `1.23.0`.
|
|
24
|
+
|
|
25
|
+
### Changed
|
|
26
|
+
|
|
27
|
+
- `plugin.py` / `worker.py` now use `logging.getLogger(__name__)` for initialization status messages instead of `print()`.
|
|
28
|
+
|
|
29
|
+
## [1.1.1] - 2026-04-07
|
|
30
|
+
|
|
31
|
+
### Added
|
|
32
|
+
|
|
33
|
+
- **OpenBoxPlugin** — drop-in `SimplePlugin` integration for Temporal Workers. Single-line setup: `plugins=[OpenBoxPlugin(openbox_url=..., openbox_api_key=...)]`. Auto-registers interceptors, OTel instrumentation, sandbox passthrough, and `send_governance_event` activity
|
|
34
|
+
- Plugin integration guide for Temporal AI Partner Ecosystem (`docs/temporal-plugin-integration-guide.md`)
|
|
35
|
+
- HTTP body truncation tests (`tests/test_http_body_truncation.py`)
|
|
36
|
+
- Plugin unit tests (`tests/test_plugin.py`) and integration/replay tests (`tests/test_plugin_integration.py`)
|
|
37
|
+
|
|
38
|
+
### Fixed
|
|
39
|
+
|
|
40
|
+
- **HTTP body truncation** — enforce `max_body_size` (default 64KB) on request/response bodies in governance spans
|
|
41
|
+
- **File I/O spans** — remove raw file content from governance payloads; only `bytes_read`/`bytes_written` metadata sent
|
|
42
|
+
- **error_type sanitization** — prevent serialized error objects from being sent as `error.cause.error_type` string in WorkflowFailed payloads
|
|
43
|
+
- Remove useless f-strings, redundant `(ImportError, Exception)` clauses, merge nested if statements
|
|
44
|
+
- Prefix unused `span` param in urllib hook
|
|
45
|
+
|
|
46
|
+
### Changed
|
|
47
|
+
|
|
48
|
+
- `temporalio>=1.23.0` (from 1.8.0) for SimplePlugin support
|
|
49
|
+
- `GovernanceConfig.max_body_size` default changed from `None` (unlimited) to `65536` (64KB)
|
|
50
|
+
- `\w` regex shorthand in API key pattern
|
|
51
|
+
|
|
52
|
+
### Refactored
|
|
53
|
+
|
|
54
|
+
- Reduce cognitive complexity across 7 modules: `activity_interceptor.py` (126→split), `workflow_interceptor.py` (40→split), `activities.py` (20→split), `db_governance_hooks.py` (34→split), `otel_setup.py` (51→split), `tracing.py` (85→split), `verdict_handler.py` (16→15)
|
|
55
|
+
- Extract shared helpers: `_run_governed_query_sync/async`, `_build_error_dict`, `_extract_dbapi_context`, `_instrument_sqlalchemy`
|
|
56
|
+
|
|
57
|
+
### Dependencies
|
|
58
|
+
|
|
59
|
+
- Bump Pygments 2.19.2 → 2.20.0 (ReDoS fix, CVSS 1.9)
|
|
60
|
+
|
|
61
|
+
## [1.1.0] - 2026-03-09
|
|
62
|
+
|
|
63
|
+
### Added
|
|
64
|
+
|
|
65
|
+
- **Hook-level governance** — real-time, per-operation governance evaluation during activity execution
|
|
66
|
+
- Every HTTP request, database query, file operation, and traced function call is evaluated at `started` (before, can block) and `completed` (after, informational) stages
|
|
67
|
+
- Same `POST /api/v1/governance/evaluate` endpoint with new `hook_trigger` field in payload
|
|
68
|
+
- Automatically enabled when using `create_openbox_worker()`
|
|
69
|
+
- **Database query governance** — per-query started/completed evaluations for psycopg2, pymysql, mysql-connector, asyncpg, pymongo, redis, sqlalchemy
|
|
70
|
+
- **File I/O governance** — per-operation evaluations for open, read, write, readline, readlines, writelines, close (opt-in via `instrument_file_io=True`)
|
|
71
|
+
- **`@traced` decorator** (`openbox.tracing`) — function-level governance with OTel spans; zero overhead when governance not configured
|
|
72
|
+
- **`GovernanceBlockedError`** — new exception type for hook-level blocking with verdict, reason, and resource identifier
|
|
73
|
+
- **Abort propagation** — once one hook blocks, all subsequent hooks for the same activity short-circuit immediately
|
|
74
|
+
- **HALT workflow termination** from hook-level governance via `client.terminate()`
|
|
75
|
+
- **REQUIRE_APPROVAL** from hook-level governance enters the same HITL approval polling flow as activity-level approvals
|
|
76
|
+
- **`duration_ns`** computed for all hook span types (HTTP, file, function — DB already had it)
|
|
77
|
+
|
|
78
|
+
### Changed
|
|
79
|
+
|
|
80
|
+
- **`hook_trigger` simplified to boolean** — was a dict with type/stage/data, now just `true`. All data moved to span root fields
|
|
81
|
+
- **Span data consolidation** — all type-specific fields at span root (`hook_type`, `http_method`, `db_system`, `file_path`, `function`, etc.)
|
|
82
|
+
- **`attributes` is OTel-original only** — no custom `openbox.*`, `http.request.*`, `db.result.*` fields injected
|
|
83
|
+
- Hook governance payloads send only the current span per evaluation (not accumulated history)
|
|
84
|
+
- Event-level payloads (ActivityStarted/Completed, Workflow events) no longer include spans
|
|
85
|
+
- Simplified `WorkflowSpanProcessor` — removed span buffering, governed span tracking, body data merging; `on_end()` now only forwards to fallback exporters
|
|
86
|
+
|
|
87
|
+
### Fixed
|
|
88
|
+
|
|
89
|
+
- HALT verdict from hooks now correctly terminates the workflow (previously only stopped the activity like BLOCK)
|
|
90
|
+
- REQUIRE_APPROVAL from hooks now enters the approval polling flow (previously raised unhandled error)
|
|
91
|
+
- Stale buffer/verdict from previous workflow run no longer carries over when workflow_id is reused
|
|
92
|
+
- Subsequent hooks no longer fire after the first hook blocks an activity
|
|
93
|
+
|
|
94
|
+
## [1.0.21] - 2026-03-04
|
|
95
|
+
|
|
96
|
+
### Added
|
|
97
|
+
|
|
98
|
+
- Human-in-the-loop approval with expiration handling
|
|
99
|
+
- Approval polling via `POST /api/v1/governance/approval`
|
|
100
|
+
- Guardrails: input/output validation and redaction
|
|
101
|
+
- `GovernanceVerdictResponse.from_dict()` with guardrails_result parsing
|
|
102
|
+
- Output redaction for activity results
|
|
103
|
+
- `_deep_update_dataclass()` for in-place dataclass field updates from redacted dicts
|
|
104
|
+
|
|
105
|
+
### Fixed
|
|
106
|
+
|
|
107
|
+
- Temporal Payload objects no longer slip through as non-serializable in governance payloads
|
|
108
|
+
- Stale buffer detection via run_id comparison
|
|
109
|
+
|
|
110
|
+
## [1.0.0] - 2026-02-15
|
|
111
|
+
|
|
112
|
+
### Added
|
|
113
|
+
|
|
114
|
+
- Initial release
|
|
115
|
+
- 6 event types: WorkflowStarted, WorkflowCompleted, WorkflowFailed, SignalReceived, ActivityStarted, ActivityCompleted
|
|
116
|
+
- 5-tier verdict system: ALLOW, CONSTRAIN, REQUIRE_APPROVAL, BLOCK, HALT
|
|
117
|
+
- HTTP instrumentation via OpenTelemetry (httpx, requests, urllib3, urllib)
|
|
118
|
+
- Database instrumentation (psycopg2, pymysql, asyncpg, pymongo, redis, sqlalchemy)
|
|
119
|
+
- File I/O instrumentation (opt-in)
|
|
120
|
+
- Zero-code setup via `create_openbox_worker()` factory
|
|
121
|
+
- Workflow and activity interceptors for governance
|
|
122
|
+
- Span buffering and activity context tracking
|
|
123
|
+
- `fail_open` / `fail_closed` error policies
|
|
124
|
+
- v1.0 backward compatibility for legacy verdict strings
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: openbox-temporal-sdk-python
|
|
3
|
-
Version: 1.1.
|
|
3
|
+
Version: 1.1.2
|
|
4
4
|
Summary: OpenBox SDK - Governance and observability for Temporal workflows
|
|
5
5
|
Project-URL: Homepage, https://github.com/OpenBox-AI/temporal-sdk-python
|
|
6
6
|
Project-URL: Documentation, https://github.com/OpenBox-AI/temporal-sdk-python#readme
|
|
@@ -15,14 +15,12 @@ Classifier: Development Status :: 3 - Alpha
|
|
|
15
15
|
Classifier: Intended Audience :: Developers
|
|
16
16
|
Classifier: License :: OSI Approved :: MIT License
|
|
17
17
|
Classifier: Programming Language :: Python :: 3
|
|
18
|
-
Classifier: Programming Language :: Python :: 3.9
|
|
19
|
-
Classifier: Programming Language :: Python :: 3.10
|
|
20
18
|
Classifier: Programming Language :: Python :: 3.11
|
|
21
19
|
Classifier: Programming Language :: Python :: 3.12
|
|
22
20
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
23
21
|
Classifier: Topic :: System :: Monitoring
|
|
24
22
|
Classifier: Typing :: Typed
|
|
25
|
-
Requires-Python: >=3.
|
|
23
|
+
Requires-Python: >=3.11
|
|
26
24
|
Requires-Dist: asyncpg>=0.29.0
|
|
27
25
|
Requires-Dist: httpx<1,>=0.28.0
|
|
28
26
|
Requires-Dist: mysql-connector-python>=8.0.0
|
|
@@ -45,7 +43,7 @@ Requires-Dist: pymongo>=4.0.0
|
|
|
45
43
|
Requires-Dist: pymysql>=1.0.0
|
|
46
44
|
Requires-Dist: redis>=5.0.0
|
|
47
45
|
Requires-Dist: sqlalchemy>=2.0.0
|
|
48
|
-
Requires-Dist: temporalio<2,>=1.
|
|
46
|
+
Requires-Dist: temporalio<2,>=1.23.0
|
|
49
47
|
Description-Content-Type: text/markdown
|
|
50
48
|
|
|
51
49
|
# OpenBox SDK for Temporal Workflows
|
|
@@ -70,13 +68,70 @@ pip install openbox-temporal-sdk-python
|
|
|
70
68
|
```
|
|
71
69
|
|
|
72
70
|
**Requirements:**
|
|
73
|
-
- Python 3.
|
|
74
|
-
- Temporal SDK 1.8+
|
|
71
|
+
- Python 3.11+
|
|
72
|
+
- Temporal SDK 1.23+ (1.8+ for factory-only usage)
|
|
75
73
|
- OpenTelemetry API/SDK 1.38.0+
|
|
76
74
|
|
|
77
75
|
---
|
|
78
76
|
|
|
79
|
-
##
|
|
77
|
+
## Plugin Integration (Recommended)
|
|
78
|
+
|
|
79
|
+
Use `OpenBoxPlugin` for drop-in integration with Temporal Workers:
|
|
80
|
+
|
|
81
|
+
```python
|
|
82
|
+
import os
|
|
83
|
+
from temporalio.worker import Worker
|
|
84
|
+
from openbox.plugin import OpenBoxPlugin
|
|
85
|
+
|
|
86
|
+
worker = Worker(
|
|
87
|
+
client,
|
|
88
|
+
task_queue="my-task-queue",
|
|
89
|
+
workflows=[MyWorkflow],
|
|
90
|
+
activities=[my_activity],
|
|
91
|
+
plugins=[
|
|
92
|
+
OpenBoxPlugin(
|
|
93
|
+
openbox_url=os.getenv("OPENBOX_URL"),
|
|
94
|
+
openbox_api_key=os.getenv("OPENBOX_API_KEY"),
|
|
95
|
+
)
|
|
96
|
+
],
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
await worker.run()
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
The plugin automatically configures governance interceptors, OTel instrumentation,
|
|
103
|
+
sandbox passthrough, W3C trace propagation through Temporal headers (via
|
|
104
|
+
`temporalio.contrib.opentelemetry.TracingInterceptor`), and the
|
|
105
|
+
`send_governance_event` activity.
|
|
106
|
+
|
|
107
|
+
**Credentials never leave the plugin.** `openbox_api_key` is captured on the
|
|
108
|
+
governance activity instance itself — it does **not** flow through activity
|
|
109
|
+
inputs, so it is never written to workflow history. To opt out of trace
|
|
110
|
+
propagation (e.g., if you already wire `OpenTelemetryPlugin`), pass
|
|
111
|
+
`enable_trace_propagation=False`.
|
|
112
|
+
|
|
113
|
+
### Composing with Other Plugins
|
|
114
|
+
|
|
115
|
+
```python
|
|
116
|
+
from temporalio.contrib.opentelemetry import OpenTelemetryPlugin
|
|
117
|
+
|
|
118
|
+
worker = Worker(
|
|
119
|
+
client,
|
|
120
|
+
task_queue="my-task-queue",
|
|
121
|
+
workflows=[MyWorkflow],
|
|
122
|
+
activities=[my_activity],
|
|
123
|
+
plugins=[
|
|
124
|
+
OpenTelemetryPlugin(),
|
|
125
|
+
OpenBoxPlugin(openbox_url=..., openbox_api_key=...),
|
|
126
|
+
],
|
|
127
|
+
)
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
> **Requires** `temporalio >= 1.23.0`. For older versions, use `create_openbox_worker()` below.
|
|
131
|
+
|
|
132
|
+
---
|
|
133
|
+
|
|
134
|
+
## Quick Start (Factory)
|
|
80
135
|
|
|
81
136
|
Use the `create_openbox_worker()` factory for simple integration:
|
|
82
137
|
|
|
@@ -101,8 +156,10 @@ The factory automatically:
|
|
|
101
156
|
1. Validates the API key
|
|
102
157
|
2. Creates span processor
|
|
103
158
|
3. Sets up OpenTelemetry instrumentation
|
|
104
|
-
4. Creates governance interceptors
|
|
105
|
-
5.
|
|
159
|
+
4. Creates governance interceptors (incl. W3C trace propagation)
|
|
160
|
+
5. Builds the `GovernanceActivities` instance with credentials captured on
|
|
161
|
+
`self` and registers its `send_governance_event` method — the API key is
|
|
162
|
+
never passed through activity inputs / workflow history
|
|
106
163
|
6. Returns fully configured Worker
|
|
107
164
|
|
|
108
165
|
---
|
|
@@ -148,6 +205,11 @@ worker = create_openbox_worker(
|
|
|
148
205
|
# File I/O instrumentation
|
|
149
206
|
instrument_file_io=False, # disabled by default
|
|
150
207
|
|
|
208
|
+
# Header-based W3C trace propagation (client → workflow → activities).
|
|
209
|
+
# Default True. Set False if you already wire OpenTelemetryPlugin or a
|
|
210
|
+
# custom propagator.
|
|
211
|
+
enable_trace_propagation=True,
|
|
212
|
+
|
|
151
213
|
# Standard Worker options (all supported)
|
|
152
214
|
activity_executor=my_executor,
|
|
153
215
|
max_concurrent_activities=10,
|
|
@@ -424,7 +486,7 @@ from openbox import (
|
|
|
424
486
|
)
|
|
425
487
|
from openbox.otel_setup import setup_opentelemetry_for_governance
|
|
426
488
|
from openbox.activity_interceptor import ActivityGovernanceInterceptor
|
|
427
|
-
from openbox.activities import
|
|
489
|
+
from openbox.activities import build_governance_activities
|
|
428
490
|
from openbox.hook_governance import FAIL_OPEN, FAIL_CLOSED # error policy constants
|
|
429
491
|
|
|
430
492
|
# 1. Initialize SDK
|
|
@@ -464,14 +526,26 @@ activity_interceptor = ActivityGovernanceInterceptor(
|
|
|
464
526
|
config=config,
|
|
465
527
|
)
|
|
466
528
|
|
|
467
|
-
# 6.
|
|
529
|
+
# 6. Build the governance activity instance (credentials captured on `self`
|
|
530
|
+
# so they never flow through workflow history).
|
|
531
|
+
governance_activities = build_governance_activities(
|
|
532
|
+
api_url="http://localhost:8086",
|
|
533
|
+
api_key="obx_test_key_1",
|
|
534
|
+
)
|
|
535
|
+
|
|
536
|
+
# 7. Create worker
|
|
468
537
|
from temporalio.worker import Worker
|
|
538
|
+
from temporalio.contrib.opentelemetry import TracingInterceptor
|
|
469
539
|
worker = Worker(
|
|
470
540
|
client=client,
|
|
471
541
|
task_queue="my-task-queue",
|
|
472
542
|
workflows=[MyWorkflow],
|
|
473
|
-
activities=[my_activity, send_governance_event],
|
|
474
|
-
interceptors=[
|
|
543
|
+
activities=[my_activity, governance_activities.send_governance_event],
|
|
544
|
+
interceptors=[
|
|
545
|
+
workflow_interceptor,
|
|
546
|
+
activity_interceptor,
|
|
547
|
+
TracingInterceptor(), # W3C header propagation for OTel spans
|
|
548
|
+
],
|
|
475
549
|
)
|
|
476
550
|
```
|
|
477
551
|
|
|
@@ -489,13 +563,13 @@ worker = Worker(
|
|
|
489
563
|
|
|
490
564
|
## Testing
|
|
491
565
|
|
|
492
|
-
The SDK includes comprehensive test coverage with
|
|
566
|
+
The SDK includes comprehensive test coverage with 15 test files:
|
|
493
567
|
|
|
494
568
|
```bash
|
|
495
569
|
pytest tests/
|
|
496
570
|
```
|
|
497
571
|
|
|
498
|
-
Test files: `test_activities.py`, `test_activity_interceptor.py`, `test_config.py`, `test_db_governance_hooks.py`, `test_file_governance_hooks.py`, `test_otel_hook_pause.py`, `test_otel_hook_pause_db.py`, `test_otel_setup.py`, `test_span_processor.py`, `test_tracing.py`, `test_types.py`, `test_worker.py`, `test_workflow_interceptor.py`
|
|
572
|
+
Test files: `test_activities.py`, `test_activity_interceptor.py`, `test_config.py`, `test_db_governance_hooks.py`, `test_file_governance_hooks.py`, `test_otel_hook_pause.py`, `test_otel_hook_pause_db.py`, `test_otel_setup.py`, `test_plugin.py`, `test_plugin_integration.py`, `test_span_processor.py`, `test_tracing.py`, `test_types.py`, `test_worker.py`, `test_workflow_interceptor.py`
|
|
499
573
|
|
|
500
574
|
---
|
|
501
575
|
|
|
@@ -512,4 +586,4 @@ MIT License - See LICENSE file for details
|
|
|
512
586
|
|
|
513
587
|
---
|
|
514
588
|
|
|
515
|
-
**Version:** 1.
|
|
589
|
+
**Version:** 1.2.0 | **Last Updated:** 2026-04-05
|
|
@@ -20,13 +20,70 @@ pip install openbox-temporal-sdk-python
|
|
|
20
20
|
```
|
|
21
21
|
|
|
22
22
|
**Requirements:**
|
|
23
|
-
- Python 3.
|
|
24
|
-
- Temporal SDK 1.8+
|
|
23
|
+
- Python 3.11+
|
|
24
|
+
- Temporal SDK 1.23+ (1.8+ for factory-only usage)
|
|
25
25
|
- OpenTelemetry API/SDK 1.38.0+
|
|
26
26
|
|
|
27
27
|
---
|
|
28
28
|
|
|
29
|
-
##
|
|
29
|
+
## Plugin Integration (Recommended)
|
|
30
|
+
|
|
31
|
+
Use `OpenBoxPlugin` for drop-in integration with Temporal Workers:
|
|
32
|
+
|
|
33
|
+
```python
|
|
34
|
+
import os
|
|
35
|
+
from temporalio.worker import Worker
|
|
36
|
+
from openbox.plugin import OpenBoxPlugin
|
|
37
|
+
|
|
38
|
+
worker = Worker(
|
|
39
|
+
client,
|
|
40
|
+
task_queue="my-task-queue",
|
|
41
|
+
workflows=[MyWorkflow],
|
|
42
|
+
activities=[my_activity],
|
|
43
|
+
plugins=[
|
|
44
|
+
OpenBoxPlugin(
|
|
45
|
+
openbox_url=os.getenv("OPENBOX_URL"),
|
|
46
|
+
openbox_api_key=os.getenv("OPENBOX_API_KEY"),
|
|
47
|
+
)
|
|
48
|
+
],
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
await worker.run()
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
The plugin automatically configures governance interceptors, OTel instrumentation,
|
|
55
|
+
sandbox passthrough, W3C trace propagation through Temporal headers (via
|
|
56
|
+
`temporalio.contrib.opentelemetry.TracingInterceptor`), and the
|
|
57
|
+
`send_governance_event` activity.
|
|
58
|
+
|
|
59
|
+
**Credentials never leave the plugin.** `openbox_api_key` is captured on the
|
|
60
|
+
governance activity instance itself — it does **not** flow through activity
|
|
61
|
+
inputs, so it is never written to workflow history. To opt out of trace
|
|
62
|
+
propagation (e.g., if you already wire `OpenTelemetryPlugin`), pass
|
|
63
|
+
`enable_trace_propagation=False`.
|
|
64
|
+
|
|
65
|
+
### Composing with Other Plugins
|
|
66
|
+
|
|
67
|
+
```python
|
|
68
|
+
from temporalio.contrib.opentelemetry import OpenTelemetryPlugin
|
|
69
|
+
|
|
70
|
+
worker = Worker(
|
|
71
|
+
client,
|
|
72
|
+
task_queue="my-task-queue",
|
|
73
|
+
workflows=[MyWorkflow],
|
|
74
|
+
activities=[my_activity],
|
|
75
|
+
plugins=[
|
|
76
|
+
OpenTelemetryPlugin(),
|
|
77
|
+
OpenBoxPlugin(openbox_url=..., openbox_api_key=...),
|
|
78
|
+
],
|
|
79
|
+
)
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
> **Requires** `temporalio >= 1.23.0`. For older versions, use `create_openbox_worker()` below.
|
|
83
|
+
|
|
84
|
+
---
|
|
85
|
+
|
|
86
|
+
## Quick Start (Factory)
|
|
30
87
|
|
|
31
88
|
Use the `create_openbox_worker()` factory for simple integration:
|
|
32
89
|
|
|
@@ -51,8 +108,10 @@ The factory automatically:
|
|
|
51
108
|
1. Validates the API key
|
|
52
109
|
2. Creates span processor
|
|
53
110
|
3. Sets up OpenTelemetry instrumentation
|
|
54
|
-
4. Creates governance interceptors
|
|
55
|
-
5.
|
|
111
|
+
4. Creates governance interceptors (incl. W3C trace propagation)
|
|
112
|
+
5. Builds the `GovernanceActivities` instance with credentials captured on
|
|
113
|
+
`self` and registers its `send_governance_event` method — the API key is
|
|
114
|
+
never passed through activity inputs / workflow history
|
|
56
115
|
6. Returns fully configured Worker
|
|
57
116
|
|
|
58
117
|
---
|
|
@@ -98,6 +157,11 @@ worker = create_openbox_worker(
|
|
|
98
157
|
# File I/O instrumentation
|
|
99
158
|
instrument_file_io=False, # disabled by default
|
|
100
159
|
|
|
160
|
+
# Header-based W3C trace propagation (client → workflow → activities).
|
|
161
|
+
# Default True. Set False if you already wire OpenTelemetryPlugin or a
|
|
162
|
+
# custom propagator.
|
|
163
|
+
enable_trace_propagation=True,
|
|
164
|
+
|
|
101
165
|
# Standard Worker options (all supported)
|
|
102
166
|
activity_executor=my_executor,
|
|
103
167
|
max_concurrent_activities=10,
|
|
@@ -374,7 +438,7 @@ from openbox import (
|
|
|
374
438
|
)
|
|
375
439
|
from openbox.otel_setup import setup_opentelemetry_for_governance
|
|
376
440
|
from openbox.activity_interceptor import ActivityGovernanceInterceptor
|
|
377
|
-
from openbox.activities import
|
|
441
|
+
from openbox.activities import build_governance_activities
|
|
378
442
|
from openbox.hook_governance import FAIL_OPEN, FAIL_CLOSED # error policy constants
|
|
379
443
|
|
|
380
444
|
# 1. Initialize SDK
|
|
@@ -414,14 +478,26 @@ activity_interceptor = ActivityGovernanceInterceptor(
|
|
|
414
478
|
config=config,
|
|
415
479
|
)
|
|
416
480
|
|
|
417
|
-
# 6.
|
|
481
|
+
# 6. Build the governance activity instance (credentials captured on `self`
|
|
482
|
+
# so they never flow through workflow history).
|
|
483
|
+
governance_activities = build_governance_activities(
|
|
484
|
+
api_url="http://localhost:8086",
|
|
485
|
+
api_key="obx_test_key_1",
|
|
486
|
+
)
|
|
487
|
+
|
|
488
|
+
# 7. Create worker
|
|
418
489
|
from temporalio.worker import Worker
|
|
490
|
+
from temporalio.contrib.opentelemetry import TracingInterceptor
|
|
419
491
|
worker = Worker(
|
|
420
492
|
client=client,
|
|
421
493
|
task_queue="my-task-queue",
|
|
422
494
|
workflows=[MyWorkflow],
|
|
423
|
-
activities=[my_activity, send_governance_event],
|
|
424
|
-
interceptors=[
|
|
495
|
+
activities=[my_activity, governance_activities.send_governance_event],
|
|
496
|
+
interceptors=[
|
|
497
|
+
workflow_interceptor,
|
|
498
|
+
activity_interceptor,
|
|
499
|
+
TracingInterceptor(), # W3C header propagation for OTel spans
|
|
500
|
+
],
|
|
425
501
|
)
|
|
426
502
|
```
|
|
427
503
|
|
|
@@ -439,13 +515,13 @@ worker = Worker(
|
|
|
439
515
|
|
|
440
516
|
## Testing
|
|
441
517
|
|
|
442
|
-
The SDK includes comprehensive test coverage with
|
|
518
|
+
The SDK includes comprehensive test coverage with 15 test files:
|
|
443
519
|
|
|
444
520
|
```bash
|
|
445
521
|
pytest tests/
|
|
446
522
|
```
|
|
447
523
|
|
|
448
|
-
Test files: `test_activities.py`, `test_activity_interceptor.py`, `test_config.py`, `test_db_governance_hooks.py`, `test_file_governance_hooks.py`, `test_otel_hook_pause.py`, `test_otel_hook_pause_db.py`, `test_otel_setup.py`, `test_span_processor.py`, `test_tracing.py`, `test_types.py`, `test_worker.py`, `test_workflow_interceptor.py`
|
|
524
|
+
Test files: `test_activities.py`, `test_activity_interceptor.py`, `test_config.py`, `test_db_governance_hooks.py`, `test_file_governance_hooks.py`, `test_otel_hook_pause.py`, `test_otel_hook_pause_db.py`, `test_otel_setup.py`, `test_plugin.py`, `test_plugin_integration.py`, `test_span_processor.py`, `test_tracing.py`, `test_types.py`, `test_worker.py`, `test_workflow_interceptor.py`
|
|
449
525
|
|
|
450
526
|
---
|
|
451
527
|
|
|
@@ -462,4 +538,4 @@ MIT License - See LICENSE file for details
|
|
|
462
538
|
|
|
463
539
|
---
|
|
464
540
|
|
|
465
|
-
**Version:** 1.
|
|
541
|
+
**Version:** 1.2.0 | **Last Updated:** 2026-04-05
|
{openbox_temporal_sdk_python-1.1.0 → openbox_temporal_sdk_python-1.1.2}/docs/code-standards.md
RENAMED
|
@@ -83,10 +83,13 @@ This document defines coding standards, architectural patterns, and best practic
|
|
|
83
83
|
async def execute_workflow(self, input: ExecuteWorkflowInput) -> Any:
|
|
84
84
|
info = workflow.info()
|
|
85
85
|
|
|
86
|
-
# Send event via activity (deterministic)
|
|
86
|
+
# Send event via activity (deterministic). NEVER pass credentials
|
|
87
|
+
# (api_key) in activity inputs — workflow history is readable by anyone
|
|
88
|
+
# with Describe permissions on the namespace. The activity holds the
|
|
89
|
+
# credentials on its instance (see rule 3).
|
|
87
90
|
await workflow.execute_activity(
|
|
88
91
|
"send_governance_event",
|
|
89
|
-
args=[{"
|
|
92
|
+
args=[{"payload": {...}, "timeout": 30.0, "on_api_error": "fail_open"}],
|
|
90
93
|
start_to_close_timeout=timedelta(seconds=30),
|
|
91
94
|
)
|
|
92
95
|
|
|
@@ -122,25 +125,36 @@ async def execute_workflow(self, input: ExecuteWorkflowInput) -> Any:
|
|
|
122
125
|
#### Solution: Lazy Imports
|
|
123
126
|
|
|
124
127
|
```python
|
|
125
|
-
# ✅ CORRECT -
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
128
|
+
# ✅ CORRECT - Class-based activity with credentials captured on self
|
|
129
|
+
class GovernanceActivities:
|
|
130
|
+
def __init__(self, api_url: str, api_key: str):
|
|
131
|
+
self._api_url = api_url
|
|
132
|
+
self._api_key = api_key
|
|
133
|
+
|
|
134
|
+
@activity.defn(name="send_governance_event")
|
|
135
|
+
async def send_governance_event(self, input: Dict[str, Any]):
|
|
136
|
+
import httpx # ✅ Loaded only when activity executes
|
|
137
|
+
async with httpx.AsyncClient() as client:
|
|
138
|
+
response = await client.post(
|
|
139
|
+
f"{self._api_url}/api/v1/governance/evaluate",
|
|
140
|
+
headers={"Authorization": f"Bearer {self._api_key}"},
|
|
141
|
+
json=input["payload"],
|
|
142
|
+
)
|
|
143
|
+
return response.json()
|
|
134
144
|
```
|
|
135
145
|
|
|
136
146
|
```python
|
|
137
|
-
# ❌ WRONG -
|
|
138
|
-
import httpx # ❌ Loaded when module imports, triggers sandbox
|
|
139
|
-
|
|
147
|
+
# ❌ WRONG - Credentials in activity input (leaks to workflow history)
|
|
140
148
|
@activity.defn(name="send_governance_event")
|
|
141
149
|
async def send_governance_event(input: Dict[str, Any]):
|
|
150
|
+
# ❌ input["api_key"] is persisted in workflow history — readable by
|
|
151
|
+
# anyone with Describe access to the namespace.
|
|
142
152
|
async with httpx.AsyncClient() as client:
|
|
143
|
-
response = await client.post(
|
|
153
|
+
response = await client.post(
|
|
154
|
+
input["api_url"],
|
|
155
|
+
headers={"Authorization": f"Bearer {input['api_key']}"},
|
|
156
|
+
json=input["payload"],
|
|
157
|
+
)
|
|
144
158
|
return response.json()
|
|
145
159
|
```
|
|
146
160
|
|
{openbox_temporal_sdk_python-1.1.0 → openbox_temporal_sdk_python-1.1.2}/docs/codebase-summary.md
RENAMED
|
@@ -22,6 +22,7 @@ openbox-temporal-sdk-python/
|
|
|
22
22
|
│ ├── types.py # Type definitions (workflow-safe)
|
|
23
23
|
│ ├── config.py # Configuration and initialization
|
|
24
24
|
│ ├── worker.py # Worker factory function
|
|
25
|
+
│ ├── plugin.py # OpenBoxPlugin(SimplePlugin) for Temporal AI Partner Ecosystem
|
|
25
26
|
│ ├── errors.py # Unified exception hierarchy
|
|
26
27
|
│ ├── client.py # GovernanceClient (centralized HTTP client)
|
|
27
28
|
│ ├── hitl.py # HITL approval handling
|
|
@@ -327,30 +328,48 @@ def create_openbox_worker(
|
|
|
327
328
|
### 11. Governance Event Activity (`activities.py`)
|
|
328
329
|
|
|
329
330
|
**Purpose:** Execute HTTP calls to OpenBox Core from workflow context
|
|
330
|
-
**Activity:** `send_governance_event`
|
|
331
|
+
**Activity:** `send_governance_event` (method of `GovernanceActivities` class)
|
|
331
332
|
|
|
332
|
-
**
|
|
333
|
+
**Class Signature:**
|
|
333
334
|
```python
|
|
334
|
-
|
|
335
|
-
|
|
335
|
+
class GovernanceActivities:
|
|
336
|
+
def __init__(self, api_url: str, api_key: str): ...
|
|
337
|
+
|
|
338
|
+
@activity.defn(name="send_governance_event")
|
|
339
|
+
async def send_governance_event(
|
|
340
|
+
self, input: Dict[str, Any]
|
|
341
|
+
) -> Optional[Dict[str, Any]]: ...
|
|
342
|
+
|
|
343
|
+
|
|
344
|
+
def build_governance_activities(api_url: str, api_key: str) -> GovernanceActivities
|
|
336
345
|
```
|
|
337
346
|
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
347
|
+
The plugin / `create_openbox_worker()` factory build one instance and register
|
|
348
|
+
its bound `send_governance_event` method with the Temporal Worker.
|
|
349
|
+
Credentials live on `self` — they do **not** flow through activity inputs,
|
|
350
|
+
so the API key is never written to workflow history.
|
|
351
|
+
|
|
352
|
+
**Input Fields (activity input dict):**
|
|
341
353
|
- `payload` - Event data (without timestamp)
|
|
342
354
|
- `timeout` - Request timeout
|
|
343
355
|
- `on_api_error` - "fail_open" or "fail_closed"
|
|
344
356
|
|
|
357
|
+
> `api_url` / `api_key` are deliberately absent from the input dict to avoid
|
|
358
|
+
> leaking credentials into workflow history. They are held on the activity
|
|
359
|
+
> instance itself.
|
|
360
|
+
|
|
345
361
|
**Behavior:**
|
|
346
362
|
- Adds RFC3339 timestamp to payload
|
|
347
|
-
- POSTs to
|
|
363
|
+
- POSTs to `{self._api_url}/api/v1/governance/evaluate`
|
|
348
364
|
- Parses verdict from response
|
|
349
365
|
- For SignalReceived: Returns verdict dict (workflow interceptor stores it)
|
|
350
366
|
- For other events with BLOCK/HALT: Raises non-retryable `ApplicationError`
|
|
367
|
+
with `type="GovernanceBlock"` / `"GovernanceHalt"`
|
|
351
368
|
- On API failure with fail_closed: Raises `GovernanceAPIError`
|
|
352
369
|
|
|
353
|
-
|
|
370
|
+
A module-level `send_governance_event(input)` helper remains for direct
|
|
371
|
+
callers (tests, scripts) that already hold credentials; it instantiates
|
|
372
|
+
`GovernanceActivities` internally and delegates.
|
|
354
373
|
|
|
355
374
|
---
|
|
356
375
|
|
{openbox_temporal_sdk_python-1.1.0 → openbox_temporal_sdk_python-1.1.2}/docs/system-architecture.md
RENAMED
|
@@ -89,6 +89,39 @@ OpenBox SDK for Temporal Workflows is a governance and observability layer that
|
|
|
89
89
|
|
|
90
90
|
---
|
|
91
91
|
|
|
92
|
+
## Plugin Integration
|
|
93
|
+
|
|
94
|
+
### OpenBoxPlugin (SimplePlugin)
|
|
95
|
+
|
|
96
|
+
**File:** `openbox/plugin.py`
|
|
97
|
+
|
|
98
|
+
`OpenBoxPlugin` extends Temporal's `SimplePlugin` base class, providing a drop-in integration for the AI Partner Ecosystem. It composes all existing SDK components (interceptors, OTel setup, governance activities) without modifying them.
|
|
99
|
+
|
|
100
|
+
```
|
|
101
|
+
Worker(client, task_queue, workflows, activities,
|
|
102
|
+
plugins=[OpenBoxPlugin(openbox_url=..., openbox_api_key=...)])
|
|
103
|
+
|
|
104
|
+
OpenBoxPlugin.__init__():
|
|
105
|
+
→ validate_api_key() # config.py
|
|
106
|
+
→ WorkflowSpanProcessor() # span_processor.py
|
|
107
|
+
→ setup_opentelemetry_for_governance() # otel_setup.py
|
|
108
|
+
→ GovernanceInterceptor() # workflow_interceptor.py
|
|
109
|
+
→ ActivityGovernanceInterceptor() # activity_interceptor.py
|
|
110
|
+
→ SimplePlugin.__init__(interceptors, activities, workflow_runner)
|
|
111
|
+
|
|
112
|
+
OpenBoxPlugin.configure_worker(config):
|
|
113
|
+
→ set_temporal_client(config["client"]) # activities.py
|
|
114
|
+
→ super().configure_worker(config) # appends interceptors, activities
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
**Key design choices:**
|
|
118
|
+
- Composition-only — no changes to existing modules
|
|
119
|
+
- Sandbox passthrough for `opentelemetry` via `workflow_runner` callback
|
|
120
|
+
- `configure_worker()` captures Temporal client ref for HALT terminate calls
|
|
121
|
+
- Plugin name: `"openbox.OpenBoxPlugin"` per Temporal's naming standard
|
|
122
|
+
|
|
123
|
+
---
|
|
124
|
+
|
|
92
125
|
## Component Architecture
|
|
93
126
|
|
|
94
127
|
### 1. Interceptor Layer
|