mirror of
https://github.com/microsoft/agent-framework.git
synced 2026-06-16 21:04:09 +08:00
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:
committed by
GitHub
Unverified
parent
ef798629e5
commit
838a7fd61d
@@ -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(
|
||||
|
||||
-1
@@ -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
|
||||
|
||||
+2
-1
@@ -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(
|
||||
|
||||
-1
@@ -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
|
||||
|
||||
+2
-1
@@ -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(
|
||||
|
||||
+2
-1
@@ -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(
|
||||
|
||||
+3
-1
@@ -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."""
|
||||
|
||||
+1
-1
@@ -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)
|
||||
|
||||
+6
-4
@@ -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(
|
||||
|
||||
+2
-2
@@ -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(
|
||||
|
||||
+2
-1
@@ -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(
|
||||
|
||||
+3
-2
@@ -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(
|
||||
|
||||
+2
-1
@@ -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(
|
||||
|
||||
+3
-2
@@ -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(
|
||||
|
||||
+2
-1
@@ -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(
|
||||
|
||||
+3
-2
@@ -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."""
|
||||
|
||||
+3
-3
@@ -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:
|
||||
|
||||
+2
-2
@@ -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(
|
||||
|
||||
+2
-1
@@ -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(
|
||||
|
||||
+2
-1
@@ -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(
|
||||
|
||||
+3
-1
@@ -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."""
|
||||
|
||||
+6
-4
@@ -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(
|
||||
|
||||
+2
-1
@@ -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(
|
||||
|
||||
+3
-2
@@ -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(
|
||||
|
||||
-1
@@ -8,7 +8,6 @@ from agent_framework import (
|
||||
CodeInterpreterToolResultContent,
|
||||
Content,
|
||||
HostedCodeInterpreterTool,
|
||||
tool,
|
||||
)
|
||||
from agent_framework.openai import OpenAIResponsesClient
|
||||
|
||||
|
||||
+2
-1
@@ -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(
|
||||
|
||||
+3
-2
@@ -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."""
|
||||
|
||||
+3
-3
@@ -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."""
|
||||
|
||||
+1
-1
@@ -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!"
|
||||
|
||||
+8
-6
@@ -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
|
||||
|
||||
+7
-6
@@ -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)
|
||||
|
||||
+1
-1
@@ -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:
|
||||
|
||||
+12
-12
@@ -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:
|
||||
|
||||
+7
-8
@@ -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
Reference in New Issue
Block a user