chore: import upstream snapshot with attribution
CodeQL / Analyze (csharp) (push) Waiting to run
CodeQL / Analyze (python) (push) Waiting to run
dotnet-build-and-test / dotnet-test-functions (push) Has been cancelled
dotnet-build-and-test / paths-filter (push) Has been cancelled
dotnet-build-and-test / dotnet-build (Debug, windows-latest, net9.0) (push) Has been cancelled
dotnet-build-and-test / dotnet-build (Release, ubuntu-latest, net10.0) (push) Has been cancelled
dotnet-build-and-test / dotnet-build (Release, ubuntu-latest, net8.0) (push) Has been cancelled
dotnet-build-and-test / dotnet-build (Release, windows-latest, net472) (push) Has been cancelled
dotnet-build-and-test / dotnet-test (Release, integration, true, ubuntu-latest, net10.0) (push) Has been cancelled
dotnet-build-and-test / dotnet-test (Release, integration, true, windows-latest, net472) (push) Has been cancelled
dotnet-build-and-test / dotnet-foundry-hosted-it (push) Has been cancelled
dotnet-build-and-test / dotnet-build-and-test-check (push) Has been cancelled
dotnet-build-and-test / Integration Test Report (push) Has been cancelled
CodeQL / Analyze (csharp) (push) Waiting to run
CodeQL / Analyze (python) (push) Waiting to run
dotnet-build-and-test / dotnet-test-functions (push) Has been cancelled
dotnet-build-and-test / paths-filter (push) Has been cancelled
dotnet-build-and-test / dotnet-build (Debug, windows-latest, net9.0) (push) Has been cancelled
dotnet-build-and-test / dotnet-build (Release, ubuntu-latest, net10.0) (push) Has been cancelled
dotnet-build-and-test / dotnet-build (Release, ubuntu-latest, net8.0) (push) Has been cancelled
dotnet-build-and-test / dotnet-build (Release, windows-latest, net472) (push) Has been cancelled
dotnet-build-and-test / dotnet-test (Release, integration, true, ubuntu-latest, net10.0) (push) Has been cancelled
dotnet-build-and-test / dotnet-test (Release, integration, true, windows-latest, net472) (push) Has been cancelled
dotnet-build-and-test / dotnet-foundry-hosted-it (push) Has been cancelled
dotnet-build-and-test / dotnet-build-and-test-check (push) Has been cancelled
dotnet-build-and-test / Integration Test Report (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,49 @@
|
||||
# Foundry Provider Samples
|
||||
|
||||
This folder contains Azure AI Foundry and Foundry Local samples for Agent Framework.
|
||||
|
||||
## FoundryAgent Samples
|
||||
|
||||
| File | Description |
|
||||
|------|-------------|
|
||||
| [`foundry_agent_basic.py`](foundry_agent_basic.py) | Foundry Agent basic example |
|
||||
| [`foundry_agent_custom_client.py`](foundry_agent_custom_client.py) | Foundry Agent custom client configuration |
|
||||
| [`foundry_agent_hosted.py`](foundry_agent_hosted.py) | Foundry Agent for hosted agents |
|
||||
| [`foundry_agent_with_function_tools.py`](foundry_agent_with_function_tools.py) | Foundry Agent with local function tools |
|
||||
|
||||
## FoundryChatClient Samples
|
||||
|
||||
| File | Description |
|
||||
|------|-------------|
|
||||
| [`foundry_chat_client.py`](foundry_chat_client.py) | Foundry Chat Client with project endpoint example |
|
||||
| [`foundry_chat_client_basic.py`](foundry_chat_client_basic.py) | Foundry Chat Client basic example |
|
||||
| [`foundry_chat_client_code_interpreter_files.py`](foundry_chat_client_code_interpreter_files.py) | Foundry Chat Client with code interpreter and files |
|
||||
| [`foundry_chat_client_image_analysis.py`](foundry_chat_client_image_analysis.py) | Foundry Chat Client with image analysis |
|
||||
| [`foundry_chat_client_with_code_interpreter.py`](foundry_chat_client_with_code_interpreter.py) | Foundry Chat Client with code interpreter |
|
||||
| [`foundry_chat_client_with_explicit_settings.py`](foundry_chat_client_with_explicit_settings.py) | Foundry Chat Client with explicit settings |
|
||||
| [`foundry_chat_client_with_file_search.py`](foundry_chat_client_with_file_search.py) | Foundry Chat Client with file search |
|
||||
| [`foundry_chat_client_with_function_tools.py`](foundry_chat_client_with_function_tools.py) | Foundry Chat Client with function tools |
|
||||
| [`foundry_chat_client_with_hosted_mcp.py`](foundry_chat_client_with_hosted_mcp.py) | Foundry Chat Client with hosted MCP |
|
||||
| [`foundry_chat_client_with_local_mcp.py`](foundry_chat_client_with_local_mcp.py) | Foundry Chat Client with local MCP |
|
||||
| [`foundry_chat_client_with_session.py`](foundry_chat_client_with_session.py) | Foundry Chat Client with session management |
|
||||
| [`foundry_chat_client_with_toolbox.py`](foundry_chat_client_with_toolbox.py) | Foundry Chat Client connected to a toolbox via its MCP endpoint using `MCPStreamableHTTPTool` |
|
||||
| [`foundry_chat_client_with_toolbox_skills.py`](foundry_chat_client_with_toolbox_skills.py) | Foundry Chat Client that discovers MCP-based skills from a Foundry Toolbox endpoint via `MCPSkillsSource` (uses an Azure AD bearer token and the toolbox preview header) |
|
||||
|
||||
## FoundryLocalClient Samples
|
||||
|
||||
### Prerequisites
|
||||
|
||||
1. Install Foundry Local and required local runtime components.
|
||||
2. Install the connector package:
|
||||
|
||||
```bash
|
||||
pip install agent-framework-foundry-local --pre
|
||||
```
|
||||
|
||||
| File | Description |
|
||||
|------|-------------|
|
||||
| [`foundry_local_agent.py`](foundry_local_agent.py) | Basic Foundry Local agent usage with streaming and non-streaming responses, plus function tool calling. |
|
||||
|
||||
### Environment Variables
|
||||
|
||||
- `FOUNDRY_LOCAL_MODEL`: Optional model alias/ID to use by default when `model` is not passed to `FoundryLocalClient`.
|
||||
@@ -0,0 +1,42 @@
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
import asyncio
|
||||
|
||||
from agent_framework.foundry import FoundryAgent
|
||||
from azure.identity import AzureCliCredential
|
||||
|
||||
"""
|
||||
Foundry Agent — Connect to a pre-configured agent in Microsoft Foundry
|
||||
|
||||
This sample shows the simplest way to connect to an existing PromptAgent
|
||||
in Azure AI Foundry and run it. The agent's instructions, model, and hosted
|
||||
tools are all configured on the service — you just connect and run.
|
||||
|
||||
Environment variables:
|
||||
FOUNDRY_PROJECT_ENDPOINT — Azure AI Foundry project endpoint
|
||||
FOUNDRY_AGENT_NAME — Name of the agent in Foundry
|
||||
FOUNDRY_AGENT_VERSION — Version of the agent (for PromptAgents)
|
||||
"""
|
||||
|
||||
|
||||
async def main() -> None:
|
||||
agent = FoundryAgent(
|
||||
project_endpoint="https://your-project.services.ai.azure.com",
|
||||
agent_name="my-prompt-agent",
|
||||
agent_version="1.0",
|
||||
credential=AzureCliCredential(),
|
||||
)
|
||||
|
||||
result = await agent.run("What is the capital of France?")
|
||||
print(f"Agent: {result}")
|
||||
|
||||
# Streaming
|
||||
print("Agent (streaming): ", end="", flush=True)
|
||||
async for chunk in agent.run("Tell me a fun fact.", stream=True):
|
||||
if chunk.text:
|
||||
print(chunk.text, end="", flush=True)
|
||||
print()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
@@ -0,0 +1,63 @@
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
import asyncio
|
||||
import os
|
||||
|
||||
from agent_framework import Agent
|
||||
from agent_framework.foundry import FoundryAgent, RawFoundryAgentChatClient
|
||||
from azure.identity import AzureCliCredential
|
||||
from dotenv import load_dotenv
|
||||
|
||||
load_dotenv()
|
||||
"""
|
||||
Foundry Agent — Custom client configuration
|
||||
|
||||
This sample demonstrates three ways to customize the FoundryAgent client layer:
|
||||
|
||||
1. Default: FoundryAgent creates a RawFoundryAgentChatClient (full middleware) internally
|
||||
2. client_type: Pass RawFoundryAgentChatClient for no client middleware
|
||||
3. Composition: Use Agent(client=RawFoundryAgentChatClient(...)) directly
|
||||
|
||||
Environment variables:
|
||||
FOUNDRY_PROJECT_ENDPOINT — Azure AI Foundry project endpoint
|
||||
FOUNDRY_AGENT_NAME — Name of the agent in Foundry
|
||||
FOUNDRY_AGENT_VERSION — Version of the agent
|
||||
"""
|
||||
|
||||
|
||||
async def main() -> None:
|
||||
# Option 1: Default — full middleware on both agent and client
|
||||
agent = FoundryAgent(
|
||||
project_endpoint=os.getenv("FOUNDRY_PROJECT_ENDPOINT"),
|
||||
agent_name=os.getenv("FOUNDRY_AGENT_NAME"),
|
||||
agent_version=os.getenv("FOUNDRY_AGENT_VERSION"),
|
||||
credential=AzureCliCredential(),
|
||||
)
|
||||
result = await agent.run("Hello from the default setup!")
|
||||
print(f"Default: {result}\n")
|
||||
|
||||
# Option 2: Raw client — no client-level middleware (agent middleware still active)
|
||||
agent_raw_client = FoundryAgent(
|
||||
project_endpoint=os.getenv("FOUNDRY_PROJECT_ENDPOINT"),
|
||||
agent_name=os.getenv("FOUNDRY_AGENT_NAME"),
|
||||
agent_version=os.getenv("FOUNDRY_AGENT_VERSION"),
|
||||
credential=AzureCliCredential(),
|
||||
client_type=RawFoundryAgentChatClient,
|
||||
)
|
||||
result = await agent_raw_client.run("Hello from raw client!")
|
||||
print(f"Raw client: {result}\n")
|
||||
|
||||
# Option 3: Composition — use Agent(client=...) directly
|
||||
# this will not run the checks that the `FoundryAgent` does on things like tools.
|
||||
client = RawFoundryAgentChatClient(
|
||||
project_endpoint=os.getenv("FOUNDRY_PROJECT_ENDPOINT"),
|
||||
agent_name=os.getenv("FOUNDRY_AGENT_NAME"),
|
||||
agent_version=os.getenv("FOUNDRY_AGENT_VERSION"),
|
||||
credential=AzureCliCredential(),
|
||||
)
|
||||
agent_composed = Agent(client=client)
|
||||
result = await agent_composed.run("Hello from composed setup!")
|
||||
print(f"Composed: {result}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
@@ -0,0 +1,37 @@
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
import asyncio
|
||||
import os
|
||||
|
||||
from agent_framework.foundry import FoundryAgent
|
||||
from azure.identity import AzureCliCredential
|
||||
from dotenv import load_dotenv
|
||||
|
||||
load_dotenv()
|
||||
|
||||
"""
|
||||
Foundry Agent — Connect to a HostedAgent (no version needed)
|
||||
|
||||
HostedAgents in Azure AI Foundry are pre-deployed agents that don't require
|
||||
a version number. You only need the agent name to connect.
|
||||
|
||||
Environment variables:
|
||||
FOUNDRY_PROJECT_ENDPOINT — Azure AI Foundry project endpoint
|
||||
FOUNDRY_AGENT_NAME — Name of the hosted agent
|
||||
"""
|
||||
|
||||
|
||||
async def main() -> None:
|
||||
# HostedAgents don't need agent_version
|
||||
agent = FoundryAgent(
|
||||
project_endpoint=os.getenv("FOUNDRY_PROJECT_ENDPOINT"),
|
||||
agent_name=os.getenv("FOUNDRY_AGENT_NAME"),
|
||||
credential=AzureCliCredential(),
|
||||
)
|
||||
|
||||
result = await agent.run("Summarize the latest news about AI.")
|
||||
print(f"Agent: {result}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
@@ -0,0 +1,50 @@
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
import asyncio
|
||||
import os
|
||||
from typing import Annotated
|
||||
|
||||
from agent_framework.foundry import FoundryAgent
|
||||
from azure.identity import AzureCliCredential
|
||||
from dotenv import load_dotenv
|
||||
|
||||
load_dotenv()
|
||||
"""
|
||||
Foundry Agent with Local Function Tools
|
||||
|
||||
This sample shows how to connect to a Foundry agent and provide local function
|
||||
tools. The Foundry agent must already have these tools defined in its configuration
|
||||
(as declaration-only tools). The local implementations are matched by name.
|
||||
|
||||
Only FunctionTool objects are accepted — hosted tools (code interpreter, file search,
|
||||
web search, etc.) must be configured on the agent definition in the service.
|
||||
|
||||
Environment variables:
|
||||
FOUNDRY_PROJECT_ENDPOINT — Azure AI Foundry project endpoint
|
||||
FOUNDRY_AGENT_NAME — Name of the agent in Foundry
|
||||
FOUNDRY_AGENT_VERSION — Version of the agent
|
||||
"""
|
||||
|
||||
|
||||
def get_weather(
|
||||
location: Annotated[str, "The city to get weather for."],
|
||||
) -> str:
|
||||
"""Get the current weather for a location."""
|
||||
return f"The weather in {location} is sunny, 22°C."
|
||||
|
||||
|
||||
async def main() -> None:
|
||||
agent = FoundryAgent(
|
||||
project_endpoint=os.getenv("FOUNDRY_PROJECT_ENDPOINT"),
|
||||
agent_name=os.getenv("FOUNDRY_AGENT_NAME"),
|
||||
agent_version=os.getenv("FOUNDRY_AGENT_VERSION"),
|
||||
credential=AzureCliCredential(),
|
||||
tools=get_weather,
|
||||
)
|
||||
|
||||
result = await agent.run("What's the weather in Paris?")
|
||||
print(f"Agent: {result}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
@@ -0,0 +1,115 @@
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
import asyncio
|
||||
import os
|
||||
from random import randint
|
||||
from typing import Annotated
|
||||
|
||||
from agent_framework import Agent, tool
|
||||
from agent_framework.foundry import FoundryChatClient
|
||||
from azure.identity import AzureCliCredential
|
||||
from dotenv import load_dotenv
|
||||
|
||||
# Load environment variables from .env file
|
||||
load_dotenv()
|
||||
|
||||
"""
|
||||
Foundry Chat Client with Project Endpoint Example
|
||||
|
||||
This sample demonstrates how to create a FoundryChatClient using a
|
||||
Foundry project endpoint. Instead of providing a service endpoint
|
||||
directly, you provide a Foundry project endpoint and the client is created via
|
||||
the Azure AI Foundry project SDK.
|
||||
|
||||
This requires:
|
||||
- The `FOUNDRY_PROJECT_ENDPOINT` environment variable set to your Foundry project endpoint.
|
||||
- The `FOUNDRY_MODEL` environment variable set to the model deployment name.
|
||||
"""
|
||||
|
||||
|
||||
@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."
|
||||
|
||||
|
||||
async def non_streaming_example() -> None:
|
||||
"""Example of non-streaming response (get the complete result at once)."""
|
||||
print("=== Non-streaming Response Example ===")
|
||||
|
||||
# 1. Create the FoundryChatClient using a Foundry project endpoint.
|
||||
# For authentication, run `az login` command in terminal or replace AzureCliCredential with preferred
|
||||
# authentication option.
|
||||
credential = AzureCliCredential()
|
||||
_client = FoundryChatClient(
|
||||
project_endpoint=os.environ["FOUNDRY_PROJECT_ENDPOINT"],
|
||||
model=os.environ["FOUNDRY_MODEL"],
|
||||
credential=credential,
|
||||
)
|
||||
agent = Agent(
|
||||
client=_client,
|
||||
instructions="You are a helpful weather agent.",
|
||||
tools=get_weather,
|
||||
)
|
||||
|
||||
# 2. Run a query and print the result.
|
||||
query = "What's the weather like in Seattle?"
|
||||
print(f"User: {query}")
|
||||
result = await agent.run(query)
|
||||
print(f"Result: {result}\n")
|
||||
|
||||
|
||||
async def streaming_example() -> None:
|
||||
"""Example of streaming response (get results as they are generated)."""
|
||||
print("=== Streaming Response Example ===")
|
||||
|
||||
# 1. Create the FoundryChatClient using a Foundry project endpoint.
|
||||
# For authentication, run `az login` command in terminal or replace AzureCliCredential with preferred
|
||||
# authentication option.
|
||||
credential = AzureCliCredential()
|
||||
_client = FoundryChatClient(
|
||||
project_endpoint=os.environ["FOUNDRY_PROJECT_ENDPOINT"],
|
||||
model=os.environ["FOUNDRY_MODEL"],
|
||||
credential=credential,
|
||||
)
|
||||
agent = Agent(
|
||||
client=_client,
|
||||
instructions="You are a helpful weather agent.",
|
||||
tools=get_weather,
|
||||
)
|
||||
|
||||
# 2. Stream the response and print each chunk as it arrives.
|
||||
query = "What's the weather like in Portland?"
|
||||
print(f"User: {query}")
|
||||
print("Agent: ", end="", flush=True)
|
||||
async for chunk in agent.run(query, stream=True):
|
||||
if chunk.text:
|
||||
print(chunk.text, end="", flush=True)
|
||||
print("\n")
|
||||
|
||||
|
||||
async def main() -> None:
|
||||
print("=== Foundry Chat Client with Project Endpoint Example ===")
|
||||
|
||||
await non_streaming_example()
|
||||
await streaming_example()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
|
||||
|
||||
"""
|
||||
Sample output:
|
||||
=== Foundry Chat Client with Project Endpoint Example ===
|
||||
=== Non-streaming Response Example ===
|
||||
User: What's the weather like in Seattle?
|
||||
Result: The weather in Seattle is cloudy with a high of 18°C.
|
||||
|
||||
=== Streaming Response Example ===
|
||||
User: What's the weather like in Portland?
|
||||
Agent: The weather in Portland is sunny with a high of 25°C.
|
||||
"""
|
||||
@@ -0,0 +1,86 @@
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
import asyncio
|
||||
from random import randint
|
||||
from typing import Annotated
|
||||
|
||||
from agent_framework import Agent, tool
|
||||
from agent_framework.foundry import FoundryChatClient
|
||||
from azure.identity import AzureCliCredential
|
||||
from dotenv import load_dotenv
|
||||
from pydantic import Field
|
||||
|
||||
# Load environment variables from .env file
|
||||
load_dotenv()
|
||||
|
||||
"""
|
||||
Foundry Chat Client Basic Example
|
||||
|
||||
This sample demonstrates basic usage of FoundryChatClient for structured
|
||||
response generation, showing both streaming and non-streaming responses.
|
||||
|
||||
This uses a deployed model in Foundry, with the Responses API endpoint of Foundry.
|
||||
The client has full support for tools, response formats, etc.
|
||||
"""
|
||||
|
||||
|
||||
# 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 non_streaming_example() -> None:
|
||||
"""Example of non-streaming response (get the complete result at once)."""
|
||||
print("=== Non-streaming Response Example ===")
|
||||
|
||||
# For authentication, run `az login` command in terminal or replace AzureCliCredential with preferred
|
||||
# authentication option.
|
||||
agent = Agent(
|
||||
client=FoundryChatClient(credential=AzureCliCredential()),
|
||||
instructions="You are a helpful weather agent.",
|
||||
tools=get_weather,
|
||||
)
|
||||
|
||||
query = "What's the weather like in Seattle?"
|
||||
print(f"User: {query}")
|
||||
result = await agent.run(query)
|
||||
print(f"Result: {result}\n")
|
||||
|
||||
|
||||
async def streaming_example() -> None:
|
||||
"""Example of streaming response (get results as they are generated)."""
|
||||
print("=== Streaming Response Example ===")
|
||||
|
||||
# For authentication, run `az login` command in terminal or replace AzureCliCredential with preferred
|
||||
# authentication option.
|
||||
agent = Agent(
|
||||
client=FoundryChatClient(credential=AzureCliCredential()),
|
||||
instructions="You are a helpful weather agent.",
|
||||
tools=get_weather,
|
||||
)
|
||||
|
||||
query = "What's the weather like in Portland?"
|
||||
print(f"User: {query}")
|
||||
print("Agent: ", end="", flush=True)
|
||||
async for chunk in agent.run(query, stream=True):
|
||||
if chunk.text:
|
||||
print(chunk.text, end="", flush=True)
|
||||
print("\n")
|
||||
|
||||
|
||||
async def main() -> None:
|
||||
print("=== Foundry Chat Client Basic Example ===")
|
||||
|
||||
await non_streaming_example()
|
||||
await streaming_example()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
+97
@@ -0,0 +1,97 @@
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
import asyncio
|
||||
import os
|
||||
import tempfile
|
||||
|
||||
from agent_framework import Agent
|
||||
from agent_framework.foundry import FoundryChatClient
|
||||
from azure.identity import AzureCliCredential
|
||||
from dotenv import load_dotenv
|
||||
from openai import AsyncOpenAI
|
||||
|
||||
load_dotenv()
|
||||
|
||||
"""
|
||||
Foundry Chat Client with Code Interpreter and Files Example
|
||||
|
||||
This sample demonstrates using get_code_interpreter_tool() with Responses on Foundry
|
||||
for Python code execution and data analysis with uploaded files.
|
||||
|
||||
Environment variables:
|
||||
FOUNDRY_PROJECT_ENDPOINT — Foundry project endpoint
|
||||
FOUNDRY_MODEL — Foundry model to use (e.g. "gpt-4o-mini")
|
||||
"""
|
||||
|
||||
# Helper functions
|
||||
|
||||
|
||||
async def create_sample_file_and_upload(openai_client: AsyncOpenAI) -> tuple[str, str]:
|
||||
"""Create a sample CSV file and upload it for Foundry code interpreter use."""
|
||||
csv_data = """name,department,salary,years_experience
|
||||
Alice Johnson,Engineering,95000,5
|
||||
Bob Smith,Sales,75000,3
|
||||
Carol Williams,Engineering,105000,8
|
||||
David Brown,Marketing,68000,2
|
||||
Emma Davis,Sales,82000,4
|
||||
Frank Wilson,Engineering,88000,6
|
||||
"""
|
||||
|
||||
# Create temporary CSV file
|
||||
with tempfile.NamedTemporaryFile(mode="w", suffix=".csv", delete=False) as temp_file:
|
||||
temp_file.write(csv_data)
|
||||
temp_file_path = temp_file.name
|
||||
|
||||
# Upload file for the code interpreter tool
|
||||
print("Uploading file for code interpreter...")
|
||||
with open(temp_file_path, "rb") as file:
|
||||
uploaded_file = await openai_client.files.create(
|
||||
file=file,
|
||||
purpose="assistants", # Required for code interpreter
|
||||
)
|
||||
|
||||
print(f"File uploaded with ID: {uploaded_file.id}")
|
||||
return temp_file_path, uploaded_file.id
|
||||
|
||||
|
||||
async def cleanup_files(openai_client: AsyncOpenAI, temp_file_path: str, file_id: str) -> None:
|
||||
"""Clean up both local temporary file and uploaded file."""
|
||||
# Clean up: delete the uploaded file
|
||||
await openai_client.files.delete(file_id)
|
||||
print(f"Cleaned up uploaded file: {file_id}")
|
||||
|
||||
# Clean up temporary local file
|
||||
os.unlink(temp_file_path)
|
||||
print(f"Cleaned up temporary file: {temp_file_path}")
|
||||
|
||||
|
||||
async def main() -> None:
|
||||
print("=== Foundry Chat Client with Code Interpreter and File Upload ===")
|
||||
|
||||
# Create the FoundryChatClient
|
||||
client = FoundryChatClient(
|
||||
project_endpoint=os.getenv("FOUNDRY_PROJECT_ENDPOINT"),
|
||||
model=os.getenv("FOUNDRY_MODEL"),
|
||||
credential=AzureCliCredential(),
|
||||
)
|
||||
# use the openai client from the foundry client to upload files for the code interpreter tool
|
||||
openai_client = getattr(client.project_client, "get_openai_client")() # noqa: B009
|
||||
temp_file_path, file_id = await create_sample_file_and_upload(openai_client)
|
||||
# Create agent with code interpreter tool with file access
|
||||
agent = Agent(
|
||||
client=client,
|
||||
instructions="You are a helpful assistant that can analyze data files using Python code.",
|
||||
tools=FoundryChatClient.get_code_interpreter_tool(file_ids=[file_id]),
|
||||
)
|
||||
try:
|
||||
# Test the code interpreter with the uploaded file
|
||||
query = "Analyze the employee data in the uploaded CSV file. Calculate average salary by department."
|
||||
print(f"User: {query}")
|
||||
result = await agent.run(query)
|
||||
print(f"Agent: {result.text}")
|
||||
finally:
|
||||
await cleanup_files(openai_client, temp_file_path, file_id)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
@@ -0,0 +1,44 @@
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
import asyncio
|
||||
|
||||
from agent_framework import Agent, Content
|
||||
from agent_framework.foundry import FoundryChatClient
|
||||
from azure.identity import AzureCliCredential
|
||||
from dotenv import load_dotenv
|
||||
|
||||
# Load environment variables from .env file
|
||||
load_dotenv()
|
||||
|
||||
"""
|
||||
Foundry Chat Client with Image Analysis Example
|
||||
|
||||
This sample demonstrates using FoundryChatClient for image analysis and vision tasks,
|
||||
showing multi-modal messages combining text and image content.
|
||||
"""
|
||||
|
||||
|
||||
async def main():
|
||||
print("=== Foundry Chat Client with Image Analysis ===")
|
||||
|
||||
# 1. Create a Foundry-backed agent with vision capabilities
|
||||
agent = Agent(
|
||||
client=FoundryChatClient(credential=AzureCliCredential()),
|
||||
name="VisionAgent",
|
||||
instructions="You are a image analysist, you get a image and need to respond with what you see in the picture.",
|
||||
)
|
||||
|
||||
# 2. Get the agent's response
|
||||
print("User: What do you see in this image? [Image provided]")
|
||||
result = await agent.run(
|
||||
Content.from_uri(
|
||||
uri="https://images.unsplash.com/photo-1506905925346-21bda4d32df4?w=800",
|
||||
media_type="image/jpeg",
|
||||
)
|
||||
)
|
||||
print(f"Agent: {result.text}")
|
||||
print()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
import asyncio
|
||||
|
||||
from agent_framework import Agent, ChatResponse
|
||||
from agent_framework.foundry import FoundryChatClient
|
||||
from azure.identity import AzureCliCredential
|
||||
from dotenv import load_dotenv
|
||||
from openai.types.responses.response import Response as OpenAIResponse
|
||||
from openai.types.responses.response_code_interpreter_tool_call import ResponseCodeInterpreterToolCall
|
||||
|
||||
# Load environment variables from .env file
|
||||
load_dotenv()
|
||||
|
||||
"""
|
||||
Foundry Chat Client with Code Interpreter Example
|
||||
|
||||
This sample demonstrates using get_code_interpreter_tool() with FoundryChatClient
|
||||
for Python code execution and mathematical problem solving.
|
||||
"""
|
||||
|
||||
|
||||
async def main() -> None:
|
||||
"""Example showing how to use the code interpreter tool with FoundryChatClient."""
|
||||
print("=== Foundry Chat Client with Code Interpreter Example ===")
|
||||
|
||||
# For authentication, run `az login` command in terminal or replace AzureCliCredential with preferred
|
||||
# authentication option.
|
||||
client = FoundryChatClient(credential=AzureCliCredential())
|
||||
|
||||
# Create code interpreter tool using instance method
|
||||
code_interpreter_tool = client.get_code_interpreter_tool()
|
||||
|
||||
agent = Agent(
|
||||
client=client,
|
||||
instructions="You are a helpful assistant that can write and execute Python code to solve problems.",
|
||||
tools=[code_interpreter_tool],
|
||||
)
|
||||
|
||||
query = "Use code to calculate the factorial of 100?"
|
||||
print(f"User: {query}")
|
||||
result = await agent.run(query)
|
||||
print(f"Result: {result}\n")
|
||||
|
||||
if (
|
||||
isinstance(result.raw_representation, ChatResponse)
|
||||
and isinstance(result.raw_representation.raw_representation, OpenAIResponse)
|
||||
and len(result.raw_representation.raw_representation.output) > 0
|
||||
and isinstance(result.raw_representation.raw_representation.output[0], ResponseCodeInterpreterToolCall)
|
||||
):
|
||||
generated_code = result.raw_representation.raw_representation.output[0].code
|
||||
|
||||
print(f"Generated code:\n{generated_code}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
import asyncio
|
||||
import os
|
||||
from random import randint
|
||||
from typing import Annotated
|
||||
|
||||
from agent_framework import Agent, tool
|
||||
from agent_framework.foundry import FoundryChatClient
|
||||
from azure.identity import AzureCliCredential
|
||||
from dotenv import load_dotenv
|
||||
from pydantic import Field
|
||||
|
||||
# Load environment variables from .env file
|
||||
load_dotenv()
|
||||
|
||||
"""
|
||||
Foundry Chat Client with Explicit Settings Example
|
||||
|
||||
This sample demonstrates creating FoundryChatClient with explicit project endpoint and
|
||||
model settings rather than relying on environment variable defaults.
|
||||
"""
|
||||
|
||||
|
||||
# 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:
|
||||
print("=== Foundry Chat Client with Explicit Settings ===")
|
||||
|
||||
# For authentication, run `az login` command in terminal or replace AzureCliCredential with preferred
|
||||
# authentication option.
|
||||
_client = FoundryChatClient(
|
||||
model=os.environ["FOUNDRY_MODEL"],
|
||||
project_endpoint=os.environ["FOUNDRY_PROJECT_ENDPOINT"],
|
||||
credential=AzureCliCredential(),
|
||||
)
|
||||
agent = Agent(
|
||||
client=_client,
|
||||
instructions="You are a helpful weather agent.",
|
||||
tools=[get_weather],
|
||||
)
|
||||
|
||||
result = await agent.run("What's the weather like in New York?")
|
||||
print(f"Result: {result}\n")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
@@ -0,0 +1,81 @@
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
import asyncio
|
||||
import contextlib
|
||||
|
||||
from agent_framework import Agent
|
||||
from agent_framework.foundry import FoundryChatClient
|
||||
from azure.identity import AzureCliCredential
|
||||
from dotenv import load_dotenv
|
||||
|
||||
# Load environment variables from .env file
|
||||
load_dotenv()
|
||||
|
||||
"""
|
||||
Foundry Chat Client with File Search Example
|
||||
|
||||
This sample demonstrates using get_file_search_tool() with FoundryChatClient
|
||||
for direct document-based question answering and information retrieval.
|
||||
|
||||
Prerequisites:
|
||||
- Set environment variables:
|
||||
- FOUNDRY_PROJECT_ENDPOINT: Your Foundry project endpoint URL
|
||||
- FOUNDRY_MODEL: Your Responses API deployment name
|
||||
- Authenticate via 'az login' for AzureCliCredential
|
||||
"""
|
||||
|
||||
# Helper functions
|
||||
|
||||
|
||||
async def create_vector_store(client: FoundryChatClient) -> tuple[str, str]:
|
||||
"""Create a vector store with sample documents."""
|
||||
file = await client.client.files.create(
|
||||
file=("todays_weather.txt", b"The weather today is sunny with a high of 75F."), purpose="assistants"
|
||||
)
|
||||
vector_store = await client.client.vector_stores.create(
|
||||
name="knowledge_base",
|
||||
expires_after={"anchor": "last_active_at", "days": 1},
|
||||
)
|
||||
result = await client.client.vector_stores.files.create_and_poll(vector_store_id=vector_store.id, file_id=file.id)
|
||||
if result.last_error is not None:
|
||||
raise Exception(f"Vector store file processing failed with status: {result.last_error.message}")
|
||||
|
||||
return file.id, vector_store.id
|
||||
|
||||
|
||||
async def delete_vector_store(client: FoundryChatClient, file_id: str, vector_store_id: str) -> None:
|
||||
"""Delete the vector store after using it."""
|
||||
with contextlib.suppress(Exception):
|
||||
await client.client.vector_stores.delete(vector_store_id=vector_store_id)
|
||||
with contextlib.suppress(Exception):
|
||||
await client.client.files.delete(file_id=file_id)
|
||||
|
||||
|
||||
async def main() -> None:
|
||||
print("=== Foundry Chat Client with File Search Example ===\n")
|
||||
|
||||
# Initialize the Foundry chat client
|
||||
# Make sure you're logged in via 'az login' before running this sample
|
||||
client = FoundryChatClient(credential=AzureCliCredential())
|
||||
|
||||
file_id, vector_store_id = await create_vector_store(client)
|
||||
|
||||
# Create file search tool using instance method
|
||||
file_search_tool = client.get_file_search_tool(vector_store_ids=[vector_store_id])
|
||||
|
||||
agent = Agent(
|
||||
client=client,
|
||||
instructions="You are a helpful assistant that can search through files to find information.",
|
||||
tools=[file_search_tool],
|
||||
)
|
||||
|
||||
query = "What is the weather today? Do a file search to find the answer."
|
||||
print(f"User: {query}")
|
||||
result = await agent.run(query)
|
||||
print(f"Agent: {result}\n")
|
||||
|
||||
await delete_vector_store(client, file_id, vector_store_id)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
+143
@@ -0,0 +1,143 @@
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
import asyncio
|
||||
from datetime import datetime, timezone
|
||||
from random import randint
|
||||
from typing import Annotated
|
||||
|
||||
from agent_framework import Agent, tool
|
||||
from agent_framework.foundry import FoundryChatClient
|
||||
from azure.identity import AzureCliCredential
|
||||
from dotenv import load_dotenv
|
||||
from pydantic import Field
|
||||
|
||||
# Load environment variables from .env file
|
||||
load_dotenv()
|
||||
|
||||
"""
|
||||
Foundry Chat Client with Function Tools Example
|
||||
|
||||
This sample demonstrates function tool integration with FoundryChatClient,
|
||||
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/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."
|
||||
|
||||
|
||||
@tool(approval_mode="never_require")
|
||||
def get_time() -> str:
|
||||
"""Get the current UTC time."""
|
||||
current_time = datetime.now(timezone.utc)
|
||||
return f"The current UTC time is {current_time.strftime('%Y-%m-%d %H:%M:%S')}."
|
||||
|
||||
|
||||
async def tools_on_agent_level() -> None:
|
||||
"""Example showing tools defined when creating the agent."""
|
||||
print("=== Tools Defined on Agent Level ===")
|
||||
|
||||
# Tools are provided when creating the agent
|
||||
# The agent can use these tools for any query during its lifetime
|
||||
# For authentication, run `az login` command in terminal or replace AzureCliCredential with preferred
|
||||
# authentication option.
|
||||
agent = Agent(
|
||||
client=FoundryChatClient(credential=AzureCliCredential()),
|
||||
instructions="You are a helpful assistant that can provide weather and time information.",
|
||||
tools=[get_weather, get_time], # Tools defined at agent creation
|
||||
)
|
||||
|
||||
# First query - agent can use weather tool
|
||||
query1 = "What's the weather like in New York?"
|
||||
print(f"User: {query1}")
|
||||
result1 = await agent.run(query1)
|
||||
print(f"Agent: {result1}\n")
|
||||
|
||||
# Second query - agent can use time tool
|
||||
query2 = "What's the current UTC time?"
|
||||
print(f"User: {query2}")
|
||||
result2 = await agent.run(query2)
|
||||
print(f"Agent: {result2}\n")
|
||||
|
||||
# Third query - agent can use both tools if needed
|
||||
query3 = "What's the weather in London and what's the current UTC time?"
|
||||
print(f"User: {query3}")
|
||||
result3 = await agent.run(query3)
|
||||
print(f"Agent: {result3}\n")
|
||||
|
||||
|
||||
async def tools_on_run_level() -> None:
|
||||
"""Example showing tools passed to the run method."""
|
||||
print("=== Tools Passed to Run Method ===")
|
||||
|
||||
# Agent created without tools
|
||||
# For authentication, run `az login` command in terminal or replace AzureCliCredential with preferred
|
||||
# authentication option.
|
||||
agent = Agent(
|
||||
client=FoundryChatClient(credential=AzureCliCredential()),
|
||||
instructions="You are a helpful assistant.",
|
||||
# No tools defined here
|
||||
)
|
||||
|
||||
# First query with weather tool
|
||||
query1 = "What's the weather like in Seattle?"
|
||||
print(f"User: {query1}")
|
||||
result1 = await agent.run(query1, tools=[get_weather]) # Tool passed to run method
|
||||
print(f"Agent: {result1}\n")
|
||||
|
||||
# Second query with time tool
|
||||
query2 = "What's the current UTC time?"
|
||||
print(f"User: {query2}")
|
||||
result2 = await agent.run(query2, tools=[get_time]) # Different tool for this query
|
||||
print(f"Agent: {result2}\n")
|
||||
|
||||
# Third query with multiple tools
|
||||
query3 = "What's the weather in Chicago and what's the current UTC time?"
|
||||
print(f"User: {query3}")
|
||||
result3 = await agent.run(query3, tools=[get_weather, get_time]) # Multiple tools
|
||||
print(f"Agent: {result3}\n")
|
||||
|
||||
|
||||
async def mixed_tools_example() -> None:
|
||||
"""Example showing both agent-level tools and run-method tools."""
|
||||
print("=== Mixed Tools Example (Agent + Run Method) ===")
|
||||
|
||||
# Agent created with some base tools
|
||||
# For authentication, run `az login` command in terminal or replace AzureCliCredential with preferred
|
||||
# authentication option.
|
||||
agent = Agent(
|
||||
client=FoundryChatClient(credential=AzureCliCredential()),
|
||||
instructions="You are a comprehensive assistant that can help with various information requests.",
|
||||
tools=[get_weather], # Base tool available for all queries
|
||||
)
|
||||
|
||||
# Query using both agent tool and additional run-method tools
|
||||
query = "What's the weather in Denver and what's the current UTC time?"
|
||||
print(f"User: {query}")
|
||||
|
||||
# Agent has access to get_weather (from creation) + additional tools from run method
|
||||
result = await agent.run(
|
||||
query,
|
||||
tools=[get_time], # Additional tools for this specific query
|
||||
)
|
||||
print(f"Agent: {result}\n")
|
||||
|
||||
|
||||
async def main() -> None:
|
||||
print("=== Foundry Chat Client with Function Tools Examples ===\n")
|
||||
|
||||
await tools_on_agent_level()
|
||||
await tools_on_run_level()
|
||||
await mixed_tools_example()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
@@ -0,0 +1,271 @@
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
import asyncio
|
||||
from typing import TYPE_CHECKING, Any
|
||||
|
||||
from agent_framework import Agent
|
||||
from agent_framework.foundry import FoundryChatClient
|
||||
from azure.identity import AzureCliCredential
|
||||
from dotenv import load_dotenv
|
||||
|
||||
# Load environment variables from .env file
|
||||
load_dotenv()
|
||||
|
||||
"""
|
||||
Foundry Chat Client with Hosted MCP Example
|
||||
|
||||
This sample demonstrates integrating hosted Model Context Protocol (MCP) tools with
|
||||
FoundryChatClient, including user approval workflows for function call security.
|
||||
"""
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from agent_framework import AgentSession
|
||||
|
||||
|
||||
async def handle_approvals_without_session(query: str, agent: Agent[Any]):
|
||||
"""When we don't have a session, we need to ensure we return with the input, approval request and approval."""
|
||||
from agent_framework import Message
|
||||
|
||||
result = await agent.run(query)
|
||||
while len(result.user_input_requests) > 0:
|
||||
new_inputs: list[Any] = [query]
|
||||
for user_input_needed in result.user_input_requests:
|
||||
if user_input_needed.function_call is None:
|
||||
continue
|
||||
print(
|
||||
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(Message(role="assistant", contents=[user_input_needed]))
|
||||
user_approval = input("Approve function call? (y/n): ")
|
||||
new_inputs.append(
|
||||
Message(
|
||||
role="user",
|
||||
contents=[user_input_needed.to_function_approval_response(user_approval.lower() == "y")],
|
||||
)
|
||||
)
|
||||
|
||||
result = await agent.run(new_inputs)
|
||||
return result
|
||||
|
||||
|
||||
async def handle_approvals_with_session(query: str, agent: Agent[Any], session: "AgentSession"):
|
||||
"""Here we let the session deal with the previous responses, and we just rerun with the approval."""
|
||||
from agent_framework import ChatOptions, Message
|
||||
|
||||
result = await agent.run(query, session=session, options=ChatOptions(store=True))
|
||||
while len(result.user_input_requests) > 0:
|
||||
new_input: list[Any] = []
|
||||
for user_input_needed in result.user_input_requests:
|
||||
if user_input_needed.function_call is None:
|
||||
continue
|
||||
print(
|
||||
f"User Input Request for function from {agent.name}: {user_input_needed.function_call.name}"
|
||||
f" with arguments: {user_input_needed.function_call.arguments}"
|
||||
)
|
||||
user_approval = input("Approve function call? (y/n): ")
|
||||
new_input.append(
|
||||
Message(
|
||||
role="user",
|
||||
contents=[user_input_needed.to_function_approval_response(user_approval.lower() == "y")],
|
||||
)
|
||||
)
|
||||
result = await agent.run(new_input, session=session, options=ChatOptions(store=True))
|
||||
return result
|
||||
|
||||
|
||||
async def handle_approvals_with_session_streaming(query: str, agent: Agent[Any], session: "AgentSession"):
|
||||
"""Here we let the session deal with the previous responses, and we just rerun with the approval."""
|
||||
from agent_framework import ChatOptions, Message
|
||||
|
||||
new_input: list[Message | str] = [query]
|
||||
new_input_added = True
|
||||
while new_input_added:
|
||||
new_input_added = False
|
||||
async for update in agent.run(new_input, session=session, options=ChatOptions(store=True), stream=True):
|
||||
if update.user_input_requests:
|
||||
# Reset input to only contain new approval responses for the next iteration
|
||||
new_input = []
|
||||
for user_input_needed in update.user_input_requests:
|
||||
if user_input_needed.function_call is None:
|
||||
continue
|
||||
print(
|
||||
f"User Input Request for function from {agent.name}: {user_input_needed.function_call.name}"
|
||||
f" with arguments: {user_input_needed.function_call.arguments}"
|
||||
)
|
||||
user_approval = input("Approve function call? (y/n): ")
|
||||
new_input.append(
|
||||
Message(
|
||||
role="user",
|
||||
contents=[user_input_needed.to_function_approval_response(user_approval.lower() == "y")],
|
||||
)
|
||||
)
|
||||
new_input_added = True
|
||||
else:
|
||||
yield update
|
||||
|
||||
|
||||
async def run_hosted_mcp_without_session_and_specific_approval() -> None:
|
||||
"""Example showing Mcp Tools with approvals without using a session."""
|
||||
print("=== Mcp with approvals and without session ===")
|
||||
credential = AzureCliCredential()
|
||||
client = FoundryChatClient(credential=credential)
|
||||
|
||||
# Create MCP tool with specific approval settings
|
||||
mcp_tool = client.get_mcp_tool(
|
||||
name="Microsoft Learn MCP",
|
||||
url="https://learn.microsoft.com/api/mcp",
|
||||
# we don't require approval for microsoft_docs_search tool calls
|
||||
# but we do for any other tool
|
||||
approval_mode={"never_require_approval": ["microsoft_docs_search"]},
|
||||
)
|
||||
|
||||
# Tools are provided when creating the agent
|
||||
# The agent can use these tools for any query during its lifetime
|
||||
async with Agent(
|
||||
client=client,
|
||||
name="DocsAgent",
|
||||
instructions="You are a helpful assistant that uses your MCP tool "
|
||||
"to help with microsoft documentation questions.",
|
||||
tools=[mcp_tool],
|
||||
) as agent:
|
||||
# First query
|
||||
query1 = "How to create an Azure storage account using az cli?"
|
||||
print(f"User: {query1}")
|
||||
result1 = await handle_approvals_without_session(query1, agent)
|
||||
print(f"{agent.name}: {result1}\n")
|
||||
print("\n=======================================\n")
|
||||
# Second query
|
||||
query2 = "What is Microsoft Agent Framework?"
|
||||
print(f"User: {query2}")
|
||||
result2 = await handle_approvals_without_session(query2, agent)
|
||||
print(f"{agent.name}: {result2}\n")
|
||||
|
||||
|
||||
async def run_hosted_mcp_without_approval() -> None:
|
||||
"""Example showing Mcp Tools without approvals."""
|
||||
print("=== Mcp without approvals ===")
|
||||
credential = AzureCliCredential()
|
||||
client = FoundryChatClient(credential=credential)
|
||||
|
||||
# Create MCP tool without approval requirements
|
||||
mcp_tool = client.get_mcp_tool(
|
||||
name="Microsoft Learn MCP",
|
||||
url="https://learn.microsoft.com/api/mcp",
|
||||
# we don't require approval for any function calls
|
||||
# this means we will not see the approval messages,
|
||||
# it is fully handled by the service and a final response is returned.
|
||||
approval_mode="never_require",
|
||||
)
|
||||
|
||||
# Tools are provided when creating the agent
|
||||
# The agent can use these tools for any query during its lifetime
|
||||
async with Agent(
|
||||
client=client,
|
||||
name="DocsAgent",
|
||||
instructions="You are a helpful assistant that uses your MCP tool "
|
||||
"to help with Microsoft documentation questions.",
|
||||
tools=[mcp_tool],
|
||||
) as agent:
|
||||
# First query
|
||||
query1 = "How to create an Azure storage account using az cli?"
|
||||
print(f"User: {query1}")
|
||||
result1 = await handle_approvals_without_session(query1, agent)
|
||||
print(f"{agent.name}: {result1}\n")
|
||||
print("\n=======================================\n")
|
||||
# Second query
|
||||
query2 = "What is Microsoft Agent Framework?"
|
||||
print(f"User: {query2}")
|
||||
result2 = await handle_approvals_without_session(query2, agent)
|
||||
print(f"{agent.name}: {result2}\n")
|
||||
|
||||
|
||||
async def run_hosted_mcp_with_session() -> None:
|
||||
"""Example showing Mcp Tools with approvals using a session."""
|
||||
print("=== Mcp with approvals and with session ===")
|
||||
credential = AzureCliCredential()
|
||||
client = FoundryChatClient(credential=credential)
|
||||
|
||||
# Create MCP tool with always require approval
|
||||
mcp_tool = client.get_mcp_tool(
|
||||
name="Microsoft Learn MCP",
|
||||
url="https://learn.microsoft.com/api/mcp",
|
||||
# we require approval for all function calls
|
||||
approval_mode="always_require",
|
||||
)
|
||||
|
||||
# Tools are provided when creating the agent
|
||||
# The agent can use these tools for any query during its lifetime
|
||||
async with Agent(
|
||||
client=client,
|
||||
name="DocsAgent",
|
||||
instructions="You are a helpful assistant that uses your MCP tool "
|
||||
"to help with microsoft documentation questions.",
|
||||
tools=[mcp_tool],
|
||||
) as agent:
|
||||
# First query
|
||||
session = agent.create_session()
|
||||
query1 = "How to create an Azure storage account using az cli?"
|
||||
print(f"User: {query1}")
|
||||
result1 = await handle_approvals_with_session(query1, agent, session)
|
||||
print(f"{agent.name}: {result1}\n")
|
||||
print("\n=======================================\n")
|
||||
# Second query
|
||||
query2 = "What is Microsoft Agent Framework?"
|
||||
print(f"User: {query2}")
|
||||
result2 = await handle_approvals_with_session(query2, agent, session)
|
||||
print(f"{agent.name}: {result2}\n")
|
||||
|
||||
|
||||
async def run_hosted_mcp_with_session_streaming() -> None:
|
||||
"""Example showing Mcp Tools with approvals using a session."""
|
||||
print("=== Mcp with approvals and with session ===")
|
||||
credential = AzureCliCredential()
|
||||
client = FoundryChatClient(credential=credential)
|
||||
|
||||
# Create MCP tool with always require approval
|
||||
mcp_tool = client.get_mcp_tool(
|
||||
name="Microsoft Learn MCP",
|
||||
url="https://learn.microsoft.com/api/mcp",
|
||||
# we require approval for all function calls
|
||||
approval_mode="always_require",
|
||||
)
|
||||
|
||||
# Tools are provided when creating the agent
|
||||
# The agent can use these tools for any query during its lifetime
|
||||
async with Agent(
|
||||
client=client,
|
||||
name="DocsAgent",
|
||||
instructions="You are a helpful assistant that uses your MCP tool "
|
||||
"to help with microsoft documentation questions.",
|
||||
tools=[mcp_tool],
|
||||
) as agent:
|
||||
# First query
|
||||
session = agent.create_session()
|
||||
query1 = "How to create an Azure storage account using az cli?"
|
||||
print(f"User: {query1}")
|
||||
print(f"{agent.name}: ", end="")
|
||||
async for update in handle_approvals_with_session_streaming(query1, agent, session):
|
||||
print(update, end="")
|
||||
print("\n")
|
||||
print("\n=======================================\n")
|
||||
# Second query
|
||||
query2 = "What is Microsoft Agent Framework?"
|
||||
print(f"User: {query2}")
|
||||
print(f"{agent.name}: ", end="")
|
||||
async for update in handle_approvals_with_session_streaming(query2, agent, session):
|
||||
print(update, end="")
|
||||
print("\n")
|
||||
|
||||
|
||||
async def main() -> None:
|
||||
print("=== Foundry Chat Client with Hosted MCP Examples ===\n")
|
||||
|
||||
await run_hosted_mcp_without_approval()
|
||||
await run_hosted_mcp_without_session_and_specific_approval()
|
||||
await run_hosted_mcp_with_session()
|
||||
await run_hosted_mcp_with_session_streaming()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
@@ -0,0 +1,66 @@
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
import asyncio
|
||||
import os
|
||||
|
||||
from agent_framework import Agent, MCPStreamableHTTPTool
|
||||
from agent_framework.foundry import FoundryChatClient
|
||||
from azure.identity import AzureCliCredential
|
||||
from dotenv import load_dotenv
|
||||
|
||||
# Load environment variables from .env file
|
||||
load_dotenv()
|
||||
|
||||
"""
|
||||
Foundry Chat Client with Local Model Context Protocol (MCP) Example
|
||||
|
||||
This sample demonstrates integration of FoundryChatClient with local Model Context Protocol (MCP)
|
||||
servers.
|
||||
"""
|
||||
|
||||
|
||||
# --- Below code uses Microsoft Learn MCP server over Streamable HTTP ---
|
||||
# --- Users can set these environment variables, or just edit the values below to their desired local MCP server
|
||||
MCP_NAME = os.environ.get("MCP_NAME", "Microsoft Learn MCP") # example name
|
||||
MCP_URL = os.environ.get("MCP_URL", "https://learn.microsoft.com/api/mcp") # example endpoint
|
||||
|
||||
# Environment variables for FoundryChatClient authentication
|
||||
# FOUNDRY_PROJECT_ENDPOINT="<your-foundry-project-endpoint>"
|
||||
# FOUNDRY_MODEL="<your-deployment-name>"
|
||||
|
||||
|
||||
async def main():
|
||||
"""Example showing local MCP tools for a Foundry Chat Client agent."""
|
||||
# AuthN: use Azure CLI
|
||||
credential = AzureCliCredential()
|
||||
|
||||
# Build an agent backed by FoundryChatClient
|
||||
# (project endpoint and model can also come from env vars above)
|
||||
responses_client = FoundryChatClient(
|
||||
credential=credential,
|
||||
)
|
||||
|
||||
agent: Agent = Agent(
|
||||
client=responses_client,
|
||||
name="DocsAgent",
|
||||
instructions=("You are a helpful assistant that can help with Microsoft documentation questions."),
|
||||
)
|
||||
|
||||
# Connect to the MCP server (Streamable HTTP)
|
||||
async with MCPStreamableHTTPTool(
|
||||
name=MCP_NAME,
|
||||
url=MCP_URL,
|
||||
) as mcp_tool:
|
||||
# First query — expect the agent to use the MCP tool if it helps
|
||||
first_query = "How to create an Azure storage account using az cli?"
|
||||
first_response = await agent.run(first_query, tools=mcp_tool)
|
||||
print("\n=== Answer 1 ===\n", first_response.text)
|
||||
|
||||
# Follow-up query (connection is reused)
|
||||
second_query = "What is Microsoft Agent Framework?"
|
||||
second_response = await agent.run(second_query, tools=mcp_tool)
|
||||
print("\n=== Answer 2 ===\n", second_response.text)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
@@ -0,0 +1,161 @@
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
import asyncio
|
||||
from random import randint
|
||||
from typing import Annotated
|
||||
|
||||
from agent_framework import Agent, AgentSession, InMemoryHistoryProvider, tool
|
||||
from agent_framework.foundry import FoundryChatClient
|
||||
from azure.identity import AzureCliCredential
|
||||
from dotenv import load_dotenv
|
||||
from pydantic import Field
|
||||
|
||||
# Load environment variables from .env file
|
||||
load_dotenv()
|
||||
|
||||
"""
|
||||
Foundry Chat Client with Session Management Example
|
||||
|
||||
This sample demonstrates session management with FoundryChatClient, comparing
|
||||
automatic session creation with explicit session management for persistent context.
|
||||
"""
|
||||
|
||||
|
||||
# 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 example_with_automatic_session_creation() -> None:
|
||||
"""Example showing automatic session creation (service-managed session)."""
|
||||
print("=== Automatic Session Creation Example ===")
|
||||
|
||||
# For authentication, run `az login` command in terminal or replace AzureCliCredential with preferred
|
||||
# authentication option.
|
||||
agent = Agent(
|
||||
client=FoundryChatClient(credential=AzureCliCredential()),
|
||||
instructions="You are a helpful weather agent.",
|
||||
tools=get_weather,
|
||||
)
|
||||
|
||||
# First conversation - no session provided, will be created automatically
|
||||
query1 = "What's the weather like in Seattle?"
|
||||
print(f"User: {query1}")
|
||||
result1 = await agent.run(query1)
|
||||
print(f"Agent: {result1.text}")
|
||||
|
||||
# Second conversation - still no session provided, will create another new session
|
||||
query2 = "What was the last city I asked about?"
|
||||
print(f"\nUser: {query2}")
|
||||
result2 = await agent.run(query2)
|
||||
print(f"Agent: {result2.text}")
|
||||
print("Note: Each call creates a separate session, so the agent doesn't remember previous context.\n")
|
||||
|
||||
|
||||
async def example_with_session_persistence() -> None:
|
||||
"""Example showing session persistence across multiple conversations."""
|
||||
print("=== Session Persistence Example ===")
|
||||
print("Using the same session across multiple conversations to maintain context.\n")
|
||||
|
||||
# For authentication, run `az login` command in terminal or replace AzureCliCredential with preferred
|
||||
# authentication option.
|
||||
agent = Agent(
|
||||
client=FoundryChatClient(credential=AzureCliCredential()),
|
||||
instructions="You are a helpful weather agent.",
|
||||
tools=get_weather,
|
||||
)
|
||||
|
||||
# Create a new session that will be reused
|
||||
session = agent.create_session()
|
||||
|
||||
# First conversation
|
||||
query1 = "What's the weather like in Tokyo?"
|
||||
print(f"User: {query1}")
|
||||
result1 = await agent.run(query1, session=session)
|
||||
print(f"Agent: {result1.text}")
|
||||
|
||||
# Second conversation using the same session - maintains context
|
||||
query2 = "How about London?"
|
||||
print(f"\nUser: {query2}")
|
||||
result2 = await agent.run(query2, session=session)
|
||||
print(f"Agent: {result2.text}")
|
||||
|
||||
# Third conversation - agent should remember both previous cities
|
||||
query3 = "Which of the cities I asked about has better weather?"
|
||||
print(f"\nUser: {query3}")
|
||||
result3 = await agent.run(query3, session=session)
|
||||
print(f"Agent: {result3.text}")
|
||||
print("Note: The agent remembers context from previous messages in the same session.\n")
|
||||
|
||||
|
||||
async def example_with_existing_session_messages() -> None:
|
||||
"""Example showing how to work with existing session messages for Foundry-backed agents."""
|
||||
print("=== Existing Session Messages Example ===")
|
||||
|
||||
# For authentication, run `az login` command in terminal or replace AzureCliCredential with preferred
|
||||
# authentication option.
|
||||
agent = Agent(
|
||||
client=FoundryChatClient(credential=AzureCliCredential()),
|
||||
instructions="You are a helpful weather agent.",
|
||||
tools=get_weather,
|
||||
)
|
||||
|
||||
# Start a conversation and build up message history
|
||||
session = agent.create_session()
|
||||
|
||||
query1 = "What's the weather in Paris?"
|
||||
print(f"User: {query1}")
|
||||
result1 = await agent.run(query1, session=session)
|
||||
print(f"Agent: {result1.text}")
|
||||
|
||||
# The session now contains the conversation history in state
|
||||
memory_state = session.state.get(InMemoryHistoryProvider.DEFAULT_SOURCE_ID, {})
|
||||
messages = memory_state.get("messages", [])
|
||||
if messages:
|
||||
print(f"Session contains {len(messages)} messages")
|
||||
|
||||
print("\n--- Continuing with the same session in a new agent instance ---")
|
||||
|
||||
# Create a new agent instance but use the existing session with its message history
|
||||
new_agent = Agent(
|
||||
client=FoundryChatClient(credential=AzureCliCredential()),
|
||||
instructions="You are a helpful weather agent.",
|
||||
tools=get_weather,
|
||||
)
|
||||
|
||||
# Use the same session object which contains the conversation history
|
||||
query2 = "What was the last city I asked about?"
|
||||
print(f"User: {query2}")
|
||||
result2 = await new_agent.run(query2, session=session)
|
||||
print(f"Agent: {result2.text}")
|
||||
print("Note: The agent continues the conversation using the local message history.\n")
|
||||
|
||||
print("\n--- Alternative: Creating a new session from existing messages ---")
|
||||
|
||||
# You can also create a new session from existing messages
|
||||
new_session = AgentSession()
|
||||
|
||||
query3 = "How does the Paris weather compare to London?"
|
||||
print(f"User: {query3}")
|
||||
result3 = await new_agent.run(query3, session=new_session)
|
||||
print(f"Agent: {result3.text}")
|
||||
print("Note: This creates a new session with the same conversation history.\n")
|
||||
|
||||
|
||||
async def main() -> None:
|
||||
print("=== Foundry Chat Client Session Management Examples ===\n")
|
||||
|
||||
await example_with_automatic_session_creation()
|
||||
await example_with_session_persistence()
|
||||
await example_with_existing_session_messages()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
@@ -0,0 +1,118 @@
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
import asyncio
|
||||
import os
|
||||
from collections.abc import Callable
|
||||
from typing import Any
|
||||
|
||||
from agent_framework import Agent, MCPStreamableHTTPTool
|
||||
from agent_framework.foundry import FoundryChatClient
|
||||
from azure.core.credentials import TokenCredential
|
||||
from azure.identity import AzureCliCredential, DefaultAzureCredential, get_bearer_token_provider
|
||||
from dotenv import load_dotenv
|
||||
|
||||
# Load environment variables from .env file
|
||||
load_dotenv()
|
||||
|
||||
"""
|
||||
Foundry Toolbox via MAF ``MCPStreamableHTTPTool``
|
||||
|
||||
Instead of fetching the toolbox and fanning out individual tool specs, point
|
||||
MAF's ``MCPStreamableHTTPTool`` at the toolbox's MCP endpoint. The agent
|
||||
discovers and calls the toolbox's tools over MCP at runtime.
|
||||
|
||||
Prerequisites:
|
||||
- A Microsoft Foundry project with a toolbox configured
|
||||
- FOUNDRY_PROJECT_ENDPOINT and FOUNDRY_MODEL environment variables set
|
||||
- FOUNDRY_TOOLBOX_ENDPOINT: the toolbox's MCP endpoint URL, e.g.
|
||||
``https://<account>.services.ai.azure.com/api/projects/<project>/toolsets/<name>/mcp?api-version=v1``
|
||||
- Azure CLI authentication (``az login``)
|
||||
"""
|
||||
|
||||
# Must match the ``<name>`` segment of FOUNDRY_TOOLBOX_ENDPOINT.
|
||||
TOOLBOX_NAME = "research_toolbox"
|
||||
|
||||
|
||||
def create_sample_toolbox(name: str) -> str:
|
||||
"""Create (or replace) a toolbox version in the Foundry project.
|
||||
|
||||
Toolboxes are normally configured in the Foundry portal or a deployment
|
||||
script, not the application itself. This helper exists so the sample can
|
||||
be run end-to-end without first setting a toolbox up by hand — delete any
|
||||
existing toolbox under ``name``, then create a fresh version containing a
|
||||
single MCP tool. Returns the created version identifier.
|
||||
"""
|
||||
from azure.ai.projects import AIProjectClient
|
||||
from azure.ai.projects.models import MCPTool, Tool
|
||||
from azure.core.exceptions import ResourceNotFoundError
|
||||
|
||||
with (
|
||||
AzureCliCredential() as credential,
|
||||
AIProjectClient(credential=credential, endpoint=os.environ["FOUNDRY_PROJECT_ENDPOINT"]) as project_client,
|
||||
):
|
||||
try:
|
||||
project_client.beta.toolboxes.delete(name)
|
||||
print(f"Toolbox `{name}` deleted")
|
||||
except ResourceNotFoundError:
|
||||
pass
|
||||
|
||||
tools: list[Tool] = [
|
||||
MCPTool(
|
||||
server_label="api_specs",
|
||||
server_url="https://gitmcp.io/Azure/azure-rest-api-specs",
|
||||
require_approval="never",
|
||||
)
|
||||
]
|
||||
|
||||
created = project_client.beta.toolboxes.create_version(
|
||||
name=name,
|
||||
description="Toolbox version with MCP require_approval set to 'never'.",
|
||||
tools=tools,
|
||||
)
|
||||
print(f"Created toolbox {created.name}@{created.version} ({len(created.tools)} tool(s))")
|
||||
return created.version
|
||||
|
||||
|
||||
def make_toolbox_header_provider(credential: TokenCredential) -> Callable[[dict[str, Any]], dict[str, str]]:
|
||||
"""Build a header_provider that injects a fresh Azure AI bearer token on every MCP request."""
|
||||
get_token = get_bearer_token_provider(credential, "https://ai.azure.com/.default")
|
||||
|
||||
def provide(_kwargs: dict[str, Any]) -> dict[str, str]:
|
||||
return {
|
||||
"Authorization": f"Bearer {get_token()}",
|
||||
}
|
||||
|
||||
return provide
|
||||
|
||||
|
||||
async def main() -> None:
|
||||
credential = DefaultAzureCredential()
|
||||
|
||||
# Comment out if the toolbox already exists in your Foundry project.
|
||||
create_sample_toolbox(TOOLBOX_NAME)
|
||||
|
||||
toolbox_tool = MCPStreamableHTTPTool(
|
||||
name="foundry_toolbox",
|
||||
description="Tools exposed by the configured Foundry toolbox",
|
||||
url=os.environ["FOUNDRY_TOOLBOX_ENDPOINT"],
|
||||
header_provider=make_toolbox_header_provider(credential),
|
||||
load_prompts=False,
|
||||
)
|
||||
|
||||
async with Agent(
|
||||
client=FoundryChatClient(
|
||||
project_endpoint=os.environ["FOUNDRY_PROJECT_ENDPOINT"],
|
||||
model=os.environ["FOUNDRY_MODEL"],
|
||||
credential=credential,
|
||||
),
|
||||
instructions="You are a helpful assistant. Use the available toolbox tools to answer the user.",
|
||||
tools=toolbox_tool,
|
||||
) as agent:
|
||||
query = "What tools do you have access to?"
|
||||
print(f"User: {query}")
|
||||
result = await agent.run(query)
|
||||
print(f"Assistant: {result}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
@@ -0,0 +1,88 @@
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
import asyncio
|
||||
import os
|
||||
from collections.abc import Generator
|
||||
|
||||
import httpx
|
||||
from agent_framework import Agent, MCPSkillsSource, SkillsProvider, ToolApprovalMiddleware
|
||||
from agent_framework.foundry import FoundryChatClient
|
||||
from azure.core.credentials import TokenCredential
|
||||
from azure.identity import AzureCliCredential, get_bearer_token_provider
|
||||
from dotenv import load_dotenv
|
||||
from mcp.client.session import ClientSession
|
||||
from mcp.client.streamable_http import streamable_http_client
|
||||
|
||||
# Load environment variables from .env file
|
||||
load_dotenv()
|
||||
|
||||
"""
|
||||
Foundry Chat Client with Toolbox-Hosted Skills
|
||||
|
||||
Discover Agent Skills served by a Microsoft Foundry Toolbox MCP endpoint
|
||||
and inject them into a ``FoundryChatClient`` agent via ``MCPSkillsSource``.
|
||||
The toolbox's discovery document (``skill://index.json``) is read once at
|
||||
startup; SKILL.md bodies are fetched on demand as the agent uses them.
|
||||
|
||||
Prerequisites:
|
||||
- A Microsoft Foundry project with a toolbox that exposes
|
||||
``skill://index.json`` with ``skill-md`` entries
|
||||
- FOUNDRY_PROJECT_ENDPOINT and FOUNDRY_MODEL environment variables set
|
||||
- FOUNDRY_TOOLBOX_MCP_SERVER_URL: the toolbox's MCP endpoint URL, e.g.
|
||||
``https://<account>.services.ai.azure.com/api/projects/<project>/toolboxes/<name>/mcp?api-version=v1``
|
||||
- Azure CLI authentication (``az login``)
|
||||
"""
|
||||
|
||||
|
||||
class _BearerAuth(httpx.Auth):
|
||||
"""Attach a fresh Foundry bearer token to every request."""
|
||||
|
||||
def __init__(self, credential: TokenCredential) -> None:
|
||||
self._get_token = get_bearer_token_provider(credential, "https://ai.azure.com/.default")
|
||||
|
||||
def auth_flow(self, request: httpx.Request) -> Generator[httpx.Request, httpx.Response, None]:
|
||||
request.headers["Authorization"] = f"Bearer {self._get_token()}"
|
||||
yield request
|
||||
|
||||
|
||||
async def main() -> None:
|
||||
"""Example showing toolbox-hosted MCP skills for a Foundry Chat Client agent."""
|
||||
credential = AzureCliCredential()
|
||||
|
||||
# HTTP client that signs every request with a fresh Foundry bearer token
|
||||
# and advertises the toolbox preview feature flag, plus the MCP streamable
|
||||
# HTTP transport that uses it.
|
||||
async with (
|
||||
httpx.AsyncClient(
|
||||
auth=_BearerAuth(credential),
|
||||
timeout=httpx.Timeout(30.0, read=300.0),
|
||||
follow_redirects=True,
|
||||
) as http_client,
|
||||
streamable_http_client(
|
||||
url=os.environ["FOUNDRY_TOOLBOX_MCP_SERVER_URL"],
|
||||
http_client=http_client,
|
||||
) as (read, write, _),
|
||||
ClientSession(read, write) as session,
|
||||
):
|
||||
await session.initialize()
|
||||
|
||||
# Discover skills served by the toolbox and inject them as a context provider.
|
||||
skills_provider = SkillsProvider(MCPSkillsSource(client=session))
|
||||
|
||||
async with Agent(
|
||||
client=FoundryChatClient(credential=credential),
|
||||
name="ToolboxMCPSkillsAgent",
|
||||
instructions="You are a helpful assistant. Use available skills to answer the user.",
|
||||
context_providers=[skills_provider],
|
||||
middleware=[ToolApprovalMiddleware(auto_approval_rules=[SkillsProvider.all_tools_auto_approval_rule])],
|
||||
) as agent:
|
||||
query = input("User: ").strip() # noqa: ASYNC250
|
||||
if not query:
|
||||
return
|
||||
session = agent.create_session()
|
||||
response = await agent.run(query, session=session)
|
||||
print(f"Assistant: {response.text}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
@@ -0,0 +1,79 @@
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
# ruff: noqa
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
from random import randint
|
||||
from typing import Annotated, Any
|
||||
|
||||
from agent_framework import Agent
|
||||
from agent_framework.foundry import FoundryLocalClient
|
||||
|
||||
"""
|
||||
This sample demonstrates basic usage of the FoundryLocalClient.
|
||||
Shows both streaming and non-streaming responses with function tools.
|
||||
|
||||
Running this sample the first time will be slow, as the model needs to be
|
||||
downloaded and initialized.
|
||||
|
||||
Also, not every model supports function calling, so be sure to check the
|
||||
model capabilities in the Foundry catalog, or pick one from the list printed
|
||||
when running this sample.
|
||||
"""
|
||||
|
||||
|
||||
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."
|
||||
|
||||
|
||||
async def non_streaming_example(agent: Agent[Any]) -> None:
|
||||
"""Example of non-streaming response (get the complete result at once)."""
|
||||
print("=== Non-streaming Response Example ===")
|
||||
|
||||
query = "What's the weather like in Seattle?"
|
||||
print(f"User: {query}")
|
||||
result = await agent.run(query)
|
||||
print(f"Agent: {result}\n")
|
||||
|
||||
|
||||
async def streaming_example(agent: Agent[Any]) -> None:
|
||||
"""Example of streaming response (get results as they are generated)."""
|
||||
print("=== Streaming Response Example ===")
|
||||
|
||||
query = "What's the weather like in Amsterdam?"
|
||||
print(f"User: {query}")
|
||||
print("Agent: ", end="", flush=True)
|
||||
async for chunk in agent.run(query, stream=True):
|
||||
if chunk.text:
|
||||
print(chunk.text, end="", flush=True)
|
||||
print("\n")
|
||||
|
||||
|
||||
async def main() -> None:
|
||||
print("=== Basic Foundry Local Client Agent Example ===")
|
||||
|
||||
client = FoundryLocalClient(model="phi-4-mini")
|
||||
print(f"Client Model ID: {client.model}\n")
|
||||
print("Other available models (tool calling supported only):")
|
||||
for model in client.manager.list_catalog_models():
|
||||
if model.supports_tool_calling:
|
||||
print(
|
||||
f"- {model.alias} for {model.task} - id={model.id} - {(model.file_size_mb / 1000):.2f} GB - {model.license}"
|
||||
)
|
||||
agent = Agent(
|
||||
client=client,
|
||||
name="LocalAgent",
|
||||
instructions="You are a helpful agent.",
|
||||
tools=get_weather,
|
||||
)
|
||||
await non_streaming_example(agent)
|
||||
await streaming_example(agent)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
@@ -0,0 +1,152 @@
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
import asyncio
|
||||
import os
|
||||
from random import randint
|
||||
from typing import Annotated
|
||||
|
||||
from agent_framework import Agent, tool
|
||||
from agent_framework.foundry import FoundryAgent, FoundryChatClient, to_prompt_agent
|
||||
from azure.ai.projects.aio import AIProjectClient
|
||||
from azure.identity.aio import AzureCliCredential
|
||||
from dotenv import load_dotenv
|
||||
from pydantic import Field
|
||||
|
||||
load_dotenv()
|
||||
|
||||
"""
|
||||
Foundry Prompt Agent: Convert, Publish, Connect, and Run
|
||||
|
||||
This sample shows the end-to-end loop:
|
||||
|
||||
1. Build an ``Agent`` backed by ``FoundryChatClient`` with a local ``@tool``
|
||||
function and Foundry-hosted tools.
|
||||
2. Run the local ``Agent`` directly against the Foundry Responses API.
|
||||
3. Convert it with ``to_prompt_agent(agent)`` and publish via
|
||||
``AIProjectClient.agents.create_version(...)``.
|
||||
4. Connect to the deployed prompt agent with ``FoundryAgent`` and pass the
|
||||
*same* ``book_hotel`` callable through ``tools=`` so the server-side prompt
|
||||
agent and the client share a single tool definition.
|
||||
|
||||
The Foundry prompt agent only receives the ``book_hotel`` *declaration* (its
|
||||
JSON schema). When the deployed agent decides to call the tool, ``FoundryAgent``
|
||||
executes the local Python implementation by matching tool names — keeping the
|
||||
schema on the server and the implementation on the client in sync.
|
||||
|
||||
Local ``Agent`` vs deployed prompt agent — compare & contrast when calling
|
||||
``run`` on each:
|
||||
|
||||
* **Runtime / latency.** ``Agent.run`` issues a single ``responses.create``
|
||||
call against the Foundry Responses API. ``FoundryAgent.run`` against a
|
||||
published prompt agent goes through the Foundry Agents service, which
|
||||
resolves the stored ``PromptAgentDefinition`` (instructions, tools,
|
||||
generation parameters, RAI config) on every call before forwarding to the
|
||||
model. Expect a small per-call overhead on the deployed path in exchange
|
||||
for centrally managed configuration.
|
||||
* **Configurability.** With the local ``Agent``, model, instructions, tools,
|
||||
``default_options``, etc. live in your process — change them, restart, and
|
||||
the next ``run`` picks them up. With the deployed prompt agent, those same
|
||||
fields are versioned server-side: publishing a new version updates every
|
||||
consumer at once and you keep an audit trail of previous versions, but you
|
||||
must call ``create_version`` (or pin ``agent_version``) to roll changes
|
||||
out or back.
|
||||
* **Persistence / sharing.** A local ``Agent`` instance only exists for the
|
||||
lifetime of the process that created it; tools and instructions are not
|
||||
discoverable by anything else. A published prompt agent is a first-class
|
||||
Foundry resource — other services, other languages, and the Foundry portal
|
||||
can all bind to it by ``agent_name`` (+ optional ``agent_version``) and get
|
||||
the same behaviour. Local ``@tool`` callables stay on the client; only
|
||||
their JSON schema is persisted, so the implementation must be supplied
|
||||
again at connection time via ``FoundryAgent(tools=[...])``.
|
||||
|
||||
``to_prompt_agent`` is experimental
|
||||
(``ExperimentalFeature.TO_PROMPT_AGENT``) and may change before being released.
|
||||
"""
|
||||
|
||||
|
||||
@tool
|
||||
def book_hotel(
|
||||
city: Annotated[str, Field(description="The city to book the hotel in.")],
|
||||
nights: Annotated[int, Field(description="Number of nights to stay.")],
|
||||
) -> str:
|
||||
"""Book a hotel room for the given city and number of nights."""
|
||||
return f"Booked a hotel in {city} for {nights} nights. Confirmation #CTX-{randint(1000, 9999)}."
|
||||
|
||||
|
||||
async def main() -> None:
|
||||
print("=== Foundry Prompt Agent: Convert, Publish, Connect, and Run ===\n")
|
||||
|
||||
project_endpoint = os.environ["FOUNDRY_PROJECT_ENDPOINT"]
|
||||
model = os.environ["FOUNDRY_MODEL"]
|
||||
|
||||
# Use ``async with`` so the credential and project client are closed even if the
|
||||
# body below raises. The ``try/finally`` around ``delete`` further guarantees we
|
||||
# don't leave an orphaned prompt agent in the Foundry project after a failure.
|
||||
async with (
|
||||
AzureCliCredential() as credential,
|
||||
AIProjectClient(endpoint=project_endpoint, credential=credential) as project_client,
|
||||
):
|
||||
# 1) Define the Agent. `name` / `description` set here become the Foundry agent identity
|
||||
# on publish; `book_hotel` is the local implementation that backs the published declaration.
|
||||
agent = Agent(
|
||||
client=FoundryChatClient(
|
||||
project_endpoint=project_endpoint,
|
||||
model=model,
|
||||
credential=credential,
|
||||
),
|
||||
name="travel-agent",
|
||||
description="Helps Contoso employees book travel.",
|
||||
instructions="You are a helpful travel assistant. Use the booking tool when asked.",
|
||||
tools=[
|
||||
FoundryChatClient.get_web_search_tool(),
|
||||
book_hotel,
|
||||
],
|
||||
default_options={"reasoning": {"effort": "medium"}},
|
||||
)
|
||||
|
||||
query = "Book me a hotel in Seattle for 3 nights."
|
||||
|
||||
# 2) Run the local Agent. This calls the Foundry Responses API directly — instructions,
|
||||
# tools, and generation parameters live in this process only.
|
||||
print(f"User (local Agent): {query}")
|
||||
local_result = await agent.run(query)
|
||||
print(f"Local Agent: {local_result}\n")
|
||||
|
||||
# 3) Convert and publish. The version returned by Foundry includes the version label
|
||||
# we need when connecting back to that specific deployment.
|
||||
if agent.name is None:
|
||||
raise ValueError("Agent name is required to create a prompt agent version.")
|
||||
created = await project_client.agents.create_version(
|
||||
agent_name=agent.name,
|
||||
# note this line:
|
||||
definition=to_prompt_agent(agent),
|
||||
description=agent.description,
|
||||
)
|
||||
print(f"Published prompt agent: {created.name} v{created.version}\n")
|
||||
|
||||
try:
|
||||
# 4) Connect to the deployed prompt agent with FoundryAgent and pass the *same* callable
|
||||
# tool. FoundryAgent runs the local function when the server-side agent invokes the tool,
|
||||
# matching by name. Compared to step 2, instructions/tools/generation parameters now
|
||||
# come from the stored PromptAgentDefinition rather than this process.
|
||||
deployed = FoundryAgent(
|
||||
project_endpoint=project_endpoint,
|
||||
agent_name=created.name,
|
||||
agent_version=created.version,
|
||||
credential=credential,
|
||||
tools=[book_hotel],
|
||||
)
|
||||
|
||||
print(f"User (deployed agent): {query}")
|
||||
deployed_result = await deployed.run(query)
|
||||
print(f"Deployed Agent: {deployed_result}")
|
||||
finally:
|
||||
# 5) Cleanup: delete the deployed prompt agent (and all its versions) even if step 4
|
||||
# raised, so re-running the sample stays idempotent and we don't leak resources in
|
||||
# the Foundry project.
|
||||
await project_client.agents.delete(agent_name=created.name)
|
||||
print(f"\nDeleted prompt agent {created.name!r} and all its versions.")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
Reference in New Issue
Block a user