mirror of
https://github.com/microsoft/agent-framework.git
synced 2026-06-16 21:04:09 +08:00
Python: Fix tool normalization and provider sample consolidation (#3953)
* Fix tool normalization and provider samples - restore callable/single-tool normalization paths and unset tool-choice behavior\n- consolidate and expand chat/provider samples (OpenAI/Azure/Anthropic/Ollama/Bedrock)\n- migrate Bedrock lazy import surface to agent_framework.amazon and move provider samples Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * small fix in sample * Finalize provider, samples, and core cleanup Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix CopilotTool passthrough in agent Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix link --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
Unverified
parent
ed113f941c
commit
aab621f5eb
@@ -1,41 +1,74 @@
|
||||
# Chat Client Examples
|
||||
|
||||
This folder contains simple examples demonstrating direct usage of various chat clients.
|
||||
This folder contains examples for direct chat client usage patterns.
|
||||
|
||||
## Examples
|
||||
|
||||
| File | Description |
|
||||
|------|-------------|
|
||||
| [`azure_assistants_client.py`](azure_assistants_client.py) | Direct usage of Azure Assistants Client for basic chat interactions with Azure OpenAI assistants. |
|
||||
| [`azure_chat_client.py`](azure_chat_client.py) | Direct usage of Azure Chat Client for chat interactions with Azure OpenAI models. |
|
||||
| [`azure_responses_client.py`](azure_responses_client.py) | Direct usage of Azure Responses Client for structured response generation with Azure OpenAI models. |
|
||||
| [`built_in_chat_clients.py`](built_in_chat_clients.py) | Consolidated sample for built-in chat clients. Uses `get_client()` to create the selected client and pass it to `main()`. |
|
||||
| [`chat_response_cancellation.py`](chat_response_cancellation.py) | Demonstrates how to cancel chat responses during streaming, showing proper cancellation handling and cleanup. |
|
||||
| [`azure_ai_chat_client.py`](azure_ai_chat_client.py) | Direct usage of Azure AI Chat Client for chat interactions with Azure AI models. |
|
||||
| [`openai_assistants_client.py`](openai_assistants_client.py) | Direct usage of OpenAI Assistants Client for basic chat interactions with OpenAI assistants. |
|
||||
| [`openai_chat_client.py`](openai_chat_client.py) | Direct usage of OpenAI Chat Client for chat interactions with OpenAI models. |
|
||||
| [`openai_responses_client.py`](openai_responses_client.py) | Direct usage of OpenAI Responses Client for structured response generation with OpenAI models. |
|
||||
| [`custom_chat_client.py`](custom_chat_client.py) | Demonstrates how to create custom chat clients by extending the `BaseChatClient` class. Shows a `EchoingChatClient` implementation and how to integrate it with `Agent` using the `as_agent()` method. |
|
||||
|
||||
## Selecting a built-in client
|
||||
|
||||
`built_in_chat_clients.py` starts with:
|
||||
|
||||
```python
|
||||
asyncio.run(main("openai_chat"))
|
||||
```
|
||||
|
||||
Change the argument to pick a client:
|
||||
|
||||
- `openai_chat`
|
||||
- `openai_responses`
|
||||
- `openai_assistants`
|
||||
- `anthropic`
|
||||
- `ollama`
|
||||
- `bedrock`
|
||||
- `azure_openai_chat`
|
||||
- `azure_openai_responses`
|
||||
- `azure_openai_responses_foundry`
|
||||
- `azure_openai_assistants`
|
||||
- `azure_ai_agent`
|
||||
|
||||
Example:
|
||||
|
||||
```bash
|
||||
uv run samples/02-agents/chat_client/built_in_chat_clients.py
|
||||
```
|
||||
|
||||
## Environment Variables
|
||||
|
||||
Depending on which client you're using, set the appropriate environment variables:
|
||||
Depending on the selected client, set the appropriate environment variables:
|
||||
|
||||
**For Azure clients:**
|
||||
- `AZURE_OPENAI_ENDPOINT`: Your Azure OpenAI endpoint
|
||||
- `AZURE_OPENAI_CHAT_DEPLOYMENT_NAME`: The name of your Azure OpenAI chat deployment
|
||||
- `AZURE_OPENAI_RESPONSES_DEPLOYMENT_NAME`: The name of your Azure OpenAI responses deployment
|
||||
|
||||
**For Azure AI client:**
|
||||
**For Azure OpenAI Foundry responses client (`azure_openai_responses_foundry`):**
|
||||
- `AZURE_AI_PROJECT_ENDPOINT`: Your Azure AI project endpoint
|
||||
- `AZURE_AI_MODEL_DEPLOYMENT_NAME`: The name of your model deployment
|
||||
- `AZURE_OPENAI_RESPONSES_DEPLOYMENT_NAME`: The name of your Azure OpenAI responses deployment
|
||||
|
||||
**For Azure AI agent client (`azure_ai_agent`):**
|
||||
- `AZURE_AI_PROJECT_ENDPOINT`: Your Azure AI project endpoint
|
||||
- `AZURE_AI_MODEL_DEPLOYMENT_NAME`: The name of your model deployment (used by `azure_ai_agent`)
|
||||
|
||||
**For OpenAI clients:**
|
||||
- `OPENAI_API_KEY`: Your OpenAI API key
|
||||
- `OPENAI_CHAT_MODEL_ID`: The OpenAI model to use for chat clients (e.g., `gpt-4o`, `gpt-4o-mini`, `gpt-3.5-turbo`)
|
||||
- `OPENAI_RESPONSES_MODEL_ID`: The OpenAI model to use for responses clients (e.g., `gpt-4o`, `gpt-4o-mini`, `gpt-3.5-turbo`)
|
||||
- `OPENAI_CHAT_MODEL_ID`: The OpenAI model for `openai_chat` and `openai_assistants`
|
||||
- `OPENAI_RESPONSES_MODEL_ID`: The OpenAI model for `openai_responses`
|
||||
|
||||
**For Ollama client:**
|
||||
- `OLLAMA_HOST`: Your Ollama server URL (defaults to `http://localhost:11434` if not set)
|
||||
- `OLLAMA_MODEL_ID`: The Ollama model to use for chat (e.g., `llama3.2`, `llama2`, `codellama`)
|
||||
**For Anthropic client (`anthropic`):**
|
||||
- `ANTHROPIC_API_KEY`: Your Anthropic API key
|
||||
- `ANTHROPIC_CHAT_MODEL_ID`: The Anthropic model ID (for example, `claude-sonnet-4-5`)
|
||||
|
||||
> **Note**: For Ollama, ensure you have Ollama installed and running locally with at least one model downloaded. Visit [https://ollama.com/](https://ollama.com/) for installation instructions.
|
||||
**For Ollama client (`ollama`):**
|
||||
- `OLLAMA_HOST`: Ollama server URL (defaults to `http://localhost:11434` if unset)
|
||||
- `OLLAMA_MODEL_ID`: Ollama model name (for example, `mistral`, `qwen2.5:8b`)
|
||||
|
||||
**For Bedrock client (`bedrock`):**
|
||||
- `BEDROCK_CHAT_MODEL_ID`: Bedrock model ID (for example, `anthropic.claude-3-5-sonnet-20240620-v1:0`)
|
||||
- `BEDROCK_REGION`: AWS region (defaults to `us-east-1` if unset)
|
||||
- AWS credentials via standard environment variables (for example, `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`)
|
||||
|
||||
@@ -1,49 +0,0 @@
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
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
|
||||
|
||||
"""
|
||||
Azure AI Chat Client Direct Usage Example
|
||||
|
||||
Demonstrates direct AzureAIChatClient usage for chat interactions with Azure AI models.
|
||||
Shows function calling capabilities with custom business logic.
|
||||
"""
|
||||
|
||||
|
||||
# NOTE: approval_mode="never_require" is for sample brevity. Use "always_require" in production; see samples/02-agents/tools/function_tool_with_approval.py and samples/02-agents/tools/function_tool_with_approval_and_sessions.py.
|
||||
@tool(approval_mode="never_require")
|
||||
def get_weather(
|
||||
location: Annotated[str, Field(description="The location to get the weather for.")],
|
||||
) -> str:
|
||||
"""Get the weather for a given location."""
|
||||
conditions = ["sunny", "cloudy", "rainy", "stormy"]
|
||||
return f"The weather in {location} is {conditions[randint(0, 3)]} with a high of {randint(10, 30)}°C."
|
||||
|
||||
|
||||
async def main() -> None:
|
||||
# For authentication, run `az login` command in terminal or replace AzureCliCredential with preferred
|
||||
# authentication option.
|
||||
async with AzureAIAgentClient(credential=AzureCliCredential()) as client:
|
||||
message = "What's the weather in Amsterdam and in Paris?"
|
||||
stream = False
|
||||
print(f"User: {message}")
|
||||
if stream:
|
||||
print("Assistant: ", end="")
|
||||
async for chunk in client.get_response(message, tools=get_weather, stream=True):
|
||||
if str(chunk):
|
||||
print(str(chunk), end="")
|
||||
print("")
|
||||
else:
|
||||
response = await client.get_response(message, tools=get_weather)
|
||||
print(f"Assistant: {response}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
@@ -1,49 +0,0 @@
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
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
|
||||
|
||||
"""
|
||||
Azure Assistants Client Direct Usage Example
|
||||
|
||||
Demonstrates direct AzureAssistantsClient usage for chat interactions with Azure OpenAI assistants.
|
||||
Shows function calling capabilities and automatic assistant creation.
|
||||
"""
|
||||
|
||||
|
||||
# NOTE: approval_mode="never_require" is for sample brevity. Use "always_require" in production; see samples/02-agents/tools/function_tool_with_approval.py and samples/02-agents/tools/function_tool_with_approval_and_sessions.py.
|
||||
@tool(approval_mode="never_require")
|
||||
def get_weather(
|
||||
location: Annotated[str, Field(description="The location to get the weather for.")],
|
||||
) -> str:
|
||||
"""Get the weather for a given location."""
|
||||
conditions = ["sunny", "cloudy", "rainy", "stormy"]
|
||||
return f"The weather in {location} is {conditions[randint(0, 3)]} with a high of {randint(10, 30)}°C."
|
||||
|
||||
|
||||
async def main() -> None:
|
||||
# For authentication, run `az login` command in terminal or replace AzureCliCredential with preferred
|
||||
# authentication option.
|
||||
async with AzureOpenAIAssistantsClient(credential=AzureCliCredential()) as client:
|
||||
message = "What's the weather in Amsterdam and in Paris?"
|
||||
stream = False
|
||||
print(f"User: {message}")
|
||||
if stream:
|
||||
print("Assistant: ", end="")
|
||||
async for chunk in client.get_response(message, tools=get_weather, stream=True):
|
||||
if str(chunk):
|
||||
print(str(chunk), end="")
|
||||
print("")
|
||||
else:
|
||||
response = await client.get_response(message, tools=get_weather)
|
||||
print(f"Assistant: {response}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
@@ -1,49 +0,0 @@
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
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
|
||||
|
||||
"""
|
||||
Azure Chat Client Direct Usage Example
|
||||
|
||||
Demonstrates direct AzureChatClient usage for chat interactions with Azure OpenAI models.
|
||||
Shows function calling capabilities with custom business logic.
|
||||
"""
|
||||
|
||||
|
||||
# NOTE: approval_mode="never_require" is for sample brevity. Use "always_require" in production; see samples/02-agents/tools/function_tool_with_approval.py and samples/02-agents/tools/function_tool_with_approval_and_sessions.py.
|
||||
@tool(approval_mode="never_require")
|
||||
def get_weather(
|
||||
location: Annotated[str, Field(description="The location to get the weather for.")],
|
||||
) -> str:
|
||||
"""Get the weather for a given location."""
|
||||
conditions = ["sunny", "cloudy", "rainy", "stormy"]
|
||||
return f"The weather in {location} is {conditions[randint(0, 3)]} with a high of {randint(10, 30)}°C."
|
||||
|
||||
|
||||
async def main() -> None:
|
||||
# For authentication, run `az login` command in terminal or replace AzureCliCredential with preferred
|
||||
# authentication option.
|
||||
client = AzureOpenAIChatClient(credential=AzureCliCredential())
|
||||
message = "What's the weather in Amsterdam and in Paris?"
|
||||
stream = False
|
||||
print(f"User: {message}")
|
||||
if stream:
|
||||
print("Assistant: ", end="")
|
||||
async for chunk in client.get_response(message, tools=get_weather, stream=True):
|
||||
if str(chunk):
|
||||
print(str(chunk), end="")
|
||||
print("")
|
||||
else:
|
||||
response = await client.get_response(message, tools=get_weather)
|
||||
print(f"Assistant: {response}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
@@ -1,95 +0,0 @@
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
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 BaseModel
|
||||
|
||||
"""
|
||||
Azure Responses Client Direct Usage Example
|
||||
|
||||
Demonstrates direct AzureResponsesClient usage for structured response generation with Azure OpenAI models.
|
||||
Shows function calling capabilities with custom business logic.
|
||||
"""
|
||||
|
||||
|
||||
# NOTE: approval_mode="never_require" is for sample brevity. Use "always_require" in production; see samples/02-agents/tools/function_tool_with_approval.py and samples/02-agents/tools/function_tool_with_approval_and_sessions.py.
|
||||
@tool(approval_mode="never_require")
|
||||
def get_weather(
|
||||
location: Annotated[str, "The location to get the weather for."],
|
||||
) -> str:
|
||||
"""Get the weather for a given location."""
|
||||
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():
|
||||
"""Get the current time."""
|
||||
from datetime import datetime
|
||||
|
||||
now = datetime.now()
|
||||
return f"The current date time is {now.strftime('%Y-%m-%d - %H:%M:%S')}."
|
||||
|
||||
|
||||
class WeatherDetail(BaseModel):
|
||||
"""Structured output for weather information."""
|
||||
|
||||
location: str
|
||||
weather: str
|
||||
|
||||
|
||||
class Weather(BaseModel):
|
||||
"""Container for multiple outputs."""
|
||||
|
||||
date_time: str
|
||||
weather_details: list[WeatherDetail]
|
||||
|
||||
|
||||
async def main() -> None:
|
||||
# For authentication, run `az login` command in terminal or replace AzureCliCredential with preferred
|
||||
# authentication option.
|
||||
client = AzureOpenAIResponsesClient(credential=AzureCliCredential(), api_version="preview")
|
||||
message = "What's the weather in Amsterdam and in Paris?"
|
||||
stream = True
|
||||
print(f"User: {message}")
|
||||
response = client.get_response(
|
||||
message,
|
||||
options={"response_format": Weather, "tools": [get_weather, get_time]},
|
||||
stream=stream,
|
||||
)
|
||||
if stream:
|
||||
response = await response.get_final_response()
|
||||
else:
|
||||
response = await response
|
||||
if result := response.value:
|
||||
print(f"Assistant: {result.model_dump_json(indent=2)}")
|
||||
else:
|
||||
print(f"Assistant: {response.text}")
|
||||
|
||||
|
||||
# Expected output (time will be different):
|
||||
"""
|
||||
User: What's the weather in Amsterdam and in Paris?
|
||||
Assistant: {
|
||||
"date_time": "2026-02-06 - 13:30:40",
|
||||
"weather_details": [
|
||||
{
|
||||
"location": "Amsterdam",
|
||||
"weather": "The weather in Amsterdam is cloudy with a high of 21°C."
|
||||
},
|
||||
{
|
||||
"location": "Paris",
|
||||
"weather": "The weather in Paris is sunny with a high of 27°C."
|
||||
}
|
||||
]
|
||||
}
|
||||
"""
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
@@ -0,0 +1,156 @@
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
import asyncio
|
||||
import os
|
||||
from random import randint
|
||||
from typing import Annotated, Any, Literal
|
||||
|
||||
from agent_framework import SupportsChatGetResponse, tool
|
||||
from agent_framework.azure import (
|
||||
AzureAIAgentClient,
|
||||
AzureOpenAIAssistantsClient,
|
||||
)
|
||||
from agent_framework.openai import OpenAIAssistantsClient
|
||||
from azure.identity import AzureCliCredential
|
||||
from azure.identity.aio import AzureCliCredential as AsyncAzureCliCredential
|
||||
from pydantic import Field
|
||||
|
||||
"""
|
||||
Built-in Chat Clients Example
|
||||
|
||||
This sample demonstrates how to run the same prompt flow against different built-in
|
||||
chat clients using a single `get_client` factory.
|
||||
|
||||
Select one of these client names:
|
||||
- openai_chat
|
||||
- openai_responses
|
||||
- openai_assistants
|
||||
- anthropic
|
||||
- ollama
|
||||
- bedrock
|
||||
- azure_openai_chat
|
||||
- azure_openai_responses
|
||||
- azure_openai_responses_foundry
|
||||
- azure_openai_assistants
|
||||
- azure_ai_agent
|
||||
"""
|
||||
|
||||
ClientName = Literal[
|
||||
"openai_chat",
|
||||
"openai_responses",
|
||||
"openai_assistants",
|
||||
"anthropic",
|
||||
"ollama",
|
||||
"bedrock",
|
||||
"azure_openai_chat",
|
||||
"azure_openai_responses",
|
||||
"azure_openai_responses_foundry",
|
||||
"azure_openai_assistants",
|
||||
"azure_ai_agent",
|
||||
]
|
||||
|
||||
|
||||
# NOTE: approval_mode="never_require" is for sample brevity.
|
||||
# Use "always_require" in production; see samples/02-agents/tools/function_tool_with_approval.py
|
||||
# and samples/02-agents/tools/function_tool_with_approval_and_sessions.py.
|
||||
@tool(approval_mode="never_require")
|
||||
def get_weather(
|
||||
location: Annotated[str, Field(description="The location to get the weather for.")],
|
||||
) -> str:
|
||||
"""Get the weather for a given location."""
|
||||
conditions = ["sunny", "cloudy", "rainy", "stormy"]
|
||||
return f"The weather in {location} is {conditions[randint(0, 3)]} with a high of {randint(10, 30)}°C."
|
||||
|
||||
|
||||
def get_client(client_name: ClientName) -> SupportsChatGetResponse[Any]:
|
||||
"""Create a built-in chat client from a name."""
|
||||
from agent_framework.amazon import BedrockChatClient
|
||||
from agent_framework.anthropic import AnthropicClient
|
||||
from agent_framework.azure import (
|
||||
AzureOpenAIChatClient,
|
||||
AzureOpenAIResponsesClient,
|
||||
)
|
||||
from agent_framework.ollama import OllamaChatClient
|
||||
from agent_framework.openai import OpenAIChatClient, OpenAIResponsesClient
|
||||
|
||||
# 1. Create OpenAI clients.
|
||||
if client_name == "openai_chat":
|
||||
return OpenAIChatClient()
|
||||
if client_name == "openai_responses":
|
||||
return OpenAIResponsesClient()
|
||||
if client_name == "openai_assistants":
|
||||
return OpenAIAssistantsClient()
|
||||
if client_name == "anthropic":
|
||||
return AnthropicClient()
|
||||
if client_name == "ollama":
|
||||
return OllamaChatClient()
|
||||
if client_name == "bedrock":
|
||||
return BedrockChatClient()
|
||||
|
||||
# 2. Create Azure OpenAI clients.
|
||||
if client_name == "azure_openai_chat":
|
||||
return AzureOpenAIChatClient(credential=AzureCliCredential())
|
||||
if client_name == "azure_openai_responses":
|
||||
return AzureOpenAIResponsesClient(credential=AzureCliCredential(), api_version="preview")
|
||||
if client_name == "azure_openai_responses_foundry":
|
||||
return AzureOpenAIResponsesClient(
|
||||
project_endpoint=os.environ["AZURE_AI_PROJECT_ENDPOINT"],
|
||||
deployment_name=os.environ["AZURE_OPENAI_RESPONSES_DEPLOYMENT_NAME"],
|
||||
credential=AzureCliCredential(),
|
||||
)
|
||||
if client_name == "azure_openai_assistants":
|
||||
return AzureOpenAIAssistantsClient(credential=AzureCliCredential())
|
||||
|
||||
# 3. Create Azure AI client.
|
||||
if client_name == "azure_ai_agent":
|
||||
return AzureAIAgentClient(credential=AsyncAzureCliCredential())
|
||||
|
||||
raise ValueError(f"Unsupported client name: {client_name}")
|
||||
|
||||
|
||||
async def main(client_name: ClientName = "openai_chat") -> None:
|
||||
"""Run a basic prompt using a selected built-in client."""
|
||||
client = get_client(client_name)
|
||||
|
||||
# 1. Configure prompt and streaming mode.
|
||||
message = "What's the weather in Amsterdam and in Paris?"
|
||||
stream = os.getenv("STREAM", "false").lower() == "true"
|
||||
print(f"Client: {client_name}")
|
||||
print(f"User: {message}")
|
||||
|
||||
# 2. Run with context-managed clients.
|
||||
if isinstance(client, OpenAIAssistantsClient | AzureOpenAIAssistantsClient | AzureAIAgentClient):
|
||||
async with client:
|
||||
if stream:
|
||||
response_stream = client.get_response(message, stream=True, options={"tools": get_weather})
|
||||
print("Assistant: ", end="")
|
||||
async for chunk in response_stream:
|
||||
if chunk.text:
|
||||
print(chunk.text, end="")
|
||||
print("")
|
||||
else:
|
||||
print(f"Assistant: {await client.get_response(message, stream=False, options={'tools': get_weather})}")
|
||||
return
|
||||
|
||||
# 3. Run with non-context-managed clients.
|
||||
if stream:
|
||||
response_stream = client.get_response(message, stream=True, options={"tools": get_weather})
|
||||
print("Assistant: ", end="")
|
||||
async for chunk in response_stream:
|
||||
if chunk.text:
|
||||
print(chunk.text, end="")
|
||||
print("")
|
||||
else:
|
||||
print(f"Assistant: {await client.get_response(message, stream=False, options={'tools': get_weather})}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main("openai_chat"))
|
||||
|
||||
|
||||
"""
|
||||
Sample output:
|
||||
User: What's the weather in Amsterdam and in Paris?
|
||||
Assistant: The weather in Amsterdam is sunny with a high of 25°C.
|
||||
...and in Paris it is cloudy with a high of 19°C.
|
||||
"""
|
||||
@@ -4,7 +4,7 @@ import asyncio
|
||||
import random
|
||||
import sys
|
||||
from collections.abc import AsyncIterable, Awaitable, Mapping, Sequence
|
||||
from typing import Any, ClassVar, Generic
|
||||
from typing import Any, ClassVar, TypeAlias, TypedDict
|
||||
|
||||
from agent_framework import (
|
||||
BaseChatClient,
|
||||
@@ -15,15 +15,9 @@ from agent_framework import (
|
||||
FunctionInvocationLayer,
|
||||
Message,
|
||||
ResponseStream,
|
||||
Role,
|
||||
)
|
||||
from agent_framework._clients import OptionsCoT
|
||||
from agent_framework.observability import ChatTelemetryLayer
|
||||
|
||||
if sys.version_info >= (3, 13):
|
||||
pass
|
||||
else:
|
||||
pass
|
||||
if sys.version_info >= (3, 12):
|
||||
from typing import override # type: ignore # pragma: no cover
|
||||
else:
|
||||
@@ -38,7 +32,18 @@ middleware, telemetry, and function invocation layers explicitly.
|
||||
"""
|
||||
|
||||
|
||||
class EchoingChatClient(BaseChatClient[OptionsCoT], Generic[OptionsCoT]):
|
||||
class EchoingChatClientOptions(TypedDict, total=False):
|
||||
"""Custom options for EchoingChatClient."""
|
||||
|
||||
uppercase: bool
|
||||
suffix: str
|
||||
stream_delay_seconds: float
|
||||
|
||||
|
||||
OptionsT: TypeAlias = EchoingChatClientOptions
|
||||
|
||||
|
||||
class EchoingChatClient(BaseChatClient[OptionsT]):
|
||||
"""A custom chat client that echoes messages back with modifications.
|
||||
|
||||
This demonstrates how to implement a custom chat client by extending BaseChatClient
|
||||
@@ -73,7 +78,7 @@ class EchoingChatClient(BaseChatClient[OptionsCoT], Generic[OptionsCoT]):
|
||||
# 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
|
||||
|
||||
@@ -82,7 +87,13 @@ class EchoingChatClient(BaseChatClient[OptionsCoT], Generic[OptionsCoT]):
|
||||
else:
|
||||
response_text = f"{self.prefix} [No text message found]"
|
||||
|
||||
response_message = Message(role=Role.ASSISTANT, contents=[Content.from_text(response_text)])
|
||||
if options.get("uppercase"):
|
||||
response_text = response_text.upper()
|
||||
if suffix := options.get("suffix"):
|
||||
response_text = f"{response_text} {suffix}"
|
||||
stream_delay_seconds = float(options.get("stream_delay_seconds", 0.05))
|
||||
|
||||
response_message = Message(role="assistant", contents=[Content.from_text(response_text)])
|
||||
|
||||
response = ChatResponse(
|
||||
messages=[response_message],
|
||||
@@ -102,21 +113,20 @@ class EchoingChatClient(BaseChatClient[OptionsCoT], Generic[OptionsCoT]):
|
||||
for char in response_text_local:
|
||||
yield ChatResponseUpdate(
|
||||
contents=[Content.from_text(char)],
|
||||
role=Role.ASSISTANT,
|
||||
role="assistant",
|
||||
response_id=f"echo-stream-resp-{random.randint(1000, 9999)}",
|
||||
model_id="echo-model-v1",
|
||||
)
|
||||
await asyncio.sleep(0.05)
|
||||
await asyncio.sleep(stream_delay_seconds)
|
||||
|
||||
return ResponseStream(_stream(), finalizer=lambda updates: response)
|
||||
|
||||
|
||||
class EchoingChatClientWithLayers( # type: ignore[misc,type-var]
|
||||
ChatMiddlewareLayer[OptionsCoT],
|
||||
ChatTelemetryLayer[OptionsCoT],
|
||||
FunctionInvocationLayer[OptionsCoT],
|
||||
EchoingChatClient[OptionsCoT],
|
||||
Generic[OptionsCoT],
|
||||
class EchoingChatClientWithLayers( # type: ignore[misc]
|
||||
ChatMiddlewareLayer[OptionsT],
|
||||
ChatTelemetryLayer[OptionsT],
|
||||
FunctionInvocationLayer[OptionsT],
|
||||
EchoingChatClient,
|
||||
):
|
||||
"""Echoing chat client that explicitly composes middleware, telemetry, and function layers."""
|
||||
|
||||
@@ -134,7 +144,14 @@ async def main() -> None:
|
||||
|
||||
# Use the chat client directly
|
||||
print("Using chat client directly:")
|
||||
direct_response = await echo_client.get_response("Hello, custom chat client!")
|
||||
direct_response = await echo_client.get_response(
|
||||
"Hello, custom chat client!",
|
||||
options={
|
||||
"uppercase": True,
|
||||
"suffix": "(CUSTOM OPTIONS)",
|
||||
"stream_delay_seconds": 0.02,
|
||||
},
|
||||
)
|
||||
print(f"Direct response: {direct_response.messages[0].text}")
|
||||
|
||||
# Create an agent using the custom chat client
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
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
|
||||
|
||||
"""
|
||||
OpenAI Assistants Client Direct Usage Example
|
||||
|
||||
Demonstrates direct OpenAIAssistantsClient usage for chat interactions with OpenAI assistants.
|
||||
Shows function calling capabilities and automatic assistant creation.
|
||||
|
||||
"""
|
||||
|
||||
|
||||
# NOTE: approval_mode="never_require" is for sample brevity. Use "always_require" in production; see samples/02-agents/tools/function_tool_with_approval.py and samples/02-agents/tools/function_tool_with_approval_and_sessions.py.
|
||||
@tool(approval_mode="never_require")
|
||||
def get_weather(
|
||||
location: Annotated[str, Field(description="The location to get the weather for.")],
|
||||
) -> str:
|
||||
"""Get the weather for a given location."""
|
||||
conditions = ["sunny", "cloudy", "rainy", "stormy"]
|
||||
return f"The weather in {location} is {conditions[randint(0, 3)]} with a high of {randint(10, 30)}°C."
|
||||
|
||||
|
||||
async def main() -> None:
|
||||
async with OpenAIAssistantsClient() as client:
|
||||
message = "What's the weather in Amsterdam and in Paris?"
|
||||
stream = False
|
||||
print(f"User: {message}")
|
||||
if stream:
|
||||
print("Assistant: ", end="")
|
||||
async for chunk in client.get_response(message, tools=get_weather, stream=True):
|
||||
if str(chunk):
|
||||
print(str(chunk), end="")
|
||||
print("")
|
||||
else:
|
||||
response = await client.get_response(message, tools=get_weather)
|
||||
print(f"Assistant: {response}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
@@ -1,47 +0,0 @@
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
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
|
||||
|
||||
"""
|
||||
OpenAI Chat Client Direct Usage Example
|
||||
|
||||
Demonstrates direct OpenAIChatClient usage for chat interactions with OpenAI models.
|
||||
Shows function calling capabilities with custom business logic.
|
||||
|
||||
"""
|
||||
|
||||
|
||||
# NOTE: approval_mode="never_require" is for sample brevity. Use "always_require" in production; see samples/02-agents/tools/function_tool_with_approval.py and samples/02-agents/tools/function_tool_with_approval_and_sessions.py.
|
||||
@tool(approval_mode="never_require")
|
||||
def get_weather(
|
||||
location: Annotated[str, Field(description="The location to get the weather for.")],
|
||||
) -> str:
|
||||
"""Get the weather for a given location."""
|
||||
conditions = ["sunny", "cloudy", "rainy", "stormy"]
|
||||
return f"The weather in {location} is {conditions[randint(0, 3)]} with a high of {randint(10, 30)}°C."
|
||||
|
||||
|
||||
async def main() -> None:
|
||||
client = OpenAIChatClient()
|
||||
message = "What's the weather in Amsterdam and in Paris?"
|
||||
stream = True
|
||||
print(f"User: {message}")
|
||||
if stream:
|
||||
print("Assistant: ", end="")
|
||||
async for chunk in client.get_response(message, tools=get_weather, stream=True):
|
||||
if chunk.text:
|
||||
print(chunk.text, end="")
|
||||
print("")
|
||||
else:
|
||||
response = await client.get_response(message, tools=get_weather)
|
||||
print(f"Assistant: {response}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
@@ -1,47 +0,0 @@
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
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
|
||||
|
||||
"""
|
||||
OpenAI Responses Client Direct Usage Example
|
||||
|
||||
Demonstrates direct OpenAIResponsesClient usage for structured response generation with OpenAI models.
|
||||
Shows function calling capabilities with custom business logic.
|
||||
|
||||
"""
|
||||
|
||||
|
||||
# NOTE: approval_mode="never_require" is for sample brevity. Use "always_require" in production; see samples/02-agents/tools/function_tool_with_approval.py and samples/02-agents/tools/function_tool_with_approval_and_sessions.py.
|
||||
@tool(approval_mode="never_require")
|
||||
def get_weather(
|
||||
location: Annotated[str, Field(description="The location to get the weather for.")],
|
||||
) -> str:
|
||||
"""Get the weather for a given location."""
|
||||
conditions = ["sunny", "cloudy", "rainy", "stormy"]
|
||||
return f"The weather in {location} is {conditions[randint(0, 3)]} with a high of {randint(10, 30)}°C."
|
||||
|
||||
|
||||
async def main() -> None:
|
||||
client = OpenAIResponsesClient()
|
||||
message = "What's the weather in Amsterdam and in Paris?"
|
||||
stream = True
|
||||
print(f"User: {message}")
|
||||
print("Assistant: ", end="")
|
||||
response = client.get_response(message, stream=stream, options={"tools": get_weather})
|
||||
if stream:
|
||||
# TODO: review names of the methods, could be related to things like HTTP clients?
|
||||
response.with_transform_hook(lambda chunk: print(chunk.text, end=""))
|
||||
await response.get_final_response()
|
||||
else:
|
||||
response = await response
|
||||
print(f"Assistant: {response}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
Reference in New Issue
Block a user