chore: import upstream snapshot with attribution
CodeQL / Analyze (csharp) (push) Waiting to run
CodeQL / Analyze (python) (push) Waiting to run
dotnet-build-and-test / dotnet-test-functions (push) Has been cancelled
dotnet-build-and-test / paths-filter (push) Has been cancelled
dotnet-build-and-test / dotnet-build (Debug, windows-latest, net9.0) (push) Has been cancelled
dotnet-build-and-test / dotnet-build (Release, ubuntu-latest, net10.0) (push) Has been cancelled
dotnet-build-and-test / dotnet-build (Release, ubuntu-latest, net8.0) (push) Has been cancelled
dotnet-build-and-test / dotnet-build (Release, windows-latest, net472) (push) Has been cancelled
dotnet-build-and-test / dotnet-test (Release, integration, true, ubuntu-latest, net10.0) (push) Has been cancelled
dotnet-build-and-test / dotnet-test (Release, integration, true, windows-latest, net472) (push) Has been cancelled
dotnet-build-and-test / dotnet-foundry-hosted-it (push) Has been cancelled
dotnet-build-and-test / dotnet-build-and-test-check (push) Has been cancelled
dotnet-build-and-test / Integration Test Report (push) Has been cancelled

This commit is contained in:
wehub-resource-sync
2026-07-13 13:39:25 +08:00
commit db620d33df
5151 changed files with 925932 additions and 0 deletions
@@ -0,0 +1,73 @@
# Single Agent
This sample demonstrates how to create a worker-client setup that hosts a single AI agent and provides interactive conversation via the Durable Task Scheduler.
## Key Concepts Demonstrated
- Using the Microsoft Agent Framework to define a simple AI agent with a name and instructions.
- Registering durable agents with the worker and interacting with them via a client.
- Conversation management (via sessions) for isolated interactions.
- Worker-client architecture for distributed agent execution.
## Environment Setup
See the [README.md](../README.md) file in the parent directory for more information on how to configure the environment, including how to install and run common sample dependencies.
## Running the Sample
With the environment setup, you can run the sample using the combined approach or separate worker and client processes:
**Option 1: Combined (Recommended for Testing)**
```bash
cd samples/04-hosting/durabletask/01_single_agent
python sample.py
```
**Option 2: Separate Processes**
Start the worker in one terminal:
```bash
python worker.py
```
In a new terminal, run the client:
```bash
python client.py
```
The client will interact with the Joker agent:
```
Starting Durable Task Agent Client...
Using taskhub: default
Using endpoint: http://localhost:8080
Getting reference to Joker agent...
Created conversation session: a1b2c3d4-e5f6-7890-abcd-ef1234567890
User: Tell me a short joke about cloud computing.
Joker: Why did the cloud break up with the server?
Because it found someone more "uplifting"!
User: Now tell me one about Python programming.
Joker: Why do Python programmers prefer dark mode?
Because light attracts bugs!
```
## Viewing Agent State
You can view the state of the agent in the Durable Task Scheduler dashboard:
1. Open your browser and navigate to `http://localhost:8082`
2. In the dashboard, you can view:
- The state of the Joker agent entity (dafx-Joker)
- Conversation history and current state
- How the durable agents extension manages conversation context
@@ -0,0 +1,123 @@
# Copyright (c) Microsoft. All rights reserved.
"""Client application for interacting with a Durable Task hosted agent.
This client connects to the Durable Task Scheduler and sends requests to
registered agents, demonstrating how to interact with agents from external processes.
Prerequisites:
- The worker must be running with the agent registered
- Set FOUNDRY_PROJECT_ENDPOINT and FOUNDRY_MODEL
- Sign in with Azure CLI for AzureCliCredential authentication
- Durable Task Scheduler must be running
"""
import asyncio
import logging
import os
from agent_framework.azure import DurableAIAgentClient
from azure.identity import AzureCliCredential
from dotenv import load_dotenv
from durabletask.azuremanaged.client import DurableTaskSchedulerClient
# Load environment variables from .env file
load_dotenv()
# Configure logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
def get_client(
taskhub: str | None = None, endpoint: str | None = None, log_handler: logging.Handler | None = None
) -> DurableAIAgentClient:
"""Create a configured DurableAIAgentClient.
Args:
taskhub: Task hub name (defaults to TASKHUB env var or "default")
endpoint: Scheduler endpoint (defaults to ENDPOINT env var or "http://localhost:8080")
log_handler: Optional logging handler for client logging
Returns:
Configured DurableAIAgentClient instance
"""
taskhub_name = taskhub or os.getenv("TASKHUB", "default")
endpoint_url = endpoint or os.getenv("ENDPOINT", "http://localhost:8080")
logger.debug(f"Using taskhub: {taskhub_name}")
logger.debug(f"Using endpoint: {endpoint_url}")
credential = None if endpoint_url == "http://localhost:8080" else AzureCliCredential()
dts_client = DurableTaskSchedulerClient(
host_address=endpoint_url,
secure_channel=endpoint_url != "http://localhost:8080",
taskhub=taskhub_name,
token_credential=credential,
log_handler=log_handler,
)
return DurableAIAgentClient(dts_client)
def run_client(agent_client: DurableAIAgentClient) -> None:
"""Run client interactions with the Joker agent.
Args:
agent_client: The DurableAIAgentClient instance
"""
# Get a reference to the Joker agent
logger.debug("Getting reference to Joker agent...")
joker = agent_client.get_agent("Joker")
# Create a new session for the conversation
session = joker.create_session()
logger.debug(f"Session ID: {session.session_id}")
logger.info("Start chatting with the Joker agent! (Type 'exit' to quit)")
# Interactive conversation loop
while True:
# Get user input
try:
user_message = input("You: ").strip()
except (EOFError, KeyboardInterrupt):
logger.info("\nExiting...")
break
# Check for exit command
if user_message.lower() == "exit":
logger.info("Goodbye!")
break
# Skip empty messages
if not user_message:
continue
# Send message to agent and get response
try:
response = joker.run(user_message, session=session)
logger.info(f"Joker: {response.text} \n")
except Exception as e:
logger.error(f"Error getting response: {e}")
logger.info("Conversation completed.")
async def main() -> None:
"""Main entry point for the client application."""
logger.debug("Starting Durable Task Agent Client...")
# Create client using helper function
agent_client = get_client()
try:
run_client(agent_client)
except Exception as e:
logger.exception(f"Error during agent interaction: {e}")
finally:
logger.debug("Client shutting down")
if __name__ == "__main__":
asyncio.run(main())
@@ -0,0 +1,14 @@
# Agent Framework packages
# To use the deployed version, uncomment the lines below and comment out the local installation lines
# agent-framework-foundry
# agent-framework-durabletask
# Local installation (for development and testing)
# Each package must be listed explicitly because pip doesn't resolve uv workspace sources.
# Without explicit entries, pip would fetch transitive dependencies from PyPI instead of local source.
-e ../../../../packages/core # Core framework - base dependency for all packages
-e ../../../../packages/foundry # Foundry support - dependency for hosted chat/agent samples
-e ../../../../packages/durabletask # Durable Task support - the main package for this sample
# Azure authentication
azure-identity
@@ -0,0 +1,51 @@
# Copyright (c) Microsoft. All rights reserved.
"""Single Agent Sample - Durable Task Integration (Combined Worker + Client)
This sample demonstrates running both the worker and client in a single process.
The worker is started first to register the agent, then client operations are
performed against the running worker.
Prerequisites:
- Set FOUNDRY_PROJECT_ENDPOINT and FOUNDRY_MODEL
- Sign in with Azure CLI for AzureCliCredential authentication
- Durable Task Scheduler must be running (e.g., using Docker)
To run this sample:
python sample.py
"""
import logging
# Import helper functions from worker and client modules
from client import get_client, run_client # pyrefly: ignore[missing-import]
from dotenv import load_dotenv
from worker import get_worker, setup_worker # pyrefly: ignore[missing-import]
# Configure logging (must be after imports to override their basicConfig)
logging.basicConfig(level=logging.INFO, force=True)
logger = logging.getLogger(__name__)
def main():
"""Main entry point - runs both worker and client in single process."""
logger.debug("Starting Durable Task Agent Sample (Combined Worker + Client)...")
silent_handler = logging.NullHandler()
# Create and start the worker using helper function and context manager
dts_worker = get_worker(log_handler=silent_handler)
with dts_worker:
# Register agents using helper function
setup_worker(dts_worker)
# Start the worker
dts_worker.start()
logger.debug("Worker started and listening for requests...")
# Create the client using helper function
agent_client = get_client(log_handler=silent_handler)
try:
# Run client interactions using helper function
run_client(agent_client)
except Exception as e:
logger.exception(f"Error during agent interaction: {e}")
logger.debug("Sample completed. Worker shutting down...")
if __name__ == "__main__":
load_dotenv()
main()
@@ -0,0 +1,132 @@
# Copyright (c) Microsoft. All rights reserved.
"""Worker process for hosting a single Azure OpenAI-powered agent using Durable Task.
This worker registers agents as durable entities and continuously listens for requests.
The worker should run as a background service, processing incoming agent requests.
Prerequisites:
- Set FOUNDRY_PROJECT_ENDPOINT and FOUNDRY_MODEL
- Sign in with Azure CLI for AzureCliCredential authentication
- Start a Durable Task Scheduler (e.g., using Docker)
"""
import asyncio
import logging
import os
from agent_framework import Agent
from agent_framework.azure import DurableAIAgentWorker
from agent_framework.foundry import FoundryChatClient
from azure.identity import AzureCliCredential
from azure.identity.aio import AzureCliCredential as AsyncAzureCliCredential
from dotenv import load_dotenv
from durabletask.azuremanaged.worker import DurableTaskSchedulerWorker
# Load environment variables from .env file
load_dotenv()
# Configure logging
logging.basicConfig(level=logging.WARNING)
logger = logging.getLogger(__name__)
def create_joker_agent() -> Agent:
"""Create the Joker agent using Azure OpenAI.
Returns:
Agent: The configured Joker agent
"""
return Agent(
client=FoundryChatClient(
project_endpoint=os.environ["FOUNDRY_PROJECT_ENDPOINT"],
model=os.environ["FOUNDRY_MODEL"],
credential=AsyncAzureCliCredential(),
),
name="Joker",
instructions="You are good at telling jokes.",
)
def get_worker(
taskhub: str | None = None, endpoint: str | None = None, log_handler: logging.Handler | None = None
) -> DurableTaskSchedulerWorker:
"""Create a configured DurableTaskSchedulerWorker.
Args:
taskhub: Task hub name (defaults to TASKHUB env var or "default")
endpoint: Scheduler endpoint (defaults to ENDPOINT env var or "http://localhost:8080")
log_handler: Optional logging handler for worker logging
Returns:
Configured DurableTaskSchedulerWorker instance
"""
taskhub_name = taskhub or os.getenv("TASKHUB", "default")
endpoint_url = endpoint or os.getenv("ENDPOINT", "http://localhost:8080")
logger.debug(f"Using taskhub: {taskhub_name}")
logger.debug(f"Using endpoint: {endpoint_url}")
credential = None if endpoint_url == "http://localhost:8080" else AzureCliCredential()
return DurableTaskSchedulerWorker(
host_address=endpoint_url,
secure_channel=endpoint_url != "http://localhost:8080",
taskhub=taskhub_name,
token_credential=credential,
log_handler=log_handler,
)
def setup_worker(worker: DurableTaskSchedulerWorker) -> DurableAIAgentWorker:
"""Set up the worker with agents registered.
Args:
worker: The DurableTaskSchedulerWorker instance
Returns:
DurableAIAgentWorker with agents registered
"""
# Wrap it with the agent worker
agent_worker = DurableAIAgentWorker(worker)
# Create and register the Joker agent
logger.debug("Creating and registering Joker agent...")
joker_agent = create_joker_agent()
agent_worker.add_agent(joker_agent)
logger.debug(f"✓ Registered agent: {joker_agent.name}")
logger.debug(f" Entity name: dafx-{joker_agent.name}")
return agent_worker
async def main():
"""Main entry point for the worker process."""
logger.debug("Starting Durable Task Agent Worker...")
# Create a worker using the helper function
worker = get_worker()
# Setup worker with agents
setup_worker(worker)
logger.info("Worker is ready and listening for requests...")
logger.info("Press Ctrl+C to stop.")
logger.info("")
try:
# Start the worker (this blocks until stopped)
worker.start()
# Keep the worker running
while True:
await asyncio.sleep(1)
except KeyboardInterrupt:
logger.debug("Worker shutdown initiated")
logger.debug("Worker stopped")
if __name__ == "__main__":
asyncio.run(main())