temporal-algenta
Docs: docs.algenta.ai · All integrations
Temporal.io integration for Algenta:
AlgentaActivities, your own self-hosted Algenta engine's MCP tool surface as durable
Temporal activities, plus the governance story durable execution actually needs:
- Typed receipts across the wire -- a successful
execute_decisionactivity returns anExecutionReceiptData(the engine's real receipt envelope as a sandbox-safe dataclass), so "did the governed action land?" is answered by the workflow history itself. - Structured denials as typed, non-retryable failures -- a blocked
execute_decisionraises atemporalio.exceptions.ApplicationErrorwhosetypeis the engine's own denial code (execution_blocked_confidence,execution_blocked_risk_floor,execution_blocked_idempotency),non_retryable=True(a policy denial is deterministic; retrying it can only deny again), and the full{"code", "gate", "message", "override_hint"}body in its details -- recoverable in workflow code viadenial_from_activity_error. - Idempotency via receipts -- Temporal retries activities; the engine's own idempotency
gate dedups deliveries by
decision_id. A retried delivery surfaces as a typedexecution_blocked_idempotencyfailure, which is positive proof the side effect already happened -- exactly-once effect, auditable in history (seerecipes/idempotent_activity_receipt_dedup.py). - Human approval via signals -- the engine intentionally exposes no approval round trip
over MCP (its real plan_hash+nonce approval system is not an MCP tool), so this package
builds approvals from Temporal's own primitives: a workflow parks on a typed denial, exposes
it via
@workflow.query, and only an operator's@workflow.signalcan setoverride_safety=Truefor the retry -- the exact operator/break-glass path the shared contract reserves that field for (seerecipes/human_approval_workflow.py). - Tool-profile enforcement --
observe(read-only, the default),govern,execute, or the opt-infullregistry, percontracts/integration-tool-contract.json, enforced at call time inside every activity.
Install
pip install temporal-algenta
Real runtime dependencies: temporalio (the Temporal Python SDK), mcp (the base Model
Context Protocol SDK -- the same SDK the test suite's stub engine is built on), and pydantic
(the receipt/denial models at the activity edge). Everything is validated against
temporalio 1.33.x.
Why no algenta-sdk dependency
This package never imports it: AlgentaActivities talks to your own self-hosted engine over
its MCP endpoint, like the haystack-algenta / maf-algenta / llamaindex-algenta packages
in this repository. The only Algenta-owned package any package here may depend on is
algenta-sdk, and a package that doesn't import it doesn't declare it.
Self-hosted-first
AlgentaMcpClient talks to your own self-hosted Algenta engine over its MCP endpoint --
never a hosted-by-Algenta cloud service. The endpoint resolves, in order, from:
AlgentaActivities(base_url=...)- the
ALGENTA_BASE_URLenvironment variable http://localhost:8000/mcp(your engine's default self-hosted bind)
Quick start
from datetime import timedelta
from temporalio import workflow
from temporalio.worker import Worker
with workflow.unsafe.imports_passed_through():
from temporal_algenta import (
AlgentaActivities,
ExecuteDecisionInput,
GovernedDecisionInput,
LogDecisionInput,
)
@workflow.defn
class GovernedDecisionWorkflow:
@workflow.run
async def run(self, input: GovernedDecisionInput):
logged = await workflow.execute_activity(
AlgentaActivities.log_decision,
LogDecisionInput(chosen_action=input.action, rationale=input.rationale),
start_to_close_timeout=timedelta(seconds=30),
)
# Returns a typed ExecutionReceiptData -- or raises a non-retryable
# ApplicationError(type="execution_blocked_<gate>") carrying the structured denial.
return await workflow.execute_activity(
AlgentaActivities.execute_decision,
ExecuteDecisionInput(decision_id=logged["decision_id"], webhook_url=input.webhook_url),
start_to_close_timeout=timedelta(seconds=60),
)
# One AlgentaActivities instance per worker; the profile gates every call it makes.
algenta = AlgentaActivities(profile="execute") # ALGENTA_BASE_URL selects your engine
worker = Worker(client, task_queue="governed-decisions", workflows=[GovernedDecisionWorkflow],
activities=algenta.all_activities())
Two Temporal-specific rules this package is built around:
- Workflow code never imports
pydantic. Temporal's workflow sandbox cannot reload pydantic's Rust core, so the pydantic receipt/denial models live at the activity edge only (temporal_algenta.receipts); everything that crosses the workflow/activity boundary is a stdlib dataclass fromtemporal_algenta.types. Import activity modules insideworkflow.unsafe.imports_passed_through()(as above) -- the pattern Temporal documents for workflow modules referencing activity definitions. - Denials are failures, not results. There is no "pending approval" receipt state (there
isn't one on the real engine either -- see
temporal_algenta.receipts). A workflow that wants to react to a policy gate catchesActivityErrorand reads the denial off it:
from temporalio.exceptions import ActivityError
with workflow.unsafe.imports_passed_through():
from temporal_algenta import denial_from_activity_error
try:
receipt = await workflow.execute_activity(...)
except ActivityError as err:
denial = denial_from_activity_error(err)
if denial is None:
raise # not a policy denial -- a real failure; let the workflow fail
# denial.gate / denial.code / denial.message / denial.override_hint, all typed
Recipes
Ten runnable recipes putting Algenta at the center of Temporal's most popular patterns. Each
is a real workflow module under recipes/ with its own main(); each runs
end-to-end with zero credentials (it starts a deterministic demo engine and a local
time-skipping Temporal test server for you), and each has a test in tests/. From a checkout:
cd python
uv sync --all-packages --all-extras
uv run python -m recipes.durable_decision_workflow # run from python/temporal-algenta
With ALGENTA_BASE_URL (and optionally TEMPORAL_ADDRESS / TEMPORAL_NAMESPACE) set, the
same recipe runs against your self-hosted engine and your own Temporal server instead.
| Recipe | What it shows | Run |
|---|---|---|
durable_decision_workflow |
The canonical durable workflow: log_decision -> execute_decision, each step a retried activity; the workflow result is the typed receipt. |
uv run python -m recipes.durable_decision_workflow |
human_approval_workflow |
Policy denial parks the workflow; an operator signal (Temporal's #1 human-in-the-loop primitive) approves with an explicit break-glass override; receipt shows safety_overridden=True. |
uv run python -m recipes.human_approval_workflow |
idempotent_activity_receipt_dedup |
The "safe to retry" question, answered: the engine's idempotency gate turns a re-delivered attempt into a deduplicated success, never a double delivery. | uv run python -m recipes.idempotent_activity_receipt_dedup |
scheduled_governed_simulation |
A real Temporal Schedule (cron) firing a nightly governed simulation into decision memory. |
uv run python -m recipes.scheduled_governed_simulation |
saga_with_policy_gates |
Temporal's flagship saga pattern with a policy denial as the trigger: reverse-order compensations recorded in decision memory. | uv run python -m recipes.saga_with_policy_gates |
retry_policy_structured_denials |
RetryPolicy meets governance: transient engine errors ride out the backoff; policy denials fail on attempt one, denial intact to the client. |
uv run python -m recipes.retry_policy_structured_denials |
durable_decision_memory |
The long-running entity workflow: decisions journaled via signals, read via queries, persisted to Algenta decision memory. | uv run python -m recipes.durable_decision_memory |
batch_simulation_pipeline |
Parallel fan-out (asyncio.gather over activities) with a fully deterministic aggregation. |
uv run python -m recipes.batch_simulation_pipeline |
agent_workflow_governed_tools |
The durable AI agent loop with a profile-filtered tool surface: under govern the agent may plan but execute_decision isn't advertised -- refused before any call. |
uv run python -m recipes.agent_workflow_governed_tools |
audit_trail_query_workflow |
A live-queryable, sequence-numbered audit trail reconciled with the engine's own audit dataset via query_data. |
uv run python -m recipes.audit_trail_query_workflow |
The denial taxonomy (what your workflow can catch)
Every governed call resolves to exactly one of these -- synchronously, in the same activity attempt (there is no asynchronous "pending" outcome to wait on):
| Outcome | Temporal shape | Retryable? |
|---|---|---|
| Delivered | ExecutionReceiptData (execution_status="delivered") |
-- |
| Webhook target rejected delivery | ExecutionReceiptData (execution_status="failed") -- the call succeeded |
-- (a result, not a failure; react in workflow code) |
Policy denial (idempotency / confidence / risk_floor) |
ApplicationError, type=execution_blocked_<gate>, denial body in details |
No (non_retryable=True) |
| Tool outside the configured profile | ApplicationError, type="algenta_tool_denied_outside_profile" |
No (non_retryable=True) |
| Unclassified tool-execution error | ApplicationError, type="algenta_tool_error" |
Yes -- bound it with your RetryPolicy |
| MCP session failure (wedged session, read timeout) | ApplicationError, type="algenta_session_error" |
Yes -- a fresh session almost always clears it |
| Transport failure (engine unreachable, etc.) | the original exception, unwrapped | Yes (Temporal default) |
Testing this package's own test suite
The suite runs a real mcp.server.fastmcp.FastMCP server
(the same deterministic demo engine the recipes use -- recipes/demo_engine.py, one
implementation, no drift) over a real local HTTP socket, drives activities through Temporal's
real ActivityEnvironment, and runs every workflow inside the SDK's real time-skipping
WorkflowEnvironment (a local test server the SDK downloads and starts itself -- no Temporal
install, no credentials). Durable timers and schedules are fast-forwarded, so the suite tests
hour-long approval deadlines in milliseconds.
cd python
uv sync --all-packages --all-extras
uv run --package temporal-algenta pytest temporal-algenta/tests -v
License
Apache-2.0. Copyright Algenta and contributors. See LICENSE.
Metadata
Release files for temporal-algenta 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| temporal_algenta-0.1.0.tar.gz | 48.9 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| temporal_algenta-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 83.2 kB
Release files / temporal_algenta-0.1.0.tar.gz
| Download URL | temporal_algenta-0.1.0.tar.gz |
|---|---|
| Size | 48.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
0fc247eddce348085f52c1a95ec72bbb1006d8613fafcd507fbd4e9094ea8d3d
|
|
BLAKE2b-256 checksum How to use checksums |
c2f7912c37e4c06c76776f8789fcd9a8dd1fa1dec9d02c29159f14f0b664ae8a
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.14
|
Release files / temporal_algenta-0.1.0-py3-none-any.whl
| Download URL | temporal_algenta-0.1.0-py3-none-any.whl |
|---|---|
| Size | 34.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
c8d6689d5071ce0e02f6140cdb20c1c4ade1da4df22982c7bcb70e0f427f4507
|
|
BLAKE2b-256 checksum How to use checksums |
fb1a0b6dcd1578ab5911b09f155d3033884256eabd07490e7fe6d0eb8ad05044
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.14
|