Python: [BREAKING] Types API Review improvements (#3647)

* Replace Role and FinishReason classes with NewType + Literal

- Remove EnumLike metaclass from _types.py
- Replace Role class with NewType('Role', str) + RoleLiteral
- Replace FinishReason class with NewType('FinishReason', str) + FinishReasonLiteral
- Update all usages across codebase to use string literals
- Remove .value access patterns (direct string comparison now works)
- Add backward compatibility for legacy dict serialization format
- Update tests to reflect new string-based types

Addresses #3591, #3615

* Simplify ChatResponse and AgentResponse type hints (#3592)

- Remove overloads from ChatResponse.__init__
- Remove text parameter from ChatResponse.__init__
- Remove | dict[str, Any] from finish_reason and usage_details params
- Remove **kwargs from AgentResponse.__init__
- Both now accept ChatMessage | Sequence[ChatMessage] | None for messages
- Update docstrings and examples to reflect changes
- Fix tests that were using removed kwargs
- Fix Role type hint usage in ag-ui utils

* Remove text parameter from ChatResponseUpdate and AgentResponseUpdate (#3597)

- Remove text parameter from ChatResponseUpdate.__init__
- Remove text parameter from AgentResponseUpdate.__init__
- Remove **kwargs from both update classes
- Simplify contents parameter type to Sequence[Content] | None
- Update all usages to use contents=[Content.from_text(...)] pattern
- Fix imports in test files
- Update docstrings and examples

* Rename from_chat_response_updates to from_updates (#3593)

- ChatResponse.from_chat_response_updates → ChatResponse.from_updates
- ChatResponse.from_chat_response_generator → ChatResponse.from_update_generator
- AgentResponse.from_agent_run_response_updates → AgentResponse.from_updates

* Remove try_parse_value method from ChatResponse and AgentResponse (#3595)

- Remove try_parse_value method from ChatResponse
- Remove try_parse_value method from AgentResponse
- Remove try_parse_value calls from from_updates and from_update_generator methods
- Update samples to use try/except with response.value instead
- Update tests to use response.value pattern
- Users should now use response.value with try/except for safe parsing

* Add agent_id to AgentResponse and clarify author_name documentation (#3596)

- Add agent_id parameter to AgentResponse class
- Document that author_name is on ChatMessage objects, not responses
- Update ChatResponse docstring with author_name note
- Update AgentResponse docstring with author_name note

* Simplify ChatMessage.__init__ signature (#3618)

- Make contents a positional argument accepting Sequence[Content | str]
- Auto-convert strings in contents to TextContent
- Remove overloads, keep text kwarg for backward compatibility with serialization
- Update _parse_content_list to handle string items
- Update all usages across codebase to use new format: ChatMessage("role", ["text"])

* Allow Content as input on run and get_response

- Update prepare_messages and normalize_messages to accept Content
- Update type signatures in _agents.py and _clients.py
- Add tests for Content input handling

* Fix ChatMessage usage across packages and samples

Update all remaining ChatMessage(role=..., text=...) to use new
ChatMessage('role', ['text']) signature.

* Fix Role string usage and response format parsing

- Fix redis provider: remove .value access on string literals
- Fix durabletask ensure_response_format: set _response_format before accessing .value

* Fix ollama .value and ai_model_id issues, handle None in content list

- Fix ollama _chat_client: remove .value on string literals
- Fix ollama _chat_client: rename ai_model_id to model_id
- Fix _parse_content_list: skip None values gracefully

* Fix A2AAgent type signature to include Content

* Fix Role/FinishReason NewType dict annotations and improve test coverage to 95%

* Fix mypy errors for Role/FinishReason NewType usage

* Fix Role.TOOL and Role.ASSISTANT usage in _orchestrator_helpers.py

* Fix Role NewType usage in durabletask _models.py
This commit is contained in:
Eduard van Valkenburg
2026-02-04 11:13:23 +01:00
committed by GitHub
Unverified
parent ef798629e5
commit 838a7fd61d
341 changed files with 3766 additions and 3228 deletions
@@ -103,7 +103,6 @@ async def run_agent_framework_with_cycle() -> None:
WorkflowContext,
WorkflowOutputEvent,
executor,
tool,
)
from agent_framework.openai import OpenAIChatClient
@@ -102,7 +102,6 @@ async def run_agent_framework() -> None:
RequestInfoEvent,
WorkflowRunState,
WorkflowStatusEvent,
tool,
)
from agent_framework.openai import OpenAIChatClient
@@ -142,7 +141,7 @@ async def run_agent_framework() -> None:
)
.set_coordinator(triage_agent)
.add_handoff(triage_agent, [billing_agent, tech_support])
.with_termination_condition(lambda conv: sum(1 for msg in conv if msg.role.value == "user") > 3)
.with_termination_condition(lambda conv: sum(1 for msg in conv if msg.role == "user") > 3)
.build()
)
@@ -18,8 +18,7 @@ from typing import Annotated, Any
import uvicorn
# Agent Framework imports
from agent_framework import AgentResponseUpdate, ChatAgent, ChatMessage, FunctionResultContent, Role
from agent_framework import tool
from agent_framework import AgentResponseUpdate, ChatAgent, ChatMessage, FunctionResultContent, tool
from agent_framework.azure import AzureOpenAIChatClient
# Agent Framework ChatKit integration
@@ -131,6 +130,7 @@ async def stream_widget(
yield ThreadItemDoneEvent(type="thread.item.done", item=widget_item)
# NOTE: approval_mode="never_require" is for sample brevity. Use "always_require" in production; see samples/getting_started/tools/function_tool_with_approval.py and samples/getting_started/tools/function_tool_with_approval_and_threads.py.
@tool(approval_mode="never_require")
def get_weather(
@@ -170,6 +170,7 @@ def get_weather(
)
return WeatherResponse(text, weather_data)
@tool(approval_mode="never_require")
def get_time() -> str:
"""Get the current UTC time."""
@@ -177,6 +178,7 @@ def get_time() -> str:
logger.info("Getting current UTC time")
return f"Current UTC time: {current_time.strftime('%Y-%m-%d %H:%M:%S')} UTC"
@tool(approval_mode="never_require")
def show_city_selector() -> str:
"""Show an interactive city selector widget to the user.
@@ -279,7 +281,7 @@ class WeatherChatKitServer(ChatKitServer[dict[str, Any]]):
title_prompt = [
ChatMessage(
role=Role.USER,
role="user",
text=(
f"Generate a very short, concise title (max 40 characters) for a conversation "
f"that starts with:\n\n{conversation_context}\n\n"
@@ -456,7 +458,7 @@ class WeatherChatKitServer(ChatKitServer[dict[str, Any]]):
weather_data: WeatherData | None = None
# Create an agent message asking about the weather
agent_messages = [ChatMessage(role=Role.USER, text=f"What's the weather in {city_label}?")]
agent_messages = [ChatMessage("user", [f"What's the weather in {city_label}?"])]
logger.debug(f"Processing weather query: {agent_messages[0].text}")
@@ -6,7 +6,7 @@ from collections.abc import MutableSequence
from dataclasses import dataclass
from typing import Any
from agent_framework import ChatMessage, Context, ContextProvider, Role
from agent_framework import ChatMessage, Context, ContextProvider
from agent_framework.azure import AzureOpenAIChatClient
from azure.ai.agentserver.agentframework import from_agent_framework # pyright: ignore[reportUnknownVariableType]
from azure.identity import DefaultAzureCredential
@@ -85,7 +85,7 @@ class TextSearchContextProvider(ContextProvider):
return Context(
messages=[
ChatMessage(
role=Role.USER, text="\n\n".join(json.dumps(result.__dict__, indent=2) for result in results)
role="user", text="\n\n".join(json.dumps(result.__dict__, indent=2) for result in results)
)
]
)
@@ -16,8 +16,7 @@ from dataclasses import dataclass
from random import randint
from typing import Annotated
from agent_framework import ChatAgent
from agent_framework import tool
from agent_framework import ChatAgent, tool
from agent_framework.openai import OpenAIChatClient
from aiohttp import web
from aiohttp.web_middlewares import middleware
@@ -77,6 +76,7 @@ def load_app_config() -> AppConfig:
port = 3978
return AppConfig(use_anonymous_mode=use_anonymous_mode, port=port, agents_sdk_config=agents_sdk_config)
# NOTE: approval_mode="never_require" is for sample brevity. Use "always_require" in production; see samples/getting_started/tools/function_tool_with_approval.py and samples/getting_started/tools/function_tool_with_approval_and_threads.py.
@tool(approval_mode="never_require")
def get_weather(
@@ -70,7 +70,7 @@ def search_hotels(
"availability": "Available"
}
]
return json.dumps({
"location": location,
"check_in": check_in,
@@ -140,7 +140,7 @@ def get_hotel_details(
"nearby_attractions": ["Eiffel Tower (0.2 mi)", "Seine River Cruise Dock (0.3 mi)", "Trocadéro (0.5 mi)"]
}
}
details = hotel_details.get(hotel_name, {
"name": hotel_name,
"description": "Comfortable hotel with modern amenities",
@@ -150,7 +150,7 @@ def get_hotel_details(
"reviews": {"total": 0, "recent_comments": []},
"nearby_attractions": []
})
return json.dumps({
"hotel_name": hotel_name,
"details": details
@@ -270,7 +270,7 @@ def search_flights(
"stops": "Nonstop"
}
]
return json.dumps({
"origin": origin,
"destination": destination,
@@ -317,7 +317,7 @@ def get_flight_details(
},
"amenities": ["WiFi", "In-flight entertainment", "Meals included"]
}
return json.dumps({
"flight_details": mock_details
})
@@ -439,7 +439,7 @@ def search_activities(
"booking_required": False
}
]
if category:
activities = [act for act in all_activities if act["category"] == category]
else:
@@ -456,7 +456,7 @@ def search_activities(
"availability": "Daily at 10:00 AM and 2:00 PM"
}
]
return json.dumps({
"location": location,
"date": date,
@@ -523,7 +523,7 @@ def get_activity_details(
"reviews_count": 2341
}
}
details = activity_details_map.get(activity_name, {
"name": activity_name,
"description": "An immersive experience that showcases the best of local culture and attractions.",
@@ -538,7 +538,7 @@ def get_activity_details(
"rating": 4.5,
"reviews_count": 100
})
return json.dumps({
"activity_details": details
})
@@ -558,7 +558,7 @@ def confirm_booking(
booking status, customer information, and next steps.
"""
confirmation_number = f"CONF-{booking_type.upper()}-{booking_id}"
confirmation_data = {
"confirmation_number": confirmation_number,
"booking_type": booking_type,
@@ -572,7 +572,7 @@ def confirm_booking(
"Bring confirmation number and valid ID"
]
}
return json.dumps({
"confirmation": confirmation_data
})
@@ -595,7 +595,7 @@ def check_hotel_availability(
and last checked timestamp.
"""
availability_status = "Available"
availability_data = {
"service_type": "hotel",
"hotel_name": hotel_name,
@@ -607,7 +607,7 @@ def check_hotel_availability(
"price_per_night": "$185",
"last_checked": datetime.now().isoformat()
}
return json.dumps({
"availability": availability_data
})
@@ -629,7 +629,7 @@ def check_flight_availability(
and last checked timestamp.
"""
availability_status = "Available"
availability_data = {
"service_type": "flight",
"flight_number": flight_number,
@@ -640,7 +640,7 @@ def check_flight_availability(
"price_per_passenger": "$520",
"last_checked": datetime.now().isoformat()
}
return json.dumps({
"availability": availability_data
})
@@ -662,7 +662,7 @@ def check_activity_availability(
and last checked timestamp.
"""
availability_status = "Available"
availability_data = {
"service_type": "activity",
"activity_name": activity_name,
@@ -673,7 +673,7 @@ def check_activity_availability(
"price_per_person": "$45",
"last_checked": datetime.now().isoformat()
}
return json.dumps({
"availability": availability_data
})
@@ -694,7 +694,7 @@ def process_payment(
payment method details, and receipt URL.
"""
transaction_id = f"TXN-{datetime.now().strftime('%Y%m%d%H%M%S')}"
payment_result = {
"transaction_id": transaction_id,
"amount": amount,
@@ -706,13 +706,12 @@ def process_payment(
"timestamp": datetime.now().isoformat(),
"receipt_url": f"https://payments.travelagency.com/receipt/{transaction_id}"
}
return json.dumps({
"payment_result": payment_result
})
# Mock payment validation tool
@tool(name="validate_payment_method", description="Validate a payment method before processing.")
def validate_payment_method(
@@ -725,11 +724,11 @@ def validate_payment_method(
validation messages, supported currencies, and processing fee information.
"""
method_type = payment_method.get("type", "credit_card")
# Validation logic
is_valid = True
validation_messages = []
if method_type == "credit_card":
if not payment_method.get("number"):
is_valid = False
@@ -740,7 +739,7 @@ def validate_payment_method(
if not payment_method.get("cvv"):
is_valid = False
validation_messages.append("CVV is required")
validation_result = {
"is_valid": is_valid,
"payment_method_type": method_type,
@@ -748,7 +747,7 @@ def validate_payment_method(
"supported_currencies": ["USD", "EUR", "GBP", "JPY"],
"processing_fee": "2.5%"
}
return json.dumps({
"validation_result": validation_result
})
@@ -51,13 +51,11 @@ from agent_framework import (
AgentRunUpdateEvent,
ChatMessage,
Executor,
Role,
WorkflowBuilder,
WorkflowContext,
WorkflowOutputEvent,
executor,
handler,
tool,
)
from agent_framework.azure import AzureAIClient
from azure.ai.projects.aio import AIProjectClient
@@ -71,7 +69,7 @@ load_dotenv()
@executor(id="start_executor")
async def start_executor(input: str, ctx: WorkflowContext[list[ChatMessage]]) -> None:
"""Initiates the workflow by sending the user query to all specialized agents."""
await ctx.send_message([ChatMessage(role="user", text=input)])
await ctx.send_message([ChatMessage("user", [input])])
class ResearchLead(Executor):
@@ -107,11 +105,11 @@ class ResearchLead(Executor):
# Generate comprehensive travel plan summary
messages = [
ChatMessage(
role=Role.SYSTEM,
role="system",
text="You are a travel planning coordinator. Summarize findings from multiple specialized travel agents and provide a clear, comprehensive travel plan based on the user's query.",
),
ChatMessage(
role=Role.USER,
role="user",
text=f"Original query: {user_query}\n\nFindings from specialized travel agents:\n{summary_text}\n\nPlease provide a comprehensive travel plan based on these findings.",
),
]
@@ -136,7 +134,7 @@ class ResearchLead(Executor):
findings = []
if response.agent_response and response.agent_response.messages:
for msg in response.agent_response.messages:
if msg.role == Role.ASSISTANT and msg.text and msg.text.strip():
if msg.role == "assistant" and msg.text and msg.text.strip():
findings.append(msg.text.strip())
if findings:
@@ -16,16 +16,15 @@ import time
from azure.ai.projects import AIProjectClient
from azure.identity import DefaultAzureCredential
from dotenv import load_dotenv
from create_workflow import create_and_run_workflow
from dotenv import load_dotenv
def print_section(title: str):
"""Print a formatted section header."""
print(f"\n{'='*80}")
print(f"\n{'=' * 80}")
print(f"{title}")
print(f"{'='*80}")
print(f"{'=' * 80}")
async def run_workflow():
@@ -37,9 +36,9 @@ async def run_workflow():
print_section("Step 1: Running Workflow")
print("Executing multi-agent travel planning workflow...")
print("This may take a few minutes...")
workflow_data = await create_and_run_workflow()
print("Workflow execution completed")
return workflow_data
@@ -47,31 +46,31 @@ async def run_workflow():
def display_response_summary(workflow_data: dict):
"""Display summary of response data."""
print_section("Step 2: Response Data Summary")
print(f"Query: {workflow_data['query']}")
print(f"\nAgents tracked: {len(workflow_data['agents'])}")
for agent_name, agent_data in workflow_data['agents'].items():
response_count = agent_data['response_count']
for agent_name, agent_data in workflow_data["agents"].items():
response_count = agent_data["response_count"]
print(f" {agent_name}: {response_count} response(s)")
def fetch_agent_responses(openai_client, workflow_data: dict, agent_names: list):
"""Fetch and display final responses from specified agents."""
print_section("Step 3: Fetching Agent Responses")
for agent_name in agent_names:
if agent_name not in workflow_data['agents']:
if agent_name not in workflow_data["agents"]:
continue
agent_data = workflow_data['agents'][agent_name]
if not agent_data['response_ids']:
agent_data = workflow_data["agents"][agent_name]
if not agent_data["response_ids"]:
continue
final_response_id = agent_data['response_ids'][-1]
final_response_id = agent_data["response_ids"][-1]
print(f"\n{agent_name}")
print(f" Response ID: {final_response_id}")
try:
response = openai_client.responses.retrieve(response_id=final_response_id)
content = response.output[-1].content[-1].text
@@ -84,9 +83,9 @@ def fetch_agent_responses(openai_client, workflow_data: dict, agent_names: list)
def create_evaluation(openai_client, model_deployment: str):
"""Create evaluation with multiple evaluators."""
print_section("Step 4: Creating Evaluation")
data_source_config = {"type": "azure_ai_source", "scenario": "responses"}
testing_criteria = [
{
"type": "azure_ai_evaluator",
@@ -113,33 +112,33 @@ def create_evaluation(openai_client, model_deployment: str):
"initialization_parameters": {"deployment_name": model_deployment}
},
]
eval_object = openai_client.evals.create(
name="Travel Workflow Multi-Evaluator Assessment",
data_source_config=data_source_config,
testing_criteria=testing_criteria,
)
evaluator_names = [criterion["name"] for criterion in testing_criteria]
print(f"Evaluation created: {eval_object.id}")
print(f"Evaluators ({len(evaluator_names)}): {', '.join(evaluator_names)}")
return eval_object
def run_evaluation(openai_client, eval_object, workflow_data: dict, agent_names: list):
"""Run evaluation on selected agent responses."""
print_section("Step 5: Running Evaluation")
selected_response_ids = []
for agent_name in agent_names:
if agent_name in workflow_data['agents']:
agent_data = workflow_data['agents'][agent_name]
if agent_data['response_ids']:
selected_response_ids.append(agent_data['response_ids'][-1])
if agent_name in workflow_data["agents"]:
agent_data = workflow_data["agents"][agent_name]
if agent_data["response_ids"]:
selected_response_ids.append(agent_data["response_ids"][-1])
print(f"Selected {len(selected_response_ids)} responses for evaluation")
data_source = {
"type": "azure_ai_responses",
"item_generation_params": {
@@ -151,24 +150,24 @@ def run_evaluation(openai_client, eval_object, workflow_data: dict, agent_names:
},
},
}
eval_run = openai_client.evals.runs.create(
eval_id=eval_object.id,
name="Multi-Agent Response Evaluation",
data_source=data_source
)
print(f"Evaluation run created: {eval_run.id}")
return eval_run
def monitor_evaluation(openai_client, eval_object, eval_run):
"""Monitor evaluation progress and display results."""
print_section("Step 6: Monitoring Evaluation")
print("Waiting for evaluation to complete...")
while eval_run.status not in ["completed", "failed"]:
eval_run = openai_client.evals.runs.retrieve(
run_id=eval_run.id,
@@ -176,7 +175,7 @@ def monitor_evaluation(openai_client, eval_object, eval_run):
)
print(f"Status: {eval_run.status}")
time.sleep(5)
if eval_run.status == "completed":
print("\nEvaluation completed successfully")
print(f"Result counts: {eval_run.result_counts}")
@@ -188,31 +187,31 @@ def monitor_evaluation(openai_client, eval_object, eval_run):
async def main():
"""Main execution flow."""
load_dotenv()
print("Travel Planning Workflow Evaluation")
workflow_data = await run_workflow()
display_response_summary(workflow_data)
project_client = AIProjectClient(
endpoint=os.environ["AZURE_AI_PROJECT_ENDPOINT"],
credential=DefaultAzureCredential(),
api_version="2025-11-15-preview"
)
openai_client = project_client.get_openai_client()
agents_to_evaluate = ["hotel-search-agent", "flight-search-agent", "activity-search-agent"]
fetch_agent_responses(openai_client, workflow_data, agents_to_evaluate)
model_deployment = os.environ.get("AZURE_AI_MODEL_DEPLOYMENT_NAME", "gpt-4o-mini")
eval_object = create_evaluation(openai_client, model_deployment)
eval_run = run_evaluation(openai_client, eval_object, workflow_data, agents_to_evaluate)
monitor_evaluation(openai_client, eval_object, eval_run)
print_section("Complete")
@@ -4,8 +4,8 @@ import asyncio
from random import randint
from typing import Annotated
from agent_framework.anthropic import AnthropicClient
from agent_framework import tool
from agent_framework.anthropic import AnthropicClient
"""
Anthropic Chat Agent Example
@@ -13,6 +13,7 @@ Anthropic Chat Agent Example
This sample demonstrates using Anthropic with an agent and a single custom tool.
"""
# NOTE: approval_mode="never_require" is for sample brevity. Use "always_require" in production; see samples/getting_started/tools/function_tool_with_approval.py and samples/getting_started/tools/function_tool_with_approval_and_threads.py.
@tool(approval_mode="never_require")
def get_weather(
@@ -4,10 +4,10 @@ import asyncio
from random import randint
from typing import Annotated
from agent_framework import tool
from agent_framework.azure import AzureAIProjectAgentProvider
from azure.identity.aio import AzureCliCredential
from pydantic import Field
from agent_framework import tool
"""
Azure AI Agent Basic Example
@@ -16,6 +16,7 @@ This sample demonstrates basic usage of AzureAIProjectAgentProvider.
Shows both streaming and non-streaming responses with function tools.
"""
# NOTE: approval_mode="never_require" is for sample brevity. Use "always_require" in production; see samples/getting_started/tools/function_tool_with_approval.py and samples/getting_started/tools/function_tool_with_approval_and_threads.py.
@tool(approval_mode="never_require")
def get_weather(
@@ -5,12 +5,12 @@ import os
from random import randint
from typing import Annotated
from agent_framework import tool
from agent_framework.azure import AzureAIProjectAgentProvider
from azure.ai.projects.aio import AIProjectClient
from azure.ai.projects.models import AgentReference, PromptAgentDefinition
from azure.identity.aio import AzureCliCredential
from pydantic import Field
from agent_framework import tool
"""
Azure AI Project Agent Provider Methods Example
@@ -26,6 +26,7 @@ with different configurations, which is efficient for multi-agent scenarios.
Each method returns a ChatAgent that can be used for conversations.
"""
# NOTE: approval_mode="never_require" is for sample brevity. Use "always_require" in production; see samples/getting_started/tools/function_tool_with_approval.py and samples/getting_started/tools/function_tool_with_approval_and_threads.py.
@tool(approval_mode="never_require")
def get_weather(
@@ -4,10 +4,10 @@ import asyncio
from random import randint
from typing import Annotated
from agent_framework import tool
from agent_framework.azure import AzureAIProjectAgentProvider
from azure.identity.aio import AzureCliCredential
from pydantic import Field
from agent_framework import tool
"""
Azure AI Agent Latest Version Example
@@ -17,6 +17,7 @@ instead of creating a new agent version on each instantiation. The first call cr
while subsequent calls with `get_agent()` reuse the latest agent version.
"""
# NOTE: approval_mode="never_require" is for sample brevity. Use "always_require" in production; see samples/getting_started/tools/function_tool_with_approval.py and samples/getting_started/tools/function_tool_with_approval_and_threads.py.
@tool(approval_mode="never_require")
def get_weather(
@@ -5,7 +5,6 @@ import asyncio
from agent_framework import (
AgentResponseUpdate,
HostedCodeInterpreterTool,
tool,
)
from agent_framework.azure import AzureAIProjectAgentProvider
from azure.identity.aio import AzureCliCredential
@@ -4,11 +4,11 @@ import os
from random import randint
from typing import Annotated
from agent_framework import tool
from agent_framework.azure import AzureAIProjectAgentProvider
from azure.ai.projects.aio import AIProjectClient
from azure.identity.aio import AzureCliCredential
from pydantic import Field
from agent_framework import tool
"""
Azure AI Agent Existing Conversation Example
@@ -16,6 +16,7 @@ Azure AI Agent Existing Conversation Example
This sample demonstrates usage of AzureAIProjectAgentProvider with existing conversation created on service side.
"""
# NOTE: approval_mode="never_require" is for sample brevity. Use "always_require" in production; see samples/getting_started/tools/function_tool_with_approval.py and samples/getting_started/tools/function_tool_with_approval_and_threads.py.
@tool(approval_mode="never_require")
def get_weather(
@@ -5,10 +5,10 @@ import os
from random import randint
from typing import Annotated
from agent_framework import tool
from agent_framework.azure import AzureAIProjectAgentProvider
from azure.identity.aio import AzureCliCredential
from pydantic import Field
from agent_framework import tool
"""
Azure AI Agent with Explicit Settings Example
@@ -17,6 +17,7 @@ This sample demonstrates creating Azure AI Agents with explicit configuration
settings rather than relying on environment variable defaults.
"""
# NOTE: approval_mode="never_require" is for sample brevity. Use "always_require" in production; see samples/getting_started/tools/function_tool_with_approval.py and samples/getting_started/tools/function_tool_with_approval_and_threads.py.
@tool(approval_mode="never_require")
def get_weather(
@@ -25,10 +25,10 @@ async def handle_approvals_without_thread(query: str, agent: "AgentProtocol") ->
f"User Input Request for function from {agent.name}: {user_input_needed.function_call.name}"
f" with arguments: {user_input_needed.function_call.arguments}"
)
new_inputs.append(ChatMessage(role="assistant", contents=[user_input_needed]))
new_inputs.append(ChatMessage("assistant", [user_input_needed]))
user_approval = input("Approve function call? (y/n): ")
new_inputs.append(
ChatMessage(role="user", contents=[user_input_needed.to_function_approval_response(user_approval.lower() == "y")])
ChatMessage("user", [user_input_needed.to_function_approval_response(user_approval.lower() == "y")])
)
result = await agent.run(new_inputs, store=False)
@@ -41,12 +41,13 @@ async def main() -> None:
print(f"User: {query}")
result = await agent.run(query)
if release_brief := result.try_parse_value(ReleaseBrief):
try:
release_brief = result.value
print("Agent:")
print(f"Feature: {release_brief.feature}")
print(f"Benefit: {release_brief.benefit}")
print(f"Launch date: {release_brief.launch_date}")
else:
except Exception:
print(f"Failed to parse response: {result.text}")
@@ -4,10 +4,10 @@ import asyncio
from random import randint
from typing import Annotated
from agent_framework import tool
from agent_framework.azure import AzureAIAgentsProvider
from azure.identity.aio import AzureCliCredential
from pydantic import Field
from agent_framework import tool
"""
Azure AI Agent Basic Example
@@ -16,6 +16,7 @@ This sample demonstrates basic usage of AzureAIAgentsProvider to create agents w
lifecycle management. Shows both streaming and non-streaming responses with function tools.
"""
# NOTE: approval_mode="never_require" is for sample brevity. Use "always_require" in production; see samples/getting_started/tools/function_tool_with_approval.py and samples/getting_started/tools/function_tool_with_approval_and_threads.py.
@tool(approval_mode="never_require")
def get_weather(
@@ -5,11 +5,11 @@ import os
from random import randint
from typing import Annotated
from agent_framework import tool
from agent_framework.azure import AzureAIAgentsProvider
from azure.ai.agents.aio import AgentsClient
from azure.identity.aio import AzureCliCredential
from pydantic import Field
from agent_framework import tool
"""
Azure AI Agent Provider Methods Example
@@ -20,6 +20,7 @@ This sample demonstrates the methods available on the AzureAIAgentsProvider clas
- as_agent(): Wrap an SDK Agent object without making HTTP calls
"""
# NOTE: approval_mode="never_require" is for sample brevity. Use "always_require" in production; see samples/getting_started/tools/function_tool_with_approval.py and samples/getting_started/tools/function_tool_with_approval_and_threads.py.
@tool(approval_mode="never_require")
def get_weather(
@@ -7,7 +7,6 @@ from agent_framework import (
AgentResponseUpdate,
HostedCodeInterpreterTool,
HostedFileContent,
tool,
)
from agent_framework.azure import AzureAIAgentsProvider
from azure.ai.agents.aio import AgentsClient
@@ -5,11 +5,11 @@ import os
from random import randint
from typing import Annotated
from agent_framework import tool
from agent_framework.azure import AzureAIAgentsProvider
from azure.ai.agents.aio import AgentsClient
from azure.identity.aio import AzureCliCredential
from pydantic import Field
from agent_framework import tool
"""
Azure AI Agent with Existing Thread Example
@@ -18,6 +18,7 @@ This sample demonstrates working with pre-existing conversation threads
by providing thread IDs for thread reuse patterns.
"""
# NOTE: approval_mode="never_require" is for sample brevity. Use "always_require" in production; see samples/getting_started/tools/function_tool_with_approval.py and samples/getting_started/tools/function_tool_with_approval_and_threads.py.
@tool(approval_mode="never_require")
def get_weather(
@@ -5,10 +5,10 @@ import os
from random import randint
from typing import Annotated
from agent_framework import tool
from agent_framework.azure import AzureAIAgentsProvider
from azure.identity.aio import AzureCliCredential
from pydantic import Field
from agent_framework import tool
"""
Azure AI Agent with Explicit Settings Example
@@ -17,6 +17,7 @@ This sample demonstrates creating Azure AI Agents with explicit configuration
settings rather than relying on environment variable defaults.
"""
# NOTE: approval_mode="never_require" is for sample brevity. Use "always_require" in production; see samples/getting_started/tools/function_tool_with_approval.py and samples/getting_started/tools/function_tool_with_approval_and_threads.py.
@tool(approval_mode="never_require")
def get_weather(
@@ -5,10 +5,10 @@ from datetime import datetime, timezone
from random import randint
from typing import Annotated
from agent_framework import tool
from agent_framework.azure import AzureAIAgentsProvider
from azure.identity.aio import AzureCliCredential
from pydantic import Field
from agent_framework import tool
"""
Azure AI Agent with Function Tools Example
@@ -17,6 +17,7 @@ This sample demonstrates function tool integration with Azure AI Agents,
showing both agent-level and query-level tool configuration patterns.
"""
# NOTE: approval_mode="never_require" is for sample brevity. Use "always_require" in production; see samples/getting_started/tools/function_tool_with_approval.py and samples/getting_started/tools/function_tool_with_approval_and_threads.py.
@tool(approval_mode="never_require")
def get_weather(
@@ -26,6 +27,7 @@ def get_weather(
conditions = ["sunny", "cloudy", "rainy", "stormy"]
return f"The weather in {location} is {conditions[randint(0, 3)]} with a high of {randint(10, 30)}°C."
@tool(approval_mode="never_require")
def get_time() -> str:
"""Get the current UTC time."""
@@ -34,9 +34,9 @@ To set up Bing Grounding:
4. Copy the connection ID and set it as the BING_CONNECTION_ID environment variable
"""
# NOTE: approval_mode="never_require" is for sample brevity. Use "always_require" in production; see samples/getting_started/tools/function_tool_with_approval.py and samples/getting_started/tools/function_tool_with_approval_and_threads.py.
@tool(approval_mode="never_require")
def get_time() -> str:
"""Get the current UTC time."""
current_time = datetime.now(timezone.utc)
@@ -56,13 +56,14 @@ async def main() -> None:
result1 = await agent.run(query1)
if weather := result1.try_parse_value(WeatherInfo):
try:
weather = result1.value
print("Agent:")
print(f" Location: {weather.location}")
print(f" Temperature: {weather.temperature}")
print(f" Conditions: {weather.conditions}")
print(f" Recommendation: {weather.recommendation}")
else:
except Exception:
print(f"Failed to parse response: {result1.text}")
# Request 2: Override response_format at runtime with CityInfo
@@ -72,12 +73,13 @@ async def main() -> None:
result2 = await agent.run(query2, options={"response_format": CityInfo})
if city := result2.try_parse_value(CityInfo):
try:
city = result2.value
print("Agent:")
print(f" City: {city.city_name}")
print(f" Population: {city.population}")
print(f" Country: {city.country}")
else:
except Exception:
print(f"Failed to parse response: {result2.text}")
@@ -4,8 +4,7 @@ import asyncio
from random import randint
from typing import Annotated
from agent_framework import AgentThread
from agent_framework import tool
from agent_framework import AgentThread, tool
from agent_framework.azure import AzureAIAgentsProvider
from azure.identity.aio import AzureCliCredential
from pydantic import Field
@@ -17,6 +16,7 @@ This sample demonstrates thread management with Azure AI Agents, comparing
automatic thread creation with explicit thread management for persistent context.
"""
# NOTE: approval_mode="never_require" is for sample brevity. Use "always_require" in production; see samples/getting_started/tools/function_tool_with_approval.py and samples/getting_started/tools/function_tool_with_approval_and_threads.py.
@tool(approval_mode="never_require")
def get_weather(
@@ -4,10 +4,10 @@ import asyncio
from random import randint
from typing import Annotated
from agent_framework import tool
from agent_framework.azure import AzureOpenAIAssistantsClient
from azure.identity import AzureCliCredential
from pydantic import Field
from agent_framework import tool
"""
Azure OpenAI Assistants Basic Example
@@ -16,6 +16,7 @@ This sample demonstrates basic usage of AzureOpenAIAssistantsClient with automat
assistant lifecycle management, showing both streaming and non-streaming responses.
"""
# NOTE: approval_mode="never_require" is for sample brevity. Use "always_require" in production; see samples/getting_started/tools/function_tool_with_approval.py and samples/getting_started/tools/function_tool_with_approval_and_threads.py.
@tool(approval_mode="never_require")
def get_weather(
@@ -5,8 +5,7 @@ import os
from random import randint
from typing import Annotated
from agent_framework import ChatAgent
from agent_framework import tool
from agent_framework import ChatAgent, tool
from agent_framework.azure import AzureOpenAIAssistantsClient
from azure.identity import AzureCliCredential, get_bearer_token_provider
from openai import AsyncAzureOpenAI
@@ -19,6 +18,7 @@ This sample demonstrates working with pre-existing Azure OpenAI Assistants
using existing assistant IDs rather than creating new ones.
"""
# NOTE: approval_mode="never_require" is for sample brevity. Use "always_require" in production; see samples/getting_started/tools/function_tool_with_approval.py and samples/getting_started/tools/function_tool_with_approval_and_threads.py.
@tool(approval_mode="never_require")
def get_weather(
@@ -5,10 +5,10 @@ import os
from random import randint
from typing import Annotated
from agent_framework import tool
from agent_framework.azure import AzureOpenAIAssistantsClient
from azure.identity import AzureCliCredential
from pydantic import Field
from agent_framework import tool
"""
Azure OpenAI Assistants with Explicit Settings Example
@@ -17,6 +17,7 @@ This sample demonstrates creating Azure OpenAI Assistants with explicit configur
settings rather than relying on environment variable defaults.
"""
# NOTE: approval_mode="never_require" is for sample brevity. Use "always_require" in production; see samples/getting_started/tools/function_tool_with_approval.py and samples/getting_started/tools/function_tool_with_approval_and_threads.py.
@tool(approval_mode="never_require")
def get_weather(
@@ -5,8 +5,7 @@ from datetime import datetime, timezone
from random import randint
from typing import Annotated
from agent_framework import ChatAgent
from agent_framework import tool
from agent_framework import ChatAgent, tool
from agent_framework.azure import AzureOpenAIAssistantsClient
from azure.identity import AzureCliCredential
from pydantic import Field
@@ -18,6 +17,7 @@ This sample demonstrates function tool integration with Azure OpenAI Assistants,
showing both agent-level and query-level tool configuration patterns.
"""
# NOTE: approval_mode="never_require" is for sample brevity. Use "always_require" in production; see samples/getting_started/tools/function_tool_with_approval.py and samples/getting_started/tools/function_tool_with_approval_and_threads.py.
@tool(approval_mode="never_require")
def get_weather(
@@ -27,6 +27,7 @@ def get_weather(
conditions = ["sunny", "cloudy", "rainy", "stormy"]
return f"The weather in {location} is {conditions[randint(0, 3)]} with a high of {randint(10, 30)}°C."
@tool(approval_mode="never_require")
def get_time() -> str:
"""Get the current UTC time."""
@@ -4,8 +4,7 @@ import asyncio
from random import randint
from typing import Annotated
from agent_framework import AgentThread, ChatAgent
from agent_framework import tool
from agent_framework import AgentThread, ChatAgent, tool
from agent_framework.azure import AzureOpenAIAssistantsClient
from azure.identity import AzureCliCredential
from pydantic import Field
@@ -17,6 +16,7 @@ This sample demonstrates thread management with Azure OpenAI Assistants, compari
automatic thread creation with explicit thread management for persistent context.
"""
# NOTE: approval_mode="never_require" is for sample brevity. Use "always_require" in production; see samples/getting_started/tools/function_tool_with_approval.py and samples/getting_started/tools/function_tool_with_approval_and_threads.py.
@tool(approval_mode="never_require")
def get_weather(
@@ -4,10 +4,10 @@ import asyncio
from random import randint
from typing import Annotated
from agent_framework import tool
from agent_framework.azure import AzureOpenAIChatClient
from azure.identity import AzureCliCredential
from pydantic import Field
from agent_framework import tool
"""
Azure OpenAI Chat Client Basic Example
@@ -16,6 +16,7 @@ This sample demonstrates basic usage of AzureOpenAIChatClient for direct chat-ba
interactions, showing both streaming and non-streaming responses.
"""
# NOTE: approval_mode="never_require" is for sample brevity. Use "always_require" in production; see samples/getting_started/tools/function_tool_with_approval.py and samples/getting_started/tools/function_tool_with_approval_and_threads.py.
@tool(approval_mode="never_require")
def get_weather(
@@ -5,10 +5,10 @@ import os
from random import randint
from typing import Annotated
from agent_framework import tool
from agent_framework.azure import AzureOpenAIChatClient
from azure.identity import AzureCliCredential
from pydantic import Field
from agent_framework import tool
"""
Azure OpenAI Chat Client with Explicit Settings Example
@@ -17,6 +17,7 @@ This sample demonstrates creating Azure OpenAI Chat Client with explicit configu
settings rather than relying on environment variable defaults.
"""
# NOTE: approval_mode="never_require" is for sample brevity. Use "always_require" in production; see samples/getting_started/tools/function_tool_with_approval.py and samples/getting_started/tools/function_tool_with_approval_and_threads.py.
@tool(approval_mode="never_require")
def get_weather(
@@ -5,8 +5,7 @@ from datetime import datetime, timezone
from random import randint
from typing import Annotated
from agent_framework import ChatAgent
from agent_framework import tool
from agent_framework import ChatAgent, tool
from agent_framework.azure import AzureOpenAIChatClient
from azure.identity import AzureCliCredential
from pydantic import Field
@@ -18,6 +17,7 @@ This sample demonstrates function tool integration with Azure OpenAI Chat Client
showing both agent-level and query-level tool configuration patterns.
"""
# NOTE: approval_mode="never_require" is for sample brevity. Use "always_require" in production; see samples/getting_started/tools/function_tool_with_approval.py and samples/getting_started/tools/function_tool_with_approval_and_threads.py.
@tool(approval_mode="never_require")
def get_weather(
@@ -27,6 +27,7 @@ def get_weather(
conditions = ["sunny", "cloudy", "rainy", "stormy"]
return f"The weather in {location} is {conditions[randint(0, 3)]} with a high of {randint(10, 30)}°C."
@tool(approval_mode="never_require")
def get_time() -> str:
"""Get the current UTC time."""
@@ -4,8 +4,7 @@ import asyncio
from random import randint
from typing import Annotated
from agent_framework import AgentThread, ChatAgent, ChatMessageStore
from agent_framework import tool
from agent_framework import AgentThread, ChatAgent, ChatMessageStore, tool
from agent_framework.azure import AzureOpenAIChatClient
from azure.identity import AzureCliCredential
from pydantic import Field
@@ -17,6 +16,7 @@ This sample demonstrates thread management with Azure OpenAI Chat Client, compar
automatic thread creation with explicit thread management for persistent context.
"""
# NOTE: approval_mode="never_require" is for sample brevity. Use "always_require" in production; see samples/getting_started/tools/function_tool_with_approval.py and samples/getting_started/tools/function_tool_with_approval_and_threads.py.
@tool(approval_mode="never_require")
def get_weather(
@@ -4,10 +4,10 @@ import asyncio
from random import randint
from typing import Annotated
from agent_framework import tool
from agent_framework.azure import AzureOpenAIResponsesClient
from azure.identity import AzureCliCredential
from pydantic import Field
from agent_framework import tool
"""
Azure OpenAI Responses Client Basic Example
@@ -16,6 +16,7 @@ This sample demonstrates basic usage of AzureOpenAIResponsesClient for structure
response generation, showing both streaming and non-streaming responses.
"""
# NOTE: approval_mode="never_require" is for sample brevity. Use "always_require" in production; see samples/getting_started/tools/function_tool_with_approval.py and samples/getting_started/tools/function_tool_with_approval_and_threads.py.
@tool(approval_mode="never_require")
def get_weather(
@@ -5,10 +5,10 @@ import os
from random import randint
from typing import Annotated
from agent_framework import tool
from agent_framework.azure import AzureOpenAIResponsesClient
from azure.identity import AzureCliCredential
from pydantic import Field
from agent_framework import tool
"""
Azure OpenAI Responses Client with Explicit Settings Example
@@ -17,6 +17,7 @@ This sample demonstrates creating Azure OpenAI Responses Client with explicit co
settings rather than relying on environment variable defaults.
"""
# NOTE: approval_mode="never_require" is for sample brevity. Use "always_require" in production; see samples/getting_started/tools/function_tool_with_approval.py and samples/getting_started/tools/function_tool_with_approval_and_threads.py.
@tool(approval_mode="never_require")
def get_weather(
@@ -5,8 +5,7 @@ from datetime import datetime, timezone
from random import randint
from typing import Annotated
from agent_framework import ChatAgent
from agent_framework import tool
from agent_framework import ChatAgent, tool
from agent_framework.azure import AzureOpenAIResponsesClient
from azure.identity import AzureCliCredential
from pydantic import Field
@@ -18,6 +17,7 @@ This sample demonstrates function tool integration with Azure OpenAI Responses C
showing both agent-level and query-level tool configuration patterns.
"""
# NOTE: approval_mode="never_require" is for sample brevity. Use "always_require" in production; see samples/getting_started/tools/function_tool_with_approval.py and samples/getting_started/tools/function_tool_with_approval_and_threads.py.
@tool(approval_mode="never_require")
def get_weather(
@@ -27,6 +27,7 @@ def get_weather(
conditions = ["sunny", "cloudy", "rainy", "stormy"]
return f"The weather in {location} is {conditions[randint(0, 3)]} with a high of {randint(10, 30)}°C."
@tool(approval_mode="never_require")
def get_time() -> str:
"""Get the current UTC time."""
@@ -30,10 +30,10 @@ async def handle_approvals_without_thread(query: str, agent: "AgentProtocol"):
f"User Input Request for function from {agent.name}: {user_input_needed.function_call.name}"
f" with arguments: {user_input_needed.function_call.arguments}"
)
new_inputs.append(ChatMessage(role="assistant", contents=[user_input_needed]))
new_inputs.append(ChatMessage("assistant", [user_input_needed]))
user_approval = input("Approve function call? (y/n): ")
new_inputs.append(
ChatMessage(role="user", contents=[user_input_needed.to_function_approval_response(user_approval.lower() == "y")])
ChatMessage("user", [user_input_needed.to_function_approval_response(user_approval.lower() == "y")])
)
result = await agent.run(new_inputs)
@@ -71,7 +71,7 @@ async def handle_approvals_with_thread_streaming(query: str, agent: "AgentProtoc
new_input_added = True
while new_input_added:
new_input_added = False
new_input.append(ChatMessage(role="user", text=query))
new_input.append(ChatMessage("user", [query]))
async for update in agent.run_stream(new_input, thread=thread, store=True):
if update.user_input_requests:
for user_input_needed in update.user_input_requests:
@@ -4,8 +4,7 @@ import asyncio
from random import randint
from typing import Annotated
from agent_framework import AgentThread, ChatAgent
from agent_framework import tool
from agent_framework import AgentThread, ChatAgent, tool
from agent_framework.azure import AzureOpenAIResponsesClient
from azure.identity import AzureCliCredential
from pydantic import Field
@@ -17,6 +16,7 @@ This sample demonstrates thread management with Azure OpenAI Responses Client, c
automatic thread creation with explicit thread management for persistent context.
"""
# NOTE: approval_mode="never_require" is for sample brevity. Use "always_require" in production; see samples/getting_started/tools/function_tool_with_approval.py and samples/getting_started/tools/function_tool_with_approval_and_threads.py.
@tool(approval_mode="never_require")
def get_weather(
@@ -11,8 +11,6 @@ from agent_framework import (
BaseAgent,
ChatMessage,
Content,
Role,
tool,
)
"""
@@ -77,8 +75,8 @@ class EchoAgent(BaseAgent):
if not normalized_messages:
response_message = ChatMessage(
role=Role.ASSISTANT,
contents=[Content.from_text(text="Hello! I'm a custom echo agent. Send me a message and I'll echo it back.")],
"assistant",
[Content.from_text(text="Hello! I'm a custom echo agent. Send me a message and I'll echo it back.")],
)
else:
# For simplicity, echo the last user message
@@ -88,7 +86,7 @@ class EchoAgent(BaseAgent):
else:
echo_text = f"{self.echo_prefix}[Non-text message received]"
response_message = ChatMessage(role=Role.ASSISTANT, contents=[Content.from_text(text=echo_text)])
response_message = ChatMessage("assistant", [Content.from_text(text=echo_text)])
# Notify the thread of new messages if provided
if thread is not None:
@@ -134,7 +132,7 @@ class EchoAgent(BaseAgent):
yield AgentResponseUpdate(
contents=[Content.from_text(text=chunk_text)],
role=Role.ASSISTANT,
role="assistant",
)
# Small delay to simulate streaming
@@ -142,7 +140,7 @@ class EchoAgent(BaseAgent):
# Notify the thread of the complete response if provided
if thread is not None:
complete_response = ChatMessage(role=Role.ASSISTANT, contents=[Content.from_text(text=response_text)])
complete_response = ChatMessage("assistant", [Content.from_text(text=response_text)])
await self._notify_thread_of_new_messages(thread, normalized_messages, complete_response)
@@ -12,10 +12,8 @@ from agent_framework import (
ChatResponse,
ChatResponseUpdate,
Content,
Role,
use_chat_middleware,
use_function_invocation,
tool,
)
from agent_framework._clients import TOptions_co
@@ -68,7 +66,7 @@ class EchoingChatClient(BaseChatClient[TOptions_co], Generic[TOptions_co]):
# Echo the last user message
last_user_message = None
for message in reversed(messages):
if message.role == Role.USER:
if message.role == "user":
last_user_message = message
break
@@ -77,7 +75,7 @@ class EchoingChatClient(BaseChatClient[TOptions_co], Generic[TOptions_co]):
else:
response_text = f"{self.prefix} [No text message found]"
response_message = ChatMessage(role=Role.ASSISTANT, contents=[Content.from_text(text=response_text)])
response_message = ChatMessage("assistant", [Content.from_text(text=response_text)])
return ChatResponse(
messages=[response_message],
@@ -104,7 +102,7 @@ class EchoingChatClient(BaseChatClient[TOptions_co], Generic[TOptions_co]):
for char in response_text:
yield ChatResponseUpdate(
contents=[Content.from_text(text=char)],
role=Role.ASSISTANT,
role="assistant",
response_id=f"echo-stream-resp-{random.randint(1000, 9999)}",
model_id="echo-model-v1",
)
@@ -3,8 +3,8 @@
import asyncio
from datetime import datetime
from agent_framework.ollama import OllamaChatClient
from agent_framework import tool
from agent_framework.ollama import OllamaChatClient
"""
Ollama Agent Basic Example
@@ -18,6 +18,7 @@ https://ollama.com/
"""
# NOTE: approval_mode="never_require" is for sample brevity. Use "always_require" in production; see samples/getting_started/tools/function_tool_with_approval.py and samples/getting_started/tools/function_tool_with_approval_and_threads.py.
@tool(approval_mode="never_require")
def get_time(location: str) -> str:
@@ -3,8 +3,8 @@
import asyncio
from datetime import datetime
from agent_framework.ollama import OllamaChatClient
from agent_framework import tool
from agent_framework.ollama import OllamaChatClient
"""
Ollama Chat Client Example
@@ -18,6 +18,7 @@ https://ollama.com/
"""
# NOTE: approval_mode="never_require" is for sample brevity. Use "always_require" in production; see samples/getting_started/tools/function_tool_with_approval.py and samples/getting_started/tools/function_tool_with_approval_and_threads.py.
@tool(approval_mode="never_require")
def get_time():
@@ -2,7 +2,7 @@
import asyncio
from agent_framework import ChatMessage, Content, Role
from agent_framework import ChatMessage, Content
from agent_framework.ollama import OllamaChatClient
"""
@@ -33,7 +33,7 @@ async def test_image() -> None:
image_uri = create_sample_image()
message = ChatMessage(
role=Role.USER,
role="user",
contents=[
Content.from_text(text="What's in this image?"),
Content.from_uri(uri=image_uri, media_type="image/png"),
@@ -5,8 +5,8 @@ import os
from random import randint
from typing import Annotated
from agent_framework.openai import OpenAIChatClient
from agent_framework import tool
from agent_framework.openai import OpenAIChatClient
"""
Ollama with OpenAI Chat Client Example
@@ -20,6 +20,7 @@ Environment Variables:
- OLLAMA_MODEL: The model name to use (e.g., "mistral", "llama3.2", "phi3")
"""
# NOTE: approval_mode="never_require" is for sample brevity. Use "always_require" in production; see samples/getting_started/tools/function_tool_with_approval.py and samples/getting_started/tools/function_tool_with_approval_and_threads.py.
@tool(approval_mode="never_require")
def get_weather(
@@ -5,10 +5,10 @@ import os
from random import randint
from typing import Annotated
from agent_framework import tool
from agent_framework.openai import OpenAIAssistantProvider
from openai import AsyncOpenAI
from pydantic import Field
from agent_framework import tool
"""
OpenAI Assistants Basic Example
@@ -17,6 +17,7 @@ This sample demonstrates basic usage of OpenAIAssistantProvider with automatic
assistant lifecycle management, showing both streaming and non-streaming responses.
"""
# NOTE: approval_mode="never_require" is for sample brevity. Use "always_require" in production; see samples/getting_started/tools/function_tool_with_approval.py and samples/getting_started/tools/function_tool_with_approval_and_threads.py.
@tool(approval_mode="never_require")
def get_weather(
@@ -5,10 +5,10 @@ import os
from random import randint
from typing import Annotated
from agent_framework import tool
from agent_framework.openai import OpenAIAssistantProvider
from openai import AsyncOpenAI
from pydantic import Field
from agent_framework import tool
"""
OpenAI Assistant Provider Methods Example
@@ -19,6 +19,7 @@ This sample demonstrates the methods available on the OpenAIAssistantProvider cl
- as_agent(): Wrap an SDK Assistant object without making HTTP calls
"""
# NOTE: approval_mode="never_require" is for sample brevity. Use "always_require" in production; see samples/getting_started/tools/function_tool_with_approval.py and samples/getting_started/tools/function_tool_with_approval_and_threads.py.
@tool(approval_mode="never_require")
def get_weather(
@@ -5,10 +5,10 @@ import os
from random import randint
from typing import Annotated
from agent_framework import tool
from agent_framework.openai import OpenAIAssistantProvider
from openai import AsyncOpenAI
from pydantic import Field
from agent_framework import tool
"""
OpenAI Assistants with Existing Assistant Example
@@ -17,6 +17,7 @@ This sample demonstrates working with pre-existing OpenAI Assistants
using the provider's get_agent() and as_agent() methods.
"""
# NOTE: approval_mode="never_require" is for sample brevity. Use "always_require" in production; see samples/getting_started/tools/function_tool_with_approval.py and samples/getting_started/tools/function_tool_with_approval_and_threads.py.
@tool(approval_mode="never_require")
def get_weather(
@@ -5,10 +5,10 @@ import os
from random import randint
from typing import Annotated
from agent_framework import tool
from agent_framework.openai import OpenAIAssistantProvider
from openai import AsyncOpenAI
from pydantic import Field
from agent_framework import tool
"""
OpenAI Assistants with Explicit Settings Example
@@ -17,6 +17,7 @@ This sample demonstrates creating OpenAI Assistants with explicit configuration
settings rather than relying on environment variable defaults.
"""
# NOTE: approval_mode="never_require" is for sample brevity. Use "always_require" in production; see samples/getting_started/tools/function_tool_with_approval.py and samples/getting_started/tools/function_tool_with_approval_and_threads.py.
@tool(approval_mode="never_require")
def get_weather(
@@ -6,10 +6,10 @@ from datetime import datetime, timezone
from random import randint
from typing import Annotated
from agent_framework import tool
from agent_framework.openai import OpenAIAssistantProvider
from openai import AsyncOpenAI
from pydantic import Field
from agent_framework import tool
"""
OpenAI Assistants with Function Tools Example
@@ -18,6 +18,7 @@ This sample demonstrates function tool integration with OpenAI Assistants,
showing both agent-level and query-level tool configuration patterns.
"""
# NOTE: approval_mode="never_require" is for sample brevity. Use "always_require" in production; see samples/getting_started/tools/function_tool_with_approval.py and samples/getting_started/tools/function_tool_with_approval_and_threads.py.
@tool(approval_mode="never_require")
def get_weather(
@@ -27,6 +28,7 @@ def get_weather(
conditions = ["sunny", "cloudy", "rainy", "stormy"]
return f"The weather in {location} is {conditions[randint(0, 3)]} with a high of {randint(10, 30)}C."
@tool(approval_mode="never_require")
def get_time() -> str:
"""Get the current UTC time."""
@@ -59,13 +59,14 @@ async def main() -> None:
result1 = await agent.run(query1)
if weather := result1.try_parse_value(WeatherInfo):
try:
weather = result1.value
print("Agent:")
print(f" Location: {weather.location}")
print(f" Temperature: {weather.temperature}")
print(f" Conditions: {weather.conditions}")
print(f" Recommendation: {weather.recommendation}")
else:
except Exception:
print(f"Failed to parse response: {result1.text}")
# Request 2: Override response_format at runtime with CityInfo
@@ -75,12 +76,13 @@ async def main() -> None:
result2 = await agent.run(query2, options={"response_format": CityInfo})
if city := result2.try_parse_value(CityInfo):
try:
city = result2.value
print("Agent:")
print(f" City: {city.city_name}")
print(f" Population: {city.population}")
print(f" Country: {city.country}")
else:
except Exception:
print(f"Failed to parse response: {result2.text}")
finally:
await client.beta.assistants.delete(agent.id)
@@ -5,8 +5,7 @@ import os
from random import randint
from typing import Annotated
from agent_framework import AgentThread
from agent_framework import tool
from agent_framework import AgentThread, tool
from agent_framework.openai import OpenAIAssistantProvider
from openai import AsyncOpenAI
from pydantic import Field
@@ -18,6 +17,7 @@ This sample demonstrates thread management with OpenAI Assistants, showing
persistent conversation threads and context preservation across interactions.
"""
# NOTE: approval_mode="never_require" is for sample brevity. Use "always_require" in production; see samples/getting_started/tools/function_tool_with_approval.py and samples/getting_started/tools/function_tool_with_approval_and_threads.py.
@tool(approval_mode="never_require")
def get_weather(
@@ -4,8 +4,8 @@ import asyncio
from random import randint
from typing import Annotated
from agent_framework.openai import OpenAIChatClient
from agent_framework import tool
from agent_framework.openai import OpenAIChatClient
"""
OpenAI Chat Client Basic Example
@@ -14,6 +14,7 @@ This sample demonstrates basic usage of OpenAIChatClient for direct chat-based
interactions, showing both streaming and non-streaming responses.
"""
# NOTE: approval_mode="never_require" is for sample brevity. Use "always_require" in production; see samples/getting_started/tools/function_tool_with_approval.py and samples/getting_started/tools/function_tool_with_approval_and_threads.py.
@tool(approval_mode="never_require")
def get_weather(
@@ -5,9 +5,9 @@ import os
from random import randint
from typing import Annotated
from agent_framework import tool
from agent_framework.openai import OpenAIChatClient
from pydantic import Field
from agent_framework import tool
"""
OpenAI Chat Client with Explicit Settings Example
@@ -16,6 +16,7 @@ This sample demonstrates creating OpenAI Chat Client with explicit configuration
settings rather than relying on environment variable defaults.
"""
# NOTE: approval_mode="never_require" is for sample brevity. Use "always_require" in production; see samples/getting_started/tools/function_tool_with_approval.py and samples/getting_started/tools/function_tool_with_approval_and_threads.py.
@tool(approval_mode="never_require")
def get_weather(
@@ -5,8 +5,7 @@ from datetime import datetime, timezone
from random import randint
from typing import Annotated
from agent_framework import ChatAgent
from agent_framework import tool
from agent_framework import ChatAgent, tool
from agent_framework.openai import OpenAIChatClient
from pydantic import Field
@@ -17,6 +16,7 @@ This sample demonstrates function tool integration with OpenAI Chat Client,
showing both agent-level and query-level tool configuration patterns.
"""
# NOTE: approval_mode="never_require" is for sample brevity. Use "always_require" in production; see samples/getting_started/tools/function_tool_with_approval.py and samples/getting_started/tools/function_tool_with_approval_and_threads.py.
@tool(approval_mode="never_require")
def get_weather(
@@ -26,6 +26,7 @@ def get_weather(
conditions = ["sunny", "cloudy", "rainy", "stormy"]
return f"The weather in {location} is {conditions[randint(0, 3)]} with a high of {randint(10, 30)}°C."
@tool(approval_mode="never_require")
def get_time() -> str:
"""Get the current UTC time."""
@@ -4,8 +4,7 @@ import asyncio
from random import randint
from typing import Annotated
from agent_framework import AgentThread, ChatAgent, ChatMessageStore
from agent_framework import tool
from agent_framework import AgentThread, ChatAgent, ChatMessageStore, tool
from agent_framework.openai import OpenAIChatClient
from pydantic import Field
@@ -16,6 +15,7 @@ This sample demonstrates thread management with OpenAI Chat Client, showing
conversation threads and message history preservation across interactions.
"""
# NOTE: approval_mode="never_require" is for sample brevity. Use "always_require" in production; see samples/getting_started/tools/function_tool_with_approval.py and samples/getting_started/tools/function_tool_with_approval_and_threads.py.
@tool(approval_mode="never_require")
def get_weather(
@@ -4,8 +4,7 @@ import asyncio
from random import randint
from typing import Annotated
from agent_framework import ChatAgent
from agent_framework import tool
from agent_framework import ChatAgent, tool
from agent_framework.openai import OpenAIResponsesClient
from pydantic import Field
@@ -16,6 +15,7 @@ This sample demonstrates basic usage of OpenAIResponsesClient for structured
response generation, showing both streaming and non-streaming responses.
"""
# NOTE: approval_mode="never_require" is for sample brevity. Use "always_require" in production; see samples/getting_started/tools/function_tool_with_approval.py and samples/getting_started/tools/function_tool_with_approval_and_threads.py.
@tool(approval_mode="never_require")
def get_weather(
@@ -8,7 +8,6 @@ from agent_framework import (
CodeInterpreterToolResultContent,
Content,
HostedCodeInterpreterTool,
tool,
)
from agent_framework.openai import OpenAIResponsesClient
@@ -5,9 +5,9 @@ import os
from random import randint
from typing import Annotated
from agent_framework import tool
from agent_framework.openai import OpenAIResponsesClient
from pydantic import Field
from agent_framework import tool
"""
OpenAI Responses Client with Explicit Settings Example
@@ -16,6 +16,7 @@ This sample demonstrates creating OpenAI Responses Client with explicit configur
settings rather than relying on environment variable defaults.
"""
# NOTE: approval_mode="never_require" is for sample brevity. Use "always_require" in production; see samples/getting_started/tools/function_tool_with_approval.py and samples/getting_started/tools/function_tool_with_approval_and_threads.py.
@tool(approval_mode="never_require")
def get_weather(
@@ -5,8 +5,7 @@ from datetime import datetime, timezone
from random import randint
from typing import Annotated
from agent_framework import ChatAgent
from agent_framework import tool
from agent_framework import ChatAgent, tool
from agent_framework.openai import OpenAIResponsesClient
from pydantic import Field
@@ -17,6 +16,7 @@ This sample demonstrates function tool integration with OpenAI Responses Client,
showing both agent-level and query-level tool configuration patterns.
"""
# NOTE: approval_mode="never_require" is for sample brevity. Use "always_require" in production; see samples/getting_started/tools/function_tool_with_approval.py and samples/getting_started/tools/function_tool_with_approval_and_threads.py.
@tool(approval_mode="never_require")
def get_weather(
@@ -26,6 +26,7 @@ def get_weather(
conditions = ["sunny", "cloudy", "rainy", "stormy"]
return f"The weather in {location} is {conditions[randint(0, 3)]} with a high of {randint(10, 30)}°C."
@tool(approval_mode="never_require")
def get_time() -> str:
"""Get the current UTC time."""
@@ -29,10 +29,10 @@ async def handle_approvals_without_thread(query: str, agent: "AgentProtocol"):
f"User Input Request for function from {agent.name}: {user_input_needed.function_call.name}"
f" with arguments: {user_input_needed.function_call.arguments}"
)
new_inputs.append(ChatMessage(role="assistant", contents=[user_input_needed]))
new_inputs.append(ChatMessage("assistant", [user_input_needed]))
user_approval = input("Approve function call? (y/n): ")
new_inputs.append(
ChatMessage(role="user", contents=[user_input_needed.to_function_approval_response(user_approval.lower() == "y")])
ChatMessage("user", [user_input_needed.to_function_approval_response(user_approval.lower() == "y")])
)
result = await agent.run(new_inputs)
@@ -70,7 +70,7 @@ async def handle_approvals_with_thread_streaming(query: str, agent: "AgentProtoc
new_input_added = True
while new_input_added:
new_input_added = False
new_input.append(ChatMessage(role="user", text=query))
new_input.append(ChatMessage("user", [query]))
async for update in agent.run_stream(new_input, thread=thread, store=True):
if update.user_input_requests:
for user_input_needed in update.user_input_requests:
@@ -4,8 +4,7 @@ import asyncio
from random import randint
from typing import Annotated
from agent_framework import AgentThread, ChatAgent
from agent_framework import tool
from agent_framework import AgentThread, ChatAgent, tool
from agent_framework.openai import OpenAIResponsesClient
from pydantic import Field
@@ -16,6 +15,7 @@ This sample demonstrates thread management with OpenAI Responses Client, showing
persistent conversation context and simplified response handling.
"""
# NOTE: approval_mode="never_require" is for sample brevity. Use "always_require" in production; see samples/getting_started/tools/function_tool_with_approval.py and samples/getting_started/tools/function_tool_with_approval_and_threads.py.
@tool(approval_mode="never_require")
def get_weather(
@@ -11,12 +11,13 @@ Prerequisites: set `AZURE_OPENAI_ENDPOINT` and `AZURE_OPENAI_CHAT_DEPLOYMENT_NAM
import logging
from typing import Any
from agent_framework import tool
from agent_framework.azure import AgentFunctionApp, AzureOpenAIChatClient
from azure.identity import AzureCliCredential
from agent_framework import tool
logger = logging.getLogger(__name__)
# NOTE: approval_mode="never_require" is for sample brevity. Use "always_require" in production; see samples/getting_started/tools/function_tool_with_approval.py and samples/getting_started/tools/function_tool_with_approval_and_threads.py.
@tool(approval_mode="never_require")
def get_weather(location: str) -> dict[str, Any]:
@@ -32,6 +33,7 @@ def get_weather(location: str) -> dict[str, Any]:
logger.info(f"✓ [TOOL RESULT] {result}")
return result
@tool(approval_mode="never_require")
def calculate_tip(bill_amount: float, tip_percentage: float = 15.0) -> dict[str, Any]:
"""Calculate tip amount and total bill."""
@@ -8,9 +8,9 @@ as a message broker. It enables clients to disconnect and reconnect without losi
import asyncio
import time
from collections.abc import AsyncIterator
from dataclasses import dataclass
from datetime import timedelta
from collections.abc import AsyncIterator
import redis.asyncio as aioredis
@@ -153,13 +153,12 @@ def _get_weather_recommendation(condition: str) -> str:
if "rain" in condition_lower or "drizzle" in condition_lower:
return "Bring an umbrella and waterproof jacket. Consider indoor activities for backup."
elif "fog" in condition_lower:
if "fog" in condition_lower:
return "Morning visibility may be limited. Plan outdoor sightseeing for afternoon."
elif "cold" in condition_lower:
if "cold" in condition_lower:
return "Layer up with warm clothing. Hot drinks and cozy cafés recommended."
elif "hot" in condition_lower or "warm" in condition_lower:
if "hot" in condition_lower or "warm" in condition_lower:
return "Stay hydrated and use sunscreen. Plan strenuous activities for cooler morning hours."
elif "thunder" in condition_lower or "storm" in condition_lower:
if "thunder" in condition_lower or "storm" in condition_lower:
return "Keep an eye on weather updates. Have indoor alternatives ready."
else:
return "Pleasant conditions expected. Great day for outdoor exploration!"
return "Pleasant conditions expected. Great day for outdoor exploration!"
@@ -102,9 +102,10 @@ def spam_detection_orchestration(context: DurableOrchestrationContext) -> Genera
options={"response_format": SpamDetectionResult},
)
spam_result = spam_result_raw.try_parse_value(SpamDetectionResult)
if spam_result is None:
raise ValueError("Failed to parse spam detection result")
try:
spam_result = spam_result_raw.value
except Exception as ex:
raise ValueError("Failed to parse spam detection result") from ex
if spam_result.is_spam:
result = yield context.call_activity("handle_spam_email", spam_result.reason) # type: ignore[misc]
@@ -125,9 +126,10 @@ def spam_detection_orchestration(context: DurableOrchestrationContext) -> Genera
options={"response_format": EmailResponse},
)
email_result = email_result_raw.try_parse_value(EmailResponse)
if email_result is None:
raise ValueError("Failed to parse email response")
try:
email_result = email_result_raw.value
except Exception as ex:
raise ValueError("Failed to parse email response") from ex
result = yield context.call_activity("send_email", email_result.response) # type: ignore[misc]
return result
@@ -137,11 +137,11 @@ def content_generation_hitl_orchestration(context: DurableOrchestrationContext)
context.set_custom_status(
"Content rejected by human reviewer. Incorporating feedback and regenerating..."
)
# Check if we've exhausted attempts
if attempt >= payload.max_review_attempts:
break
rewrite_prompt = (
"The content was rejected by a human reviewer. Please rewrite the article incorporating their feedback.\n\n"
f"Human Feedback: {approval_payload.feedback or 'No feedback provided.'}"
@@ -152,9 +152,10 @@ def content_generation_hitl_orchestration(context: DurableOrchestrationContext)
options={"response_format": GeneratedContent},
)
content = rewritten_raw.try_parse_value(GeneratedContent)
if content is None:
raise ValueError("Agent returned no content after rewrite.")
try:
content = rewritten_raw.value
except Exception as ex:
raise ValueError("Agent returned no content after rewrite.") from ex
else:
context.set_custom_status(
f"Human approval timed out after {payload.approval_timeout_hours} hour(s). Treating as rejection."
@@ -162,7 +163,7 @@ def content_generation_hitl_orchestration(context: DurableOrchestrationContext)
raise TimeoutError(
f"Human approval timed out after {payload.approval_timeout_hours} hour(s)."
)
# If we exit the loop without returning, max attempts were exhausted
context.set_custom_status("Max review attempts exhausted.")
raise RuntimeError(
@@ -4,10 +4,10 @@ import asyncio
from random import randint
from typing import Annotated
from agent_framework import tool
from agent_framework.azure import AzureAIAgentClient
from azure.identity.aio import AzureCliCredential
from pydantic import Field
from agent_framework import tool
"""
Azure AI Chat Client Direct Usage Example
@@ -16,6 +16,7 @@ Demonstrates direct AzureAIChatClient usage for chat interactions with Azure AI
Shows function calling capabilities with custom business logic.
"""
# NOTE: approval_mode="never_require" is for sample brevity. Use "always_require" in production; see samples/getting_started/tools/function_tool_with_approval.py and samples/getting_started/tools/function_tool_with_approval_and_threads.py.
@tool(approval_mode="never_require")
def get_weather(
@@ -4,10 +4,10 @@ import asyncio
from random import randint
from typing import Annotated
from agent_framework import tool
from agent_framework.azure import AzureOpenAIAssistantsClient
from azure.identity import AzureCliCredential
from pydantic import Field
from agent_framework import tool
"""
Azure Assistants Client Direct Usage Example
@@ -16,6 +16,7 @@ Demonstrates direct AzureAssistantsClient usage for chat interactions with Azure
Shows function calling capabilities and automatic assistant creation.
"""
# NOTE: approval_mode="never_require" is for sample brevity. Use "always_require" in production; see samples/getting_started/tools/function_tool_with_approval.py and samples/getting_started/tools/function_tool_with_approval_and_threads.py.
@tool(approval_mode="never_require")
def get_weather(
@@ -4,10 +4,10 @@ import asyncio
from random import randint
from typing import Annotated
from agent_framework import tool
from agent_framework.azure import AzureOpenAIChatClient
from azure.identity import AzureCliCredential
from pydantic import Field
from agent_framework import tool
"""
Azure Chat Client Direct Usage Example
@@ -16,6 +16,7 @@ Demonstrates direct AzureChatClient usage for chat interactions with Azure OpenA
Shows function calling capabilities with custom business logic.
"""
# NOTE: approval_mode="never_require" is for sample brevity. Use "always_require" in production; see samples/getting_started/tools/function_tool_with_approval.py and samples/getting_started/tools/function_tool_with_approval_and_threads.py.
@tool(approval_mode="never_require")
def get_weather(
@@ -4,8 +4,7 @@ import asyncio
from random import randint
from typing import Annotated
from agent_framework import ChatResponse
from agent_framework import tool
from agent_framework import ChatResponse, tool
from agent_framework.azure import AzureOpenAIResponsesClient
from azure.identity import AzureCliCredential
from pydantic import BaseModel, Field
@@ -17,6 +16,7 @@ Demonstrates direct AzureResponsesClient usage for structured response generatio
Shows function calling capabilities with custom business logic.
"""
# NOTE: approval_mode="never_require" is for sample brevity. Use "always_require" in production; see samples/getting_started/tools/function_tool_with_approval.py and samples/getting_started/tools/function_tool_with_approval_and_threads.py.
@tool(approval_mode="never_require")
def get_weather(
@@ -42,19 +42,21 @@ async def main() -> None:
stream = True
print(f"User: {message}")
if stream:
response = await ChatResponse.from_chat_response_generator(
response = await ChatResponse.from_update_generator(
client.get_streaming_response(message, tools=get_weather, options={"response_format": OutputStruct}),
output_format_type=OutputStruct,
)
if result := response.try_parse_value(OutputStruct):
try:
result = response.value
print(f"Assistant: {result}")
else:
except Exception:
print(f"Assistant: {response.text}")
else:
response = await client.get_response(message, tools=get_weather, options={"response_format": OutputStruct})
if result := response.try_parse_value(OutputStruct):
try:
result = response.value
print(f"Assistant: {result}")
else:
except Exception:
print(f"Assistant: {response.text}")
@@ -4,9 +4,9 @@ import asyncio
from random import randint
from typing import Annotated
from agent_framework import tool
from agent_framework.openai import OpenAIAssistantsClient
from pydantic import Field
from agent_framework import tool
"""
OpenAI Assistants Client Direct Usage Example
@@ -16,6 +16,7 @@ Shows function calling capabilities and automatic assistant creation.
"""
# NOTE: approval_mode="never_require" is for sample brevity. Use "always_require" in production; see samples/getting_started/tools/function_tool_with_approval.py and samples/getting_started/tools/function_tool_with_approval_and_threads.py.
@tool(approval_mode="never_require")
def get_weather(
@@ -4,9 +4,9 @@ import asyncio
from random import randint
from typing import Annotated
from agent_framework import tool
from agent_framework.openai import OpenAIChatClient
from pydantic import Field
from agent_framework import tool
"""
OpenAI Chat Client Direct Usage Example
@@ -16,6 +16,7 @@ Shows function calling capabilities with custom business logic.
"""
# NOTE: approval_mode="never_require" is for sample brevity. Use "always_require" in production; see samples/getting_started/tools/function_tool_with_approval.py and samples/getting_started/tools/function_tool_with_approval_and_threads.py.
@tool(approval_mode="never_require")
def get_weather(
@@ -4,9 +4,9 @@ import asyncio
from random import randint
from typing import Annotated
from agent_framework import tool
from agent_framework.openai import OpenAIResponsesClient
from pydantic import Field
from agent_framework import tool
"""
OpenAI Responses Client Direct Usage Example
@@ -16,6 +16,7 @@ Shows function calling capabilities with custom business logic.
"""
# NOTE: approval_mode="never_require" is for sample brevity. Use "always_require" in production; see samples/getting_started/tools/function_tool_with_approval.py and samples/getting_started/tools/function_tool_with_approval_and_threads.py.
@tool(approval_mode="never_require")
def get_weather(
@@ -3,10 +3,11 @@
import asyncio
import uuid
from agent_framework import tool
from agent_framework.azure import AzureAIAgentClient
from agent_framework.mem0 import Mem0Provider
from azure.identity.aio import AzureCliCredential
from agent_framework import tool
# NOTE: approval_mode="never_require" is for sample brevity. Use "always_require" in production; see samples/getting_started/tools/function_tool_with_approval.py and samples/getting_started/tools/function_tool_with_approval_and_threads.py.
@tool(approval_mode="never_require")
@@ -3,11 +3,12 @@
import asyncio
import uuid
from agent_framework import tool
from agent_framework.azure import AzureAIAgentClient
from agent_framework.mem0 import Mem0Provider
from azure.identity.aio import AzureCliCredential
from mem0 import AsyncMemory
from agent_framework import tool
# NOTE: approval_mode="never_require" is for sample brevity. Use "always_require" in production; see samples/getting_started/tools/function_tool_with_approval.py and samples/getting_started/tools/function_tool_with_approval_and_threads.py.
@tool(approval_mode="never_require")
@@ -3,10 +3,11 @@
import asyncio
import uuid
from agent_framework import tool
from agent_framework.azure import AzureAIAgentClient
from agent_framework.mem0 import Mem0Provider
from azure.identity.aio import AzureCliCredential
from agent_framework import tool
# NOTE: approval_mode="never_require" is for sample brevity. Use "always_require" in production; see samples/getting_started/tools/function_tool_with_approval.py and samples/getting_started/tools/function_tool_with_approval_and_threads.py.
@tool(approval_mode="never_require")
@@ -30,13 +30,13 @@ Run:
import asyncio
import os
from agent_framework import ChatMessage, Role
from agent_framework import tool
from agent_framework import ChatMessage, tool
from agent_framework.openai import OpenAIChatClient
from agent_framework_redis._provider import RedisProvider
from redisvl.extensions.cache.embeddings import EmbeddingsCache
from redisvl.utils.vectorize import OpenAITextVectorizer
# NOTE: approval_mode="never_require" is for sample brevity. Use "always_require" in production; see samples/getting_started/tools/function_tool_with_approval.py and samples/getting_started/tools/function_tool_with_approval_and_threads.py.
@tool(approval_mode="never_require")
def search_flights(origin_airport_code: str, destination_airport_code: str, detailed: bool = False) -> str:
@@ -128,9 +128,9 @@ async def main() -> None:
# Build sample chat messages to persist to Redis
messages = [
ChatMessage(role=Role.USER, text="runA CONVO: User Message"),
ChatMessage(role=Role.ASSISTANT, text="runA CONVO: Assistant Message"),
ChatMessage(role=Role.SYSTEM, text="runA CONVO: System Message"),
ChatMessage("user", ["runA CONVO: User Message"]),
ChatMessage("assistant", ["runA CONVO: Assistant Message"]),
ChatMessage("system", ["runA CONVO: System Message"]),
]
# Declare/start a conversation/thread and write messages under 'runA'.
@@ -142,7 +142,7 @@ async def main() -> None:
# Retrieve relevant memories for a hypothetical model call. The provider uses
# the current request messages as the retrieval query and returns context to
# be injected into the model's instructions.
ctx = await provider.invoking([ChatMessage(role=Role.SYSTEM, text="B: Assistant Message")])
ctx = await provider.invoking([ChatMessage("system", ["B: Assistant Message"])])
# Inspect retrieved memories that would be injected into instructions
# (Debug-only output so you can verify retrieval works as expected.)
@@ -39,7 +39,7 @@ class UserInfoMemory(ContextProvider):
) -> None:
"""Extract user information from messages after each agent call."""
# Check if we need to extract user info from user messages
user_messages = [msg for msg in request_messages if hasattr(msg, "role") and msg.role.value == "user"] # type: ignore
user_messages = [msg for msg in request_messages if hasattr(msg, "role") and msg.role == "user"] # type: ignore
if (self.user_info.name is None or self.user_info.age is None) and user_messages:
try:
@@ -52,11 +52,14 @@ class UserInfoMemory(ContextProvider):
)
# Update user info with extracted data
if extracted := result.try_parse_value(UserInfo):
try:
extracted = result.value
if self.user_info.name is None and extracted.name:
self.user_info.name = extracted.name
if self.user_info.age is None and extracted.age:
self.user_info.age = extracted.age
except Exception:
pass # Failed to extract, continue without updating
except Exception:
pass # Failed to extract, continue without updating
@@ -20,10 +20,11 @@ async def main():
agent = AgentFactory(client_kwargs={"credential": AzureCliCredential()}).create_agent_from_yaml(yaml_str)
# use the agent
response = await agent.run("Why is the sky blue, answer in Dutch?")
# Use try_parse_value() for safe parsing - returns None if no response_format or parsing fails
if parsed := response.try_parse_value():
# Use response.value with try/except for safe parsing
try:
parsed = response.value
print("Agent response:", parsed.model_dump_json(indent=2))
else:
except Exception:
print("Agent response:", response.text)
@@ -19,10 +19,11 @@ async def main():
agent = AgentFactory().create_agent_from_yaml(yaml_str)
# use the agent
response = await agent.run("Why is the sky blue, answer in Dutch?")
# Use try_parse_value() for safe parsing - returns None if no response_format or parsing fails
if parsed := response.try_parse_value():
# Use response.value with try/except for safe parsing
try:
parsed = response.value
print("Agent response:", parsed)
else:
except Exception:
print("Agent response:", response.text)
@@ -25,7 +25,6 @@ from agent_framework import (
WorkflowBuilder,
WorkflowContext,
handler,
tool,
)
from pydantic import BaseModel, Field
from typing_extensions import Never
@@ -8,12 +8,12 @@ Make sure to run 'az login' before starting devui.
import os
from typing import Annotated
from agent_framework import ChatAgent
from agent_framework import tool
from agent_framework import ChatAgent, tool
from agent_framework.azure import AzureAIAgentClient
from azure.identity.aio import AzureCliCredential
from pydantic import Field
# NOTE: approval_mode="never_require" is for sample brevity. Use "always_require" in production; see samples/getting_started/tools/function_tool_with_approval.py and samples/getting_started/tools/function_tool_with_approval_and_threads.py.
@tool(approval_mode="never_require")
def get_weather(
@@ -24,6 +24,7 @@ def get_weather(
temperature = 22
return f"The weather in {location} is {conditions[0]} with a high of {temperature}°C."
@tool(approval_mode="never_require")
def get_forecast(
location: Annotated[str, Field(description="The location to get the forecast for.")],
@@ -10,8 +10,7 @@ import logging
import os
from typing import Annotated
from agent_framework import ChatAgent, Executor, WorkflowBuilder, WorkflowContext, handler
from agent_framework import tool
from agent_framework import ChatAgent, Executor, WorkflowBuilder, WorkflowContext, handler, tool
from agent_framework.azure import AzureOpenAIChatClient
from agent_framework.devui import serve
from typing_extensions import Never
@@ -29,6 +28,7 @@ def get_weather(
temperature = 53
return f"The weather in {location} is {conditions[0]} with a high of {temperature}°C."
@tool(approval_mode="never_require")
def get_time(
timezone: Annotated[str, "The timezone to get time for."] = "UTC",
@@ -27,7 +27,6 @@ from agent_framework import (
WorkflowContext,
handler,
response_handler,
tool,
)
from pydantic import BaseModel, Field
from typing_extensions import Never
@@ -14,10 +14,9 @@ from agent_framework import (
ChatResponseUpdate,
Content,
FunctionInvocationContext,
Role,
tool,
chat_middleware,
function_middleware,
tool,
)
from agent_framework.azure import AzureOpenAIChatClient
from agent_framework_devui import register_cleanup
@@ -43,7 +42,7 @@ async def security_filter_middleware(
# Check only the last message (most recent user input)
last_message = context.messages[-1] if context.messages else None
if last_message and last_message.role == Role.USER and last_message.text:
if last_message and last_message.role == "user" and last_message.text:
message_lower = last_message.text.lower()
for term in blocked_terms:
if term in message_lower:
@@ -58,7 +57,7 @@ async def security_filter_middleware(
async def blocked_stream() -> AsyncIterable[ChatResponseUpdate]:
yield ChatResponseUpdate(
contents=[Content.from_text(text=error_message)],
role=Role.ASSISTANT,
role="assistant",
)
context.result = blocked_stream()
@@ -67,7 +66,7 @@ async def security_filter_middleware(
context.result = ChatResponse(
messages=[
ChatMessage(
role=Role.ASSISTANT,
role="assistant",
text=error_message,
)
]
@@ -40,12 +40,12 @@ def get_client(
"""
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 DefaultAzureCredential()
dts_client = DurableTaskSchedulerClient(
host_address=endpoint_url,
secure_channel=endpoint_url != "http://localhost:8080",
@@ -53,7 +53,7 @@ def get_client(
token_credential=credential,
log_handler=log_handler
)
return DurableAIAgentClient(dts_client)
@@ -66,12 +66,12 @@ def run_client(agent_client: DurableAIAgentClient) -> None:
# Get a reference to the Joker agent
logger.debug("Getting reference to Joker agent...")
joker = agent_client.get_agent("Joker")
# Create a new thread for the conversation
thread = joker.get_new_thread()
logger.debug(f"Thread ID: {thread.session_id}")
logger.info("Start chatting with the Joker agent! (Type 'exit' to quit)")
# Interactive conversation loop
while True:
# Get user input
@@ -80,33 +80,33 @@ def run_client(agent_client: DurableAIAgentClient) -> None:
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, thread=thread)
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:
@@ -15,40 +15,40 @@ To run this sample:
import logging
from dotenv import load_dotenv
# Import helper functions from worker and client modules
from client import get_client, run_client
from dotenv import load_dotenv
from worker import get_worker, setup_worker
# 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
with get_worker(log_handler=silent_handler) as 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...")
@@ -52,12 +52,12 @@ def get_worker(
"""
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 DefaultAzureCredential()
return DurableTaskSchedulerWorker(
host_address=endpoint_url,
secure_channel=endpoint_url != "http://localhost:8080",
@@ -78,42 +78,42 @@ def setup_worker(worker: DurableTaskSchedulerWorker) -> DurableAIAgentWorker:
"""
# 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")
@@ -41,12 +41,12 @@ def get_client(
"""
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 DefaultAzureCredential()
dts_client = DurableTaskSchedulerClient(
host_address=endpoint_url,
secure_channel=endpoint_url != "http://localhost:8080",
@@ -54,7 +54,7 @@ def get_client(
token_credential=credential,
log_handler=log_handler
)
return DurableAIAgentClient(dts_client)
@@ -65,45 +65,45 @@ def run_client(agent_client: DurableAIAgentClient) -> None:
agent_client: The DurableAIAgentClient instance
"""
logger.debug("Testing WeatherAgent")
# Get reference to WeatherAgent
weather_agent = agent_client.get_agent("WeatherAgent")
weather_thread = weather_agent.get_new_thread()
logger.debug(f"Created weather conversation thread: {weather_thread.session_id}")
# Test WeatherAgent
weather_message = "What is the weather in Seattle?"
logger.info(f"User: {weather_message}")
weather_response = weather_agent.run(weather_message, thread=weather_thread)
logger.info(f"WeatherAgent: {weather_response.text} \n")
logger.debug("Testing MathAgent")
# Get reference to MathAgent
math_agent = agent_client.get_agent("MathAgent")
math_thread = math_agent.get_new_thread()
logger.debug(f"Created math conversation thread: {math_thread.session_id}")
# Test MathAgent
math_message = "Calculate a 20% tip on a $50 bill"
logger.info(f"User: {math_message}")
math_response = math_agent.run(math_message, thread=math_thread)
logger.info(f"MathAgent: {math_response.text} \n")
logger.debug("Both agents completed successfully!")
async def main() -> None:
"""Main entry point for the client application."""
logger.debug("Starting Durable Task Multi-Agent Client...")
# Create client using helper function
agent_client = get_client()
try:
run_client(agent_client)
except Exception as e:
@@ -15,10 +15,9 @@ To run this sample:
import logging
from dotenv import load_dotenv
# Import helper functions from worker and client modules
from client import get_client, run_client
from dotenv import load_dotenv
from worker import get_worker, setup_worker
# Configure logging
@@ -29,26 +28,26 @@ logger = logging.getLogger(__name__)
def main():
"""Main entry point - runs both worker and client in single process."""
logger.debug("Starting Durable Task Multi-Agent Sample (Combined Worker + Client)...")
silent_handler = logging.NullHandler()
# Create and start the worker using helper function and context manager
with get_worker(log_handler=silent_handler) as 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...")
@@ -101,12 +101,12 @@ def get_worker(
"""
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 DefaultAzureCredential()
return DurableTaskSchedulerWorker(
host_address=endpoint_url,
secure_channel=endpoint_url != "http://localhost:8080",
@@ -127,43 +127,43 @@ def setup_worker(worker: DurableTaskSchedulerWorker) -> DurableAIAgentWorker:
"""
# Wrap it with the agent worker
agent_worker = DurableAIAgentWorker(worker)
# Create and register both agents
logger.debug("Creating and registering agents...")
weather_agent = create_weather_agent()
math_agent = create_math_agent()
agent_worker.add_agent(weather_agent)
agent_worker.add_agent(math_agent)
logger.debug(f"✓ Registered agents: {weather_agent.name}, {math_agent.name}")
return agent_worker
async def main():
"""Main entry point for the worker process."""
logger.debug("Starting Durable Task Multi-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. \n")
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.info("Worker stopped")
@@ -23,7 +23,6 @@ import redis.asyncio as aioredis
from agent_framework.azure import DurableAIAgentClient
from azure.identity import DefaultAzureCredential
from durabletask.azuremanaged.client import DurableTaskSchedulerClient
from redis_stream_response_handler import RedisStreamResponseHandler
# Configure logging
@@ -71,12 +70,12 @@ def get_client(
"""
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 DefaultAzureCredential()
dts_client = DurableTaskSchedulerClient(
host_address=endpoint_url,
secure_channel=endpoint_url != "http://localhost:8080",
@@ -84,7 +83,7 @@ def get_client(
token_credential=credential,
log_handler=log_handler
)
return DurableAIAgentClient(dts_client)
@@ -100,33 +99,33 @@ async def stream_from_redis(thread_id: str, cursor: str | None = None) -> None:
logger.debug(f"To manually check Redis, run: redis-cli XLEN {stream_key}")
if cursor:
logger.info(f"Resuming from cursor: {cursor}")
async with await get_stream_handler() as stream_handler:
logger.info(f"Stream handler created, starting to read...")
logger.info("Stream handler created, starting to read...")
try:
chunk_count = 0
async for chunk in stream_handler.read_stream(thread_id, cursor):
chunk_count += 1
logger.debug(f"Received chunk #{chunk_count}: error={chunk.error}, is_done={chunk.is_done}, text_len={len(chunk.text) if chunk.text else 0}")
if chunk.error:
logger.error(f"Stream error: {chunk.error}")
break
if chunk.is_done:
print("\n✓ Response complete!", flush=True)
logger.info(f"Stream completed after {chunk_count} chunks")
break
if chunk.text:
# Print directly to console with flush for immediate display
print(chunk.text, end='', flush=True)
print(chunk.text, end="", flush=True)
if chunk_count == 0:
logger.warning("No chunks received from Redis stream!")
logger.warning(f"Check Redis manually: redis-cli XLEN {stream_key}")
logger.warning(f"View stream contents: redis-cli XREAD STREAMS {stream_key} 0")
except Exception as ex:
logger.error(f"Error reading from Redis: {ex}", exc_info=True)
@@ -140,47 +139,47 @@ def run_client(agent_client: DurableAIAgentClient) -> None:
# Get a reference to the TravelPlanner agent
logger.debug("Getting reference to TravelPlanner agent...")
travel_planner = agent_client.get_agent("TravelPlanner")
# Create a new thread for the conversation
thread = travel_planner.get_new_thread()
if not thread.session_id:
logger.error("Failed to create a new thread with session ID!")
return
key = thread.session_id.key
logger.info(f"Thread ID: {key}")
# Get user input
print("\nEnter your travel planning request:")
user_message = input("> ").strip()
if not user_message:
logger.warning("No input provided. Using default message.")
user_message = "Plan a 3-day trip to Tokyo with emphasis on culture and food"
logger.info(f"\nYou: {user_message}\n")
logger.info("TravelPlanner (streaming from Redis):")
logger.info("-" * 80)
# Start the agent run with wait_for_response=False for non-blocking execution
# This signals the agent to start processing without waiting for completion
# The agent will execute in the background and write chunks to Redis
travel_planner.run(user_message, thread=thread, options={"wait_for_response": False})
# Stream the response from Redis
# This demonstrates that the client can stream from Redis while
# the agent is still processing (or after it completes)
asyncio.run(stream_from_redis(str(key)))
logger.info("\nDemo completed!")
if __name__ == "__main__":
from dotenv import load_dotenv
load_dotenv()
# Create the client
client = get_client()
# Run the demo
run_client(client)
@@ -8,9 +8,9 @@ as a message broker. It enables clients to disconnect and reconnect without losi
import asyncio
import time
from collections.abc import AsyncIterator
from dataclasses import dataclass
from datetime import timedelta
from collections.abc import AsyncIterator
import redis.asyncio as aioredis
@@ -20,40 +20,40 @@ To run this sample:
import logging
from dotenv import load_dotenv
# Import helper functions from worker and client modules
from client import get_client, run_client
from dotenv import load_dotenv
from worker import get_worker, setup_worker
# 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 with Redis Streaming...")
silent_handler = logging.NullHandler()
# Create and start the worker using helper function and context manager
with get_worker(log_handler=silent_handler) as dts_worker:
# Register agents and callbacks 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...")
@@ -153,13 +153,12 @@ def _get_weather_recommendation(condition: str) -> str:
if "rain" in condition_lower or "drizzle" in condition_lower:
return "Bring an umbrella and waterproof jacket. Consider indoor activities for backup."
elif "fog" in condition_lower:
if "fog" in condition_lower:
return "Morning visibility may be limited. Plan outdoor sightseeing for afternoon."
elif "cold" in condition_lower:
if "cold" in condition_lower:
return "Layer up with warm clothing. Hot drinks and cozy cafés recommended."
elif "hot" in condition_lower or "warm" in condition_lower:
if "hot" in condition_lower or "warm" in condition_lower:
return "Stay hydrated and use sunscreen. Plan strenuous activities for cooler morning hours."
elif "thunder" in condition_lower or "storm" in condition_lower:
if "thunder" in condition_lower or "storm" in condition_lower:
return "Keep an eye on weather updates. Have indoor alternatives ready."
else:
return "Pleasant conditions expected. Great day for outdoor exploration!"
return "Pleasant conditions expected. Great day for outdoor exploration!"
@@ -27,7 +27,6 @@ from agent_framework.azure import (
)
from azure.identity import AzureCliCredential, DefaultAzureCredential
from durabletask.azuremanaged.worker import DurableTaskSchedulerWorker
from redis_stream_response_handler import RedisStreamResponseHandler
from tools import get_local_events, get_weather_forecast
@@ -186,12 +185,12 @@ def get_worker(
"""
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 DefaultAzureCredential()
return DurableTaskSchedulerWorker(
host_address=endpoint_url,
secure_channel=endpoint_url != "http://localhost:8080",
@@ -212,34 +211,34 @@ def setup_worker(worker: DurableTaskSchedulerWorker) -> DurableAIAgentWorker:
"""
# Create the Redis streaming callback
redis_callback = RedisStreamCallback()
# Wrap it with the agent worker
agent_worker = DurableAIAgentWorker(worker, callback=redis_callback)
# Create and register the TravelPlanner agent
logger.debug("Creating and registering TravelPlanner agent...")
travel_agent = create_travel_agent()
agent_worker.add_agent(travel_agent)
logger.debug(f"✓ Registered agent: {travel_agent.name}")
return agent_worker
async def main():
"""Main entry point for the worker process."""
logger.debug("Starting Durable Task Agent Worker with Redis Streaming...")
# Create a worker using the helper function
worker = get_worker()
# Setup worker with agent and callback
setup_worker(worker)
# Start the worker
logger.debug("Worker started and listening for requests...")
worker.start()
try:
# Keep the worker running
while True:
@@ -41,12 +41,12 @@ def get_client(
"""
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 DefaultAzureCredential()
return DurableTaskSchedulerClient(
host_address=endpoint_url,
secure_channel=endpoint_url != "http://localhost:8080",
@@ -63,32 +63,32 @@ def run_client(client: DurableTaskSchedulerClient) -> None:
client: The DurableTaskSchedulerClient instance
"""
logger.debug("Starting single agent chaining orchestration...")
# Start the orchestration
instance_id = client.schedule_new_orchestration( # type: ignore
orchestrator="single_agent_chaining_orchestration",
input="",
)
logger.info(f"Orchestration started with instance ID: {instance_id}")
logger.debug("Waiting for orchestration to complete...")
# Retrieve the final state
metadata = client.wait_for_orchestration_completion(
instance_id=instance_id,
timeout=300
)
if metadata and metadata.runtime_status.name == "COMPLETED":
result = metadata.serialized_output
logger.debug("Orchestration completed successfully!")
# Parse and display the result
if result:
final_text = json.loads(result)
logger.info("Final refined sentence: %s \n", final_text)
elif metadata:
logger.error(f"Orchestration ended with status: {metadata.runtime_status.name}")
if metadata.serialized_output:
@@ -100,10 +100,10 @@ def run_client(client: DurableTaskSchedulerClient) -> None:
async def main() -> None:
"""Main entry point for the client application."""
logger.debug("Starting Durable Task Single Agent Chaining Orchestration Client...")
# Create client using helper function
client = get_client()
try:
run_client(client)
except Exception as e:
@@ -21,10 +21,9 @@ To run this sample:
import logging
from dotenv import load_dotenv
# Import helper functions from worker and client modules
from client import get_client, run_client
from dotenv import load_dotenv
from worker import get_worker, setup_worker
# Configure logging
@@ -35,22 +34,22 @@ logger = logging.getLogger(__name__)
def main():
"""Main entry point - runs both worker and client in single process."""
logger.debug("Starting Single Agent Orchestration Chaining Sample...")
silent_handler = logging.NullHandler()
# Create and start the worker using helper function and context manager
with get_worker(log_handler=silent_handler) as dts_worker:
# Register agents and orchestrations 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
client = get_client(log_handler=silent_handler)
logger.debug("CLIENT: Starting orchestration...")
# Run the client in the same process
try:
run_client(client)
@@ -60,7 +59,7 @@ def main():
logger.exception(f"Error during orchestration: {e}")
finally:
logger.debug("Worker stopping...")
logger.debug("")
logger.debug("Sample completed")

Some files were not shown because too many files have changed in this diff Show More