Python: Add Durabletask samples and minor fixes (#3157)

* Add samples and minor fixes

* Add redis sample and wait-for-completion

* Add wait-for-completion support

* ADd missing docs
This commit is contained in:
Laveesh Rohra
2026-01-14 10:56:11 -08:00
committed by GitHub
parent 1e36ba33c4
commit 3df916064c
48 changed files with 4221 additions and 1153 deletions
@@ -17,6 +17,7 @@ from agent_framework_durabletask import (
load_agent_response,
)
from azure.durable_functions.models import TaskBase
from azure.durable_functions.models.actions.NoOpAction import NoOpAction
from azure.durable_functions.models.Task import CompoundTask, TaskState
from pydantic import BaseModel
@@ -42,6 +43,25 @@ else:
_TypedCompoundTask = CompoundTask
class PreCompletedTask(TaskBase):
"""A simple task that is already completed with a result.
Used for fire-and-forget mode where we want to return immediately
with an acceptance response without waiting for entity processing.
"""
def __init__(self, result: Any):
"""Initialize with a completed result.
Args:
result: The result value for this completed task
"""
# Initialize with a NoOp action since we don't need actual orchestration actions
super().__init__(-1, NoOpAction())
# Immediately mark as completed with the result
self.set_value(is_error=False, value=result)
class AgentTask(_TypedCompoundTask):
"""A custom Task that wraps entity calls and provides typed AgentRunResponse results.
@@ -62,10 +82,13 @@ class AgentTask(_TypedCompoundTask):
response_format: Optional Pydantic model for response parsing
correlation_id: Correlation ID for logging
"""
super().__init__([entity_task])
# Set instance variables BEFORE calling super().__init__
# because super().__init__ may trigger try_set_value for pre-completed tasks
self._response_format = response_format
self._correlation_id = correlation_id
super().__init__([entity_task])
# Override action_repr to expose the inner task's action directly
# This ensures compatibility with ReplaySchema V3 which expects Action objects.
self.action_repr = entity_task.action_repr
@@ -130,16 +153,27 @@ class AzureFunctionsAgentExecutor(DurableAgentExecutor[AgentTask]):
message: str,
response_format: type[BaseModel] | None,
enable_tool_calls: bool,
wait_for_response: bool = True,
) -> RunRequest:
"""Get the current run request from the orchestration context.
Args:
message: The message to send to the agent
response_format: Optional Pydantic model for response parsing
enable_tool_calls: Whether to enable tool calls
wait_for_response: Must be True for orchestration contexts
Returns:
RunRequest: The current run request
Raises:
ValueError: If wait_for_response=False (not supported in orchestrations)
"""
request = super().get_run_request(
message,
response_format,
enable_tool_calls,
wait_for_response,
)
request.orchestration_id = self.context.instance_id
return request
@@ -166,7 +200,24 @@ class AzureFunctionsAgentExecutor(DurableAgentExecutor[AgentTask]):
session_id,
)
entity_task = self.context.call_entity(entity_id, "run", run_request.to_dict())
# Branch based on wait_for_response
if not run_request.wait_for_response:
# Fire-and-forget mode: signal entity and return pre-completed task
logger.debug(
"[AzureFunctionsAgentExecutor] Fire-and-forget mode: signaling entity (correlation: %s)",
run_request.correlation_id,
)
self.context.signal_entity(entity_id, "run", run_request.to_dict())
# Create acceptance response using base class helper
acceptance_response = self._create_acceptance_response(run_request.correlation_id)
# Create a pre-completed task with the acceptance response
entity_task = PreCompletedTask(acceptance_response)
else:
# Blocking mode: call entity and wait for response
entity_task = self.context.call_entity(entity_id, "run", run_request.to_dict())
return AgentTask(
entity_task=entity_task,
response_format=run_request.response_format,