Python: Add integration tests for durabletask package (#3317)

* Add integration tests

* Fix flaky test

* Fix env viz

* Fix tests and address feedback
This commit is contained in:
Laveesh Rohra
2026-01-21 14:31:24 -08:00
committed by GitHub
Unverified
parent cd77193742
commit e032133748
19 changed files with 1482 additions and 25 deletions
@@ -18,7 +18,7 @@ import os
from datetime import timedelta
import redis.asyncio as aioredis
from agent_framework import AgentRunResponseUpdate
from agent_framework import AgentResponseUpdate
from agent_framework.azure import AzureOpenAIChatClient
from agent_framework_durabletask import AgentCallbackContext, AgentResponseCallbackProtocol, DurableAIAgentWorker
from azure.identity import AzureCliCredential, DefaultAzureCredential
@@ -66,7 +66,7 @@ class RedisStreamCallback(AgentResponseCallbackProtocol):
async def on_streaming_response_update(
self,
update: AgentRunResponseUpdate,
update: AgentResponseUpdate,
context: AgentCallbackContext,
) -> None:
"""Write streaming update to Redis Stream.
@@ -15,7 +15,7 @@ from collections.abc import Generator
import logging
import os
from agent_framework import AgentRunResponse
from agent_framework import AgentResponse
from agent_framework.azure import AzureOpenAIChatClient
from agent_framework_durabletask import DurableAIAgentOrchestrationContext, DurableAIAgentWorker
from azure.identity import AzureCliCredential, DefaultAzureCredential
@@ -61,7 +61,7 @@ def get_orchestration():
def single_agent_chaining_orchestration(
context: OrchestrationContext, _: str
) -> Generator[Task[AgentRunResponse], AgentRunResponse, str]:
) -> Generator[Task[AgentResponse], AgentResponse, str]:
"""Orchestration that runs the writer agent twice on the same thread.
This demonstrates chaining behavior where the output of the first agent run
@@ -72,7 +72,7 @@ def create_writer_agent():
)
def notify_user_for_approval(context: ActivityContext, content: dict[str, str]) -> None:
def notify_user_for_approval(context: ActivityContext, content: dict[str, str]) -> str:
"""Activity function to notify user for approval.
Args:
@@ -84,8 +84,9 @@ def notify_user_for_approval(context: ActivityContext, content: dict[str, str])
logger.info(f"Title: {model.title or '(untitled)'}")
logger.info(f"Content: {model.content}")
logger.info("Use the client to send approval or rejection.")
return "Notification sent to user for approval."
def publish_content(context: ActivityContext, content: dict[str, str]) -> None:
def publish_content(context: ActivityContext, content: dict[str, str]) -> str:
"""Activity function to publish approved content.
Args:
@@ -96,6 +97,7 @@ def publish_content(context: ActivityContext, content: dict[str, str]) -> None:
logger.info("PUBLISHING: Content has been published successfully:")
logger.info(f"Title: {model.title or '(untitled)'}")
logger.info(f"Content: {model.content}")
return "Published content successfully."
def content_generation_hitl_orchestration(
@@ -230,6 +232,14 @@ def content_generation_hitl_orchestration(
# Content rejected - incorporate feedback and regenerate
logger.debug(f"[Orchestration] Content rejected. Feedback: {approval.feedback}")
# Check if we've exhausted attempts
if attempt >= payload.max_review_attempts:
context.set_custom_status("Max review attempts exhausted.")
# Max attempts exhausted
logger.error(f"[Orchestration] Max attempts ({payload.max_review_attempts}) exhausted")
break
context.set_custom_status(f"Content rejected by human reviewer. Regenerating...")
rewrite_prompt = (
@@ -262,7 +272,8 @@ def content_generation_hitl_orchestration(
f"Human approval timed out after {payload.approval_timeout_seconds} second(s)."
)
# Max attempts exhausted
# If we exit the loop without returning, max attempts were exhausted
context.set_custom_status("Max review attempts exhausted.")
raise RuntimeError(
f"Content could not be approved after {payload.max_review_attempts} iteration(s)."
)